diff --git a/CMakeLists.txt b/CMakeLists.txt index 71f36fe3de..7b87e35447 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,7 +346,10 @@ if(WIN32) FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*") INSTALL(FILES ${files} DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") - INSTALL(FILES "${OpenMW_SOURCE_DIR}/readme.txt" DESTINATION ".") + INSTALL(FILES + "${OpenMW_SOURCE_DIR}/readme.txt" + "${OpenMW_BINARY_DIR}/settings-default.cfg" + DESTINATION ".") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") SET(CPACK_GENERATOR "NSIS") @@ -356,8 +359,12 @@ if(WIN32) SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;esmtool;Esmtool;omwlauncher;OpenMW Launcher") - set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'") + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher") + SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'") + SET(CPACK_NSIS_DELETE_ICONS_EXTRA " + !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP + Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\" + ") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt") SET(CPACK_RESOURCE_FILE_LICENSE "${OpenMW_SOURCE_DIR}/GPL3.txt") SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2342882e07..a8ae0bd49d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -55,7 +55,7 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanager stat creaturestats magiceffects movement actors + mechanicsmanager stat creaturestats magiceffects movement actors drawstate ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 97aaf4090a..259733600f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -361,6 +361,7 @@ void OMW::Engine::go() addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); + addResourcesDirectory(mResDir / "gbuffer"); // Create the window mOgre->createWindow("OpenMW"); diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index d27d0bc71d..90db40b5ae 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -70,6 +70,14 @@ namespace MWClass return ref->base->script; } + int Apparatus::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Apparatus::registerSelf() { boost::shared_ptr instance (new Apparatus); diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index c0849e1fe2..861610f6cc 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -25,6 +25,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 9956a56fb5..8e1f81136b 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -160,6 +160,14 @@ namespace MWClass return ESM::Skill::HeavyArmor; } + int Armor::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Armor::registerSelf() { boost::shared_ptr instance (new Armor); diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 2b66ff8280..de5ca39835 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -40,6 +40,9 @@ namespace MWClass /// Return the index of the skill this item corresponds to when equiopped or -1, if there is /// no such skill. + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 76370dc5c0..9069d94765 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -72,6 +72,14 @@ namespace MWClass return ref->base->script; } + int Book::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Book::registerSelf() { boost::shared_ptr instance (new Book); diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index ccbbfb4b2d..4738187cd6 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -25,6 +25,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 2357851d75..1fbc11c631 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -123,6 +123,14 @@ namespace MWClass return -1; } + int Clothing::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Clothing::registerSelf() { boost::shared_ptr instance (new Clothing); diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 171b062461..97e09012d5 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -34,6 +34,9 @@ namespace MWClass /// Return the index of the skill this item corresponds to when equiopped or -1, if there is /// no such skill. + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index cbe153ba3a..9707e79a8f 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -68,6 +68,14 @@ namespace MWClass return ref->base->script; } + int Ingredient::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Ingredient::registerSelf() { boost::shared_ptr instance (new Ingredient); diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 9463dcf8d7..2d77176727 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -25,6 +25,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 71e4775916..f67dd4cf05 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -110,6 +110,14 @@ namespace MWClass return std::make_pair (slots, false); } + int Light::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Light::registerSelf() { boost::shared_ptr instance (new Light); diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 46a4d60ba4..bde252c289 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -34,6 +34,9 @@ namespace MWClass ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 1eef0db8ba..76bc3948f6 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -81,6 +81,14 @@ namespace MWClass return std::make_pair (slots, false); } + int Lockpick::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Lockpick::registerSelf() { boost::shared_ptr instance (new Lockpick); diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 0c9189c548..1b56234af1 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -29,6 +29,9 @@ namespace MWClass ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index def1a90a86..84099caaaf 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -70,6 +70,14 @@ namespace MWClass return ref->base->script; } + int Miscellaneous::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Miscellaneous::registerSelf() { boost::shared_ptr instance (new Miscellaneous); diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index b07964f990..fc002280cf 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -25,6 +25,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index ed1733e2d2..642211df3c 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -70,6 +70,14 @@ namespace MWClass return ref->base->script; } + int Potion::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Potion::registerSelf() { boost::shared_ptr instance (new Potion); diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index be9e713fba..7d30179376 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -25,6 +25,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 8013e2e80f..923c29ee69 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -80,6 +80,14 @@ namespace MWClass return std::make_pair (slots, false); } + int Probe::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Probe::registerSelf() { boost::shared_ptr instance (new Probe); diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 1507d65aab..232b523645 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -29,6 +29,9 @@ namespace MWClass ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index d49979861b..d6433f5df5 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -70,6 +70,14 @@ namespace MWClass return ref->base->script; } + int Repair::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Repair::registerSelf() { boost::shared_ptr instance (new Repair); diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 17b606f4cb..0a9d9c2535 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -25,6 +25,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index e36e9202fa..7790e6a80b 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -139,6 +139,14 @@ namespace MWClass return -1; } + int Weapon::getValue (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->data.value; + } + void Weapon::registerSelf() { boost::shared_ptr instance (new Weapon); diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index f863c0bfe2..505c45645f 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -40,6 +40,9 @@ namespace MWClass /// Return the index of the skill this item corresponds to when equiopped or -1, if there is /// no such skill. + virtual int getValue (const MWWorld::Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + static void registerSelf(); virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const; diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 3721d10431..bd27de029c 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -65,6 +65,8 @@ namespace MWInput A_QuickLoad, A_QuickMenu, A_GameMenu, + A_ToggleWeapon, + A_ToggleSpell, A_LAST // Marker for the last item }; @@ -86,6 +88,38 @@ namespace MWInput /* InputImpl Methods */ + void toggleSpell() + { + DrawState state = player.getDrawState(); + if(state == DrawState_Weapon || state == DrawState_Nothing) + { + player.setDrawState(DrawState_Spell); + std::cout << "Player has now readied his hands for spellcasting!\n"; + } + else + { + player.setDrawState(DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n"; + } + + } + + void toggleWeapon() + { + DrawState state = player.getDrawState(); + if(state == DrawState_Spell || state == DrawState_Nothing) + { + player.setDrawState(DrawState_Weapon); + std::cout << "Player is now drawing his weapon.\n"; + } + else + { + player.setDrawState(DrawState_Nothing); + std::cout << "Player does not have any kind of attack ready now.\n"; + } + + } + void screenshot() { mEngine.screenshot(); @@ -197,7 +231,10 @@ namespace MWInput "Auto Move"); disp->funcs.bind(A_ToggleWalk, boost::bind(&InputImpl::toggleWalking, this), "Toggle Walk/Run"); - + disp->funcs.bind(A_ToggleWeapon,boost::bind(&InputImpl::toggleWeapon,this), + "Draw Weapon"); + disp->funcs.bind(A_ToggleSpell,boost::bind(&InputImpl::toggleSpell,this), + "Ready hands"); // Add the exit listener ogre.getRoot()->addFrameListener(&exit); @@ -242,6 +279,8 @@ namespace MWInput disp->bind(A_AutoMove, KC_Z); disp->bind(A_ToggleSneak, KC_X); disp->bind(A_ToggleWalk, KC_C); + disp->bind(A_ToggleWeapon,KC_F); + disp->bind(A_ToggleSpell,KC_R); // Key bindings for polled keys // NOTE: These keys are constantly being polled. Only add keys that must be checked each frame. diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e68b99597e..7d9f748d4a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -17,9 +17,9 @@ namespace MWMechanics void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) { - if (!paused) + if (!paused && ptr.getRefData().getHandle()!="player") MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip ( - MWWorld::Class::get (ptr).getNpcStats (ptr)); + MWWorld::Class::get (ptr).getNpcStats (ptr), mEnvironment); } Actors::Actors (MWWorld::Environment& environment) : mEnvironment (environment), mDuration (0) {} diff --git a/apps/openmw/mwmechanics/drawstate.hpp b/apps/openmw/mwmechanics/drawstate.hpp new file mode 100644 index 0000000000..ded25f8d5d --- /dev/null +++ b/apps/openmw/mwmechanics/drawstate.hpp @@ -0,0 +1,11 @@ +#ifndef GAME_MWMECHANICS_DRAWSTATE_H +#define GAME_MWMECHANICS_DRAWSTATE_H + +enum DrawState +{ + DrawState_Weapon = 0, + DrawState_Spell = 1, + DrawState_Nothing = 2, +}; + +#endif diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index aeb5f56d5b..7c5d37972d 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -4,13 +4,13 @@ #include #include "stat.hpp" +#include "drawstate.hpp" namespace MWMechanics { /// \brief Additional stats for NPCs /// /// For non-NPC-specific stats, see the CreatureStats struct. - struct NpcStats { // NPCs other than the player can only have one faction. But for the sake of consistency @@ -24,9 +24,10 @@ namespace MWMechanics bool mRun; bool mSneak; bool mCombat; + DrawState mDrawState; NpcStats() : mForceRun (false), mForceSneak (false), mRun (false), mSneak (false), - mCombat (false) {} + mCombat (false) , mDrawState(DrawState_Nothing) {} }; } diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 4de6453aa6..e0eb5ccc20 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,4 +1,5 @@ #include "creatureanimation.hpp" +#include "renderconst.hpp" #include "../mwworld/world.hpp" @@ -20,6 +21,28 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, MWWorld::Environme std::string meshNumbered = mesh + getUniqueID(mesh) + ">|"; NifOgre::NIFLoader::load(meshNumbered); base = mRend.getScene()->createEntity(meshNumbered); + base->setVisibilityFlags(RV_Actors); + + bool transparent = false; + for (unsigned int i=0; igetNumSubEntities(); ++i) + { + Ogre::MaterialPtr mat = base->getSubEntity(i)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements()) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); + + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } + } + } + base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + std::string meshZero = mesh + "0000>|"; if((transformations = (NIFLoader::getSingletonPtr())->getAnim(meshZero))){ diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 6a94551deb..cb3c0a204b 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -4,6 +4,7 @@ #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" #include "../mwgui/window_manager.hpp" +#include "renderconst.hpp" #include #include @@ -223,7 +224,8 @@ void LocalMap::render(const float x, const float y, vp->setOverlaysEnabled(false); vp->setShadowsEnabled(false); vp->setBackgroundColour(ColourValue(0, 0, 0)); - //vp->setVisibilityMask( ... ); + vp->setVisibilityMask(RV_Map); + vp->setMaterialScheme("Map"); rtt->update(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c6fe023d69..9de5705e3f 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -1,5 +1,6 @@ #include "npcanimation.hpp" #include "../mwworld/world.hpp" +#include "renderconst.hpp" using namespace Ogre; @@ -65,6 +66,27 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env,O NifOgre::NIFLoader::load(smodel); base = mRend.getScene()->createEntity(smodel); + base->setVisibilityFlags(RV_Actors); + bool transparent = false; + for (unsigned int i=0; igetNumSubEntities(); ++i) + { + Ogre::MaterialPtr mat = base->getSubEntity(i)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements()) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); + + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } + } + } + base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones //stay in the same place when we skipanim, or open a gui window diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 39ab0b0897..b633330fa9 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -4,6 +4,7 @@ #include #include +#include "renderconst.hpp" using namespace MWRender; @@ -112,17 +113,49 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) bounds.scale(insert->getScale()); mBounds[ptr.getCell()].merge(bounds); + bool transparent = false; + for (unsigned int i=0; igetNumSubEntities(); ++i) + { + Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements()) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); + + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } + } + } + if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects")) { insert->attachObject(ent); - ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); /// \todo config value + ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); + ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); + ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } else { Ogre::StaticGeometry* sg = 0; - if (small) +/* if (transparent) + { + if( mStaticGeometryAlpha.find(ptr.getCell()) == mStaticGeometryAlpha.end()) + { + uniqueID = uniqueID +1; + sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); + mStaticGeometryAlpha[ptr.getCell()] = sg; + } + else + sg = mStaticGeometryAlpha[ptr.getCell()]; + } + else*/ if (small) { if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end()) { @@ -130,7 +163,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); mStaticGeometrySmall[ptr.getCell()] = sg; - sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance")); /// \todo config value + sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance")); } else sg = mStaticGeometrySmall[ptr.getCell()]; @@ -158,6 +191,10 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); + sg->setVisibilityFlags(small ? RV_StaticsSmall : RV_Statics); + + sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + mRenderer.getScene()->destroyEntity(ent); } } @@ -251,6 +288,13 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store) mRenderer.getScene()->destroyStaticGeometry (sg); sg = 0; } + /*if(mStaticGeometryAlpha.find(store) != mStaticGeometryAlpha.end()) + { + Ogre::StaticGeometry* sg = mStaticGeometryAlpha[store]; + mStaticGeometryAlpha.erase(store); + mRenderer.getScene()->destroyStaticGeometry (sg); + sg = 0; + }*/ if(mBounds.find(store) != mBounds.end()) mBounds.erase(store); @@ -268,6 +312,11 @@ void Objects::buildStaticGeometry(ESMS::CellStore& cell) Ogre::StaticGeometry* sg = mStaticGeometrySmall[&cell]; sg->build(); } + /*if(mStaticGeometryAlpha.find(&cell) != mStaticGeometryAlpha.end()) + { + Ogre::StaticGeometry* sg = mStaticGeometryAlpha[&cell]; + sg->build(); + }*/ } Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell) diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 5911aa4ccf..0c19f9f33f 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -15,6 +15,7 @@ class Objects{ std::map mCellSceneNodes; std::map mStaticGeometry; std::map mStaticGeometrySmall; + //std::map mStaticGeometryAlpha; std::map mBounds; std::vector mLights; Ogre::SceneNode* mMwRoot; diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index 0c917cda19..d789b8c4e9 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -1,4 +1,5 @@ #include "occlusionquery.hpp" +#include "renderconst.hpp" #include #include @@ -40,9 +41,6 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod return; } - // This means that everything up to RENDER_QUEUE_MAIN can occlude the objects that are tested - const int queue = RENDER_QUEUE_MAIN+1; - MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting"); MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels"); matQueryArea->setDepthWriteEnabled(false); @@ -65,14 +63,14 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mBBQueryTotal->setDefaultDimensions(150, 150); mBBQueryTotal->createBillboard(Vector3::ZERO); mBBQueryTotal->setMaterialName("QueryTotalPixels"); - mBBQueryTotal->setRenderQueueGroup(queue+1); + mBBQueryTotal->setRenderQueueGroup(RQG_OcclusionQuery+1); mBBNodeReal->attachObject(mBBQueryTotal); mBBQueryVisible = mRendering->getScene()->createBillboardSet(1); mBBQueryVisible->setDefaultDimensions(150, 150); mBBQueryVisible->createBillboard(Vector3::ZERO); mBBQueryVisible->setMaterialName("QueryVisiblePixels"); - mBBQueryVisible->setRenderQueueGroup(queue+1); + mBBQueryVisible->setRenderQueueGroup(RQG_OcclusionQuery+1); mBBNodeReal->attachObject(mBBQueryVisible); mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1); @@ -80,7 +78,7 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003); mBBQuerySingleObject->createBillboard(Vector3::ZERO); mBBQuerySingleObject->setMaterialName("QueryVisiblePixels"); - mBBQuerySingleObject->setRenderQueueGroup(queue); + mBBQuerySingleObject->setRenderQueueGroup(RQG_OcclusionQuery); mObjectNode->attachObject(mBBQuerySingleObject); mRendering->getScene()->addRenderObjectListener(this); @@ -153,7 +151,7 @@ void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocati * this can happen for example if the object that is tested is outside of the view frustum * to prevent this, check if the queries have been performed after everything has been rendered and if not, start them manually */ - if (queueGroupId == RENDER_QUEUE_SKIES_LATE) + if (queueGroupId == RQG_SkiesLate) { if (mWasVisible == false && mDoQuery) { diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp new file mode 100644 index 0000000000..2c7f9e9acb --- /dev/null +++ b/apps/openmw/mwrender/renderconst.hpp @@ -0,0 +1,62 @@ +#ifndef GAME_RENDER_CONST_H +#define GAME_RENDER_CONST_H + +#include + +namespace MWRender +{ + +// Render queue groups +enum RenderQueueGroups +{ + // Sky early (atmosphere, clouds, moons) + RQG_SkiesEarly = Ogre::RENDER_QUEUE_SKIES_EARLY, + + RQG_Main = Ogre::RENDER_QUEUE_MAIN, + + RQG_Water = Ogre::RENDER_QUEUE_7+1, + + RQG_Alpha = Ogre::RENDER_QUEUE_MAIN, + + RQG_UnderWater = Ogre::RENDER_QUEUE_7+1, + + RQG_OcclusionQuery = Ogre::RENDER_QUEUE_8, + + // Sky late (sun & sun flare) + RQG_SkiesLate = Ogre::RENDER_QUEUE_SKIES_LATE +}; + +// Visibility flags +enum VisibilityFlags +{ + // Terrain + RV_Terrain = 1, + + // Statics (e.g. trees, houses) + RV_Statics = 2, + + // Small statics + RV_StaticsSmall = 4, + + // Water + RV_Water = 8, + + // Actors (player, npcs, creatures) + RV_Actors = 16, + + // Misc objects (containers, dynamic objects) + RV_Misc = 32, + + RV_Sky = 64, + + // Sun glare (not visible in reflection) + RV_Glare = 128, + + RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water, + + /// \todo markers (normally hidden) +}; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a08fc276ae..aadc92369c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -27,6 +27,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mTerrainManager = new TerrainManager(mRendering.getScene(), environment); + mWater = 0; + //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog configureFog(1, ColourValue(1,1,1)); @@ -39,14 +41,34 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const std::string filter = Settings::Manager::getString("texture filtering", "General"); if (filter == "anisotropic") tfo = TFO_ANISOTROPIC; else if (filter == "trilinear") tfo = TFO_TRILINEAR; - else /* if (filter == "bilinear") */ tfo = TFO_BILINEAR; + else if (filter == "bilinear") tfo = TFO_BILINEAR; + else if (filter == "none") tfo = TFO_NONE; MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); - MaterialManager::getSingleton().setDefaultAnisotropy(Settings::Manager::getInt("anisotropy", "General")); + MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); // Load resources ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); + // disable unsupported effects + const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities(); + if (caps->getNumMultiRenderTargets() < 2) + Settings::Manager::setBool("shader", "Water", false); + + // note that the order is important here + if (useMRT()) + { + CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "gbuffer"); + CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbuffer", true); + CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "Underwater"); + CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "gbufferFinalizer"); + CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbufferFinalizer", true); + } + else + { + CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "UnderwaterNoMRT"); + } + // Turn the entire scene (represented by the 'root' node) -90 // degrees around the x axis. This makes Z go upwards, and Y go into // the screen (when x is to the right.) This is the orientation that @@ -69,8 +91,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - mWater = 0; - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mSun = 0; @@ -122,8 +142,7 @@ void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store) void RenderingManager::removeWater () { if(mWater){ - delete mWater; - mWater = 0; + mWater->setActive(false); } } @@ -139,6 +158,7 @@ void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) mDebugging->cellAdded(store); if (store->cell->isExterior()) mTerrainManager->cellAdded(store); + waterAdded(store); } void RenderingManager::addObject (const MWWorld::Ptr& ptr){ @@ -195,11 +215,10 @@ void RenderingManager::update (float duration){ void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){ if(store->cell->data.flags & store->cell->HasWater){ if(mWater == 0) - mWater = new MWRender::Water(mRendering.getCamera(), store->cell); + mWater = new MWRender::Water(mRendering.getCamera(), mSkyManager, store->cell); else mWater->changeCell(store->cell); - //else - + mWater->setActive(true); } else removeWater(); @@ -263,11 +282,25 @@ bool RenderingManager::toggleRenderMode(int mode) { if (mRendering.getCamera()->getPolygonMode() == PM_SOLID) { + // disable compositors + if (useMRT()) + { + CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbuffer", false); + CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbufferFinalizer", false); + } + mRendering.getCamera()->setPolygonMode(PM_WIREFRAME); return true; } else { + // re-enable compositors + if (useMRT()) + { + CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbuffer", true); + CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbufferFinalizer", true); + } + mRendering.getCamera()->setPolygonMode(PM_SOLID); return false; } @@ -276,23 +309,29 @@ bool RenderingManager::toggleRenderMode(int mode) void RenderingManager::configureFog(ESMS::CellStore &mCell) { - Ogre::ColourValue color; - color.setAsABGR (mCell.cell->ambi.fog); + Ogre::ColourValue color; + color.setAsABGR (mCell.cell->ambi.fog); - configureFog(mCell.cell->ambi.fogDensity, color); + configureFog(mCell.cell->ambi.fogDensity, color); } void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) { - float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance"); + float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance"); - float low = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); - float high = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); + float low = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); + float high = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); - mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high); + mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high); - mRendering.getCamera()->setFarClipDistance ( max / density ); - mRendering.getViewport()->setBackgroundColour (colour); + mRendering.getCamera()->setFarClipDistance ( max / density ); + mRendering.getViewport()->setBackgroundColour (colour); + + CompositorInstance* inst = CompositorManager::getSingleton().getCompositorChain(mRendering.getViewport())->getCompositor("gbuffer"); + if (inst != 0) + inst->getCompositor()->getTechnique(0)->getTargetPass(0)->getPass(0)->setClearColour(colour); + if (mWater) + mWater->setViewportBackground(colour); } @@ -319,41 +358,43 @@ void RenderingManager::setAmbientMode() void RenderingManager::configureAmbient(ESMS::CellStore &mCell) { - mAmbientColor.setAsABGR (mCell.cell->ambi.ambient); - setAmbientMode(); + mAmbientColor.setAsABGR (mCell.cell->ambi.ambient); + setAmbientMode(); - // Create a "sun" that shines light downwards. It doesn't look - // completely right, but leave it for now. - if(!mSun) - { - mSun = mRendering.getScene()->createLight(); - } - Ogre::ColourValue colour; - colour.setAsABGR (mCell.cell->ambi.sunlight); - mSun->setDiffuseColour (colour); - mSun->setType(Ogre::Light::LT_DIRECTIONAL); - mSun->setDirection(0,-1,0); + // Create a "sun" that shines light downwards. It doesn't look + // completely right, but leave it for now. + if(!mSun) + { + mSun = mRendering.getScene()->createLight(); + } + Ogre::ColourValue colour; + colour.setAsABGR (mCell.cell->ambi.sunlight); + mSun->setDiffuseColour (colour); + mSun->setType(Ogre::Light::LT_DIRECTIONAL); + mSun->setDirection(0,-1,0); } // Switch through lighting modes. void RenderingManager::toggleLight() { - if (mAmbientMode==2) - mAmbientMode = 0; - else - ++mAmbientMode; + if (mAmbientMode==2) + mAmbientMode = 0; + else + ++mAmbientMode; - switch (mAmbientMode) - { - case 0: std::cout << "Setting lights to normal\n"; break; - case 1: std::cout << "Turning the lights up\n"; break; - case 2: std::cout << "Turning the lights to full\n"; break; - } + switch (mAmbientMode) + { + case 0: std::cout << "Setting lights to normal\n"; break; + case 1: std::cout << "Turning the lights up\n"; break; + case 2: std::cout << "Turning the lights to full\n"; break; + } - setAmbientMode(); + setAmbientMode(); } -void RenderingManager::checkUnderwater(){ - if(mWater){ +void RenderingManager::checkUnderwater() +{ + if(mWater) + { mWater->checkUnderwater( mRendering.getCamera()->getRealPosition().y ); } } @@ -372,6 +413,7 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr) void RenderingManager::setSunColour(const Ogre::ColourValue& colour) { mSun->setDiffuseColour(colour); + mSun->setSpecularColour(colour); mTerrainManager->setDiffuse(colour); } @@ -428,4 +470,9 @@ void RenderingManager::enableLights() mObjects.enableLights(); } +const bool RenderingManager::useMRT() +{ + return Settings::Manager::getBool("shader", "Water"); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ff9cd168ec..da9c55cb5a 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -31,11 +31,8 @@ namespace Ogre { - class Camera; - class Viewport; class SceneManager; class SceneNode; - class RaySceneQuery; class Quaternion; class Vector3; } @@ -84,6 +81,8 @@ class RenderingManager: private RenderingInterface { void removeWater(); + static const bool useMRT(); + void preCellChange (MWWorld::Ptr::CellStore* store); ///< this event is fired immediately before changing cell diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 781f7abd5d..188ec3b831 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -12,7 +12,8 @@ #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" -#include "occlusionquery.hpp" +#include "renderconst.hpp" +#include "renderingmanager.hpp" using namespace MWRender; using namespace Ogre; @@ -60,6 +61,11 @@ Vector3 BillboardObject::getPosition() const return Vector3(p.x, -p.z, p.y); } +void BillboardObject::setVisibilityFlags(int flags) +{ + mBBSet->setVisibilityFlags(flags); +} + void BillboardObject::setColour(const ColourValue& pColour) { mMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(pColour); @@ -89,9 +95,9 @@ void BillboardObject::init(const String& textureName, /// \todo These billboards are not 100% correct, might want to revisit them later mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1); mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize); - mBBSet->setRenderQueueGroup(RENDER_QUEUE_MAIN+2); mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON); mBBSet->setCommonDirection( -position.normalisedCopy() ); + mBBSet->setVisibilityFlags(RV_Sky); mNode = rootNode->createChildSceneNode(); mNode->setPosition(finalPosition); mNode->attachObject(mBBSet); @@ -109,6 +115,65 @@ void BillboardObject::init(const String& textureName, p->createTextureUnitState(textureName); mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount)); + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + HighLevelGpuProgramPtr vshader; + if (mgr.resourceExists("BBO_VP")) + vshader = mgr.getByName("BBO_VP"); + else + vshader = mgr.createProgram("BBO_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); + vshader->setParameter("profiles", "vs_2_x arbvp1"); + vshader->setParameter("entry_point", "main_vp"); + StringUtil::StrStreamType outStream; + outStream << + "void main_vp( \n" + " float4 position : POSITION, \n" + " in float2 uv : TEXCOORD0, \n" + " out float2 oUV : TEXCOORD0, \n" + " out float4 oPosition : POSITION, \n" + " uniform float4x4 worldViewProj \n" + ") \n" + "{ \n" + " oUV = uv; \n" + " oPosition = mul( worldViewProj, position ); \n" + "}"; + vshader->setSource(outStream.str()); + vshader->load(); + vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); + mMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); + + HighLevelGpuProgramPtr fshader; + if (mgr.resourceExists("BBO_FP")) + fshader = mgr.getByName("BBO_FP"); + else + fshader = mgr.createProgram("BBO_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); + + fshader->setParameter("profiles", "ps_2_x arbfp1"); + fshader->setParameter("entry_point", "main_fp"); + StringUtil::StrStreamType outStream2; + outStream2 << + "void main_fp( \n" + " in float2 uv : TEXCOORD0, \n" + " out float4 oColor : COLOR, \n"; + if (RenderingManager::useMRT()) outStream2 << + " out float4 oColor1 : COLOR1, \n"; + outStream2 << + " uniform sampler2D texture : TEXUNIT0, \n" + " uniform float4 diffuse, \n" + " uniform float4 emissive \n" + ") \n" + "{ \n" + " float4 tex = tex2D(texture, uv); \n" + " oColor = float4(emissive.xyz,1) * tex * float4(1,1,1,diffuse.a); \n"; + if (RenderingManager::useMRT()) outStream2 << + " oColor1 = float4(1, 0, 0, 1); \n"; + outStream2 << + "}"; + fshader->setSource(outStream2.str()); + fshader->load(); + fshader->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); + fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); + mMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); + bodyCount++; } @@ -157,7 +222,10 @@ Moon::Moon( const String& textureName, outStream2 << "void main_fp( \n" " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n" + " out float4 oColor : COLOR, \n"; + if (RenderingManager::useMRT()) outStream2 << + " out float4 oColor1 : COLOR1, \n"; + outStream2 << " uniform sampler2D texture : TEXUNIT0, \n" " uniform float4 skyColour, \n" " uniform float4 diffuse, \n" @@ -165,7 +233,10 @@ Moon::Moon( const String& textureName, ") \n" "{ \n" " float4 tex = tex2D(texture, uv); \n" - " oColor = float4(emissive.xyz,1) * tex; \n" + " oColor = float4(emissive.xyz,1) * tex; \n"; + if (RenderingManager::useMRT()) outStream2 << + " oColor1 = float4(1, 0, 0, 1); \n"; + outStream2 << // use a circle for the alpha (compute UV distance to center) // looks a bit bad because its not filtered on the edges, // but it's cheaper than a seperate alpha texture. @@ -358,15 +429,17 @@ void SkyManager::create() mSecunda = new Moon("textures\\tx_secunda_full.dds", 0.5, Vector3(-0.4, 0.4, 0.5), mRootNode); mSecunda->setType(Moon::Type_Secunda); - mSecunda->setRenderQueue(RENDER_QUEUE_SKIES_EARLY+4); + mSecunda->setRenderQueue(RQG_SkiesEarly+4); mMasser = new Moon("textures\\tx_masser_full.dds", 0.75, Vector3(-0.4, 0.4, 0.5), mRootNode); - mMasser->setRenderQueue(RENDER_QUEUE_SKIES_EARLY+3); + mMasser->setRenderQueue(RQG_SkiesEarly+3); mMasser->setType(Moon::Type_Masser); mSun = new BillboardObject("textures\\tx_sun_05.dds", 1, Vector3(0.4, 0.4, 0.4), mRootNode); + mSun->setRenderQueue(RQG_SkiesEarly+4); mSunGlare = new BillboardObject("textures\\tx_sun_flash_grey_05.dds", 3, Vector3(0.4, 0.4, 0.4), mRootNode); - mSunGlare->setRenderQueue(RENDER_QUEUE_SKIES_LATE); + mSunGlare->setRenderQueue(RQG_SkiesLate); + mSunGlare->setVisibilityFlags(RV_Glare); HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); @@ -375,7 +448,8 @@ void SkyManager::create() /// \todo sky_night_02.nif (available in Bloodmoon) MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); Entity* night1_ent = mSceneMgr->createEntity("meshes\\sky_night_01.nif"); - night1_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+1); + night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); + night1_ent->setVisibilityFlags(RV_Sky); mAtmosphereNight = mRootNode->createChildSceneNode(); mAtmosphereNight->attachObject(night1_ent); @@ -413,7 +487,10 @@ void SkyManager::create() outStream5 << "void main_fp( \n" " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n" + " out float4 oColor : COLOR, \n"; + if (RenderingManager::useMRT()) outStream5 << + " out float4 oColor1 : COLOR1, \n"; + outStream5 << " in float fade : TEXCOORD1, \n" " uniform sampler2D texture : TEXUNIT0, \n" " uniform float opacity, \n" @@ -421,7 +498,10 @@ void SkyManager::create() " uniform float4 emissive \n" ") \n" "{ \n" - " oColor = tex2D(texture, uv) * float4(emissive.xyz, 1) * float4(1,1,1,fade*diffuse.a); \n" + " oColor = tex2D(texture, uv) * float4(emissive.xyz, 1) * float4(1,1,1,fade*diffuse.a); \n"; + if (RenderingManager::useMRT()) outStream5 << + " oColor1 = float4(1, 0, 0, 1); \n"; + outStream5 << "}"; stars_fp->setSource(outStream5.str()); stars_fp->load(); @@ -448,7 +528,8 @@ void SkyManager::create() ModVertexAlpha(atmosphere_ent, 0); - atmosphere_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY); + atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); + atmosphere_ent->setVisibilityFlags(RV_Sky); mAtmosphereDay = mRootNode->createChildSceneNode(); mAtmosphereDay->attachObject(atmosphere_ent); mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial(); @@ -466,26 +547,52 @@ void SkyManager::create() " float4 position : POSITION, \n" " in float4 color : COLOR, \n" " out float4 oPosition : POSITION, \n" - " out float4 oColor : COLOR, \n" - " uniform float4 emissive, \n" + " out float4 oVertexColor : TEXCOORD0, \n" " uniform float4x4 worldViewProj \n" ") \n" "{ \n" " oPosition = mul( worldViewProj, position ); \n" - " oColor = color * emissive; \n" + " oVertexColor = color; \n" "}"; vshader->setSource(outStream.str()); vshader->load(); vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - vshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName()); - mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(""); + + HighLevelGpuProgramPtr fshader = mgr.createProgram("Atmosphere_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_FRAGMENT_PROGRAM); + + fshader->setParameter("profiles", "ps_2_x arbfp1"); + fshader->setParameter("entry_point", "main_fp"); + + StringUtil::StrStreamType _outStream; + _outStream << + "void main_fp( \n" + " in float4 iVertexColor : TEXCOORD0, \n" + " out float4 oColor : COLOR, \n"; + if (RenderingManager::useMRT()) _outStream << + " out float4 oColor1 : COLOR1, \n"; + _outStream << + " uniform float4 emissive \n" + ") \n" + "{ \n" + " oColor = iVertexColor * emissive; \n"; + if (RenderingManager::useMRT()) _outStream << + " oColor1 = float4(1, 0, 0, 1); \n"; + _outStream << + "}"; + fshader->setSource(_outStream.str()); + fshader->load(); + + fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); + mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName()); // Clouds NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); Entity* clouds_ent = mSceneMgr->createEntity("meshes\\sky_clouds_01.nif"); - clouds_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+5); + clouds_ent->setVisibilityFlags(RV_Sky); + clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); SceneNode* clouds_node = mRootNode->createChildSceneNode(); clouds_node->attachObject(clouds_ent); mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial(); @@ -525,8 +632,11 @@ void SkyManager::create() outStream2 << "void main_fp( \n" " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n" " in float4 color : TEXCOORD1, \n" + " out float4 oColor : COLOR, \n"; + if (RenderingManager::useMRT()) outStream2 << + " out float4 oColor1 : COLOR1, \n"; + outStream2 << " uniform sampler2D texture : TEXUNIT0, \n" " uniform sampler2D secondTexture : TEXUNIT1, \n" " uniform float transitionFactor, \n" @@ -538,7 +648,10 @@ void SkyManager::create() "{ \n" " uv += float2(0,1) * time * speed * 0.003; \n" // Scroll in y direction " float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n" - " oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n" + " oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n"; + if (RenderingManager::useMRT()) outStream2 << + " oColor1 = float4(1, 0, 0, 1); \n"; + outStream2 << "}"; mCloudFragmentShader->setSource(outStream2.str()); mCloudFragmentShader->load(); @@ -734,7 +847,8 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) strength = 1.f; mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength); - mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0); + + mSun->setVisibility(weather.mGlareView * strength); mAtmosphereNight->setVisible(weather.mNight && mEnabled); } @@ -837,3 +951,18 @@ Ogre::SceneNode* SkyManager::getSunNode() if (!mCreated) return 0; return mSun->getNode(); } + +void SkyManager::setSkyPosition(const Ogre::Vector3& position) +{ + mRootNode->_setDerivedPosition(position); +} + +void SkyManager::resetSkyPosition() +{ + mRootNode->setPosition(0,0,0); +} + +void SkyManager::scaleSky(float scale) +{ + mRootNode->setScale(scale, scale, scale); +} diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index baf5933cbc..64d5c16a03 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -36,24 +36,25 @@ namespace MWRender BillboardObject(); virtual ~BillboardObject() {} - + void setColour(const Ogre::ColourValue& pColour); void setPosition(const Ogre::Vector3& pPosition); void setVisible(const bool visible); void setRenderQueue(unsigned int id); + void setVisibilityFlags(int flags); void setSize(const float size); Ogre::Vector3 getPosition() const; - + void setVisibility(const float visibility); - + Ogre::SceneNode* getNode(); - + protected: virtual void init(const Ogre::String& textureName, const float size, const Ogre::Vector3& position, Ogre::SceneNode* rootNode); - + Ogre::SceneNode* mNode; Ogre::MaterialPtr mMaterial; Ogre::BillboardSet* mBBSet; @@ -71,9 +72,9 @@ namespace MWRender const Ogre::Vector3& position, Ogre::SceneNode* rootNode ); - + virtual ~Moon() {} - + enum Phase { Phase_New = 0, @@ -85,20 +86,20 @@ namespace MWRender Phase_WaningHalf, Phase_WaningCrescent }; - + enum Type { Type_Masser = 0, Type_Secunda }; - + void setPhase(const Phase& phase); void setType(const Type& type); void setSkyColour(const Ogre::ColourValue& colour); - + Phase getPhase() const; unsigned int getPhaseInt() const; - + private: Type mType; Phase mPhase; @@ -168,6 +169,10 @@ namespace MWRender void setGlare(const float glare); Ogre::Vector3 getRealSunPos(); + void setSkyPosition(const Ogre::Vector3& position); + void resetSkyPosition(); + void scaleSky(float scale); + private: bool mCreated; @@ -180,21 +185,21 @@ namespace MWRender BillboardObject* mSunGlare; Moon* mMasser; Moon* mSecunda; - + Ogre::Viewport* mViewport; Ogre::SceneNode* mRootNode; Ogre::SceneManager* mSceneMgr; - + Ogre::SceneNode* mAtmosphereDay; Ogre::SceneNode* mAtmosphereNight; - + Ogre::MaterialPtr mCloudMaterial; Ogre::MaterialPtr mAtmosphereMaterial; - + Ogre::MaterialPtr mStarsMaterials[7]; - + Ogre::HighLevelGpuProgramPtr mCloudFragmentShader; - + // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; @@ -204,17 +209,17 @@ namespace MWRender float mStarsOpacity; Ogre::ColourValue mCloudColour; Ogre::ColourValue mSkyColour; - + Ogre::Overlay* mThunderOverlay; Ogre::TextureUnitState* mThunderTextureUnit; - + float mRemainingTransitionTime; - + float mGlare; // target float mGlareFade; // actual - + void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType); - + bool mEnabled; bool mSunEnabled; bool mMasserEnabled; diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 0b8f933a27..67dd4e0b05 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -6,6 +6,7 @@ #include "terrainmaterial.hpp" #include "terrain.hpp" +#include "renderconst.hpp" using namespace Ogre; @@ -162,6 +163,8 @@ namespace MWRender x * numTextures, y * numTextures, numTextures, indexes); + terrain->setVisibilityFlags(RV_Terrain); + terrain->setRenderQueueGroup(RQG_Main); if ( land && land->landData->usingColours ) { diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 922ea2280e..87798006c3 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -37,6 +37,7 @@ THE SOFTWARE. #include "OgreShadowCameraSetupPSSM.h" #include +#include "renderingmanager.hpp" namespace Ogre { @@ -557,7 +558,10 @@ namespace Ogre params->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); //params->setNamedAutoConstant("lightSpecularColour"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, i); } - + + if (MWRender::RenderingManager::useMRT()) + params->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE); + params->setNamedAutoConstant("eyePosObjSpace", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE); params->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); @@ -753,12 +757,7 @@ namespace Ogre ret->unload(); } - if(prof->isLayerNormalMappingEnabled() || prof->isLayerParallaxMappingEnabled()) - ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); - //else - //ret->setParameter("profiles", "ps_3_0 ps_2_0 fp30 arbfp1"); - else // fp30 doesn't work (black terrain) - ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); + ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); ret->setParameter("entry_point", "main_fp"); return ret; @@ -917,7 +916,7 @@ namespace Ogre outStream << - "float4 main_fp(\n" + "void main_fp(\n" "float4 position : TEXCOORD0,\n"; uint texCoordSet = 1; @@ -1034,8 +1033,15 @@ namespace Ogre __FUNCTION__); } + if (MWRender::RenderingManager::useMRT()) outStream << + " , out float4 oColor : COLOR \n" + " , out float4 oColor1 : COLOR1 \n" + " , uniform float far \n"; + else outStream << + " , out float4 oColor : COLOR \n"; + outStream << - ") : COLOR\n" + ")\n" "{\n" " float4 outputCol;\n" " float shadow = 1.0;\n" @@ -1241,6 +1247,10 @@ namespace Ogre " oPos = mul(viewProjMatrix, worldPos);\n" " oUVMisc.xy = uv.xy;\n"; + outStream << + " // pass cam depth\n" + " oUVMisc.z = oPos.z;\n"; + bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; if (fog) { @@ -1337,7 +1347,12 @@ namespace Ogre } // Final return - outStream << " return outputCol;\n" + outStream << " oColor = outputCol;\n"; + + if (MWRender::RenderingManager::useMRT()) outStream << + " oColor1 = float4(uvMisc.z / far, 0, 0, 1); \n"; + + outStream << "}\n"; } @@ -1511,14 +1526,6 @@ namespace Ogre } } - - if (prof->getReceiveDynamicShadowsPSSM()) - { - outStream << - " // pass cam depth\n" - " oUVMisc.z = oPos.z;\n"; - } - } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsParams( diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 9de55e3a59..7981def0b8 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -1,29 +1,43 @@ #include "water.hpp" +#include +#include "sky.hpp" +#include "renderingmanager.hpp" + +using namespace Ogre; namespace MWRender { -Water::Water (Ogre::Camera *camera, const ESM::Cell* cell) : +Water::Water (Ogre::Camera *camera, SkyManager* sky, const ESM::Cell* cell) : mCamera (camera), mViewport (camera->getViewport()), mSceneManager (camera->getSceneManager()), - mIsUnderwater(false) + mIsUnderwater(false), mVisibilityFlags(0), + mReflectionTarget(0), mActive(1) { + mSky = sky; + try { - Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "Water", -1); - Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false); + CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false); } catch(...) {} mTop = cell->water; mIsUnderwater = false; - mWaterPlane = Ogre::Plane(Ogre::Vector3::UNIT_Y, 0); + mWaterPlane = Plane(Vector3::UNIT_Y, 0); - Ogre::MeshManager::getSingleton().createPlane("water", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,5, Ogre::Vector3::UNIT_Z); + MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Z); mWater = mSceneManager->createEntity("water"); + mWater->setVisibilityFlags(RV_Water); + mWater->setRenderQueueGroup(RQG_Water); - mWater->setMaterialName("Examples/Water0"); + mVisibilityFlags = RV_Terrain * Settings::Manager::getBool("reflect terrain", "Water") + + RV_Statics * Settings::Manager::getBool("reflect statics", "Water") + + RV_StaticsSmall * Settings::Manager::getBool("reflect small statics", "Water") + + RV_Actors * Settings::Manager::getBool("reflect actors", "Water") + + RV_Misc * Settings::Manager::getBool("reflect misc", "Water") + + RV_Sky; mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode(); mWaterNode->setPosition(0, mTop, 0); @@ -33,18 +47,51 @@ Water::Water (Ogre::Camera *camera, const ESM::Cell* cell) : mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY)); } mWaterNode->attachObject(mWater); + + // Create rendertarget for reflection + int rttsize = Settings::Manager::getInt("rtt size", "Water"); + + if (Settings::Manager::getBool("shader", "Water")) + { + TexturePtr tex = TextureManager::getSingleton().createManual("WaterReflection", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, rttsize, rttsize, 0, PF_R8G8B8, TU_RENDERTARGET); + + RenderTarget* rtt = tex->getBuffer()->getRenderTarget(); + Viewport* vp = rtt->addViewport(mCamera); + vp->setOverlaysEnabled(false); + vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f)); + vp->setShadowsEnabled(false); + vp->setVisibilityMask( mVisibilityFlags ); + rtt->addListener(this); + rtt->setActive(true); + + mReflectionTarget = rtt; + } + + mCompositorName = RenderingManager::useMRT() ? "Underwater" : "UnderwaterNoMRT"; + + createMaterial(); + mWater->setMaterial(mMaterial); + + mUnderwaterEffect = Settings::Manager::getBool("underwater effect", "Water"); } +void Water::setActive(bool active) +{ + mActive = active; + if (mReflectionTarget) mReflectionTarget->setActive(active && !mIsUnderwater); + mWater->setVisible(active); +} Water::~Water() { - Ogre::MeshManager::getSingleton().remove("water"); + MeshManager::getSingleton().remove("water"); mWaterNode->detachObject(mWater); mSceneManager->destroyEntity(mWater); mSceneManager->destroySceneNode(mWaterNode); - Ogre::CompositorManager::getSingleton().removeCompositorChain(mViewport); + CompositorManager::getSingleton().removeCompositorChain(mViewport); } void Water::changeCell(const ESM::Cell* cell) @@ -65,31 +112,122 @@ void Water::setHeight(const float height) void Water::toggle() { - mWater->setVisible(!mWater->getVisible()); + if (mActive) + mWater->setVisible(!mWater->getVisible()); } void Water::checkUnderwater(float y) { - if ((mIsUnderwater && y > mTop) || !mWater->isVisible()) + if (!mActive) return; + if ((mIsUnderwater && y > mTop) || !mWater->isVisible() || mCamera->getPolygonMode() != Ogre::PM_SOLID) { - try { - Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false); - } catch(...) {} + CompositorManager::getSingleton().setCompositorEnabled(mViewport, mCompositorName, false); + + // tell the shader we are not underwater + Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0); + if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false)) + pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(0)); + + if (mReflectionTarget) + mReflectionTarget->setActive(mActive); + + mWater->setRenderQueueGroup(RQG_Water); + mIsUnderwater = false; } - if (!mIsUnderwater && y < mTop && mWater->isVisible()) + if (!mIsUnderwater && y < mTop && mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID) { - try { - Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", true); - } catch(...) {} + if (mUnderwaterEffect) + CompositorManager::getSingleton().setCompositorEnabled(mViewport, mCompositorName, true); + + // tell the shader we are underwater + Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0); + if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false)) + pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(1)); + + if (mReflectionTarget) + mReflectionTarget->setActive(false); + + mWater->setRenderQueueGroup(RQG_UnderWater); + mIsUnderwater = true; } } -Ogre::Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY) +Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY) { - return Ogre::Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2)); + return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2)); +} + +void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) +{ + if (evt.source == mReflectionTarget) + { + mWater->setVisible(false); + + // Some messy code to get the skybox to show up at all + // The problem here is that it gets clipped by the water plane + // Therefore scale it up a bit + Vector3 pos = mCamera->getRealPosition(); + pos.y = mTop*2 - pos.y; + mSky->setSkyPosition(pos); + mSky->scaleSky(mCamera->getFarClipDistance() / 1000.f); + + mCamera->enableCustomNearClipPlane(Plane(Vector3::UNIT_Y, mTop)); + mCamera->enableReflection(Plane(Vector3::UNIT_Y, mTop)); + } +} + +void Water::postRenderTargetUpdate(const RenderTargetEvent& evt) +{ + mWater->setVisible(true); + + if (evt.source == mReflectionTarget) + { + mSky->resetSkyPosition(); + mSky->scaleSky(1); + mCamera->disableReflection(); + mCamera->disableCustomNearClipPlane(); + } +} + +void Water::createMaterial() +{ + mMaterial = MaterialManager::getSingleton().getByName("Water"); + + // these have to be set in code + std::string textureNames[32]; + for (int i=0; i<32; ++i) + { + textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds"; + } + mMaterial->getTechnique(1)->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2); + + // use technique without shaders if reflection is disabled + if (mReflectionTarget == 0) + mMaterial->removeTechnique(0); + + if (Settings::Manager::getBool("shader", "Water")) + { + CompositorInstance* compositor = CompositorManager::getSingleton().getCompositorChain(mViewport)->getCompositor("gbuffer"); + + TexturePtr colorTexture = compositor->getTextureInstance("mrt_output", 0); + TextureUnitState* tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("refractionMap"); + if (tus != 0) + tus->setTexture(colorTexture); + + TexturePtr depthTexture = compositor->getTextureInstance("mrt_output", 1); + tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("depthMap"); + if (tus != 0) + tus->setTexture(depthTexture); + } +} + +void Water::setViewportBackground(const ColourValue& bg) +{ + if (mReflectionTarget) + mReflectionTarget->getViewport(0)->setBackgroundColour(bg); } } // namespace diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 5a5d1cca09..0e23f5b0c3 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -4,10 +4,14 @@ #include #include +#include "renderconst.hpp" + namespace MWRender { + class SkyManager; + /// Water rendering - class Water : Ogre::RenderTargetListener, Ogre::Camera::Listener + class Water : public Ogre::RenderTargetListener { static const int CELL_SIZE = 8192; Ogre::Camera *mCamera; @@ -19,16 +23,37 @@ namespace MWRender { Ogre::Entity *mWater; bool mIsUnderwater; + bool mActive; int mTop; Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY); + protected: + void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + + SkyManager* mSky; + + std::string mCompositorName; + + void createMaterial(); + Ogre::MaterialPtr mMaterial; + + Ogre::RenderTarget* mReflectionTarget; + + bool mUnderwaterEffect; + int mVisibilityFlags; + public: - Water (Ogre::Camera *camera, const ESM::Cell* cell); + Water (Ogre::Camera *camera, SkyManager* sky, const ESM::Cell* cell); ~Water(); + void setActive(bool active); + void toggle(); + void setViewportBackground(const Ogre::ColourValue& bg); + void checkUnderwater(float y); void changeCell(const ESM::Cell* cell); void setHeight(const float height); diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 9d766909f7..d49b98d0fb 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -26,7 +26,9 @@ namespace MWWorld { } - void Class::insertObject(const Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const{ + + void Class::insertObject(const Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const + { } @@ -137,6 +139,11 @@ namespace MWWorld return -1; } + int Class::getValue (const Ptr& ptr) const + { + throw std::logic_error ("value not supported by this class"); + } + const Class& Class::get (const std::string& key) { std::map >::const_iterator iter = sClasses.find (key); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 67320b3e07..e474e9b926 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -155,6 +155,10 @@ namespace MWWorld /// no such skill. /// (default implementation: return -1) + virtual int getValue (const Ptr& ptr) const; + ///< Return trade value of the object. Throws an exception, if the object can't be traded. + /// (default implementation: throws an exception) + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index aedd119c8a..650418201b 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -4,6 +4,8 @@ #include #include +#include "../mwmechanics/npcstats.hpp" + #include "class.hpp" #include /// \todo remove after rendering is implemented @@ -94,27 +96,64 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) return mSlots[slot]; } -void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) +void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats, + const Environment& environment) { TSlots slots; initSlots (slots); for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) { + Ptr test = *iter; + int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test, environment); + std::pair, bool> itemsSlots = MWWorld::Class::get (*iter).getEquipmentSlots (*iter); for (std::vector::const_iterator iter2 (itemsSlots.first.begin()); iter2!=itemsSlots.first.end(); ++iter2) { - /// \todo comapre item with item in slot - if (slots.at (*iter2)==end()) - { - /// \todo unstack, if reqquired (itemsSlots.second) + bool use = false; - slots[*iter2] = iter; - break; + if (slots.at (*iter2)==end()) + use = true; // slot was empty before -> skill all further checks + else + { + Ptr old = *slots.at (*iter2); + + if (!use) + { + // check skill + int oldSkill = + MWWorld::Class::get (old).getEquipmentSkill (old, environment); + + if (testSkill!=-1 || oldSkill!=-1 || testSkill!=oldSkill) + { + if (stats.mSkill[oldSkill].getModified()>stats.mSkill[testSkill].getModified()) + continue; // rejected, because old item better matched the NPC's skills. + + if (stats.mSkill[oldSkill].getModified()= + MWWorld::Class::get (test).getValue (test)) + { + continue; + } + + use = true; + } } + + /// \todo unstack, if reqquired (itemsSlots.second) + + slots[*iter2] = iter; + break; } } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 5eeaf570d0..60b89b0b47 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -10,6 +10,8 @@ namespace MWMechanics namespace MWWorld { + class Environment; + ///< \brief Variant of the ContainerStore for NPCs class InventoryStore : public ContainerStore { @@ -62,7 +64,7 @@ namespace MWWorld ContainerStoreIterator getSlot (int slot); - void autoEquip (const MWMechanics::NpcStats& stats); + void autoEquip (const MWMechanics::NpcStats& stats, const Environment& environment); ///< Auto equip items according to stats and item value. }; } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5bfb82138c..d24780ec10 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -4,6 +4,7 @@ #include "../mwrender/player.hpp" #include "../mwmechanics/movement.hpp" +#include "../mwmechanics/npcstats.hpp" #include "world.hpp" #include "class.hpp" @@ -48,6 +49,12 @@ namespace MWWorld mClass = new_class; } + void Player::setDrawState(const DrawState& value) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).getNpcStats(ptr).mDrawState = value; + } + void Player::setAutoMove (bool enable) { MWWorld::Ptr ptr = getPlayer(); @@ -89,4 +96,10 @@ namespace MWWorld MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); } + + DrawState Player::getDrawState() + { + MWWorld::Ptr ptr = getPlayer(); + return MWWorld::Class::get(ptr).getNpcStats(ptr).mDrawState; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 01c71da437..8dcd9fcc65 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -8,6 +8,8 @@ #include "../mwworld/refdata.hpp" #include "../mwworld/ptr.hpp" +#include "../mwmechanics/drawstate.hpp" + namespace MWRender { class Player; @@ -18,7 +20,7 @@ namespace MWWorld class World; /// \brief NPC object representing the player and additional player data - class Player + class Player { ESMS::LiveCellRef mPlayer; MWWorld::Ptr::CellStore *mCellStore; @@ -31,7 +33,6 @@ namespace MWWorld ESM::Class *mClass; bool mAutoMove; int mForwardBackward; - public: Player(MWRender::Player *renderer, const ESM::NPC *player, MWWorld::World& world); @@ -76,6 +77,8 @@ namespace MWWorld void setClass (const ESM::Class& class_); + void setDrawState(const DrawState& state); + std::string getName() const { return mName; @@ -106,6 +109,8 @@ namespace MWWorld return mAutoMove; } + DrawState getDrawState(); + void setAutoMove (bool enable); void setLeftRight (int value); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index df7d20bb60..2123b47999 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -204,7 +204,6 @@ namespace MWWorld mWorld->adjustSky(); mCellChanged = true; - mRendering.waterAdded(mCurrentCell); } //We need the ogre renderer and a scene node. @@ -257,8 +256,6 @@ namespace MWWorld mWorld->adjustSky(); mCellChanged = true; - - mRendering.waterAdded(cell); } void Scene::changeToExteriorCell (const ESM::Position& position) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 09fd200762..fb0480171d 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -334,14 +334,16 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering, MWWorld::E void WeatherManager::setWeather(const String& weather, bool instant) { - if (weather == mCurrentWeather && mNextWeather == "") + if (weather == mCurrentWeather && mNextWeather == "") + { + mFirstUpdate = false; return; + } if (instant || mFirstUpdate) { mNextWeather = ""; mCurrentWeather = weather; - mFirstUpdate = false; } else { @@ -355,6 +357,7 @@ void WeatherManager::setWeather(const String& weather, bool instant) mNextWeather = weather; mRemainingTransitionTime = mWeatherSettings[mCurrentWeather].mTransitionDelta*24.f*3600; } + mFirstUpdate = false; } WeatherResult WeatherManager::getResult(const String& weather) @@ -472,6 +475,7 @@ WeatherResult WeatherManager::transition(float factor) result.mCloudSpeed = current.mCloudSpeed; result.mCloudOpacity = lerp(current.mCloudOpacity, other.mCloudOpacity); result.mGlareView = lerp(current.mGlareView, other.mGlareView); + result.mNightFade = lerp(current.mNightFade, other.mNightFade); result.mNight = current.mNight; @@ -520,23 +524,23 @@ void WeatherManager::update(float duration) srand(time(NULL)); float random = ((rand()%100)/100.f) * total; - //if (random > snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + //if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear) // weather = "blizzard"; - //else if (random > blight+ash+thunder+rain+overcast+foggy+cloudy+clear) + //else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear) // weather = "snow"; - /*else*/ if (random > ash+thunder+rain+overcast+foggy+cloudy+clear) + /*else*/ if (random >= ash+thunder+rain+overcast+foggy+cloudy+clear) weather = "blight"; - else if (random > thunder+rain+overcast+foggy+cloudy+clear) + else if (random >= thunder+rain+overcast+foggy+cloudy+clear) weather = "ashstorm"; - else if (random > rain+overcast+foggy+cloudy+clear) + else if (random >= rain+overcast+foggy+cloudy+clear) weather = "thunderstorm"; - else if (random > overcast+foggy+cloudy+clear) + else if (random >= overcast+foggy+cloudy+clear) weather = "rain"; - else if (random > foggy+cloudy+clear) + else if (random >= foggy+cloudy+clear) weather = "overcast"; - else if (random > cloudy+clear) + else if (random >= cloudy+clear) weather = "foggy"; - else if (random > clear) + else if (random >= clear) weather = "cloudy"; else weather = "clear"; @@ -584,8 +588,8 @@ void WeatherManager::update(float duration) int facing = (mHour > 13.f) ? 1 : -1; Vector3 final( - (1-height)*facing, - (1-height)*facing, + -(1-height)*facing, + -(1-height)*facing, height); mRendering->setSunDirection(final); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 7fa45815fb..835534eff2 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -302,6 +302,8 @@ void NIFLoader::createMaterial(const String &name, if (Settings::Manager::getBool("shaders", "Objects")) { + bool mrt = Settings::Manager::getBool("shader", "Water"); + // Create shader for the material // vertex HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); @@ -324,9 +326,8 @@ void NIFLoader::createMaterial(const String &name, " out float4 oPosition : POSITION, \n" " out float4 oPositionObjSpace : TEXCOORD1, \n" " out float4 oNormal : TEXCOORD2, \n" - " out float oFogValue : TEXCOORD3, \n" + " out float oDepth : TEXCOORD3, \n" " out float4 oVertexColour : TEXCOORD4, \n" - " uniform float4 fogParams, \n" " uniform float4x4 worldViewProj \n" ") \n" "{ \n" @@ -334,13 +335,12 @@ void NIFLoader::createMaterial(const String &name, " oUV = uv; \n" " oNormal = normal; \n" " oPosition = mul( worldViewProj, position ); \n" - " oFogValue = saturate((oPosition.z - fogParams.y) * fogParams.w); \n" + " oDepth = oPosition.z; \n" " oPositionObjSpace = position; \n" "}"; vertex->setSource(outStream.str()); vertex->load(); vertex->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); - vertex->getDefaultParameters()->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS); } else vertex = mgr.getByName("main_vp"); @@ -370,9 +370,14 @@ void NIFLoader::createMaterial(const String &name, " uniform sampler2D texture : TEXUNIT0, \n" " float4 positionObjSpace : TEXCOORD1, \n" " float4 normal : TEXCOORD2, \n" - " float fogValue : TEXCOORD3, \n" + " float iDepth : TEXCOORD3, \n" " float4 vertexColour : TEXCOORD4, \n" - " uniform float4 fogColour, \n"; + " uniform float4 fogColour, \n" + " uniform float4 fogParams, \n"; + + if (mrt) outStream << + " out float4 oColor1 : COLOR1, \n" + " uniform float far, \n"; for (int i=0; isetSource(outStream.str()); fragment->load(); @@ -425,6 +436,9 @@ void NIFLoader::createMaterial(const String &name, fragment->getDefaultParameters()->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_SURFACE_AMBIENT_COLOUR); fragment->getDefaultParameters()->setNamedAutoConstant("lightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); fragment->getDefaultParameters()->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); + fragment->getDefaultParameters()->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS); + if (mrt) + fragment->getDefaultParameters()->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE); } else fragment = mgr.getByName("main_fp"); diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index 203b406810..507f82c1a3 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -1,13 +1,13 @@ project(resources) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/caustic_0.png "${OpenMW_BINARY_DIR}/resources/water/caustic_0.png" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_Fresnel.cg "${OpenMW_BINARY_DIR}/resources/water/Example_Fresnel.cg" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_FresnelPS.asm "${OpenMW_BINARY_DIR}/resources/water/Example_FresnelPS.asm" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassFP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassFP.cg" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassVP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassVP.cg" COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/underwater.cg "${OpenMW_BINARY_DIR}/resources/water/underwater.cg" COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/perlinvolume.dds "${OpenMW_BINARY_DIR}/resources/water/perlinvolume.dds" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Water02.jpg "${OpenMW_BINARY_DIR}/resources/water/Water02.jpg" COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.compositor "${OpenMW_BINARY_DIR}/resources/water/water.compositor" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/waves2.dds "${OpenMW_BINARY_DIR}/resources/water/waves2.dds" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Examples-Water.material "${OpenMW_BINARY_DIR}/resources/water/Examples-Water.material" COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/WaterNormal1.tga "${OpenMW_BINARY_DIR}/resources/water/WaterNormal1.tga" COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.material "${OpenMW_BINARY_DIR}/resources/water/water.material" COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/WaterNormal2.tga "${OpenMW_BINARY_DIR}/resources/water/WaterNormal2.tga" COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.cg "${OpenMW_BINARY_DIR}/resources/water/water.cg" COPYONLY) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/gbuffer.cg "${OpenMW_BINARY_DIR}/resources/gbuffer/gbuffer.cg" COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/gbuffer.material "${OpenMW_BINARY_DIR}/resources/gbuffer/gbuffer.material" COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gbuffer/gbuffer.compositor "${OpenMW_BINARY_DIR}/resources/gbuffer/gbuffer.compositor" COPYONLY) diff --git a/files/gbuffer/gbuffer.cg b/files/gbuffer/gbuffer.cg new file mode 100644 index 0000000000..c7f2fe6788 --- /dev/null +++ b/files/gbuffer/gbuffer.cg @@ -0,0 +1,18 @@ +void RenderScene_vs(in float4 position : POSITION + ,in float2 uv :TEXCOORD0 + ,uniform float4x4 wvp + ,out float4 oPosition : POSITION + ,out float2 oUV :TEXCOORD0) +{ + oPosition = mul(wvp, position); + oUV = uv; +} + +void RenderScene_ps(in float4 position : POSITION + ,in float2 uv :TEXCOORD0 + ,uniform sampler2D tex1 : TEXUNIT0 + ,out float4 oColor : COLOR) +{ + float4 scene =tex2D(tex1, uv); + oColor= scene; +} diff --git a/files/gbuffer/gbuffer.compositor b/files/gbuffer/gbuffer.compositor new file mode 100644 index 0000000000..316003af6b --- /dev/null +++ b/files/gbuffer/gbuffer.compositor @@ -0,0 +1,95 @@ +// Compositor that just controls output to the MRT textures +compositor gbuffer +{ + technique + { + // MRT output. Currently this is a color texture plus a depth texture + texture mrt_output target_width target_height PF_FLOAT16_RGBA PF_FLOAT16_RGBA chain_scope depth_pool 2 + + target mrt_output + { + input none + pass clear + { + clear + { + // make sure to set this to the viewport background color from outside + colour_value 0 0 0 1 + } + } + pass render_scene + { + // Renders everything except water + first_render_queue 0 + last_render_queue 70 + } + + } + + target_output + { + input none + + pass render_quad + { + material RenderScene + input 0 mrt_output 0 + } + } + } +} + +// Finalizer compositor to render objects that we don't want in the MRT textures (ex. water) +// NB the water has to be rendered in a seperate compositor anyway, because it +// accesses the MRT textures which can't be done while they are still being rendered to. +compositor gbufferFinalizer +{ + technique + { + texture no_mrt_output target_width target_height PF_R8G8B8A8 depth_pool 2 no_fsaa + texture previousscene target_width target_height PF_R8G8B8A8 + + target previousscene + { + input previous + } + target no_mrt_output + { + input none + shadows off + pass clear + { + clear + { + buffers colour + colour_value 0 0 0 0 + } + } + pass render_quad + { + material RenderSceneNoDepth + input 0 previousscene + } + pass render_scene + { + first_render_queue 71 + last_render_queue 100 + } + } + target_output + { + input none + pass clear + { + clear + { + } + } + pass render_quad + { + material RenderSceneNoDepth + input 0 no_mrt_output + } + } + } +} diff --git a/files/gbuffer/gbuffer.material b/files/gbuffer/gbuffer.material new file mode 100644 index 0000000000..faa8dd4980 --- /dev/null +++ b/files/gbuffer/gbuffer.material @@ -0,0 +1,63 @@ +vertex_program RenderGBuffer_vs cg +{ + source gbuffer.cg + profiles vs_4_0 vs_1_1 arbvp1 + entry_point RenderScene_vs + default_params + { + param_named_auto wvp worldviewproj_matrix + } +} +fragment_program RenderGBuffer_ps cg +{ + source gbuffer.cg + entry_point RenderScene_ps + profiles ps_4_0 ps_2_x arbfp1 + default_params + { + } +} +material RenderScene +{ + technique + { + pass + { + vertex_program_ref RenderGBuffer_vs + { + } + + fragment_program_ref RenderGBuffer_ps + { + } + + texture_unit tex1 + { + //scenebuffer + } + } + } +} + +material RenderSceneNoDepth +{ + technique + { + pass + { + depth_write off + vertex_program_ref RenderGBuffer_vs + { + } + + fragment_program_ref RenderGBuffer_ps + { + } + + texture_unit tex1 + { + //scenebuffer + } + } + } +} diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 878b9b0957..d5a8bdfd46 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1,8 +1,12 @@ +# WARNING: Editing this file might have no effect, as these +# settings are overwritten by your user settings file. + [General] # Camera field of view field of view = 55 # Texture filtering mode. valid values: +# none # anisotropic # bilinear # trilinear @@ -54,6 +58,26 @@ fog end factor = 1.0 # Max. number of lights that affect the terrain. Setting to 1 will only reflect sunlight num lights = 8 +[Water] +# Enable this to get fancy-looking water with reflections and refractions +# All the settings below have no effect if this is false +shader = true + +rtt size = 512 + +reflect terrain = true + +reflect statics = false + +reflect small statics = false + +reflect actors = true + +reflect misc = false + +# Enable underwater effect. It is not resource intensive, so only disable it if you have problems. +underwater effect = true + [Sound] # Device name. Blank means default device = diff --git a/files/water/Example_Fresnel.cg b/files/water/Example_Fresnel.cg deleted file mode 100644 index e091fc5878..0000000000 --- a/files/water/Example_Fresnel.cg +++ /dev/null @@ -1,116 +0,0 @@ -// Vertex program for fresnel reflections / refractions -void main_vp( - float4 pos : POSITION, - float4 normal : NORMAL, - float2 tex : TEXCOORD0, - - out float4 oPos : POSITION, - out float3 noiseCoord : TEXCOORD0, - out float4 projectionCoord : TEXCOORD1, - out float3 oEyeDir : TEXCOORD2, - out float3 oNormal : TEXCOORD3, - - uniform float4x4 worldViewProjMatrix, - uniform float3 eyePosition, // object space - uniform float timeVal, - uniform float scale, // the amount to scale the noise texture by - uniform float scroll, // the amount by which to scroll the noise - uniform float noise // the noise perturb as a factor of the time - ) -{ - oPos = mul(worldViewProjMatrix, pos); - // Projective texture coordinates, adjust for mapping - 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); - projectionCoord = mul(scalemat, oPos); - // Noise map coords - noiseCoord.xy = (tex + (timeVal * scroll)) * scale; - noiseCoord.z = noise * timeVal; - - oEyeDir = normalize(pos.xyz - eyePosition); - oNormal = normal.rgb; - -} - -// Fragment program for distorting a texture using a 3D noise texture -void main_fp( - float3 noiseCoord : TEXCOORD0, - float4 projectionCoord : TEXCOORD1, - float3 eyeDir : TEXCOORD2, - float3 normal : TEXCOORD3, - - out float4 col : COLOR, - - uniform float4 tintColour, - uniform float noiseScale, - uniform float fresnelBias, - uniform float fresnelScale, - uniform float fresnelPower, - uniform sampler2D waterTex : register(s0), - uniform sampler2D noiseMap : register(s1), - uniform sampler2D reflectMap : register(s2), - uniform sampler2D refractMap : register(s3) - ) -{ - // Do the tex projection manually so we can distort _after_ - float2 final = projectionCoord.xy / projectionCoord.w; - - // Noise - float3 noiseNormal = (tex2D(noiseMap, (noiseCoord.xy / 5)).rgb - 0.5).rbg * noiseScale; - final += noiseNormal.xz; - - // Fresnel - //normal = normalize(normal + noiseNormal.xz); - float fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower); - - // Reflection / refraction - float4 reflectionColour = tex2D(reflectMap, final); - float4 refractionColour = tex2D(refractMap, final) + tintColour; - - // Final colour - col = lerp(refractionColour, reflectionColour, fresnel) * tex2D(waterTex, noiseNormal) / 3 ; - - -} - - -// Old version to match ATI PS 1.3 implementation -void main_vp_old( - float4 pos : POSITION, - float4 normal : NORMAL, - float2 tex : TEXCOORD0, - - out float4 oPos : POSITION, - out float fresnel : COLOR, - out float3 noiseCoord : TEXCOORD0, - out float4 projectionCoord : TEXCOORD1, - - uniform float4x4 worldViewProjMatrix, - uniform float3 eyePosition, // object space - uniform float fresnelBias, - uniform float fresnelScale, - uniform float fresnelPower, - uniform float timeVal, - uniform float scale, // the amount to scale the noise texture by - uniform float scroll, // the amount by which to scroll the noise - uniform float noise // the noise perturb as a factor of the time - ) -{ - oPos = mul(worldViewProjMatrix, pos); - // Projective texture coordinates, adjust for mapping - 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); - projectionCoord = mul(scalemat, oPos); - // Noise map coords - noiseCoord.xy = (tex + (timeVal * scroll)) * scale; - noiseCoord.z = noise * timeVal; - - // calc fresnel factor (reflection coefficient) - float3 eyeDir = normalize(pos.xyz - eyePosition); - fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower); - -} diff --git a/files/water/Example_FresnelPS.asm b/files/water/Example_FresnelPS.asm deleted file mode 100644 index 2de078ef5a..0000000000 --- a/files/water/Example_FresnelPS.asm +++ /dev/null @@ -1,72 +0,0 @@ -ps.1.4 - // conversion from Cg generated ARB_fragment_program to ps.1.4 by NFZ - // command line args: -profile arbfp1 -entry main_fp - // program main_fp - // c0 : distortionRange - // c1 : tintColour - // testure 0 : noiseMap - // texture 1 : reflectMap - // texture 2 : refractMap - // v0.x : fresnel - // t0.xyz : noiseCoord - // t1.xyw : projectionCoord - -def c2, 2, 1, 0, 0 - - // Cg: distort.x = tex3D(noiseMap, noiseCoord).x; - // arbfp1: TEX R0.x, fragment.texcoord[0], texture[0], 3D; - // sample noise map using noiseCoord in TEX unit 0 - -texld r0, t0.xyz - - // get projected texture coordinates from TEX coord 1 - // will be used in phase 2 - -texcrd r1.xy, t1_dw.xyw -mov r1.z, c2.y - - // Cg: distort.y = tex3D(noiseMap, noiseCoord + yoffset).x; - // arbfp1: ADD R1.xyz, fragment.texcoord[0], c1; - // arbfp1: TEX R1.x, R1, texture[0], 3D; - // arbfp1: MOV R0.y, R1.x; - - // Cg: distort = (distort * 2 - 1) * distortionRange; - // arbfp1: MAD R0.xy, R0, c0.x, -c0.y; - // arbfp1: MUL R0.xy, R0, u0.x; - // (distort * 2 - 1) same as 2*(distort -.5) so use _bx2 - - - // Cg: final = projectionCoord.xy / projectionCoord.w; - // Cg: final += distort; - // arbfp1: RCP R0.w, fragment.texcoord[1].w; - // arbfp1: MAD R0.xy, fragment.texcoord[1], R0.w, R0; - // final = (distort * projectionCoord.w) + projectionCoord.xy - // for ps.1.4 have to re-arrange things a bit to perturb projected texture coordinates - -mad r0.xyz, r0_bx2, c0.x, r1 - -phase - - // do dependant texture reads - // Cg: reflectionColour = tex2D(reflectMap, final); - // arbfp1: TEX R0, R0, texture[1], 2D; - // sampe reflectMap using dependant read : texunit 1 - -texld r1, r0.xyz - - // Cg: refractionColour = tex2D(refractMap, final) + tintColour; - // arbfp1: TEX R1, R0, texture[2], 2D; - // sample refractMap : texunit 2 - -texld r2, r0.xyz - - // adding tintColour that is in global c1 - // arbfp1: ADD R1, R1, u1; - -add r2, r2, c1 - - // Cg: col = lerp(refractionColour, reflectionColour, fresnel); - // arbfp1: ADD R0, R0, -R1; - // arbfp1: MAD result.color, fragment.color.primary.x, R0, R1; - -lrp r0, v0.x, r1, r2 diff --git a/files/water/Examples-Water.material b/files/water/Examples-Water.material deleted file mode 100644 index 2b46d6e08d..0000000000 --- a/files/water/Examples-Water.material +++ /dev/null @@ -1,149 +0,0 @@ - -vertex_program Water/GlassVP cg -{ - source GlassVP.cg - entry_point glass_vp - profiles vs_1_1 arbvp1 - - default_params - { - param_named_auto worldViewProj worldviewproj_matrix - } -} - - -fragment_program Water/GlassFP cg -{ - source GlassFP.cg - entry_point main_ps - profiles ps_2_0 arbfp1 -} - -material Water/Compositor -{ - technique - { - pass - { - depth_check off - vertex_program_ref Water/GlassVP - { - param_named_auto timeVal time 0.25 - param_named scale float 0.1 - } - - fragment_program_ref Water/GlassFP - { - param_named tintColour float4 0 0.35 0.35 1 - } - - texture_unit RT - { - tex_coord_set 0 - tex_address_mode clamp - filtering linear linear linear - } - - texture_unit - { - texture WaterNormal1.tga 2d - tex_coord_set 1 - //tex_address_mode clamp - filtering linear linear linear - } - texture_unit - { - texture caustic_0.png 2d - tex_coord_set 2 - //tex_address_mode clamp - filtering linear linear linear - } - } - } -} -vertex_program Water/RefractReflectVP cg -{ - source Example_Fresnel.cg - entry_point main_vp - profiles vs_1_1 arbvp1 -} -vertex_program Water/RefractReflectVPold cg -{ - source Example_Fresnel.cg - entry_point main_vp_old - profiles vs_1_1 arbvp1 -} - -fragment_program Water/RefractReflectFP cg -{ - source Example_Fresnel.cg - entry_point main_fp - // sorry, ps_1_1 and fp20 can't do this - profiles ps_2_0 arbfp1 -} - -fragment_program Water/RefractReflectPS asm -{ - source Example_FresnelPS.asm - // sorry, only for ps_1_4 :) - syntax ps_1_4 - -} -material Examples/Water0 -{ - - technique - { - pass - { - // - - depth_write off - vertex_program_ref Water/RefractReflectVP - { - param_named_auto worldViewProjMatrix worldviewproj_matrix - param_named_auto eyePosition camera_position_object_space - param_named_auto timeVal time 0.15 - param_named scroll float 1 - param_named scale float 1 - param_named noise float 1 - // scroll and noisePos will need updating per frame - } - fragment_program_ref Water/RefractReflectFP - { - param_named fresnelBias float -0.1 - param_named fresnelScale float 0.8 - param_named fresnelPower float 20 - param_named tintColour float4 1 1 1 1 - param_named noiseScale float 0.05 - } - // Water - scene_blend alpha_blend - texture_unit - { - - // Water texture - texture Water02.jpg - // min / mag filtering, no mip - filtering linear linear none - alpha_op_ex source1 src_manual src_current 0.9 - - } - // Noise - texture_unit - { - alpha_op_ex source1 src_manual src_current 0.9 - // Perlin noise volume - texture waves2.dds - // min / mag filtering, no mip - filtering linear linear none - } - - - } - - - } - -} - diff --git a/files/water/GlassFP.cg b/files/water/GlassFP.cg deleted file mode 100644 index eb18885d25..0000000000 --- a/files/water/GlassFP.cg +++ /dev/null @@ -1,15 +0,0 @@ -sampler RT : register(s0); -sampler NormalMap : register(s1); -sampler CausticMap : register(s2); - -float4 main_ps(float2 iTexCoord : TEXCOORD0, - float3 noiseCoord : TEXCOORD1, - uniform float4 tintColour) : COLOR -{ - float4 normal = tex2D(NormalMap, noiseCoord); - - - return tex2D(RT, iTexCoord + normal.xy * 0.05) + - (tex2D(CausticMap, noiseCoord) / 5) + - tintColour ; -} diff --git a/files/water/GlassVP.cg b/files/water/GlassVP.cg deleted file mode 100644 index 71153769c2..0000000000 --- a/files/water/GlassVP.cg +++ /dev/null @@ -1,24 +0,0 @@ -void glass_vp -( - in float4 inPos : POSITION, - - out float4 pos : POSITION, - out float2 uv0 : TEXCOORD0, - out float4 noiseCoord : TEXCOORD1, - - uniform float4x4 worldViewProj, - uniform float timeVal, - uniform float scale -) -{ - // Use standardise transform, so work accord with render system specific (RS depth, requires texture flipping, etc) - pos = mul(worldViewProj, inPos); - - // The input positions adjusted by texel offsets, so clean up inaccuracies - inPos.xy = sign(inPos.xy); - - // Convert to image-space - uv0 = (float2(inPos.x, -inPos.y) + 1.0f) * 0.5f; - noiseCoord = (pos + timeVal) * scale; -} - diff --git a/files/water/Water02.jpg b/files/water/Water02.jpg deleted file mode 100644 index 3efda7b6a8..0000000000 Binary files a/files/water/Water02.jpg and /dev/null differ diff --git a/files/water/WaterNormal1.tga b/files/water/WaterNormal1.tga deleted file mode 100644 index a9ca11b7ed..0000000000 Binary files a/files/water/WaterNormal1.tga and /dev/null differ diff --git a/files/water/WaterNormal2.tga b/files/water/WaterNormal2.tga new file mode 100644 index 0000000000..771d150419 Binary files /dev/null and b/files/water/WaterNormal2.tga differ diff --git a/files/water/underwater.cg b/files/water/underwater.cg new file mode 100644 index 0000000000..b853dd5355 --- /dev/null +++ b/files/water/underwater.cg @@ -0,0 +1,61 @@ +void main_vp +( + in float4 inPos : POSITION, + + out float4 pos : POSITION, + out float2 uv0 : TEXCOORD0, + out float4 noiseCoord : TEXCOORD1, + + uniform float4x4 worldViewProj, + uniform float timeVal, + uniform float scale +) +{ + // Use standardise transform, so work accord with render system specific (RS depth, requires texture flipping, etc) + pos = mul(worldViewProj, inPos); + + // The input positions adjusted by texel offsets, so clean up inaccuracies + inPos.xy = sign(inPos.xy); + + // Convert to image-space + uv0 = (float2(inPos.x, -inPos.y) + 1.0f) * 0.5f; + noiseCoord = (pos + timeVal) * scale; +} + + + +float4 main_fp_nomrt (float2 iTexCoord : TEXCOORD0, + float3 noiseCoord : TEXCOORD1, + uniform sampler2D RT : register(s0), + uniform sampler2D NormalMap : register(s1), + uniform sampler2D CausticMap : register(s2), + uniform float4 tintColour) : COLOR +{ + float4 normal = tex2D(NormalMap, noiseCoord) * 2 - 1; + + return tex2D(RT, iTexCoord + normal.xy * 0.015) + + (tex2D(CausticMap, noiseCoord) / 5) + + tintColour ; +} + + +float4 main_fp (float2 iTexCoord : TEXCOORD0, + float3 noiseCoord : TEXCOORD1, + uniform float far, + uniform sampler2D RT : register(s0), + uniform sampler2D NormalMap : register(s1), + uniform sampler2D CausticMap : register(s2), + uniform sampler2D DepthMap : register(s3), + uniform float4 tintColour) : COLOR +{ + float4 normal = tex2D(NormalMap, noiseCoord) * 2 - 1; + + float depth = tex2D(DepthMap, iTexCoord + normal.xy * 0.015).r * far; + depth = saturate(depth / 2000.f); + + float4 color = tex2D(RT, iTexCoord + normal.xy * 0.015) + + (tex2D(CausticMap, noiseCoord) / 5) + + tintColour; + + return lerp(color, float4(0, 0.65, 0.65, 1), depth); +} diff --git a/files/water/water.cg b/files/water/water.cg new file mode 100644 index 0000000000..bf6d04c5cc --- /dev/null +++ b/files/water/water.cg @@ -0,0 +1,121 @@ +void main_vp +( + in float4 iPos : POSITION + , in float2 iUv : TEXCOORD0 + + , out float4 oPos : POSITION + , out float3 oScreenCoords : TEXCOORD0 + , out float2 oUv : TEXCOORD1 + , out float oDepth : TEXCOORD2 + , out float4 oEyeVector : TEXCOORD3 + + , uniform float4x4 wvpMat + , uniform float4 camPosObjSpace +) +{ + oPos = mul(wvpMat, iPos); + + oUv = iUv * 10; // uv scale + oDepth = oPos.z; + + 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 ); + float4 texcoordProj = mul(scalemat, oPos); + oScreenCoords = float3(texcoordProj.x, texcoordProj.y, texcoordProj.w); + + oEyeVector = camPosObjSpace - iPos; +} + +void main_fp +( + out float4 oColor : COLOR + + , in float3 iScreenCoords : TEXCOORD0 + , in float2 iUv : TEXCOORD1 + , in float iDepth : TEXCOORD2 + , in float4 iEyeVector : TEXCOORD3 + , uniform float renderTargetFlipping + , uniform float4 lightPosObjSpace0 + , uniform float4 lightSpecularColour0 + + , uniform sampler2D reflectionMap : register(s0) + , uniform sampler2D refractionMap : register(s1) + , uniform sampler2D depthMap : register(s2) + , uniform sampler2D normalMap : register(s3) + , uniform float time + , uniform float far + , uniform float4 fogParams + , uniform float4 fogColour + , uniform float isUnderwater +) +{ + + float2 screenCoords = iScreenCoords.xy / iScreenCoords.z; + screenCoords.y = (1-saturate(renderTargetFlipping))+renderTargetFlipping*screenCoords.y; + + // No need for transparency since we are using a refraction map + oColor.a = 1; + + // Sample screen-space depth map and subtract pixel depth to get the real water depth + float depthTex = tex2D(depthMap, screenCoords).r; + float depth1 = depthTex * far - iDepth; + depth1 = saturate(depth1 / 500.f); + + // Simple wave effect. to be replaced by something better + float2 uv1 = iUv + time * float2(0.5, 0); + float2 uv2 = iUv + time * float2(0, 0.5); + float2 uv3 = iUv + time * float2(-0.5, 0); + float2 uv4 = iUv + time * float2(0, -0.5); + float4 normal = tex2D(normalMap, uv1) + tex2D(normalMap, uv2) + tex2D(normalMap, uv3) + tex2D(normalMap, uv4); + normal = normal / 4.f; + normal = 2*normal - 1; + + float2 screenCoords_reflect = screenCoords + normal.yx * 0.05; + float2 screenCoords_refract = screenCoords + normal.yx * 0.05 * depth1; + + // Sample depth again with the refracted coordinates + depthTex = tex2D(depthMap, screenCoords_refract).r; + float depth2 = (depthTex * far - iDepth) / 500.f; + depth2 = (depthTex == 0 ? 1 : depth2); + // if depth2 is less than 0, this means we would refract something which is above water, + // which we don't want to - so in that case, don't refract + if (depth2 < 0.25) // delta due to inaccuracies + { + screenCoords_refract = screenCoords; + depth2 = depth1; + } + depth2 = saturate(depth2); + + float4 reflection = tex2D(reflectionMap, screenCoords_reflect); + float4 refraction = tex2D(refractionMap, screenCoords_refract); + + // tangent to object space + normal.xyz = normal.xzy; + + iEyeVector.xyz = normalize(iEyeVector.xyz); + + // fresnel + float facing = 1.0 - max(abs(dot(iEyeVector.xyz, normal.xyz)), 0); + float reflectionFactor = saturate(0.35 + 0.65 * pow(facing, 2)); + + // specular + float3 lightDir = normalize(lightPosObjSpace0.xyz); // assumes that light 0 is a directional light + float3 halfVector = normalize(iEyeVector + lightDir); + float specular = pow(max(dot(normal.xyz, halfVector.xyz), 0), 64); + + float opacity = depth2 * saturate(reflectionFactor + specular); + opacity *= (1-isUnderwater); + + reflection.xyz += lightSpecularColour0.xyz * specular; + + oColor.xyz = lerp(refraction.xyz, reflection.xyz, opacity); + + oColor.xyz += isUnderwater * float3(0, 0.35, 0.35); // underwater tint color + oColor.xyz = lerp(oColor.xyz, float3(0, 0.65, 0.65), saturate(isUnderwater * (iDepth / 2000.f))); // underwater fog + + // add fog + //float fogValue = saturate((iDepth - fogParams.y) * fogParams.w); + //oColor.xyz = lerp(oColor.xyz, fogColour, fogValue); +} diff --git a/files/water/water.compositor b/files/water/water.compositor index 67bf908960..8d9c3cb396 100644 --- a/files/water/water.compositor +++ b/files/water/water.compositor @@ -1,4 +1,4 @@ -compositor Water +compositor UnderwaterNoMRT { technique { @@ -13,9 +13,34 @@ compositor Water pass render_quad { - material Water/Compositor + material Water/CompositorNoMRT input 0 rt0 } } } -} +} + + +compositor Underwater +{ + technique + { + texture_ref scene gbuffer mrt_output + texture rt0 target_width target_height PF_R8G8B8 + + target rt0 { input previous } + + target_output + { + // Start with clear output + input none + + pass render_quad + { + material Water/Compositor + input 0 rt0 + input 3 scene 1 + } + } + } +} diff --git a/files/water/water.material b/files/water/water.material new file mode 100644 index 0000000000..7ce6e7ba29 --- /dev/null +++ b/files/water/water.material @@ -0,0 +1,198 @@ +vertex_program UnderwaterEffectVP cg +{ + source underwater.cg + entry_point main_vp + profiles vs_1_1 arbvp1 + + default_params + { + param_named_auto worldViewProj worldviewproj_matrix + } +} + + +fragment_program UnderwaterEffectFP_NoMRT cg +{ + source underwater.cg + entry_point main_fp_nomrt + profiles ps_2_0 arbfp1 +} + +fragment_program UnderwaterEffectFP cg +{ + source underwater.cg + entry_point main_fp + profiles ps_2_0 arbfp1 +} + +vertex_program Water_VP cg +{ + source water.cg + entry_point main_vp + profiles vs_2_x arbvp1 + + default_params + { + param_named_auto wvpMat worldviewproj_matrix + } +} + +fragment_program Water_FP cg +{ + source water.cg + entry_point main_fp + profiles ps_2_x arbfp1 +} + +material Water +{ + technique + { + pass + { + cull_hardware none + + vertex_program_ref Water_VP + { + param_named_auto camPosObjSpace camera_position_object_space + } + fragment_program_ref Water_FP + { + param_named_auto time time 0.1 + //param_named_auto fogColour fog_colour + //param_named_auto fogParams fog_params + param_named_auto renderTargetFlipping render_target_flipping + param_named_auto far far_clip_distance + param_named_auto lightPosObjSpace0 light_position_object_space 0 + param_named_auto lightSpecularColour0 light_specular_colour 0 + param_named isUnderwater float 0 + } + + texture_unit reflectionMap + { + texture WaterReflection + tex_address_mode clamp + } + + texture_unit refractionMap + { + tex_address_mode clamp + } + + texture_unit depthMap + { + tex_address_mode clamp + } + + texture_unit normalMap + { + texture WaterNormal2.tga + } + } + } + technique + { + scheme Map + pass + { + cull_hardware none + scene_blend alpha_blend + depth_write off + diffuse 0 0 0 1 + emissive 0.6 0.7 1.0 + ambient 0 0 0 + texture_unit + { + // texture names set via code + scale 0.1 0.1 + alpha_op_ex source1 src_manual src_current 0.7 + } + } + } +} + +material Water/CompositorNoMRT +{ + technique + { + pass + { + depth_check off + vertex_program_ref UnderwaterEffectVP + { + param_named_auto timeVal time 0.25 + param_named scale float 0.1 + } + + fragment_program_ref UnderwaterEffectFP_NoMRT + { + param_named tintColour float4 0 0.35 0.35 1 + } + + texture_unit RT + { + tex_coord_set 0 + tex_address_mode clamp + filtering linear linear linear + } + + texture_unit + { + texture WaterNormal2.tga 2d + tex_coord_set 1 + //tex_address_mode clamp + filtering linear linear linear + } + texture_unit + { + texture caustic_0.png 2d + tex_coord_set 2 + //tex_address_mode clamp + filtering linear linear linear + } + } + } +} + +material Water/Compositor +{ + technique + { + pass + { + depth_check off + vertex_program_ref UnderwaterEffectVP + { + param_named_auto timeVal time 0.25 + param_named scale float 0.1 + } + + fragment_program_ref UnderwaterEffectFP + { + param_named tintColour float4 0 0.35 0.35 1 + param_named_auto far far_clip_distance + } + + texture_unit RT + { + tex_coord_set 0 + tex_address_mode clamp + } + + texture_unit + { + texture WaterNormal2.tga 2d + tex_coord_set 2 + } + texture_unit + { + texture caustic_0.png 2d + tex_coord_set 3 + } + + texture_unit DepthMap + { + } + } + } +} diff --git a/files/water/waves2.dds b/files/water/waves2.dds deleted file mode 100644 index c379886fae..0000000000 Binary files a/files/water/waves2.dds and /dev/null differ