diff --git a/.travis.yml b/.travis.yml index 9308ccf871..c35d292019 100644 --- a/.travis.yml +++ b/.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 diff --git a/CI/before_install.linux.sh b/CI/before_install.linux.sh new file mode 100755 index 0000000000..998c285db2 --- /dev/null +++ b/CI/before_install.linux.sh @@ -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 diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh new file mode 100755 index 0000000000..b1d4f991ba --- /dev/null +++ b/CI/before_install.osx.sh @@ -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 diff --git a/CI/before_script.linux.sh b/CI/before_script.linux.sh new file mode 100755 index 0000000000..b4889c9e17 --- /dev/null +++ b/CI/before_script.linux.sh @@ -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 diff --git a/CI/before_script.osx.sh b/CI/before_script.osx.sh new file mode 100755 index 0000000000..772284f914 --- /dev/null +++ b/CI/before_script.osx.sh @@ -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" .. diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ff29a7da20..ff28dfce47 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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 diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index bb4fa0b436..78349a8db6 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -539,6 +539,16 @@ CSMWorld::IdCollection& CSMWorld::Data::getDebugProfiles() return mDebugProfiles; } +const CSMWorld::IdCollection& CSMWorld::Data::getLand() const +{ + return mLand; +} + +const CSMWorld::IdCollection& 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 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 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 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()); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index d0a07c6777..aa4e640c28 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -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 mCells; + IdCollection mLandTextures; + IdCollection mLand; RefIdCollection mReferenceables; RefCollection mRefs; IdCollection mFilters; @@ -88,6 +92,8 @@ namespace CSMWorld bool mProject; std::map > mRefLoadCache; + std::vector > mReaders; + // not implemented Data (const Data&); Data& operator= (const Data&); @@ -195,6 +201,10 @@ namespace CSMWorld IdCollection& getDebugProfiles(); + const IdCollection& getLand() const; + + const IdCollection& 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 \ No newline at end of file +#endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 3a48317eff..c68b717896 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -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(); -} \ No newline at end of file +} diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp new file mode 100644 index 0000000000..119e187616 --- /dev/null +++ b/apps/opencs/model/world/land.cpp @@ -0,0 +1,28 @@ +#include "land.hpp" + +#include + +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 + } + +} diff --git a/apps/opencs/model/world/land.hpp b/apps/opencs/model/world/land.hpp new file mode 100644 index 0000000000..e97a2d7dd8 --- /dev/null +++ b/apps/opencs/model/world/land.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_WORLD_LAND_H +#define CSM_WORLD_LAND_H + +#include +#include +#include + +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 mLand; + + std::string mId; + + /// Loads the metadata and ID + void load (ESM::ESMReader &esm); + + void blank(); + }; +} + +#endif diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp new file mode 100644 index 0000000000..4725866a56 --- /dev/null +++ b/apps/opencs/model/world/landtexture.cpp @@ -0,0 +1,21 @@ +#include "landtexture.hpp" + +#include + +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(); + } + +} diff --git a/apps/opencs/model/world/landtexture.hpp b/apps/opencs/model/world/landtexture.hpp new file mode 100644 index 0000000000..b13903186b --- /dev/null +++ b/apps/opencs/model/world/landtexture.hpp @@ -0,0 +1,22 @@ +#ifndef CSM_WORLD_LANDTEXTURE_H +#define CSM_WORLD_LANDTEXTURE_H + +#include + +#include + +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 diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 1e25f42e25..4fd66b14aa 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -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::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& 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); -} \ No newline at end of file +} diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 70adebe45f..e63e095200 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -6,6 +6,8 @@ #include +#include + #include "object.hpp" class QModelIndex; @@ -29,6 +31,7 @@ namespace CSVRender std::string mId; Ogre::SceneNode *mCellNode; std::map mObjects; + std::auto_ptr mTerrain; /// Ignored if cell does not have an object with the given ID. /// diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index d818de79b2..d2bf4cb05c 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -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) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp new file mode 100644 index 0000000000..a14eea5dd6 --- /dev/null +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -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"); + } + +} diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp new file mode 100644 index 0000000000..97782ad17e --- /dev/null +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -0,0 +1,29 @@ +#ifndef OPENCS_RENDER_TERRAINSTORAGE_H +#define OPENCS_RENDER_TERRAINSTORAGE_H + +#include + +#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 diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 00ad90a026..2eebb8c28f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -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 diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 73fa92529b..71a45a92cc 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0d88a2e103..7753dc5689 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -230,7 +230,7 @@ namespace MWClass const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); MWMechanics::DynamicStat 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; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5d5b8a6891..4149d31d35 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -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 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(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"; diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 17730d6ecf..f89258234d 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -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; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 2888a6e08b..629d99cc2a 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -68,7 +68,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const return false; MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor); - std::map::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction)); + std::map::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::iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction)); + std::map::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 faction = + const std::pair faction = *mActor.getClass().getNpcStats (mActor).getFactionRanks().begin(); int rank = getFactionRank (player, faction.first); diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 096c7db0b3..c55650c453 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -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(attr.at("width")); int height = boost::lexical_cast(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("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 ("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(parent->getChildCount())); std::string image = Misc::ResourceHelpers::correctBookartPath(src, width, mImageHeight); diff --git a/apps/openmw/mwgui/formatting.hpp b/apps/openmw/mwgui/formatting.hpp index cf55b36fb9..3e21b62e27 100644 --- a/apps/openmw/mwgui/formatting.hpp +++ b/apps/openmw/mwgui/formatting.hpp @@ -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 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(); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index a4e5bbc160..9ef926bd3b 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -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) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 10cd347490..29b166a6a7 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -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) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 82f60221b7..da6aec0be3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -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; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index f28809d4fd..d76e513f62 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -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()) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7c7ea0c959..f39a80d48a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1428,7 +1428,9 @@ void CharacterController::update(float duration) const MWWorld::Store &gmst = world->getStore().get(); 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 fatigue = cls.getCreatureStats(mPtr).getFatigue(); fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index f48e82324d..9225a57999 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -124,7 +124,7 @@ namespace MWMechanics const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat(); const float fWeaponFatigueBlockMult = gmst.find("fWeaponFatigueBlockMult")->getFloat(); MWMechanics::DynamicStat 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; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 370c47b1f9..13fc14318c 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -69,9 +69,41 @@ const std::map& MWMechanics::NpcStats::getFactionRanks() const return mFactionRank; } -std::map& MWMechanics::NpcStats::getFactionRanks() +void MWMechanics::NpcStats::raiseRank(const std::string &faction) { - return mFactionRank; + const std::string lower = Misc::StringUtils::lowerCase(faction); + std::map::iterator it = mFactionRank.find(lower); + if (it != mFactionRank.end()) + { + // Does the next rank exist? + const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().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::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::iterator it = mFactionRank.find(lower); + if (it == mFactionRank.end()) + mFactionRank[lower] = 0; } bool MWMechanics::NpcStats::getExpelled(const std::string& factionID) const diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 4ea5d45786..ee897033bf 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -70,7 +70,15 @@ namespace MWMechanics SkillValue& getSkill (int index); const std::map& getFactionRanks() const; - std::map& 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& getExpelled() const { return mExpelled; } bool getExpelled(const std::string& factionID) const; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 373ca7af93..35432e9797 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -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().find("fFatigueSpellBase")->getFloat(); static const float fFatigueSpellMult = store.get().find("fFatigueSpellMult")->getFloat(); DynamicStat 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); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 66e91d055d..c01fede37d 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -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); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 12ba3c2da8..625d0706f8 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -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); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6ed11b71f3..b1e2240bae 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -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); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2da0840749..c3eedce7b5 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -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(); diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index cd9ae16b8b..a409e88073 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -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 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; diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 93711d036e..96e30e3966 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -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); } }; diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 21fee5f571..c43a010b4f 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -310,7 +310,7 @@ namespace MWScript std::string InterpreterContext::getNPCRank() const { - std::map ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks(); + const std::map& ranks = getReferenceImp().getClass().getNpcStats (getReferenceImp()).getFactionRanks(); std::map::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 ranks = player.getClass().getNpcStats (player).getFactionRanks(); + const std::map& ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map::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 ranks = player.getClass().getNpcStats (player).getFactionRanks(); + const std::map& ranks = player.getClass().getNpcStats (player).getFactionRanks(); std::map::const_iterator it = ranks.find(factionId); int rank = -1; if (it != ranks.end()) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index c012cb12ee..91f6d2abe3 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -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& 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& ranks = ptr.getClass().getNpcStats(ptr).getFactionRanks (); - ranks[factionID] = std::max(0, ranks[factionID]-1); + ptr.getClass().getNpcStats(ptr).lowerRank(factionID); } }; diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 3368aa1c0e..7fd6ba0246 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -5,6 +5,23 @@ #include "../mwbase/mechanicsmanager.hpp" #include "player.hpp" +namespace +{ + + void getFollowers (const MWWorld::Ptr& actor, std::set& out) + { + std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); + for(std::list::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 followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); - for(std::list::iterator it = followers.begin();it != followers.end();++it) + std::set followers; + getFollowers(actor, followers); + for(std::set::iterator it = followers.begin();it != followers.end();++it) { teleport(*it); } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index ebe08dc44c..ea1c8827ac 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -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; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 8ac39eaa9b..5f266b45c1 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -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 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. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index f515f1efba..ecc6790139 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -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 diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 41caae4e52..16d965cda3 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -102,6 +102,8 @@ namespace MWWorld typedef std::vector > TRechargingItems; TRechargingItems mRechargingItems; + bool mRechargingItemsUpToDate; + void copySlots (const InventoryStore& store); void initSlots (TSlots& slots_); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7ca3c10cfd..ad0f5bea51 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -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); + } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index e66c179b02..7dc8acaa19 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWWORLD_PHYSICSSYSTEM_H #define GAME_MWWORLD_PHYSICSSYSTEM_H +#include + #include #include @@ -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 handleToMesh; @@ -124,6 +132,12 @@ namespace MWWorld float mTimeAccum; + float mWaterHeight; + float mWaterEnabled; + + std::auto_ptr mWaterCollisionObject; + std::auto_ptr mWaterCollisionShape; + PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); }; diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index b04f67b001..afda6fe606 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -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 > collisions = mPhysEngine.rayTest2(from, to); + + std::vector > collisions = mPhysEngine.rayTest2(from, to, OEngine::Physic::CollisionType_Projectile); bool hit=false; for (std::vector >::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 > collisions = mPhysEngine.rayTest2(from, to); + std::vector > collisions = mPhysEngine.rayTest2(from, to, OEngine::Physic::CollisionType_Projectile); bool hit=false; for (std::vector >::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; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c98485dc9a..66c9f5b712 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -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); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf8c11574f..dd6e7d7fcd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1a25edbe5b..a05d450d5c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -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); }; } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 028341ced5..019bf41611 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -98,6 +98,8 @@ struct Land void load(ESMReader &esm); void save(ESMWriter &esm) const; + void blank() {} + /** * Actually loads data */ diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index bd28c84883..c3e2d50ff5 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -19,4 +19,10 @@ void LandTexture::save(ESMWriter &esm) const esm.writeHNCString("DATA", mTexture); } +void LandTexture::blank() +{ + mTexture.clear(); + mIndex = -1; +} + } diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 5e84428b29..8b45f82118 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -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; }; diff --git a/components/files/androidpath.cpp b/components/files/androidpath.cpp index 52ae73803b..e2f9e948a1 100644 --- a/components/files/androidpath.cpp +++ b/components/files/androidpath.cpp @@ -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; } diff --git a/files/materials/clouds.shader b/files/materials/clouds.shader index 90c4d0f847..93d0780e09 100644 --- a/files/materials/clouds.shader +++ b/files/materials/clouds.shader @@ -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); } diff --git a/files/materials/moon.shader b/files/materials/moon.shader index d369a1c684..eb7243d3f2 100644 --- a/files/materials/moon.shader +++ b/files/materials/moon.shader @@ -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 } diff --git a/files/materials/shadowcaster.shader b/files/materials/shadowcaster.shader index b992366a7e..8f7911553a 100644 --- a/files/materials/shadowcaster.shader +++ b/files/materials/shadowcaster.shader @@ -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 diff --git a/files/materials/stars.shader b/files/materials/stars.shader index b84d371358..f2eb616a27 100644 --- a/files/materials/stars.shader +++ b/files/materials/stars.shader @@ -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 diff --git a/files/materials/water.shader b/files/materials/water.shader index 418b137158..a6f49e0a1a 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -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 diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index b4d5e49a5f..0c61253bf0 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -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 > PhysicEngine::rayTest2(const btVector3& from, const btVector3& to) + std::vector< std::pair > 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 > results = resultCallback1.results; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 0d280c4fea..a77b60ab6f 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -46,7 +46,9 @@ namespace Physic CollisionType_World = 1<<0, // mShape; @@ -288,7 +301,7 @@ namespace Physic /** * Return all objects hit by a ray. */ - std::vector< std::pair > rayTest2(const btVector3 &from, const btVector3 &to); + std::vector< std::pair > rayTest2(const btVector3 &from, const btVector3 &to, int filterGroup=0xff); std::pair sphereCast (float radius, btVector3& from, btVector3& to); ///< @return (hit, relative distance) diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index de5fecfcac..c0f653dae7 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -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);