forked from teamnwah/openmw-tes3coop
Merge remote-tracking branch 'upstream/master' into terrain
This commit is contained in:
commit
406135ee96
66 changed files with 1225 additions and 692 deletions
31
.travis.yml
31
.travis.yml
|
@ -1,37 +1,26 @@
|
|||
os:
|
||||
- linux
|
||||
- osx
|
||||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /openmw-.*$/
|
||||
before_install:
|
||||
- pwd
|
||||
- echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||
- echo "yes" | sudo apt-add-repository ppa:openmw/openmw
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libgtest-dev google-mock
|
||||
- sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libboost-wave-dev
|
||||
- sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
|
||||
- sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
|
||||
- sudo mkdir /usr/src/gtest/build
|
||||
- cd /usr/src/gtest/build
|
||||
- sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||
- sudo make -j4
|
||||
- sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
||||
- sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi
|
||||
before_script:
|
||||
- cd -
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_script.linux.sh; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
|
||||
script:
|
||||
- cd ./build
|
||||
- make -j4
|
||||
after_script:
|
||||
- ./openmw_test_suite
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
||||
notifications:
|
||||
recipients:
|
||||
- lgromanowski+travis.ci@gmail.com
|
||||
- corrmage+travis-ci@gmail.com
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
|
18
CI/before_install.linux.sh
Executable file
18
CI/before_install.linux.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
export CXX=g++
|
||||
export CC=gcc
|
||||
|
||||
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||
echo "yes" | sudo apt-add-repository ppa:openmw/openmw
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq libgtest-dev google-mock
|
||||
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev libboost-wave-dev
|
||||
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
|
||||
sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
|
||||
sudo mkdir /usr/src/gtest/build
|
||||
cd /usr/src/gtest/build
|
||||
sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||
sudo make -j4
|
||||
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
||||
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
9
CI/before_install.osx.sh
Executable file
9
CI/before_install.osx.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
export CXX=clang++
|
||||
export CC=clang
|
||||
|
||||
brew tap openmw/openmw
|
||||
brew update
|
||||
brew unlink boost
|
||||
brew install cmake openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg pkg-config qt unshield
|
5
CI/before_script.linux.sh
Executable file
5
CI/before_script.linux.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
|
5
CI/before_script.osx.sh
Executable file
5
CI/before_script.osx.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_FRAMEWORK_PATH="/usr/local/lib/macosx/Release" -DCMAKE_EXE_LINKER_FLAGS="-F/usr/local/lib/macosx/Release" -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" -DCMAKE_BUILD_TYPE=Debug -DBUILD_MYGUI_PLUGIN=OFF -G"Unix Makefiles" ..
|
|
@ -26,7 +26,7 @@ opencs_units (model/world
|
|||
|
||||
opencs_units_noqt (model/world
|
||||
universalid record commands columnbase scriptcontext cell refidcollection
|
||||
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
|
||||
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope landtexture land
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/world
|
||||
|
@ -76,7 +76,7 @@ opencs_units (view/widget
|
|||
|
||||
opencs_units (view/render
|
||||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||
previewwidget
|
||||
previewwidget terrainstorage
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/render
|
||||
|
|
|
@ -539,6 +539,16 @@ CSMWorld::IdCollection<ESM::DebugProfile>& CSMWorld::Data::getDebugProfiles()
|
|||
return mDebugProfiles;
|
||||
}
|
||||
|
||||
const CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand() const
|
||||
{
|
||||
return mLand;
|
||||
}
|
||||
|
||||
const CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures() const
|
||||
{
|
||||
return mLandTextures;
|
||||
}
|
||||
|
||||
const CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) const
|
||||
{
|
||||
return mResourcesManager.get (id.getType());
|
||||
|
@ -573,8 +583,11 @@ void CSMWorld::Data::merge()
|
|||
|
||||
int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project)
|
||||
{
|
||||
delete mReader;
|
||||
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
|
||||
boost::shared_ptr<ESM::ESMReader> ptr(mReader);
|
||||
mReaders.push_back(ptr);
|
||||
mReader = 0;
|
||||
|
||||
mDialogue = 0;
|
||||
mRefLoadCache.clear();
|
||||
|
||||
|
@ -598,8 +611,11 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
|
|||
|
||||
if (!mReader->hasMoreRecs())
|
||||
{
|
||||
delete mReader;
|
||||
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
|
||||
boost::shared_ptr<ESM::ESMReader> ptr(mReader);
|
||||
mReaders.push_back(ptr);
|
||||
mReader = 0;
|
||||
|
||||
mDialogue = 0;
|
||||
mRefLoadCache.clear();
|
||||
return true;
|
||||
|
@ -626,6 +642,9 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages)
|
|||
case ESM::REC_ENCH: mEnchantments.load (*mReader, mBase); break;
|
||||
case ESM::REC_BODY: mBodyParts.load (*mReader, mBase); break;
|
||||
|
||||
case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break;
|
||||
case ESM::REC_LAND: mLand.load(*mReader, mBase); break;
|
||||
|
||||
case ESM::REC_CELL:
|
||||
{
|
||||
mCells.load (*mReader, mBase);
|
||||
|
@ -775,7 +794,9 @@ bool CSMWorld::Data::hasId (const std::string& id) const
|
|||
getCells().searchId (id)!=-1 ||
|
||||
getEnchantments().searchId (id)!=-1 ||
|
||||
getBodyParts().searchId (id)!=-1 ||
|
||||
getReferenceables().searchId (id)!=-1;
|
||||
getReferenceables().searchId (id)!=-1 ||
|
||||
getLand().searchId (id) != -1 ||
|
||||
getLandTextures().searchId (id) != -1;
|
||||
}
|
||||
|
||||
int CSMWorld::Data::count (RecordBase::State state) const
|
||||
|
@ -795,7 +816,9 @@ int CSMWorld::Data::count (RecordBase::State state) const
|
|||
count (state, mCells) +
|
||||
count (state, mEnchantments) +
|
||||
count (state, mBodyParts) +
|
||||
count (state, mReferenceables);
|
||||
count (state, mReferenceables) +
|
||||
count (state, mLand) +
|
||||
count (state, mLandTextures);
|
||||
}
|
||||
|
||||
void CSMWorld::Data::setDescription (const std::string& description)
|
||||
|
@ -838,6 +861,8 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
|
|||
appendIds (ids, mEnchantments, listDeleted);
|
||||
appendIds (ids, mBodyParts, listDeleted);
|
||||
appendIds (ids, mReferenceables, listDeleted);
|
||||
appendIds (ids, mLand, listDeleted);
|
||||
appendIds (ids, mLandTextures, listDeleted);
|
||||
|
||||
std::sort (ids.begin(), ids.end());
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "idcollection.hpp"
|
||||
#include "universalid.hpp"
|
||||
#include "cell.hpp"
|
||||
#include "land.hpp"
|
||||
#include "landtexture.hpp"
|
||||
#include "refidcollection.hpp"
|
||||
#include "refcollection.hpp"
|
||||
#include "infocollection.hpp"
|
||||
|
@ -74,6 +76,8 @@ namespace CSMWorld
|
|||
InfoCollection mTopicInfos;
|
||||
InfoCollection mJournalInfos;
|
||||
IdCollection<Cell> mCells;
|
||||
IdCollection<LandTexture> mLandTextures;
|
||||
IdCollection<Land> mLand;
|
||||
RefIdCollection mReferenceables;
|
||||
RefCollection mRefs;
|
||||
IdCollection<ESM::Filter> mFilters;
|
||||
|
@ -88,6 +92,8 @@ namespace CSMWorld
|
|||
bool mProject;
|
||||
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
|
||||
|
||||
std::vector<boost::shared_ptr<ESM::ESMReader> > mReaders;
|
||||
|
||||
// not implemented
|
||||
Data (const Data&);
|
||||
Data& operator= (const Data&);
|
||||
|
@ -195,6 +201,10 @@ namespace CSMWorld
|
|||
|
||||
IdCollection<ESM::DebugProfile>& getDebugProfiles();
|
||||
|
||||
const IdCollection<CSMWorld::Land>& getLand() const;
|
||||
|
||||
const IdCollection<CSMWorld::LandTexture>& getLandTextures() const;
|
||||
|
||||
/// Throws an exception, if \a id does not match a resources list.
|
||||
const Resources& getResources (const UniversalId& id) const;
|
||||
|
||||
|
@ -247,4 +257,4 @@ namespace CSMWorld
|
|||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -29,7 +29,7 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
|
|||
|
||||
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
|
||||
{
|
||||
if (role!=Qt::DisplayRole && role!=Qt::EditRole)
|
||||
if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0)
|
||||
return QVariant();
|
||||
|
||||
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
|
||||
|
@ -229,4 +229,4 @@ bool CSMWorld::IdTable::isDeleted (const std::string& id) const
|
|||
int CSMWorld::IdTable::getColumnId(int column) const
|
||||
{
|
||||
return mIdCollection->getColumn(column).getId();
|
||||
}
|
||||
}
|
||||
|
|
28
apps/opencs/model/world/land.cpp
Normal file
28
apps/opencs/model/world/land.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "land.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
||||
Land::Land()
|
||||
{
|
||||
mLand.reset(new ESM::Land());
|
||||
}
|
||||
|
||||
void Land::load(ESM::ESMReader &esm)
|
||||
{
|
||||
mLand->load(esm);
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "#" << mLand->mX << " " << mLand->mY;
|
||||
|
||||
mId = stream.str();
|
||||
}
|
||||
|
||||
void Land::blank()
|
||||
{
|
||||
/// \todo
|
||||
}
|
||||
|
||||
}
|
29
apps/opencs/model/world/land.hpp
Normal file
29
apps/opencs/model/world/land.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef CSM_WORLD_LAND_H
|
||||
#define CSM_WORLD_LAND_H
|
||||
|
||||
#include <string>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <components/esm/loadland.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
/// \brief Wrapper for Land record. Encodes X and Y cell index in the ID.
|
||||
///
|
||||
/// \todo Add worldspace support to the Land record.
|
||||
/// \todo Add a proper copy constructor (currently worked around using shared_ptr)
|
||||
struct Land
|
||||
{
|
||||
Land();
|
||||
|
||||
boost::shared_ptr<ESM::Land> mLand;
|
||||
|
||||
std::string mId;
|
||||
|
||||
/// Loads the metadata and ID
|
||||
void load (ESM::ESMReader &esm);
|
||||
|
||||
void blank();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
21
apps/opencs/model/world/landtexture.cpp
Normal file
21
apps/opencs/model/world/landtexture.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include "landtexture.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
||||
void LandTexture::load(ESM::ESMReader &esm)
|
||||
{
|
||||
ESM::LandTexture::load(esm);
|
||||
|
||||
int plugin = esm.getIndex();
|
||||
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << mIndex << "_" << plugin;
|
||||
|
||||
mId = stream.str();
|
||||
}
|
||||
|
||||
}
|
22
apps/opencs/model/world/landtexture.hpp
Normal file
22
apps/opencs/model/world/landtexture.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef CSM_WORLD_LANDTEXTURE_H
|
||||
#define CSM_WORLD_LANDTEXTURE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/loadltex.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
/// \brief Wrapper for LandTexture record. Encodes mIndex and the plugin index (obtained from ESMReader)
|
||||
/// in the ID.
|
||||
///
|
||||
/// \attention The mId field of the ESM::LandTexture struct is not used.
|
||||
struct LandTexture : public ESM::LandTexture
|
||||
{
|
||||
std::string mId;
|
||||
|
||||
void load (ESM::ESMReader &esm);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -10,6 +10,9 @@
|
|||
#include "../../model/world/columns.hpp"
|
||||
#include "../../model/world/data.hpp"
|
||||
|
||||
#include "elements.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
|
||||
bool CSVRender::Cell::removeObject (const std::string& id)
|
||||
{
|
||||
std::map<std::string, Object *>::iterator iter =
|
||||
|
@ -67,6 +70,18 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
|
|||
int rows = references.rowCount();
|
||||
|
||||
addObjects (0, rows-1);
|
||||
|
||||
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
|
||||
int landIndex = land.searchId(mId);
|
||||
if (landIndex != -1)
|
||||
{
|
||||
mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true,
|
||||
Terrain::Align_XY));
|
||||
|
||||
const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get();
|
||||
mTerrain->loadCell(esmLand->mX,
|
||||
esmLand->mY);
|
||||
}
|
||||
}
|
||||
|
||||
CSVRender::Cell::~Cell()
|
||||
|
@ -198,4 +213,4 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
|
|||
return false;
|
||||
|
||||
return addObjects (start, end);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include <components/terrain/terraingrid.hpp>
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
class QModelIndex;
|
||||
|
@ -29,6 +31,7 @@ namespace CSVRender
|
|||
std::string mId;
|
||||
Ogre::SceneNode *mCellNode;
|
||||
std::map<std::string, Object *> mObjects;
|
||||
std::auto_ptr<Terrain::TerrainGrid> mTerrain;
|
||||
|
||||
/// Ignored if cell does not have an object with the given ID.
|
||||
///
|
||||
|
|
|
@ -110,7 +110,7 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
|
|||
{
|
||||
int index = cells.searchId (iter->getId (mWorldspace));
|
||||
|
||||
if (index!=0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted &&
|
||||
if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted &&
|
||||
mCells.find (*iter)==mCells.end())
|
||||
{
|
||||
if (setCamera)
|
||||
|
|
43
apps/opencs/view/render/terrainstorage.cpp
Normal file
43
apps/opencs/view/render/terrainstorage.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "terrainstorage.hpp"
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
TerrainStorage::TerrainStorage(const CSMWorld::Data &data)
|
||||
: mData(data)
|
||||
{
|
||||
}
|
||||
|
||||
ESM::Land* TerrainStorage::getLand(int cellX, int cellY)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "#" << cellX << " " << cellY;
|
||||
|
||||
// The cell isn't guaranteed to have Land. This is because the terrain implementation
|
||||
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
|
||||
int index = mData.getLand().searchId(stream.str());
|
||||
if (index == -1)
|
||||
return NULL;
|
||||
|
||||
ESM::Land* land = mData.getLand().getRecord(index).get().mLand.get();
|
||||
int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX;
|
||||
if (!land->isDataLoaded(mask))
|
||||
land->loadData(mask);
|
||||
return land;
|
||||
}
|
||||
|
||||
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << index << "_" << plugin;
|
||||
|
||||
return &mData.getLandTextures().getRecord(stream.str()).get();
|
||||
}
|
||||
|
||||
void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY)
|
||||
{
|
||||
// not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells
|
||||
throw std::runtime_error("getBounds not implemented");
|
||||
}
|
||||
|
||||
}
|
29
apps/opencs/view/render/terrainstorage.hpp
Normal file
29
apps/opencs/view/render/terrainstorage.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef OPENCS_RENDER_TERRAINSTORAGE_H
|
||||
#define OPENCS_RENDER_TERRAINSTORAGE_H
|
||||
|
||||
#include <components/esmterrain/storage.hpp>
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A bridge between the terrain component and OpenCS's terrain data storage.
|
||||
*/
|
||||
class TerrainStorage : public ESMTerrain::Storage
|
||||
{
|
||||
public:
|
||||
TerrainStorage(const CSMWorld::Data& data);
|
||||
private:
|
||||
const CSMWorld::Data& mData;
|
||||
|
||||
virtual ESM::Land* getLand (int cellX, int cellY);
|
||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
|
||||
|
||||
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -478,7 +478,7 @@ void OMW::Engine::go()
|
|||
}
|
||||
|
||||
// Start the main rendering loop
|
||||
while (!mEnvironment.get().getStateManager()->hasQuitRequest())
|
||||
while (!MWBase::Environment::get().getStateManager()->hasQuitRequest())
|
||||
Ogre::Root::getSingleton().renderOneFrame();
|
||||
|
||||
// Save user settings
|
||||
|
|
|
@ -541,6 +541,8 @@ namespace MWBase
|
|||
|
||||
/// Resets all actors in the current active cells to their original location within that cell.
|
||||
virtual void resetActors() = 0;
|
||||
|
||||
virtual bool isWalkingOnWater (const MWWorld::Ptr& actor) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ namespace MWClass
|
|||
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
|
||||
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
|
||||
MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();
|
||||
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
|
||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
|
||||
if (!weapon.isEmpty())
|
||||
fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult;
|
||||
|
@ -537,7 +537,7 @@ namespace MWClass
|
|||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
|
||||
|
||||
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
|
||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||
|
||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
|
||||
|
@ -738,7 +738,7 @@ namespace MWClass
|
|||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return 2;
|
||||
if(world->isOnGround(ptr))
|
||||
return 0;
|
||||
|
@ -748,7 +748,7 @@ namespace MWClass
|
|||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return 3;
|
||||
if(world->isOnGround(ptr))
|
||||
return 1;
|
||||
|
|
|
@ -302,11 +302,11 @@ namespace MWClass
|
|||
Misc::StringUtils::toLower(faction);
|
||||
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt52.mRank;
|
||||
data->mNpcStats.setFactionRank(faction, (int)ref->mBase->mNpdt52.mRank);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt12.mRank;
|
||||
data->mNpcStats.setFactionRank(faction, (int)ref->mBase->mNpdt12.mRank);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -495,7 +495,7 @@ namespace MWClass
|
|||
const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat();
|
||||
const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat();
|
||||
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
|
||||
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
|
||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
|
||||
if (!weapon.isEmpty())
|
||||
fatigueLoss += weapon.getClass().getWeight(weapon) * getNpcStats(ptr).getAttackStrength() * fWeaponFatigueMult;
|
||||
|
@ -911,7 +911,7 @@ namespace MWClass
|
|||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||
|
||||
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);
|
||||
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
|
||||
|
||||
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
|
||||
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
|
@ -927,7 +927,7 @@ namespace MWClass
|
|||
gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
|
||||
|
||||
float moveSpeed;
|
||||
if(normalizedEncumbrance >= 1.0f)
|
||||
if(getEncumbrance(ptr) > getCapacity(ptr))
|
||||
moveSpeed = 0.0f;
|
||||
else if(mageffects.get(ESM::MagicEffect::Levitate).getMagnitude() > 0 &&
|
||||
world->isLevitationEnabled())
|
||||
|
@ -1222,7 +1222,7 @@ namespace MWClass
|
|||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isSwimming(ptr))
|
||||
return "Swim Left";
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return "FootWaterLeft";
|
||||
if(world->isOnGround(ptr))
|
||||
{
|
||||
|
@ -1249,7 +1249,7 @@ namespace MWClass
|
|||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isSwimming(ptr))
|
||||
return "Swim Right";
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return "FootWaterRight";
|
||||
if(world->isOnGround(ptr))
|
||||
{
|
||||
|
@ -1274,7 +1274,7 @@ namespace MWClass
|
|||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return "DefaultLandWater";
|
||||
if(world->isOnGround(ptr))
|
||||
return "Body Fall Medium";
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace MWClass
|
|||
|
||||
virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const;
|
||||
///< Return item max health or throw an exception, if class does not have item health
|
||||
/// (default implementation: throw an exceoption)
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual float getWeight (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
|
|||
return false;
|
||||
|
||||
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
|
||||
std::map<std::string, int>::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
|
||||
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
|
||||
|
||||
if (iter==stats.getFactionRanks().end())
|
||||
return false;
|
||||
|
@ -112,7 +112,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
|
|||
if (!info.mPcFaction.empty())
|
||||
{
|
||||
MWMechanics::NpcStats& stats = player.getClass().getNpcStats (player);
|
||||
std::map<std::string,int>::iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction));
|
||||
std::map<std::string,int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction));
|
||||
|
||||
if(iter==stats.getFactionRanks().end())
|
||||
return false;
|
||||
|
@ -379,7 +379,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
|
|||
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
|
||||
return 0;
|
||||
|
||||
std::pair<std::string, int> faction =
|
||||
const std::pair<std::string, int> faction =
|
||||
*mActor.getClass().getNpcStats (mActor).getFactionRanks().begin();
|
||||
|
||||
int rank = getFactionRank (player, faction.first);
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace MWGui
|
|||
{
|
||||
/* BookTextParser */
|
||||
BookTextParser::BookTextParser(const std::string & text)
|
||||
: mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true)
|
||||
: mIndex(0), mText(text), mIgnoreNewlineTags(true), mIgnoreLineEndings(true), mClosingTag(false)
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
|
||||
mText = Interpreter::fixDefinesBook(mText, interpreterContext);
|
||||
|
@ -40,7 +40,7 @@ namespace MWGui
|
|||
mTagTypes[tag] = type;
|
||||
}
|
||||
|
||||
std::string BookTextParser::getReadyText()
|
||||
std::string BookTextParser::getReadyText() const
|
||||
{
|
||||
return mReadyText;
|
||||
}
|
||||
|
@ -81,7 +81,6 @@ namespace MWGui
|
|||
|
||||
if (type == Event_ImgTag)
|
||||
{
|
||||
mIgnoreLineEndings = false;
|
||||
mIgnoreNewlineTags = false;
|
||||
}
|
||||
|
||||
|
@ -117,12 +116,27 @@ namespace MWGui
|
|||
return mAttributes;
|
||||
}
|
||||
|
||||
bool BookTextParser::isClosingTag() const
|
||||
{
|
||||
return mClosingTag;
|
||||
}
|
||||
|
||||
void BookTextParser::parseTag(std::string tag)
|
||||
{
|
||||
size_t tagNameEndPos = tag.find(' ');
|
||||
mAttributes.clear();
|
||||
mTag = tag.substr(0, tagNameEndPos);
|
||||
Misc::StringUtils::toLower(mTag);
|
||||
mAttributes.clear();
|
||||
if (mTag.empty())
|
||||
return;
|
||||
|
||||
mClosingTag = (mTag[0] == '/');
|
||||
if (mClosingTag)
|
||||
{
|
||||
mTag.erase(mTag.begin());
|
||||
return;
|
||||
}
|
||||
|
||||
if (tagNameEndPos == std::string::npos)
|
||||
return;
|
||||
tag.erase(0, tagNameEndPos+1);
|
||||
|
@ -183,6 +197,9 @@ namespace MWGui
|
|||
paper->setNeedMouseFocus(false);
|
||||
|
||||
BookTextParser parser(markup);
|
||||
|
||||
bool brBeforeLastTag = false;
|
||||
bool isPrevImg = false;
|
||||
for (;;)
|
||||
{
|
||||
BookTextParser::Events event = parser.next();
|
||||
|
@ -190,10 +207,20 @@ namespace MWGui
|
|||
continue;
|
||||
|
||||
std::string plainText = parser.getReadyText();
|
||||
if (!plainText.empty())
|
||||
if (plainText.empty())
|
||||
brBeforeLastTag = false;
|
||||
else
|
||||
{
|
||||
// if there's a newline at the end of the box caption, remove it
|
||||
if (plainText[plainText.size()-1] == '\n')
|
||||
// Each block of text (between two tags / boundary and tag) will be displayed in a separate editbox widget,
|
||||
// which means an additional linebreak will be created between them.
|
||||
// ^ This is not what vanilla MW assumes, so we must deal with line breaks around tags appropriately.
|
||||
bool brAtStart = (plainText[0] == '\n');
|
||||
bool brAtEnd = (plainText[plainText.size()-1] == '\n');
|
||||
|
||||
if (brAtStart && !brBeforeLastTag && !isPrevImg)
|
||||
plainText.erase(plainText.begin());
|
||||
|
||||
if (plainText.size() && brAtEnd)
|
||||
plainText.erase(plainText.end()-1);
|
||||
|
||||
#if (MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 2, 2))
|
||||
|
@ -206,13 +233,21 @@ namespace MWGui
|
|||
}
|
||||
#endif
|
||||
|
||||
TextElement elem(paper, pag, mTextStyle, plainText);
|
||||
elem.paginate();
|
||||
if (!plainText.empty() || brBeforeLastTag || isPrevImg)
|
||||
{
|
||||
TextElement elem(paper, pag, mBlockStyle,
|
||||
mTextStyle, plainText);
|
||||
elem.paginate();
|
||||
}
|
||||
|
||||
brBeforeLastTag = brAtEnd;
|
||||
}
|
||||
|
||||
if (event == BookTextParser::Event_EOF)
|
||||
break;
|
||||
|
||||
isPrevImg = (event == BookTextParser::Event_ImgTag);
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case BookTextParser::Event_ImgTag:
|
||||
|
@ -226,12 +261,16 @@ namespace MWGui
|
|||
int width = boost::lexical_cast<int>(attr.at("width"));
|
||||
int height = boost::lexical_cast<int>(attr.at("height"));
|
||||
|
||||
ImageElement elem(paper, pag, src, width, height);
|
||||
ImageElement elem(paper, pag, mBlockStyle,
|
||||
src, width, height);
|
||||
elem.paginate();
|
||||
break;
|
||||
}
|
||||
case BookTextParser::Event_FontTag:
|
||||
handleFont(parser.getAttributes());
|
||||
if (parser.isClosingTag())
|
||||
resetFontProperties();
|
||||
else
|
||||
handleFont(parser.getAttributes());
|
||||
break;
|
||||
case BookTextParser::Event_DivTag:
|
||||
handleDiv(parser.getAttributes());
|
||||
|
@ -255,6 +294,11 @@ namespace MWGui
|
|||
return markupToWidget(parent, markup, parent->getWidth(), parent->getHeight());
|
||||
}
|
||||
|
||||
void BookFormatter::resetFontProperties()
|
||||
{
|
||||
mTextStyle = TextStyle();
|
||||
}
|
||||
|
||||
void BookFormatter::handleDiv(const BookTextParser::Attributes & attr)
|
||||
{
|
||||
if (attr.find("align") == attr.end())
|
||||
|
@ -263,9 +307,11 @@ namespace MWGui
|
|||
std::string align = attr.at("align");
|
||||
|
||||
if (Misc::StringUtils::ciEqual(align, "center"))
|
||||
mTextStyle.mTextAlign = MyGUI::Align::HCenter;
|
||||
mBlockStyle.mAlign = MyGUI::Align::HCenter;
|
||||
else if (Misc::StringUtils::ciEqual(align, "left"))
|
||||
mTextStyle.mTextAlign = MyGUI::Align::Left;
|
||||
mBlockStyle.mAlign = MyGUI::Align::Left;
|
||||
else if (Misc::StringUtils::ciEqual(align, "right"))
|
||||
mBlockStyle.mAlign = MyGUI::Align::Right;
|
||||
}
|
||||
|
||||
void BookFormatter::handleFont(const BookTextParser::Attributes & attr)
|
||||
|
@ -296,8 +342,8 @@ namespace MWGui
|
|||
}
|
||||
|
||||
/* GraphicElement */
|
||||
GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag)
|
||||
: mParent(parent), mPaginator(pag)
|
||||
GraphicElement::GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle)
|
||||
: mParent(parent), mPaginator(pag), mBlockStyle(blockStyle)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -320,10 +366,10 @@ namespace MWGui
|
|||
}
|
||||
|
||||
/* TextElement */
|
||||
TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag,
|
||||
const TextStyle & style, const std::string & text)
|
||||
: GraphicElement(parent, pag),
|
||||
mStyle(style)
|
||||
TextElement::TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
|
||||
const TextStyle & textStyle, const std::string & text)
|
||||
: GraphicElement(parent, pag, blockStyle),
|
||||
mTextStyle(textStyle)
|
||||
{
|
||||
MyGUI::EditBox* box = parent->createWidget<MyGUI::EditBox>("NormalText",
|
||||
MyGUI::IntCoord(0, pag.getCurrentTop(), pag.getPageWidth(), 0), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
|
@ -333,9 +379,9 @@ namespace MWGui
|
|||
box->setProperty("WordWrap", "true");
|
||||
box->setProperty("NeedMouse", "false");
|
||||
box->setMaxTextLength(text.size());
|
||||
box->setTextAlign(mStyle.mTextAlign);
|
||||
box->setTextColour(mStyle.mColour);
|
||||
box->setFontName(mStyle.mFont);
|
||||
box->setTextAlign(mBlockStyle.mAlign);
|
||||
box->setTextColour(mTextStyle.mColour);
|
||||
box->setFontName(mTextStyle.mFont);
|
||||
box->setCaption(MyGUI::TextIterator::toTagsString(text));
|
||||
box->setSize(box->getSize().width, box->getTextSize().height);
|
||||
mEditBox = box;
|
||||
|
@ -343,7 +389,7 @@ namespace MWGui
|
|||
|
||||
int TextElement::currentFontHeight() const
|
||||
{
|
||||
std::string fontName(mStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mStyle.mFont);
|
||||
std::string fontName(mTextStyle.mFont == "Default" ? MyGUI::FontManager::getInstance().getDefaultFont() : mTextStyle.mFont);
|
||||
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
|
||||
}
|
||||
|
||||
|
@ -375,13 +421,21 @@ namespace MWGui
|
|||
}
|
||||
|
||||
/* ImageElement */
|
||||
ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag,
|
||||
ImageElement::ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
|
||||
const std::string & src, int width, int height)
|
||||
: GraphicElement(parent, pag),
|
||||
: GraphicElement(parent, pag, blockStyle),
|
||||
mImageHeight(height)
|
||||
{
|
||||
int left = 0;
|
||||
if (mBlockStyle.mAlign.isHCenter())
|
||||
left += (pag.getPageWidth() - width) / 2;
|
||||
else if (mBlockStyle.mAlign.isLeft())
|
||||
left = 0;
|
||||
else if (mBlockStyle.mAlign.isRight())
|
||||
left += pag.getPageWidth() - width;
|
||||
|
||||
mImageBox = parent->createWidget<MyGUI::ImageBox> ("ImageBox",
|
||||
MyGUI::IntCoord(0, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
MyGUI::IntCoord(left, pag.getCurrentTop(), width, mImageHeight), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
parent->getName() + boost::lexical_cast<std::string>(parent->getChildCount()));
|
||||
|
||||
std::string image = Misc::ResourceHelpers::correctBookartPath(src, width, mImageHeight);
|
||||
|
|
|
@ -14,14 +14,22 @@ namespace MWGui
|
|||
mColour(0,0,0)
|
||||
, mFont("Default")
|
||||
, mTextSize(16)
|
||||
, mTextAlign(MyGUI::Align::Left | MyGUI::Align::Top)
|
||||
{
|
||||
}
|
||||
|
||||
MyGUI::Colour mColour;
|
||||
std::string mFont;
|
||||
int mTextSize;
|
||||
MyGUI::Align mTextAlign;
|
||||
};
|
||||
|
||||
struct BlockStyle
|
||||
{
|
||||
BlockStyle() :
|
||||
mAlign(MyGUI::Align::Left | MyGUI::Align::Top)
|
||||
{
|
||||
}
|
||||
|
||||
MyGUI::Align mAlign;
|
||||
};
|
||||
|
||||
class BookTextParser
|
||||
|
@ -40,15 +48,18 @@ namespace MWGui
|
|||
};
|
||||
|
||||
BookTextParser(const std::string & text);
|
||||
void registerTag(const std::string & tag, Events type);
|
||||
std::string getReadyText();
|
||||
|
||||
Events next();
|
||||
void flushBuffer();
|
||||
|
||||
const Attributes & getAttributes() const;
|
||||
void parseTag(std::string tag);
|
||||
std::string getReadyText() const;
|
||||
bool isClosingTag() const;
|
||||
|
||||
private:
|
||||
void registerTag(const std::string & tag, Events type);
|
||||
void flushBuffer();
|
||||
void parseTag(std::string tag);
|
||||
|
||||
size_t mIndex;
|
||||
std::string mText;
|
||||
std::string mReadyText;
|
||||
|
@ -57,6 +68,7 @@ namespace MWGui
|
|||
bool mIgnoreLineEndings;
|
||||
Attributes mAttributes;
|
||||
std::string mTag;
|
||||
bool mClosingTag;
|
||||
std::map<std::string, Events> mTagTypes;
|
||||
std::string mBuffer;
|
||||
};
|
||||
|
@ -101,18 +113,20 @@ namespace MWGui
|
|||
Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup, const int pageWidth, const int pageHeight);
|
||||
Paginator::Pages markupToWidget(MyGUI::Widget * parent, const std::string & markup);
|
||||
|
||||
protected:
|
||||
void handleImg(const BookTextParser::Attributes & attr);
|
||||
private:
|
||||
void resetFontProperties();
|
||||
|
||||
void handleDiv(const BookTextParser::Attributes & attr);
|
||||
void handleFont(const BookTextParser::Attributes & attr);
|
||||
private:
|
||||
|
||||
TextStyle mTextStyle;
|
||||
BlockStyle mBlockStyle;
|
||||
};
|
||||
|
||||
class GraphicElement
|
||||
{
|
||||
public:
|
||||
GraphicElement(MyGUI::Widget * parent, Paginator & pag);
|
||||
GraphicElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle);
|
||||
virtual int getHeight() = 0;
|
||||
virtual void paginate();
|
||||
virtual int pageSplit();
|
||||
|
@ -120,24 +134,27 @@ namespace MWGui
|
|||
protected:
|
||||
MyGUI::Widget * mParent;
|
||||
Paginator & mPaginator;
|
||||
BlockStyle mBlockStyle;
|
||||
};
|
||||
|
||||
class TextElement : public GraphicElement
|
||||
{
|
||||
public:
|
||||
TextElement(MyGUI::Widget * parent, Paginator & pag, const TextStyle & style, const std::string & text);
|
||||
TextElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
|
||||
const TextStyle & textStyle, const std::string & text);
|
||||
virtual int getHeight();
|
||||
virtual int pageSplit();
|
||||
private:
|
||||
int currentFontHeight() const;
|
||||
TextStyle mStyle;
|
||||
TextStyle mTextStyle;
|
||||
MyGUI::EditBox * mEditBox;
|
||||
};
|
||||
|
||||
class ImageElement : public GraphicElement
|
||||
{
|
||||
public:
|
||||
ImageElement(MyGUI::Widget * parent, Paginator & pag, const std::string & src, int width, int height);
|
||||
ImageElement(MyGUI::Widget * parent, Paginator & pag, const BlockStyle & blockStyle,
|
||||
const std::string & src, int width, int height);
|
||||
virtual int getHeight();
|
||||
virtual int pageSplit();
|
||||
|
||||
|
|
|
@ -333,7 +333,8 @@ namespace MWGui
|
|||
float e1 = 0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float f1 = 0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified();
|
||||
|
||||
float pcTerm = (clampedDisposition - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm();
|
||||
float dispositionTerm = gmst.find("fDispositionMod")->getFloat() * (clampedDisposition - 50);
|
||||
float pcTerm = (dispositionTerm - 50 + a1 + b1 + c1) * playerStats.getFatigueTerm();
|
||||
float npcTerm = (d1 + e1 + f1) * sellerStats.getFatigueTerm();
|
||||
float x = gmst.find("fBargainOfferMulti")->getFloat() * d + gmst.find("fBargainOfferBase")->getFloat();
|
||||
if (buying)
|
||||
|
|
|
@ -385,7 +385,7 @@ namespace MWInput
|
|||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
||||
mOverencumberedMessageDelay -= dt;
|
||||
if (player.getClass().getEncumbrance(player) >= player.getClass().getCapacity(player))
|
||||
if (player.getClass().getEncumbrance(player) > player.getClass().getCapacity(player))
|
||||
{
|
||||
mPlayer->setAutoMove (false);
|
||||
if (mOverencumberedMessageDelay <= 0)
|
||||
|
|
|
@ -445,9 +445,7 @@ namespace MWMechanics
|
|||
|
||||
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
|
||||
|
||||
float capacity = ptr.getClass().getCapacity(ptr);
|
||||
float encumbrance = ptr.getClass().getEncumbrance(ptr);
|
||||
float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity);
|
||||
float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
|
||||
if (normalizedEncumbrance > 1)
|
||||
normalizedEncumbrance = 1;
|
||||
|
||||
|
@ -1447,6 +1445,8 @@ namespace MWMechanics
|
|||
continue;
|
||||
if (followTarget == actor)
|
||||
list.push_back(iter->first);
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if ((*it)->getTypeId() != MWMechanics::AiPackage::TypeIdCombat)
|
||||
break;
|
||||
|
|
|
@ -148,7 +148,8 @@ bool AiSequence::isPackageDone() const
|
|||
|
||||
void AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr()
|
||||
&& !actor.getClass().getCreatureStats(actor).getKnockedDown())
|
||||
{
|
||||
if (!mPackages.empty())
|
||||
{
|
||||
|
|
|
@ -1428,7 +1428,9 @@ void CharacterController::update(float duration)
|
|||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat();
|
||||
const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat();
|
||||
const float normalizedEncumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr);
|
||||
float normalizedEncumbrance = mPtr.getClass().getNormalizedEncumbrance(mPtr);
|
||||
if (normalizedEncumbrance > 1)
|
||||
normalizedEncumbrance = 1;
|
||||
const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult;
|
||||
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
|
||||
fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease);
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace MWMechanics
|
|||
const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat();
|
||||
const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat();
|
||||
MWMechanics::DynamicStat<float> fatigue = blockerStats.getFatigue();
|
||||
float normalizedEncumbrance = blocker.getClass().getEncumbrance(blocker) / blocker.getClass().getCapacity(blocker);
|
||||
float normalizedEncumbrance = blocker.getClass().getNormalizedEncumbrance(blocker);
|
||||
normalizedEncumbrance = std::min(1.f, normalizedEncumbrance);
|
||||
float fatigueLoss = fFatigueBlockBase + normalizedEncumbrance * fFatigueBlockMult;
|
||||
fatigueLoss += weapon.getClass().getWeight(weapon) * attackerStats.getAttackStrength() * fWeaponFatigueBlockMult;
|
||||
|
|
|
@ -69,9 +69,41 @@ const std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks() const
|
|||
return mFactionRank;
|
||||
}
|
||||
|
||||
std::map<std::string, int>& MWMechanics::NpcStats::getFactionRanks()
|
||||
void MWMechanics::NpcStats::raiseRank(const std::string &faction)
|
||||
{
|
||||
return mFactionRank;
|
||||
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
||||
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
|
||||
if (it != mFactionRank.end())
|
||||
{
|
||||
// Does the next rank exist?
|
||||
const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(lower);
|
||||
if (it->second+1 < 10 && !faction->mRanks[it->second+1].empty())
|
||||
it->second += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::lowerRank(const std::string &faction)
|
||||
{
|
||||
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
||||
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
|
||||
if (it != mFactionRank.end())
|
||||
{
|
||||
it->second = std::max(0, it->second-1);
|
||||
}
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::setFactionRank(const std::string &faction, int rank)
|
||||
{
|
||||
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
||||
mFactionRank[lower] = rank;
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::joinFaction(const std::string& faction)
|
||||
{
|
||||
const std::string lower = Misc::StringUtils::lowerCase(faction);
|
||||
std::map<std::string, int>::iterator it = mFactionRank.find(lower);
|
||||
if (it == mFactionRank.end())
|
||||
mFactionRank[lower] = 0;
|
||||
}
|
||||
|
||||
bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const
|
||||
|
|
|
@ -70,7 +70,15 @@ namespace MWMechanics
|
|||
SkillValue& getSkill (int index);
|
||||
|
||||
const std::map<std::string, int>& getFactionRanks() const;
|
||||
std::map<std::string, int>& getFactionRanks();
|
||||
/// Increase the rank in this faction by 1, if such a rank exists.
|
||||
void raiseRank(const std::string& faction);
|
||||
/// Lower the rank in this faction by 1, if such a rank exists.
|
||||
void lowerRank(const std::string& faction);
|
||||
/// Join this faction, setting the initial rank to 0.
|
||||
void joinFaction(const std::string& faction);
|
||||
/// Warning: this function performs no check whether the rank exists,
|
||||
/// and should be used in initial actor setup only.
|
||||
void setFactionRank(const std::string& faction, int rank);
|
||||
|
||||
const std::set<std::string>& getExpelled() const { return mExpelled; }
|
||||
bool getExpelled(const std::string& factionID) const;
|
||||
|
|
|
@ -246,10 +246,7 @@ namespace MWMechanics
|
|||
const ESM::Spell* spell, const MagicEffects* effects)
|
||||
{
|
||||
float resistance = getEffectResistance(effectId, actor, caster, spell, effects);
|
||||
if (resistance >= 0)
|
||||
return 1 - resistance / 100.f;
|
||||
else
|
||||
return -(resistance-100) / 100.f;
|
||||
return 1 - resistance / 100.f;
|
||||
}
|
||||
|
||||
/// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure.
|
||||
|
@ -774,7 +771,7 @@ namespace MWMechanics
|
|||
static const float fFatigueSpellBase = store.get<ESM::GameSetting>().find("fFatigueSpellBase")->getFloat();
|
||||
static const float fFatigueSpellMult = store.get<ESM::GameSetting>().find("fFatigueSpellMult")->getFloat();
|
||||
DynamicStat<float> fatigue = stats.getFatigue();
|
||||
const float normalizedEncumbrance = mCaster.getClass().getEncumbrance(mCaster) / mCaster.getClass().getCapacity(mCaster);
|
||||
const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);
|
||||
float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
|
||||
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue);
|
||||
|
||||
|
|
|
@ -51,6 +51,10 @@ namespace MWMechanics
|
|||
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
|
||||
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
|
||||
|
||||
/// Get an effect multiplier for applying an effect cast by the given actor in the given spell (optional).
|
||||
/// @return effect multiplier from 0 to 2. (100% net resistance to 100% net weakness)
|
||||
/// @param effects Override the actor's current magicEffects. Useful if there are effects currently
|
||||
/// being applied (but not applied yet) that should also be considered.
|
||||
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
|
||||
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
|
||||
|
||||
|
|
|
@ -728,7 +728,7 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co
|
|||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx;
|
||||
if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0)
|
||||
if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0)
|
||||
type = MWBase::SoundManager::Play_TypeFoot;
|
||||
sndMgr->playSound3D(mPtr, sound, volume, pitch, type);
|
||||
}
|
||||
|
|
|
@ -119,11 +119,14 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
// Set default texture filtering options
|
||||
TextureFilterOptions tfo;
|
||||
std::string filter = Settings::Manager::getString("texture filtering", "General");
|
||||
#ifndef ANDROID
|
||||
if (filter == "anisotropic") tfo = TFO_ANISOTROPIC;
|
||||
else if (filter == "trilinear") tfo = TFO_TRILINEAR;
|
||||
else if (filter == "bilinear") tfo = TFO_BILINEAR;
|
||||
else /*if (filter == "none")*/ tfo = TFO_NONE;
|
||||
|
||||
#else
|
||||
tfo = TFO_NONE;
|
||||
#endif
|
||||
MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
|
||||
MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 );
|
||||
|
||||
|
@ -250,7 +253,6 @@ void RenderingManager::cellAdded (MWWorld::CellStore *store)
|
|||
mObjects->buildStaticGeometry (*store);
|
||||
sh::Factory::getInstance().unloadUnreferencedMaterials();
|
||||
mDebugging->cellAdded(store);
|
||||
waterAdded(store);
|
||||
}
|
||||
|
||||
void RenderingManager::addObject (const MWWorld::Ptr& ptr){
|
||||
|
@ -421,18 +423,12 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt)
|
|||
mOcclusionQuery->setActive(false);
|
||||
}
|
||||
|
||||
void RenderingManager::waterAdded (MWWorld::CellStore *store)
|
||||
void RenderingManager::setWaterEnabled(bool enable)
|
||||
{
|
||||
if (store->getCell()->mData.mFlags & ESM::Cell::HasWater)
|
||||
{
|
||||
mWater->changeCell (store->getCell());
|
||||
mWater->setActive(true);
|
||||
}
|
||||
else
|
||||
removeWater();
|
||||
mWater->setActive(enable);
|
||||
}
|
||||
|
||||
void RenderingManager::setWaterHeight(const float height)
|
||||
void RenderingManager::setWaterHeight(float height)
|
||||
{
|
||||
mWater->setHeight(height);
|
||||
}
|
||||
|
|
|
@ -100,7 +100,6 @@ public:
|
|||
/// \todo this function should be removed later. Instead the rendering subsystems should track
|
||||
/// when rebatching is needed and update automatically at the end of each frame.
|
||||
void cellAdded (MWWorld::CellStore *store);
|
||||
void waterAdded(MWWorld::CellStore *store);
|
||||
|
||||
/// Clear all savegame-specific data (i.e. fog of war textures)
|
||||
void clear();
|
||||
|
@ -121,7 +120,8 @@ public:
|
|||
/// Updates an object's rotation
|
||||
void rotateObject (const MWWorld::Ptr& ptr);
|
||||
|
||||
void setWaterHeight(const float height);
|
||||
void setWaterHeight(float height);
|
||||
void setWaterEnabled(bool enabled);
|
||||
bool toggleWater();
|
||||
bool toggleWorld();
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor)
|
|||
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
|
||||
MWMechanics::CreatureStats& attackerStats = actor.getClass().getCreatureStats(actor);
|
||||
MWMechanics::DynamicStat<float> fatigue = attackerStats.getFatigue();
|
||||
const float normalizedEncumbrance = actor.getClass().getEncumbrance(actor) / actor.getClass().getCapacity(actor);
|
||||
const float normalizedEncumbrance = actor.getClass().getNormalizedEncumbrance(actor);
|
||||
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
|
||||
if (!weapon->isEmpty())
|
||||
fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult;
|
||||
|
|
|
@ -290,18 +290,15 @@ namespace MWScript
|
|||
const std::string &name = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
int count = 0;
|
||||
MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore (ptr);
|
||||
for (MWWorld::ContainerStoreIterator it = invStore.begin(MWWorld::ContainerStore::Type_Miscellaneous);
|
||||
it != invStore.end(); ++it)
|
||||
{
|
||||
|
||||
if (::Misc::StringUtils::ciEqual(it->getCellRef().getSoul(), name))
|
||||
{
|
||||
runtime.push(1);
|
||||
return;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
runtime.push(0);
|
||||
runtime.push(count);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ namespace MWScript
|
|||
|
||||
std::string InterpreterContext::getNPCRank() const
|
||||
{
|
||||
std::map<std::string, int> ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks();
|
||||
const std::map<std::string, int>& ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks();
|
||||
std::map<std::string, int>::const_iterator it = ranks.begin();
|
||||
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
@ -348,7 +348,7 @@ namespace MWScript
|
|||
|
||||
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
|
||||
|
||||
std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks();
|
||||
const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
|
||||
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
|
||||
int rank = -1;
|
||||
if (it != ranks.end())
|
||||
|
@ -375,7 +375,7 @@ namespace MWScript
|
|||
|
||||
std::string factionId = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks().begin()->first;
|
||||
|
||||
std::map<std::string, int> ranks = player.getClass().getNpcStats (player).getFactionRanks();
|
||||
const std::map<std::string, int>& ranks = player.getClass().getNpcStats (player).getFactionRanks();
|
||||
std::map<std::string, int>::const_iterator it = ranks.find(factionId);
|
||||
int rank = -1;
|
||||
if (it != ranks.end())
|
||||
|
|
|
@ -548,10 +548,7 @@ namespace MWScript
|
|||
if(factionID != "")
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end())
|
||||
{
|
||||
player.getClass().getNpcStats(player).getFactionRanks()[factionID] = 0;
|
||||
}
|
||||
player.getClass().getNpcStats(player).joinFaction(factionID);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -585,13 +582,11 @@ namespace MWScript
|
|||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end())
|
||||
{
|
||||
player.getClass().getNpcStats(player).getFactionRanks()[factionID] = 0;
|
||||
player.getClass().getNpcStats(player).joinFaction(factionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
player.getClass().getNpcStats(player).getFactionRanks()[factionID] =
|
||||
std::min(player.getClass().getNpcStats(player).getFactionRanks()[factionID] +1,
|
||||
9);
|
||||
player.getClass().getNpcStats(player).raiseRank(factionID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -624,11 +619,7 @@ namespace MWScript
|
|||
if(factionID != "")
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end())
|
||||
{
|
||||
player.getClass().getNpcStats(player).getFactionRanks()[factionID] =
|
||||
std::max(0, player.getClass().getNpcStats(player).getFactionRanks()[factionID]-1);
|
||||
}
|
||||
player.getClass().getNpcStats(player).lowerRank(factionID);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -668,7 +659,7 @@ namespace MWScript
|
|||
{
|
||||
if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end())
|
||||
{
|
||||
runtime.push(player.getClass().getNpcStats(player).getFactionRanks()[factionID]);
|
||||
runtime.push(player.getClass().getNpcStats(player).getFactionRanks().at(factionID));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1036,8 +1027,7 @@ namespace MWScript
|
|||
if (ptr == player)
|
||||
return;
|
||||
|
||||
std::map<std::string, int>& ranks = ptr.getClass().getNpcStats(ptr).getFactionRanks ();
|
||||
ranks[factionID] = std::min(9, ranks[factionID]+1);
|
||||
ptr.getClass().getNpcStats(ptr).raiseRank(factionID);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1063,8 +1053,7 @@ namespace MWScript
|
|||
if (ptr == player)
|
||||
return;
|
||||
|
||||
std::map<std::string, int>& ranks = ptr.getClass().getNpcStats(ptr).getFactionRanks ();
|
||||
ranks[factionID] = std::max(0, ranks[factionID]-1);
|
||||
ptr.getClass().getNpcStats(ptr).lowerRank(factionID);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,23 @@
|
|||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "player.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void getFollowers (const MWWorld::Ptr& actor, std::set<MWWorld::Ptr>& out)
|
||||
{
|
||||
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
|
||||
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||
{
|
||||
if (out.insert(*it).second)
|
||||
{
|
||||
getFollowers(*it, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
ActionTeleport::ActionTeleport (const std::string& cellName,
|
||||
|
@ -16,8 +33,9 @@ namespace MWWorld
|
|||
void ActionTeleport::executeImp (const Ptr& actor)
|
||||
{
|
||||
//find any NPC that is following the actor and teleport him too
|
||||
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
|
||||
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||
std::set<MWWorld::Ptr> followers;
|
||||
getFollowers(actor, followers);
|
||||
for(std::set<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||
{
|
||||
teleport(*it);
|
||||
}
|
||||
|
|
|
@ -420,4 +420,13 @@ namespace MWWorld
|
|||
{
|
||||
throw std::runtime_error("this is not a door");
|
||||
}
|
||||
|
||||
float Class::getNormalizedEncumbrance(const Ptr &ptr) const
|
||||
{
|
||||
float capacity = getCapacity(ptr);
|
||||
if (capacity == 0)
|
||||
return 1.f;
|
||||
|
||||
return getEncumbrance(ptr) / capacity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ namespace MWWorld
|
|||
|
||||
virtual MWMechanics::CreatureStats& getCreatureStats (const Ptr& ptr) const;
|
||||
///< Return creature stats or throw an exception, if class does not have creature stats
|
||||
/// (default implementation: throw an exceoption)
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual bool hasToolTip (const Ptr& ptr) const;
|
||||
///< @return true if this object has a tooltip when focused (default implementation: false)
|
||||
|
@ -106,7 +106,7 @@ namespace MWWorld
|
|||
|
||||
virtual MWMechanics::NpcStats& getNpcStats (const Ptr& ptr) const;
|
||||
///< Return NPC stats or throw an exception, if class does not have NPC stats
|
||||
/// (default implementation: throw an exceoption)
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual bool hasItemHealth (const Ptr& ptr) const;
|
||||
///< \return Item health data available? (default implementation: false)
|
||||
|
@ -123,7 +123,7 @@ namespace MWWorld
|
|||
/// of the given attacker, and whoever is hit.
|
||||
/// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType
|
||||
/// enums. ignored for creature attacks.
|
||||
/// (default implementation: throw an exceoption)
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
|
||||
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
|
||||
|
@ -139,7 +139,7 @@ namespace MWWorld
|
|||
///< Sets a new current health value for the actor, optionally specifying the object causing
|
||||
/// the change. Use this instead of using CreatureStats directly as this will make sure the
|
||||
/// correct dialog and actor states are properly handled when being hurt or healed.
|
||||
/// (default implementation: throw an exceoption)
|
||||
/// (default implementation: throw an exception)
|
||||
|
||||
virtual boost::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;
|
||||
///< Generate action for activation (default implementation: return a null action).
|
||||
|
@ -151,11 +151,11 @@ namespace MWWorld
|
|||
|
||||
virtual ContainerStore& getContainerStore (const Ptr& ptr) const;
|
||||
///< Return container store or throw an exception, if class does not have a
|
||||
/// container store (default implementation: throw an exceoption)
|
||||
/// container store (default implementation: throw an exception)
|
||||
|
||||
virtual InventoryStore& getInventoryStore (const Ptr& ptr) const;
|
||||
///< Return inventory store or throw an exception, if class does not have a
|
||||
/// inventory store (default implementation: throw an exceoption)
|
||||
/// inventory store (default implementation: throw an exception)
|
||||
|
||||
virtual bool hasInventoryStore (const Ptr& ptr) const;
|
||||
///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false)
|
||||
|
@ -223,6 +223,9 @@ namespace MWWorld
|
|||
/// effects). Throws an exception, if the object can't hold other objects.
|
||||
/// (default implementation: throws an exception)
|
||||
|
||||
virtual float getNormalizedEncumbrance (const MWWorld::Ptr& ptr) const;
|
||||
///< Returns encumbrance re-scaled to capacity
|
||||
|
||||
virtual bool apply (const MWWorld::Ptr& ptr, const std::string& id,
|
||||
const MWWorld::Ptr& actor) const;
|
||||
///< Apply \a id on \a ptr.
|
||||
|
|
|
@ -68,6 +68,7 @@ MWWorld::InventoryStore::InventoryStore()
|
|||
, mUpdatesEnabled (true)
|
||||
, mFirstAutoEquip(true)
|
||||
, mListener(NULL)
|
||||
, mRechargingItemsUpToDate(false)
|
||||
{
|
||||
initSlots (mSlots);
|
||||
}
|
||||
|
@ -80,6 +81,7 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
|
|||
, mListener(store.mListener)
|
||||
, mUpdatesEnabled(store.mUpdatesEnabled)
|
||||
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
||||
, mRechargingItemsUpToDate(false)
|
||||
{
|
||||
copySlots (store);
|
||||
}
|
||||
|
@ -90,6 +92,7 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor
|
|||
mMagicEffects = store.mMagicEffects;
|
||||
mFirstAutoEquip = store.mFirstAutoEquip;
|
||||
mPermanentMagicEffectMagnitudes = store.mPermanentMagicEffectMagnitudes;
|
||||
mRechargingItemsUpToDate = false;
|
||||
ContainerStore::operator= (store);
|
||||
mSlots.clear();
|
||||
copySlots (store);
|
||||
|
@ -110,8 +113,6 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr,
|
|||
autoEquip(actorPtr);
|
||||
}
|
||||
|
||||
updateRechargingItems();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -485,8 +486,6 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor
|
|||
mSelectedEnchantItem = end();
|
||||
}
|
||||
|
||||
updateRechargingItems();
|
||||
|
||||
return retCount;
|
||||
}
|
||||
|
||||
|
@ -606,6 +605,11 @@ void MWWorld::InventoryStore::updateRechargingItems()
|
|||
|
||||
void MWWorld::InventoryStore::rechargeItems(float duration)
|
||||
{
|
||||
if (!mRechargingItemsUpToDate)
|
||||
{
|
||||
updateRechargingItems();
|
||||
mRechargingItemsUpToDate = true;
|
||||
}
|
||||
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
|
||||
{
|
||||
if (it->first->getCellRef().getEnchantmentCharge() == -1
|
||||
|
|
|
@ -102,6 +102,8 @@ namespace MWWorld
|
|||
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
|
||||
TRechargingItems mRechargingItems;
|
||||
|
||||
bool mRechargingItemsUpToDate;
|
||||
|
||||
void copySlots (const InventoryStore& store);
|
||||
|
||||
void initSlots (TSlots& slots_);
|
||||
|
|
|
@ -238,10 +238,31 @@ namespace MWWorld
|
|||
physicActor->setOnGround(false);
|
||||
return position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if we actually found a valid spawn point (use an infinitely thin ray this time).
|
||||
// Required for some broken door destinations in Morrowind.esm, where the spawn point
|
||||
// intersects with other geometry if the actor's base is taken into account
|
||||
btVector3 from = BtOgre::Convert::toBullet(position);
|
||||
btVector3 to = from - btVector3(0,0,maxHeight);
|
||||
|
||||
physicActor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope);
|
||||
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
|
||||
resultCallback1.m_collisionFilterGroup = 0xff;
|
||||
resultCallback1.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap;
|
||||
|
||||
return tracer.mEndPos;
|
||||
engine->mDynamicsWorld->rayTest(from, to, resultCallback1);
|
||||
if (resultCallback1.hasHit() &&
|
||||
(BtOgre::Convert::toOgre(resultCallback1.m_hitPointWorld).distance(tracer.mEndPos) > 30
|
||||
|| getSlope(tracer.mPlaneNormal) > sMaxSlope))
|
||||
{
|
||||
physicActor->setOnGround(getSlope(BtOgre::Convert::toOgre(resultCallback1.m_hitNormalWorld)) <= sMaxSlope);
|
||||
return BtOgre::Convert::toOgre(resultCallback1.m_hitPointWorld) + Ogre::Vector3(0,0,1.f);
|
||||
}
|
||||
|
||||
physicActor->setOnGround(getSlope(tracer.mPlaneNormal) <= sMaxSlope);
|
||||
|
||||
return tracer.mEndPos;
|
||||
}
|
||||
}
|
||||
|
||||
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
|
||||
|
@ -257,8 +278,10 @@ namespace MWWorld
|
|||
if (!ptr.getClass().canWalk(ptr) && !ptr.getClass().canFly(ptr) && !ptr.getClass().canSwim(ptr))
|
||||
return position;
|
||||
|
||||
/* Anything to collide with? */
|
||||
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
|
||||
// Reset per-frame data
|
||||
physicActor->setWalkingOnWater(false);
|
||||
/* Anything to collide with? */
|
||||
if(!physicActor || !physicActor->getCollisionMode())
|
||||
{
|
||||
return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
|
@ -286,9 +309,6 @@ namespace MWWorld
|
|||
*/
|
||||
|
||||
OEngine::Physic::ActorTracer tracer;
|
||||
bool isOnGround = physicActor->getOnGround();
|
||||
if (movement.z > 0.f)
|
||||
isOnGround = false;
|
||||
Ogre::Vector3 inertia(0.0f);
|
||||
Ogre::Vector3 velocity;
|
||||
|
||||
|
@ -405,8 +425,6 @@ namespace MWWorld
|
|||
if((ptr.getClass().canSwim(ptr) && !ptr.getClass().canWalk(ptr))
|
||||
&& newPosition.z > (waterlevel - halfExtents.z * 0.5))
|
||||
newPosition = oldPosition;
|
||||
else // Only on the ground if there's gravity
|
||||
isOnGround = !(newPosition.z < waterlevel || isFlying);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -423,10 +441,11 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
if (!(inertia.z > 0.f) && !(newPosition.z < waterlevel || isFlying))
|
||||
bool isOnGround = false;
|
||||
if (!(inertia.z > 0.f) && !(newPosition.z < waterlevel))
|
||||
{
|
||||
Ogre::Vector3 from = newPosition;
|
||||
Ogre::Vector3 to = newPosition - (isOnGround ?
|
||||
Ogre::Vector3 to = newPosition - (physicActor->getOnGround() ?
|
||||
Ogre::Vector3(0,0,sStepSize+2.f) : Ogre::Vector3(0,0,2.f));
|
||||
tracer.doTrace(colobj, from, to, engine);
|
||||
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
|
||||
|
@ -436,8 +455,11 @@ namespace MWWorld
|
|||
{
|
||||
standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName;
|
||||
}
|
||||
if (standingOn->getBroadphaseHandle()->m_collisionFilterGroup == OEngine::Physic::CollisionType_Water)
|
||||
physicActor->setWalkingOnWater(true);
|
||||
|
||||
newPosition.z = tracer.mEndPos.z + 1.0f;
|
||||
if (!isFlying)
|
||||
newPosition.z = tracer.mEndPos.z + 1.0f;
|
||||
|
||||
isOnGround = true;
|
||||
}
|
||||
|
@ -464,7 +486,7 @@ namespace MWWorld
|
|||
|
||||
|
||||
PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) :
|
||||
mRender(_rend), mEngine(0), mTimeAccum(0.0f)
|
||||
mRender(_rend), mEngine(0), mTimeAccum(0.0f), mWaterEnabled(false), mWaterHeight(0)
|
||||
{
|
||||
// Create physics. shapeLoader is deleted by the physic engine
|
||||
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
|
||||
|
@ -473,6 +495,8 @@ namespace MWWorld
|
|||
|
||||
PhysicsSystem::~PhysicsSystem()
|
||||
{
|
||||
if (mWaterCollisionObject.get())
|
||||
mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get());
|
||||
delete mEngine;
|
||||
}
|
||||
|
||||
|
@ -640,9 +664,9 @@ namespace MWWorld
|
|||
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
|
||||
handleToMesh[node->getName()] = mesh;
|
||||
mEngine->createAndAdjustRigidBody(
|
||||
mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable);
|
||||
mesh, node->getName(), ptr.getCellRef().getScale(), node->getPosition(), node->getOrientation(), 0, 0, false, placeable);
|
||||
mEngine->createAndAdjustRigidBody(
|
||||
mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable);
|
||||
mesh, node->getName(), ptr.getCellRef().getScale(), node->getPosition(), node->getOrientation(), 0, 0, true, placeable);
|
||||
}
|
||||
|
||||
void PhysicsSystem::addActor (const Ptr& ptr)
|
||||
|
@ -834,14 +858,10 @@ namespace MWWorld
|
|||
Ogre::Vector3(iter->first.getRefData().getPosition().pos)))
|
||||
waterCollision = true;
|
||||
|
||||
btStaticPlaneShape planeShape(btVector3(0,0,1), waterlevel);
|
||||
btCollisionObject object;
|
||||
object.setCollisionShape(&planeShape);
|
||||
|
||||
// TODO: this seems to have a slight performance impact
|
||||
if (waterCollision)
|
||||
mEngine->mDynamicsWorld->addCollisionObject(&object,
|
||||
0xff, OEngine::Physic::CollisionType_Actor);
|
||||
OEngine::Physic::PhysicActor *physicActor = mEngine->getCharacter(iter->first.getRefData().getHandle());
|
||||
if (!physicActor) // actor was already removed from the scene
|
||||
continue;
|
||||
physicActor->setCanWaterWalk(waterCollision);
|
||||
|
||||
// 100 points of slowfall reduce gravity by 90% (this is just a guess)
|
||||
float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).getMagnitude() / 100.f) * 0.9f), 0.9f);
|
||||
|
@ -850,9 +870,6 @@ namespace MWWorld
|
|||
world->isFlying(iter->first),
|
||||
waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions);
|
||||
|
||||
if (waterCollision)
|
||||
mEngine->mDynamicsWorld->removeCollisionObject(&object);
|
||||
|
||||
float heightDiff = newpos.z - oldHeight;
|
||||
|
||||
if (heightDiff < 0)
|
||||
|
@ -928,4 +945,48 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicsSystem::disableWater()
|
||||
{
|
||||
if (mWaterEnabled)
|
||||
{
|
||||
mWaterEnabled = false;
|
||||
updateWater();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsSystem::enableWater(float height)
|
||||
{
|
||||
if (!mWaterEnabled || mWaterHeight != height)
|
||||
{
|
||||
mWaterEnabled = true;
|
||||
mWaterHeight = height;
|
||||
updateWater();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsSystem::setWaterHeight(float height)
|
||||
{
|
||||
if (mWaterHeight != height)
|
||||
{
|
||||
mWaterHeight = height;
|
||||
updateWater();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsSystem::updateWater()
|
||||
{
|
||||
if (mWaterCollisionObject.get())
|
||||
{
|
||||
mEngine->mDynamicsWorld->removeCollisionObject(mWaterCollisionObject.get());
|
||||
}
|
||||
|
||||
if (!mWaterEnabled)
|
||||
return;
|
||||
|
||||
mWaterCollisionObject.reset(new btCollisionObject());
|
||||
mWaterCollisionShape.reset(new btStaticPlaneShape(btVector3(0,0,1), mWaterHeight));
|
||||
mWaterCollisionObject->setCollisionShape(mWaterCollisionShape.get());
|
||||
mEngine->mDynamicsWorld->addCollisionObject(mWaterCollisionObject.get(), OEngine::Physic::CollisionType_Water,
|
||||
OEngine::Physic::CollisionType_Actor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef GAME_MWWORLD_PHYSICSSYSTEM_H
|
||||
#define GAME_MWWORLD_PHYSICSSYSTEM_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include <btBulletCollisionCommon.h>
|
||||
|
@ -32,6 +34,10 @@ namespace MWWorld
|
|||
PhysicsSystem (OEngine::Render::OgreRenderer &_rend);
|
||||
~PhysicsSystem ();
|
||||
|
||||
void enableWater(float height);
|
||||
void setWaterHeight(float height);
|
||||
void disableWater();
|
||||
|
||||
void addObject (const MWWorld::Ptr& ptr, bool placeable=false);
|
||||
|
||||
void addActor (const MWWorld::Ptr& ptr);
|
||||
|
@ -108,6 +114,8 @@ namespace MWWorld
|
|||
|
||||
private:
|
||||
|
||||
void updateWater();
|
||||
|
||||
OEngine::Render::OgreRenderer &mRender;
|
||||
OEngine::Physic::PhysicEngine* mEngine;
|
||||
std::map<std::string, std::string> handleToMesh;
|
||||
|
@ -124,6 +132,12 @@ namespace MWWorld
|
|||
|
||||
float mTimeAccum;
|
||||
|
||||
float mWaterHeight;
|
||||
float mWaterEnabled;
|
||||
|
||||
std::auto_ptr<btCollisionObject> mWaterCollisionObject;
|
||||
std::auto_ptr<btCollisionShape> mWaterCollisionShape;
|
||||
|
||||
PhysicsSystem (const PhysicsSystem&);
|
||||
PhysicsSystem& operator= (const PhysicsSystem&);
|
||||
};
|
||||
|
|
|
@ -153,7 +153,8 @@ namespace MWWorld
|
|||
Ogre::Vector3 pos(it->mNode->getPosition());
|
||||
Ogre::Vector3 newPos = pos + direction * duration * speed;
|
||||
|
||||
it->mSound->setPosition(newPos);
|
||||
if (it->mSound.get())
|
||||
it->mSound->setPosition(newPos);
|
||||
|
||||
it->mNode->setPosition(newPos);
|
||||
|
||||
|
@ -163,7 +164,8 @@ namespace MWWorld
|
|||
// TODO: use a proper btRigidBody / btGhostObject?
|
||||
btVector3 from(pos.x, pos.y, pos.z);
|
||||
btVector3 to(newPos.x, newPos.y, newPos.z);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to);
|
||||
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to, OEngine::Physic::CollisionType_Projectile);
|
||||
bool hit=false;
|
||||
|
||||
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
|
||||
|
@ -239,7 +241,7 @@ namespace MWWorld
|
|||
// TODO: use a proper btRigidBody / btGhostObject?
|
||||
btVector3 from(pos.x, pos.y, pos.z);
|
||||
btVector3 to(newPos.x, newPos.y, newPos.z);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to, OEngine::Physic::CollisionType_Projectile);
|
||||
bool hit=false;
|
||||
|
||||
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
|
||||
|
@ -408,6 +410,7 @@ namespace MWWorld
|
|||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f,
|
||||
MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||
state.mSoundId = esm.mSound;
|
||||
|
||||
mMagicBolts.push_back(state);
|
||||
return true;
|
||||
|
|
|
@ -83,7 +83,12 @@ namespace
|
|||
ptr.getClass().insertObject (ptr, mPhysics);
|
||||
|
||||
updateObjectLocalRotation(ptr, mPhysics, mRendering);
|
||||
MWBase::Environment::get().getWorld()->scaleObject (ptr, ptr.getCellRef().getScale());
|
||||
if (ptr.getRefData().getBaseNode())
|
||||
{
|
||||
float scale = ptr.getCellRef().getScale();
|
||||
ptr.getClass().adjustScale(ptr, scale);
|
||||
mRendering.scaleObject(ptr, Ogre::Vector3(scale));
|
||||
}
|
||||
ptr.getClass().adjustPosition (ptr, false);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
@ -233,6 +238,15 @@ namespace MWWorld
|
|||
insertCell (*cell, true, loadingListener);
|
||||
|
||||
mRendering.cellAdded (cell);
|
||||
bool waterEnabled = cell->getCell()->hasWater();
|
||||
mRendering.setWaterEnabled(waterEnabled);
|
||||
if (waterEnabled)
|
||||
{
|
||||
mPhysics->enableWater(cell->getWaterLevel());
|
||||
mRendering.setWaterHeight(cell->getWaterLevel());
|
||||
}
|
||||
else
|
||||
mPhysics->disableWater();
|
||||
|
||||
mRendering.configureAmbient(*cell);
|
||||
}
|
||||
|
|
|
@ -1253,7 +1253,7 @@ namespace MWWorld
|
|||
|
||||
if (force || !isFlying(ptr))
|
||||
{
|
||||
Ogre::Vector3 traced = mPhysics->traceDown(ptr, 200);
|
||||
Ogre::Vector3 traced = mPhysics->traceDown(ptr, 300);
|
||||
if (traced.z < pos.pos[2])
|
||||
pos.pos[2] = traced.z;
|
||||
}
|
||||
|
@ -1660,6 +1660,7 @@ namespace MWWorld
|
|||
|
||||
void World::setWaterHeight(const float height)
|
||||
{
|
||||
mPhysics->setWaterHeight(height);
|
||||
mRendering->setWaterHeight(height);
|
||||
}
|
||||
|
||||
|
@ -1994,7 +1995,7 @@ namespace MWWorld
|
|||
Ogre::Vector3 playerPos(refdata.getPosition().pos);
|
||||
|
||||
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
|
||||
if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos))
|
||||
if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player))
|
||||
return 2;
|
||||
if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) ||
|
||||
player.getClass().getNpcStats(player).isWerewolf())
|
||||
|
@ -3071,4 +3072,12 @@ namespace MWWorld
|
|||
cellstore->forEach(functor);
|
||||
}
|
||||
}
|
||||
|
||||
bool World::isWalkingOnWater(const Ptr &actor)
|
||||
{
|
||||
OEngine::Physic::PhysicActor* physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle());
|
||||
if (physicActor && physicActor->isWalkingOnWater())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -615,6 +615,8 @@ namespace MWWorld
|
|||
|
||||
/// Resets all actors in the current active cells to their original location within that cell.
|
||||
virtual void resetActors();
|
||||
|
||||
virtual bool isWalkingOnWater (const MWWorld::Ptr& actor);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,8 @@ struct Land
|
|||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank() {}
|
||||
|
||||
/**
|
||||
* Actually loads data
|
||||
*/
|
||||
|
|
|
@ -19,4 +19,10 @@ void LandTexture::save(ESMWriter &esm) const
|
|||
esm.writeHNCString("DATA", mTexture);
|
||||
}
|
||||
|
||||
void LandTexture::blank()
|
||||
{
|
||||
mTexture.clear();
|
||||
mIndex = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ struct LandTexture
|
|||
std::string mId, mTexture;
|
||||
int mIndex;
|
||||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID).
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
|
|
|
@ -53,22 +53,22 @@ AndroidPath::AndroidPath(const std::string& application_name)
|
|||
|
||||
boost::filesystem::path AndroidPath::getUserConfigPath() const
|
||||
{
|
||||
return getEnv("XDG_CONFIG_HOME", "/sdcard/morrowind/config") / mName;
|
||||
return getEnv("XDG_CONFIG_HOME", "/sdcard/libopenmw/config") / mName;
|
||||
}
|
||||
|
||||
boost::filesystem::path AndroidPath::getUserDataPath() const
|
||||
{
|
||||
return getEnv("XDG_DATA_HOME", "/sdcard/morrowind/share") / mName;
|
||||
return getEnv("XDG_DATA_HOME", "/sdcard/libopenmw/share") / mName;
|
||||
}
|
||||
|
||||
boost::filesystem::path AndroidPath::getCachePath() const
|
||||
{
|
||||
return getEnv("XDG_CACHE_HOME", "/sdcard/morrowind/cache") / mName;
|
||||
return getEnv("XDG_CACHE_HOME", "/sdcard/libopenmw/cache") / mName;
|
||||
}
|
||||
|
||||
boost::filesystem::path AndroidPath::getGlobalConfigPath() const
|
||||
{
|
||||
boost::filesystem::path globalPath("/sdcard/morrowind/");
|
||||
boost::filesystem::path globalPath("/sdcard/libopenmw/");
|
||||
return globalPath / mName;
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ boost::filesystem::path AndroidPath::getLocalPath() const
|
|||
|
||||
boost::filesystem::path AndroidPath::getGlobalDataPath() const
|
||||
{
|
||||
boost::filesystem::path globalDataPath("/sdcard/morrowind/data");
|
||||
boost::filesystem::path globalDataPath("/sdcard/libopenmw/data");
|
||||
return globalDataPath / mName;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
// Scroll in y direction
|
||||
float2 scrolledUV = UV + float2(0,1) * cloudAnimationTimer * 0.003;
|
||||
|
||||
float4 albedo = shSample(diffuseMap1, scrolledUV) * (1-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor;
|
||||
float4 albedo = shSample(diffuseMap1, scrolledUV) * (1.0-cloudBlendFactor) + shSample(diffuseMap2, scrolledUV) * cloudBlendFactor;
|
||||
|
||||
shOutputColour(0) = float4(cloudColour, 1) * albedo * float4(1,1,1, cloudOpacity * alphaFade);
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ shUniform(float4x4, projection) @shAutoConstant(projection, projection_matrix)
|
|||
|
||||
shOutputColour(0).a = shSample(alphaMap, UV).a * materialDiffuse.a;
|
||||
|
||||
shOutputColour(0).rgb += (1-tex.a) * shOutputColour(0).a * atmosphereColour.rgb; //fill dark side of moon with atmosphereColour
|
||||
shOutputColour(0).rgb += (1-materialDiffuse.a) * atmosphereColour.rgb; //fade bump
|
||||
shOutputColour(0).rgb += (1.0-tex.a) * shOutputColour(0).a * atmosphereColour.rgb; //fill dark side of moon with atmosphereColour
|
||||
shOutputColour(0).rgb += (1.0-materialDiffuse.a) * atmosphereColour.rgb; //fade bump
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,55 +1,55 @@
|
|||
#include "core.h"
|
||||
|
||||
#define ALPHA @shPropertyBool(shadow_transparency)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
#if ALPHA
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
#endif
|
||||
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
|
||||
shOutput(float2, depth)
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
// this is the view space position
|
||||
shOutputPosition = shMatrixMult(wvp, shInputPosition);
|
||||
|
||||
// depth info for the fragment.
|
||||
depth.x = shOutputPosition.z;
|
||||
depth.y = shOutputPosition.w;
|
||||
|
||||
// clamp z to zero. seem to do the trick. :-/
|
||||
shOutputPosition.z = max(shOutputPosition.z, 0);
|
||||
|
||||
#if ALPHA
|
||||
UV = uv0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
#if ALPHA
|
||||
shInput(float2, UV)
|
||||
shSampler2D(texture1)
|
||||
#endif
|
||||
shInput(float2, depth)
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
float finalDepth = depth.x / depth.y;
|
||||
|
||||
|
||||
#if ALPHA
|
||||
// use alpha channel of the first texture
|
||||
float alpha = shSample(texture1, UV).a;
|
||||
|
||||
if (alpha < 0.5)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
shOutputColour(0) = float4(finalDepth, finalDepth, finalDepth, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
#include "core.h"
|
||||
|
||||
#define ALPHA @shPropertyBool(shadow_transparency)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
#if ALPHA
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
#endif
|
||||
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
|
||||
shOutput(float2, depth)
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
// this is the view space position
|
||||
shOutputPosition = shMatrixMult(wvp, shInputPosition);
|
||||
|
||||
// depth info for the fragment.
|
||||
depth.x = shOutputPosition.z;
|
||||
depth.y = shOutputPosition.w;
|
||||
|
||||
// clamp z to zero. seem to do the trick. :-/
|
||||
shOutputPosition.z = max(shOutputPosition.z, 0.0);
|
||||
|
||||
#if ALPHA
|
||||
UV = uv0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
#if ALPHA
|
||||
shInput(float2, UV)
|
||||
shSampler2D(texture1)
|
||||
#endif
|
||||
shInput(float2, depth)
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
float finalDepth = depth.x / depth.y;
|
||||
|
||||
|
||||
#if ALPHA
|
||||
// use alpha channel of the first texture
|
||||
float alpha = shSample(texture1, UV).a;
|
||||
|
||||
if (alpha < 0.5)
|
||||
discard;
|
||||
#endif
|
||||
|
||||
shOutputColour(0) = float4(finalDepth, finalDepth, finalDepth, 1.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,48 +1,48 @@
|
|||
#include "core.h"
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shUniform(float4x4, worldview) @shAutoConstant(worldview, worldview_matrix)
|
||||
shUniform(float4x4, proj) @shAutoConstant(proj, projection_matrix)
|
||||
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
shOutput(float, fade)
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
float4x4 worldviewFixed = worldview;
|
||||
#if !SH_GLSL
|
||||
worldviewFixed[0][3] = 0;
|
||||
worldviewFixed[1][3] = 0;
|
||||
worldviewFixed[2][3] = 0;
|
||||
#else
|
||||
worldviewFixed[3][0] = 0;
|
||||
worldviewFixed[3][1] = 0;
|
||||
worldviewFixed[3][2] = 0;
|
||||
#endif
|
||||
|
||||
shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition));
|
||||
UV = uv0;
|
||||
|
||||
fade = (shInputPosition.z > 50) ? 1 : 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
|
||||
shInput(float2, UV)
|
||||
shInput(float, fade)
|
||||
|
||||
shSampler2D(diffuseMap)
|
||||
shUniform(float, nightFade) @shSharedParameter(nightFade)
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputColour(0) = shSample(diffuseMap, UV) * float4(1,1,1, nightFade * fade);
|
||||
}
|
||||
|
||||
#endif
|
||||
#include "core.h"
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shUniform(float4x4, worldview) @shAutoConstant(worldview, worldview_matrix)
|
||||
shUniform(float4x4, proj) @shAutoConstant(proj, projection_matrix)
|
||||
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
shOutput(float, fade)
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
float4x4 worldviewFixed = worldview;
|
||||
#if !SH_GLSL
|
||||
worldviewFixed[0][3] = 0.0;
|
||||
worldviewFixed[1][3] = 0.0;
|
||||
worldviewFixed[2][3] = 0.0;
|
||||
#else
|
||||
worldviewFixed[3][0] = 0.0;
|
||||
worldviewFixed[3][1] = 0.0;
|
||||
worldviewFixed[3][2] = 0.0;
|
||||
#endif
|
||||
|
||||
shOutputPosition = shMatrixMult(proj, shMatrixMult(worldviewFixed, shInputPosition));
|
||||
UV = uv0;
|
||||
|
||||
fade = (shInputPosition.z > 50.0) ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
|
||||
shInput(float2, UV)
|
||||
shInput(float, fade)
|
||||
|
||||
shSampler2D(diffuseMap)
|
||||
shUniform(float, nightFade) @shSharedParameter(nightFade)
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputColour(0) = shSample(diffuseMap, UV) * float4(1,1,1, nightFade * fade);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,370 +1,370 @@
|
|||
#include "core.h"
|
||||
|
||||
|
||||
#define SIMPLE_WATER @shGlobalSettingBool(simple_water)
|
||||
|
||||
#if SIMPLE_WATER
|
||||
// --------------------------------------- SIMPLE WATER ---------------------------------------------------
|
||||
|
||||
#define FOG @shGlobalSettingBool(fog)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
|
||||
#if FOG
|
||||
shOutput(float, depth)
|
||||
#endif
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputPosition = shMatrixMult(wvp, shInputPosition);
|
||||
UV = uv0;
|
||||
#if FOG
|
||||
depth = shOutputPosition.z;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shSampler2D(animatedTexture)
|
||||
shInput(float2, UV)
|
||||
shInput(float, depth)
|
||||
|
||||
shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour)
|
||||
shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params)
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputColour(0).xyz = shSample(animatedTexture, UV * float2(15.0, 15.0)).xyz * float3(1.0, 1.0, 1.0);
|
||||
shOutputColour(0).w = 0.7;
|
||||
|
||||
#if FOG
|
||||
float fogValue = shSaturate((depth - fogParams.y) * fogParams.w);
|
||||
shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
|
||||
|
||||
// Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )
|
||||
|
||||
#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm)
|
||||
#define SHADOWS @shGlobalSettingBool(shadows)
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
#include "shadows.h"
|
||||
#endif
|
||||
|
||||
#define RIPPLES 1
|
||||
#define REFRACTION @shGlobalSettingBool(refraction)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
|
||||
shOutput(float3, screenCoordsPassthrough)
|
||||
shOutput(float4, position)
|
||||
shOutput(float, depthPassthrough)
|
||||
|
||||
|
||||
#if SHADOWS
|
||||
shOutput(float4, lightSpacePos0)
|
||||
shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix)
|
||||
#endif
|
||||
|
||||
#if SHADOWS_PSSM
|
||||
@shForeach(3)
|
||||
shOutput(float4, lightSpacePos@shIterator)
|
||||
shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator)
|
||||
@shEndForeach
|
||||
#endif
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix)
|
||||
#endif
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputPosition = shMatrixMult(wvp, shInputPosition);
|
||||
UV = uv0;
|
||||
|
||||
|
||||
#if !SH_GLSL
|
||||
float4x4 scalemat = float4x4( 0.5, 0, 0, 0.5,
|
||||
0, -0.5, 0, 0.5,
|
||||
0, 0, 0.5, 0.5,
|
||||
0, 0, 0, 1 );
|
||||
#else
|
||||
mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0,
|
||||
0.0, -0.5, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0);
|
||||
#endif
|
||||
|
||||
float4 texcoordProj = shMatrixMult(scalemat, shOutputPosition);
|
||||
screenCoordsPassthrough = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w);
|
||||
|
||||
position = shInputPosition;
|
||||
|
||||
depthPassthrough = shOutputPosition.z;
|
||||
|
||||
|
||||
#if SHADOWS
|
||||
lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition));
|
||||
#endif
|
||||
#if SHADOWS_PSSM
|
||||
float4 wPos = shMatrixMult(worldMatrix, shInputPosition);
|
||||
@shForeach(3)
|
||||
lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos);
|
||||
@shEndForeach
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// tweakables ----------------------------------------------------
|
||||
|
||||
#define VISIBILITY 1500.0 // how far you can look through water
|
||||
|
||||
#define BIG_WAVES_X 0.3 // strength of big waves
|
||||
#define BIG_WAVES_Y 0.3
|
||||
|
||||
#define MID_WAVES_X 0.3 // strength of middle sized waves
|
||||
#define MID_WAVES_Y 0.15
|
||||
|
||||
#define SMALL_WAVES_X 0.15 // strength of small waves
|
||||
#define SMALL_WAVES_Y 0.1
|
||||
|
||||
#define WAVE_CHOPPYNESS 0.15 // wave choppyness
|
||||
#define WAVE_SCALE 75 // overall wave scale
|
||||
|
||||
#define BUMP 1.5 // overall water surface bumpiness
|
||||
#define REFL_BUMP 0.08 // reflection distortion amount
|
||||
#define REFR_BUMP 0.06 // refraction distortion amount
|
||||
|
||||
#define SCATTER_AMOUNT 0.3 // amount of sunlight scattering
|
||||
#define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering
|
||||
|
||||
#define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction
|
||||
|
||||
#define SPEC_HARDNESS 256 // specular highlights hardness
|
||||
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
float fresnel_dielectric(float3 Incoming, float3 Normal, float eta)
|
||||
{
|
||||
/* compute fresnel reflectance without explicitly computing
|
||||
the refracted direction */
|
||||
float c = abs(dot(Incoming, Normal));
|
||||
float g = eta * eta - 1.0 + c * c;
|
||||
float result;
|
||||
|
||||
if(g > 0.0) {
|
||||
g = sqrt(g);
|
||||
float A =(g - c)/(g + c);
|
||||
float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0);
|
||||
result = 0.5 * A * A *(1.0 + B * B);
|
||||
}
|
||||
else
|
||||
result = 1.0; /* TIR (no refracted component) */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shInput(float2, UV)
|
||||
shInput(float3, screenCoordsPassthrough)
|
||||
shInput(float4, position)
|
||||
shInput(float, depthPassthrough)
|
||||
|
||||
#if RIPPLES
|
||||
shUniform(float3, rippleCenter) @shSharedParameter(rippleCenter, rippleCenter)
|
||||
shUniform(float, rippleAreaLength) @shSharedParameter(rippleAreaLength, rippleAreaLength)
|
||||
#endif
|
||||
|
||||
shUniform(float, far) @shAutoConstant(far, far_clip_distance)
|
||||
|
||||
shSampler2D(reflectionMap)
|
||||
#if REFRACTION
|
||||
shSampler2D(refractionMap)
|
||||
#endif
|
||||
shSampler2D(depthMap)
|
||||
shSampler2D(normalMap)
|
||||
|
||||
#if RIPPLES
|
||||
shSampler2D(rippleNormalMap)
|
||||
shUniform(float4x4, wMat) @shAutoConstant(wMat, world_matrix)
|
||||
#endif
|
||||
|
||||
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
|
||||
#define WIND_SPEED windDir_windSpeed.z
|
||||
#define WIND_DIR windDir_windSpeed.xy
|
||||
|
||||
shUniform(float, waterTimer) @shSharedParameter(waterTimer)
|
||||
shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight)
|
||||
|
||||
shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0)
|
||||
shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0)
|
||||
|
||||
shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping)
|
||||
|
||||
|
||||
shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour)
|
||||
shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params)
|
||||
|
||||
shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space)
|
||||
|
||||
|
||||
#if SHADOWS
|
||||
shInput(float4, lightSpacePos0)
|
||||
shSampler2D(shadowMap0)
|
||||
shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1)
|
||||
#endif
|
||||
#if SHADOWS_PSSM
|
||||
@shForeach(3)
|
||||
shInput(float4, lightSpacePos@shIterator)
|
||||
shSampler2D(shadowMap@shIterator)
|
||||
shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1))
|
||||
@shEndForeach
|
||||
shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints)
|
||||
#endif
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart)
|
||||
#endif
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
#if SHADOWS
|
||||
float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0);
|
||||
#endif
|
||||
#if SHADOWS_PSSM
|
||||
float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints);
|
||||
#endif
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y;
|
||||
float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange);
|
||||
shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow);
|
||||
#endif
|
||||
|
||||
#if !SHADOWS && !SHADOWS_PSSM
|
||||
float shadow = 1.0;
|
||||
#endif
|
||||
|
||||
|
||||
float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;
|
||||
screenCoords.y = (1-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y;
|
||||
|
||||
float2 nCoord = float2(0,0);
|
||||
|
||||
nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04);
|
||||
float3 normal0 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 0.1) + WIND_DIR * waterTimer * (WIND_SPEED*0.08)-(normal0.xy/normal0.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal1 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.020,+waterTimer*0.015)).rgb - 1.0;
|
||||
|
||||
nCoord = UV * (WAVE_SCALE * 0.25) + WIND_DIR * waterTimer * (WIND_SPEED*0.07)-(normal1.xy/normal1.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal2 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.04,-waterTimer*0.03)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 0.5) + WIND_DIR * waterTimer * (WIND_SPEED*0.09)-(normal2.xy/normal2.z)*WAVE_CHOPPYNESS;
|
||||
float3 normal3 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.03,+waterTimer*0.04)).rgb - 1.0;
|
||||
|
||||
nCoord = UV * (WAVE_SCALE* 1.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.4)-(normal3.xy/normal3.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal4 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.02,+waterTimer*0.1)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 2.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.7)-(normal4.xy/normal4.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal5 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.1,-waterTimer*0.06)).rgb - 1.0;
|
||||
|
||||
|
||||
|
||||
float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y +
|
||||
normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y +
|
||||
normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y);
|
||||
|
||||
float4 worldPosition = shMatrixMult(wMat, float4(position.xyz, 1));
|
||||
float2 relPos = (worldPosition.xy - rippleCenter.xy) / rippleAreaLength + 0.5;
|
||||
float3 normal_ripple = normalize(shSample(rippleNormalMap, relPos.xy).xyz * 2 - 1);
|
||||
|
||||
//normal = normalize(normal + normal_ripple);
|
||||
normal = normalize(float3(normal.x * BUMP + normal_ripple.x, normal.y * BUMP + normal_ripple.y, normal.z));
|
||||
normal = float3(normal.x, normal.y, -normal.z);
|
||||
|
||||
// normal for sunlight scattering
|
||||
float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 +
|
||||
normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 +
|
||||
normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xyz;
|
||||
lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z));
|
||||
lNormal = float3(lNormal.x, lNormal.y, -lNormal.z);
|
||||
|
||||
|
||||
float3 lVec = normalize(sunPosition.xyz);
|
||||
float3 vVec = normalize(position.xyz - cameraPos.xyz);
|
||||
|
||||
|
||||
float isUnderwater = (cameraPos.z > 0) ? 0.0 : 1.0;
|
||||
|
||||
// sunlight scattering
|
||||
float3 pNormal = float3(0,0,1);
|
||||
float3 lR = reflect(lVec, lNormal);
|
||||
float3 llR = reflect(lVec, pNormal);
|
||||
|
||||
float s = shSaturate(dot(lR, vVec)*2.0-1.2);
|
||||
float lightScatter = shadow * shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y));
|
||||
float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT)));
|
||||
|
||||
// fresnel
|
||||
float ior = (cameraPos.z>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air
|
||||
float fresnel = fresnel_dielectric(-vVec, normal, ior);
|
||||
|
||||
fresnel = shSaturate(fresnel);
|
||||
|
||||
// reflection
|
||||
float3 reflection = shSample(reflectionMap, screenCoords+(normal.xy*REFL_BUMP)).rgb;
|
||||
|
||||
// refraction
|
||||
float3 R = reflect(vVec, normal);
|
||||
|
||||
#if REFRACTION
|
||||
float3 refraction = shSample(refractionMap, (screenCoords-(normal.xy*REFR_BUMP))*1.0).rgb;
|
||||
|
||||
// brighten up the refraction underwater
|
||||
refraction = (cameraPos.z < 0) ? shSaturate(refraction * 1.5) : refraction;
|
||||
#endif
|
||||
|
||||
// specular
|
||||
float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow;
|
||||
|
||||
#if REFRACTION
|
||||
shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz;
|
||||
#else
|
||||
shOutputColour(0).xyz = shLerp(reflection, float3(0.090195, 0.115685, 0.12745), (1.0-fresnel)*0.5) + specular * sunSpecular.xyz;
|
||||
#endif
|
||||
// fog
|
||||
float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w);
|
||||
shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue);
|
||||
|
||||
#if REFRACTION
|
||||
shOutputColour(0).w = 1;
|
||||
#else
|
||||
shOutputColour(0).w = shSaturate(fresnel*2 + specular);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
#include "core.h"
|
||||
|
||||
|
||||
#define SIMPLE_WATER @shGlobalSettingBool(simple_water)
|
||||
|
||||
#if SIMPLE_WATER
|
||||
// --------------------------------------- SIMPLE WATER ---------------------------------------------------
|
||||
|
||||
#define FOG @shGlobalSettingBool(fog)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
|
||||
#if FOG
|
||||
shOutput(float, depth)
|
||||
#endif
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputPosition = shMatrixMult(wvp, shInputPosition);
|
||||
UV = uv0;
|
||||
#if FOG
|
||||
depth = shOutputPosition.z;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shSampler2D(animatedTexture)
|
||||
shInput(float2, UV)
|
||||
shInput(float, depth)
|
||||
|
||||
shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour)
|
||||
shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params)
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputColour(0).xyz = shSample(animatedTexture, UV * float2(15.0, 15.0)).xyz * float3(1.0, 1.0, 1.0);
|
||||
shOutputColour(0).w = 0.7;
|
||||
|
||||
#if FOG
|
||||
float fogValue = shSaturate((depth - fogParams.y) * fogParams.w);
|
||||
shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
|
||||
|
||||
// Inspired by Blender GLSL Water by martinsh ( http://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )
|
||||
|
||||
#define SHADOWS_PSSM @shGlobalSettingBool(shadows_pssm)
|
||||
#define SHADOWS @shGlobalSettingBool(shadows)
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
#include "shadows.h"
|
||||
#endif
|
||||
|
||||
#define RIPPLES 1
|
||||
#define REFRACTION @shGlobalSettingBool(refraction)
|
||||
|
||||
#ifdef SH_VERTEX_SHADER
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix)
|
||||
shVertexInput(float2, uv0)
|
||||
shOutput(float2, UV)
|
||||
|
||||
shOutput(float3, screenCoordsPassthrough)
|
||||
shOutput(float4, position)
|
||||
shOutput(float, depthPassthrough)
|
||||
|
||||
|
||||
#if SHADOWS
|
||||
shOutput(float4, lightSpacePos0)
|
||||
shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix)
|
||||
#endif
|
||||
|
||||
#if SHADOWS_PSSM
|
||||
@shForeach(3)
|
||||
shOutput(float4, lightSpacePos@shIterator)
|
||||
shUniform(float4x4, texViewProjMatrix@shIterator) @shAutoConstant(texViewProjMatrix@shIterator, texture_viewproj_matrix, @shIterator)
|
||||
@shEndForeach
|
||||
#endif
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
shUniform(float4x4, worldMatrix) @shAutoConstant(worldMatrix, world_matrix)
|
||||
#endif
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
shOutputPosition = shMatrixMult(wvp, shInputPosition);
|
||||
UV = uv0;
|
||||
|
||||
|
||||
#if !SH_GLSL
|
||||
float4x4 scalemat = float4x4( 0.5, 0.0, 0.0, 0.5,
|
||||
0.0, -0.5, 0.0, 0.5,
|
||||
0.0, 0.0, 0.5, 0.5,
|
||||
0.0, 0.0, 0.0, 1.0 );
|
||||
#else
|
||||
mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0,
|
||||
0.0, -0.5, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0);
|
||||
#endif
|
||||
|
||||
float4 texcoordProj = shMatrixMult(scalemat, shOutputPosition);
|
||||
screenCoordsPassthrough = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w);
|
||||
|
||||
position = shInputPosition;
|
||||
|
||||
depthPassthrough = shOutputPosition.z;
|
||||
|
||||
|
||||
#if SHADOWS
|
||||
lightSpacePos0 = shMatrixMult(texViewProjMatrix0, shMatrixMult(worldMatrix, shInputPosition));
|
||||
#endif
|
||||
#if SHADOWS_PSSM
|
||||
float4 wPos = shMatrixMult(worldMatrix, shInputPosition);
|
||||
@shForeach(3)
|
||||
lightSpacePos@shIterator = shMatrixMult(texViewProjMatrix@shIterator, wPos);
|
||||
@shEndForeach
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// tweakables ----------------------------------------------------
|
||||
|
||||
#define VISIBILITY 1500.0 // how far you can look through water
|
||||
|
||||
#define BIG_WAVES_X 0.3 // strength of big waves
|
||||
#define BIG_WAVES_Y 0.3
|
||||
|
||||
#define MID_WAVES_X 0.3 // strength of middle sized waves
|
||||
#define MID_WAVES_Y 0.15
|
||||
|
||||
#define SMALL_WAVES_X 0.15 // strength of small waves
|
||||
#define SMALL_WAVES_Y 0.1
|
||||
|
||||
#define WAVE_CHOPPYNESS 0.15 // wave choppyness
|
||||
#define WAVE_SCALE 75.0 // overall wave scale
|
||||
|
||||
#define BUMP 1.5 // overall water surface bumpiness
|
||||
#define REFL_BUMP 0.08 // reflection distortion amount
|
||||
#define REFR_BUMP 0.06 // refraction distortion amount
|
||||
|
||||
#define SCATTER_AMOUNT 0.3 // amount of sunlight scattering
|
||||
#define SCATTER_COLOUR float3(0.0,1.0,0.95) // colour of sunlight scattering
|
||||
|
||||
#define SUN_EXT float3(0.45, 0.55, 0.68) //sunlight extinction
|
||||
|
||||
#define SPEC_HARDNESS 256.0 // specular highlights hardness
|
||||
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
float fresnel_dielectric(float3 Incoming, float3 Normal, float eta)
|
||||
{
|
||||
/* compute fresnel reflectance without explicitly computing
|
||||
the refracted direction */
|
||||
float c = abs(dot(Incoming, Normal));
|
||||
float g = eta * eta - 1.0 + c * c;
|
||||
float result;
|
||||
|
||||
if(g > 0.0) {
|
||||
g = sqrt(g);
|
||||
float A =(g - c)/(g + c);
|
||||
float B =(c *(g + c)- 1.0)/(c *(g - c)+ 1.0);
|
||||
result = 0.5 * A * A *(1.0 + B * B);
|
||||
}
|
||||
else
|
||||
result = 1.0; /* TIR (no refracted component) */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SH_BEGIN_PROGRAM
|
||||
shInput(float2, UV)
|
||||
shInput(float3, screenCoordsPassthrough)
|
||||
shInput(float4, position)
|
||||
shInput(float, depthPassthrough)
|
||||
|
||||
#if RIPPLES
|
||||
shUniform(float3, rippleCenter) @shSharedParameter(rippleCenter, rippleCenter)
|
||||
shUniform(float, rippleAreaLength) @shSharedParameter(rippleAreaLength, rippleAreaLength)
|
||||
#endif
|
||||
|
||||
shUniform(float, far) @shAutoConstant(far, far_clip_distance)
|
||||
|
||||
shSampler2D(reflectionMap)
|
||||
#if REFRACTION
|
||||
shSampler2D(refractionMap)
|
||||
#endif
|
||||
shSampler2D(depthMap)
|
||||
shSampler2D(normalMap)
|
||||
|
||||
#if RIPPLES
|
||||
shSampler2D(rippleNormalMap)
|
||||
shUniform(float4x4, wMat) @shAutoConstant(wMat, world_matrix)
|
||||
#endif
|
||||
|
||||
shUniform(float3, windDir_windSpeed) @shSharedParameter(windDir_windSpeed)
|
||||
#define WIND_SPEED windDir_windSpeed.z
|
||||
#define WIND_DIR windDir_windSpeed.xy
|
||||
|
||||
shUniform(float, waterTimer) @shSharedParameter(waterTimer)
|
||||
shUniform(float2, waterSunFade_sunHeight) @shSharedParameter(waterSunFade_sunHeight)
|
||||
|
||||
shUniform(float4, sunPosition) @shAutoConstant(sunPosition, light_position, 0)
|
||||
shUniform(float4, sunSpecular) @shAutoConstant(sunSpecular, light_specular_colour, 0)
|
||||
|
||||
shUniform(float, renderTargetFlipping) @shAutoConstant(renderTargetFlipping, render_target_flipping)
|
||||
|
||||
|
||||
shUniform(float3, fogColor) @shAutoConstant(fogColor, fog_colour)
|
||||
shUniform(float4, fogParams) @shAutoConstant(fogParams, fog_params)
|
||||
|
||||
shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position_object_space)
|
||||
|
||||
|
||||
#if SHADOWS
|
||||
shInput(float4, lightSpacePos0)
|
||||
shSampler2D(shadowMap0)
|
||||
shUniform(float2, invShadowmapSize0) @shAutoConstant(invShadowmapSize0, inverse_texture_size, 1)
|
||||
#endif
|
||||
#if SHADOWS_PSSM
|
||||
@shForeach(3)
|
||||
shInput(float4, lightSpacePos@shIterator)
|
||||
shSampler2D(shadowMap@shIterator)
|
||||
shUniform(float2, invShadowmapSize@shIterator) @shAutoConstant(invShadowmapSize@shIterator, inverse_texture_size, @shIterator(1))
|
||||
@shEndForeach
|
||||
shUniform(float3, pssmSplitPoints) @shSharedParameter(pssmSplitPoints)
|
||||
#endif
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
shUniform(float4, shadowFar_fadeStart) @shSharedParameter(shadowFar_fadeStart)
|
||||
#endif
|
||||
|
||||
|
||||
SH_START_PROGRAM
|
||||
{
|
||||
#if SHADOWS
|
||||
float shadow = depthShadowPCF (shadowMap0, lightSpacePos0, invShadowmapSize0);
|
||||
#endif
|
||||
#if SHADOWS_PSSM
|
||||
float shadow = pssmDepthShadow (lightSpacePos0, invShadowmapSize0, shadowMap0, lightSpacePos1, invShadowmapSize1, shadowMap1, lightSpacePos2, invShadowmapSize2, shadowMap2, depthPassthrough, pssmSplitPoints);
|
||||
#endif
|
||||
|
||||
#if SHADOWS || SHADOWS_PSSM
|
||||
float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y;
|
||||
float fade = 1.0-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange);
|
||||
shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow);
|
||||
#endif
|
||||
|
||||
#if !SHADOWS && !SHADOWS_PSSM
|
||||
float shadow = 1.0;
|
||||
#endif
|
||||
|
||||
|
||||
float2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;
|
||||
screenCoords.y = (1.0-shSaturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y;
|
||||
|
||||
float2 nCoord = float2(0.0,0.0);
|
||||
|
||||
nCoord = UV * (WAVE_SCALE * 0.05) + WIND_DIR * waterTimer * (WIND_SPEED*0.04);
|
||||
float3 normal0 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.015,-waterTimer*0.005)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 0.1) + WIND_DIR * waterTimer * (WIND_SPEED*0.08)-(normal0.xy/normal0.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal1 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.020,+waterTimer*0.015)).rgb - 1.0;
|
||||
|
||||
nCoord = UV * (WAVE_SCALE * 0.25) + WIND_DIR * waterTimer * (WIND_SPEED*0.07)-(normal1.xy/normal1.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal2 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.04,-waterTimer*0.03)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 0.5) + WIND_DIR * waterTimer * (WIND_SPEED*0.09)-(normal2.xy/normal2.z)*WAVE_CHOPPYNESS;
|
||||
float3 normal3 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.03,+waterTimer*0.04)).rgb - 1.0;
|
||||
|
||||
nCoord = UV * (WAVE_SCALE* 1.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.4)-(normal3.xy/normal3.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal4 = 2.0 * shSample(normalMap, nCoord + float2(-waterTimer*0.02,+waterTimer*0.1)).rgb - 1.0;
|
||||
nCoord = UV * (WAVE_SCALE * 2.0) + WIND_DIR * waterTimer * (WIND_SPEED*0.7)-(normal4.xy/normal4.zz)*WAVE_CHOPPYNESS;
|
||||
float3 normal5 = 2.0 * shSample(normalMap, nCoord + float2(+waterTimer*0.1,-waterTimer*0.06)).rgb - 1.0;
|
||||
|
||||
|
||||
|
||||
float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y +
|
||||
normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y +
|
||||
normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y);
|
||||
|
||||
float4 worldPosition = shMatrixMult(wMat, float4(position.xyz, 1));
|
||||
float2 relPos = (worldPosition.xy - rippleCenter.xy) / rippleAreaLength + 0.5;
|
||||
float3 normal_ripple = normalize(shSample(rippleNormalMap, relPos.xy).xyz * 2.0 - 1.0);
|
||||
|
||||
//normal = normalize(normal + normal_ripple);
|
||||
normal = normalize(float3(normal.x * BUMP + normal_ripple.x, normal.y * BUMP + normal_ripple.y, normal.z));
|
||||
normal = float3(normal.x, normal.y, -normal.z);
|
||||
|
||||
// normal for sunlight scattering
|
||||
float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 +
|
||||
normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 +
|
||||
normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xyz;
|
||||
lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z));
|
||||
lNormal = float3(lNormal.x, lNormal.y, -lNormal.z);
|
||||
|
||||
|
||||
float3 lVec = normalize(sunPosition.xyz);
|
||||
float3 vVec = normalize(position.xyz - cameraPos.xyz);
|
||||
|
||||
|
||||
float isUnderwater = (cameraPos.z > 0.0) ? 0.0 : 1.0;
|
||||
|
||||
// sunlight scattering
|
||||
float3 pNormal = float3(0,0,1);
|
||||
float3 lR = reflect(lVec, lNormal);
|
||||
float3 llR = reflect(lVec, pNormal);
|
||||
|
||||
float s = shSaturate(dot(lR, vVec)*2.0-1.2);
|
||||
float lightScatter = shadow * shSaturate(dot(-lVec,lNormal)*0.7+0.3) * s * SCATTER_AMOUNT * waterSunFade_sunHeight.x * shSaturate(1.0-exp(-waterSunFade_sunHeight.y));
|
||||
float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*float3(1.0,0.4,0.0), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT)));
|
||||
|
||||
// fresnel
|
||||
float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); //air to water; water to air
|
||||
float fresnel = fresnel_dielectric(-vVec, normal, ior);
|
||||
|
||||
fresnel = shSaturate(fresnel);
|
||||
|
||||
// reflection
|
||||
float3 reflection = shSample(reflectionMap, screenCoords+(normal.xy*REFL_BUMP)).rgb;
|
||||
|
||||
// refraction
|
||||
float3 R = reflect(vVec, normal);
|
||||
|
||||
#if REFRACTION
|
||||
float3 refraction = shSample(refractionMap, (screenCoords-(normal.xy*REFR_BUMP))*1.0).rgb;
|
||||
|
||||
// brighten up the refraction underwater
|
||||
refraction = (cameraPos.z < 0.0) ? shSaturate(refraction * 1.5) : refraction;
|
||||
#endif
|
||||
|
||||
// specular
|
||||
float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow;
|
||||
|
||||
#if REFRACTION
|
||||
shOutputColour(0).xyz = shLerp( shLerp(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpecular.xyz;
|
||||
#else
|
||||
shOutputColour(0).xyz = shLerp(reflection, float3(0.090195, 0.115685, 0.12745), (1.0-fresnel)*0.5) + specular * sunSpecular.xyz;
|
||||
#endif
|
||||
// fog
|
||||
float fogValue = shSaturate((depthPassthrough - fogParams.y) * fogParams.w);
|
||||
shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, fogColor, fogValue);
|
||||
|
||||
#if REFRACTION
|
||||
shOutputColour(0).w = 1.0;
|
||||
#else
|
||||
shOutputColour(0).w = shSaturate(fresnel*2.0 + specular);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -74,6 +74,8 @@ namespace Physic
|
|||
, mExternalCollisionMode(true)
|
||||
, mForce(0.0f)
|
||||
, mScale(scale)
|
||||
, mWalkingOnWater(false)
|
||||
, mCanWaterWalk(false)
|
||||
{
|
||||
if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation))
|
||||
{
|
||||
|
@ -103,8 +105,7 @@ namespace Physic
|
|||
setPosition(position);
|
||||
setRotation(rotation);
|
||||
|
||||
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor,
|
||||
CollisionType_Actor|CollisionType_World|CollisionType_HeightMap);
|
||||
updateCollisionMask();
|
||||
}
|
||||
|
||||
PhysicActor::~PhysicActor()
|
||||
|
@ -123,10 +124,22 @@ namespace Physic
|
|||
|
||||
void PhysicActor::enableCollisionBody(bool collision)
|
||||
{
|
||||
assert(mBody);
|
||||
if(collision && !mExternalCollisionMode) enableCollisionBody();
|
||||
if(!collision && mExternalCollisionMode) disableCollisionBody();
|
||||
mExternalCollisionMode = collision;
|
||||
if (mExternalCollisionMode != collision)
|
||||
{
|
||||
mExternalCollisionMode = collision;
|
||||
updateCollisionMask();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicActor::updateCollisionMask()
|
||||
{
|
||||
mEngine->mDynamicsWorld->removeRigidBody(mBody);
|
||||
int collisionMask = CollisionType_World | CollisionType_HeightMap;
|
||||
if (mExternalCollisionMode)
|
||||
collisionMask |= CollisionType_Actor | CollisionType_Projectile;
|
||||
if (mCanWaterWalk)
|
||||
collisionMask |= CollisionType_Water;
|
||||
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, collisionMask);
|
||||
}
|
||||
|
||||
const Ogre::Vector3& PhysicActor::getPosition() const
|
||||
|
@ -178,18 +191,23 @@ namespace Physic
|
|||
mOnGround = grounded;
|
||||
}
|
||||
|
||||
void PhysicActor::disableCollisionBody()
|
||||
bool PhysicActor::isWalkingOnWater() const
|
||||
{
|
||||
mEngine->mDynamicsWorld->removeRigidBody(mBody);
|
||||
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor,
|
||||
CollisionType_World|CollisionType_HeightMap);
|
||||
return mWalkingOnWater;
|
||||
}
|
||||
|
||||
void PhysicActor::enableCollisionBody()
|
||||
void PhysicActor::setWalkingOnWater(bool walkingOnWater)
|
||||
{
|
||||
mEngine->mDynamicsWorld->removeRigidBody(mBody);
|
||||
mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor,
|
||||
CollisionType_Actor|CollisionType_World|CollisionType_HeightMap);
|
||||
mWalkingOnWater = walkingOnWater;
|
||||
}
|
||||
|
||||
void PhysicActor::setCanWaterWalk(bool waterWalk)
|
||||
{
|
||||
if (waterWalk != mCanWaterWalk)
|
||||
{
|
||||
mCanWaterWalk = waterWalk;
|
||||
updateCollisionMask();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -381,7 +399,7 @@ namespace Physic
|
|||
mHeightFieldMap [name] = hf;
|
||||
|
||||
mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap,
|
||||
CollisionType_Actor|CollisionType_Raycasting);
|
||||
CollisionType_Actor|CollisionType_Raycasting|CollisionType_Projectile);
|
||||
}
|
||||
|
||||
void PhysicEngine::removeHeightField(int x, int y)
|
||||
|
@ -494,7 +512,7 @@ namespace Physic
|
|||
{
|
||||
assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end());
|
||||
mRaycastingObjectMap[name] = body;
|
||||
mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting);
|
||||
mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_Projectile);
|
||||
}
|
||||
|
||||
return body;
|
||||
|
@ -800,10 +818,10 @@ namespace Physic
|
|||
return std::make_pair(false, 1);
|
||||
}
|
||||
|
||||
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to)
|
||||
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to, int filterGroup)
|
||||
{
|
||||
MyRayResultCallback resultCallback1;
|
||||
resultCallback1.m_collisionFilterGroup = 0xff;
|
||||
resultCallback1.m_collisionFilterGroup = filterGroup;
|
||||
resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor|CollisionType_HeightMap;
|
||||
mDynamicsWorld->rayTest(from, to, resultCallback1);
|
||||
std::vector< std::pair<float, const btCollisionObject*> > results = resultCallback1.results;
|
||||
|
|
|
@ -46,7 +46,9 @@ namespace Physic
|
|||
CollisionType_World = 1<<0, //<Collide with world objects
|
||||
CollisionType_Actor = 1<<1, //<Collide sith actors
|
||||
CollisionType_HeightMap = 1<<2, //<collide with heightmap
|
||||
CollisionType_Raycasting = 1<<3 //Still used?
|
||||
CollisionType_Raycasting = 1<<3,
|
||||
CollisionType_Projectile = 1<<4,
|
||||
CollisionType_Water = 1<<5
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -130,9 +132,20 @@ namespace Physic
|
|||
return mBody;
|
||||
}
|
||||
|
||||
|
||||
/// Sets whether this actor should be able to collide with the water surface
|
||||
void setCanWaterWalk(bool waterWalk);
|
||||
|
||||
/// Sets whether this actor has been walking on the water surface in the last frame
|
||||
void setWalkingOnWater(bool walkingOnWater);
|
||||
bool isWalkingOnWater() const;
|
||||
|
||||
private:
|
||||
void disableCollisionBody();
|
||||
void enableCollisionBody();
|
||||
/// Removes then re-adds the collision body to the dynamics world
|
||||
void updateCollisionMask();
|
||||
|
||||
bool mCanWaterWalk;
|
||||
bool mWalkingOnWater;
|
||||
|
||||
boost::shared_ptr<btCollisionShape> mShape;
|
||||
|
||||
|
@ -288,7 +301,7 @@ namespace Physic
|
|||
/**
|
||||
* Return all objects hit by a ray.
|
||||
*/
|
||||
std::vector< std::pair<float, std::string> > rayTest2(const btVector3 &from, const btVector3 &to);
|
||||
std::vector< std::pair<float, std::string> > rayTest2(const btVector3 &from, const btVector3 &to, int filterGroup=0xff);
|
||||
|
||||
std::pair<bool, float> sphereCast (float radius, btVector3& from, btVector3& to);
|
||||
///< @return (hit, relative distance)
|
||||
|
|
|
@ -105,6 +105,7 @@ void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Og
|
|||
// Inherit the actor's collision group and mask
|
||||
newTraceCallback.m_collisionFilterGroup = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterGroup;
|
||||
newTraceCallback.m_collisionFilterMask = actor->getCollisionBody()->getBroadphaseHandle()->m_collisionFilterMask;
|
||||
newTraceCallback.m_collisionFilterMask &= ~CollisionType_Actor;
|
||||
|
||||
btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z);
|
||||
|
||||
|
|
Loading…
Reference in a new issue