diff --git a/CMakeLists.txt b/CMakeLists.txt index c565cf930..2b372e40b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/mouselook.cpp ${LIBDIR}/openengine/ogre/fader.cpp + ${LIBDIR}/openengine/ogre/imagerotate.cpp ) set(OENGINE_GUI ${LIBDIR}/openengine/gui/events.cpp @@ -231,6 +232,9 @@ endif (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg "${OpenMW_BINARY_DIR}/settings-default.cfg") +configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg + "${OpenMW_BINARY_DIR}/transparency-overrides.cfg") + configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local "${OpenMW_BINARY_DIR}/openmw.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg @@ -312,6 +316,7 @@ if(DPKG_PROGRAM) #Install global configuration files INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") + INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "../etc/openmw/" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2342882e0..c4b3776ed 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects - renderinginterface localmap occlusionquery terrain terrainmaterial water + renderinginterface localmap occlusionquery terrain terrainmaterial water shadows shaderhelper ) add_openmw_dir (mwinput @@ -25,7 +25,7 @@ add_openmw_dir (mwinput add_openmw_dir (mwgui layouts text_input widgets race class birth review window_manager console dialogue dialogue_history window_base stats_window messagebox journalwindow charactercreation - map_window window_pinnable_base + map_window window_pinnable_base cursorreplace ) add_openmw_dir (mwdialogue @@ -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 spells ) # Main executable diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 97aaf4090..2d3c872dd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ #include "mwinput/inputmanager.hpp" #include "mwgui/window_manager.hpp" +#include "mwgui/cursorreplace.hpp" #include "mwscript/scriptmanager.hpp" #include "mwscript/compilercontext.hpp" @@ -349,6 +351,13 @@ void OMW::Engine::go() mFpsLevel = settings.getInt("fps", "HUD"); + // load nif overrides + NifOverrides::Overrides nifOverrides; + if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg")) + nifOverrides.loadTransparencyOverrides(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg"); + else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg")) + nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg"); + mOgre->configure(!boost::filesystem::is_regular_file(mCfgMgr.getOgreConfigPath()), mCfgMgr.getOgreConfigPath().string(), mCfgMgr.getLogPath().string(), @@ -361,12 +370,17 @@ void OMW::Engine::go() addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); + addResourcesDirectory(mResDir / "gbuffer"); + addResourcesDirectory(mResDir / "shadows"); // Create the window mOgre->createWindow("OpenMW"); loadBSA(); + // cursor replacer (converts the cursor from the bsa so they can be used by mygui) + MWGui::CursorReplace replacer; + // Create the world mEnvironment.mWorld = new MWWorld::World (*mOgre, mFileCollections, mMaster, mResDir, mNewGame, mEnvironment, mEncoding, mFallbackMap); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8bb394129..a5feba4b5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -61,6 +61,10 @@ namespace MWClass for (int i=0; i<27; ++i) data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]); + for (std::vector::const_iterator iter (ref->base->spells.list.begin()); + iter!=ref->base->spells.list.end(); ++iter) + data->mCreatureStats.mSpells.add (*iter); + // creature stats data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength); data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence); diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp new file mode 100644 index 000000000..2079538fc --- /dev/null +++ b/apps/openmw/mwgui/cursorreplace.cpp @@ -0,0 +1,16 @@ +#include "cursorreplace.hpp" + +#include +#include + +#include +#include + +using namespace MWGui; + +CursorReplace::CursorReplace() +{ + OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90); + OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45); + OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); +} diff --git a/apps/openmw/mwgui/cursorreplace.hpp b/apps/openmw/mwgui/cursorreplace.hpp new file mode 100644 index 000000000..06fe28e39 --- /dev/null +++ b/apps/openmw/mwgui/cursorreplace.hpp @@ -0,0 +1,16 @@ +#ifndef GAME_CURSORREPLACE_H +#define GAME_CURSORREPLACE_H + +#include + +namespace MWGui +{ + /// \brief MyGUI does not support rotating cursors, so we have to do it manually + class CursorReplace + { + public: + CursorReplace(); + }; +} + +#endif diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 485898d36..c6841ae43 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -66,6 +66,8 @@ namespace MWInput A_QuickLoad, A_QuickMenu, A_GameMenu, + A_ToggleWeapon, + A_ToggleSpell, A_LAST // Marker for the last item }; @@ -87,6 +89,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(); @@ -198,7 +232,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); @@ -243,6 +280,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/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index d2edc031d..ab008da9e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -6,6 +6,7 @@ #include "stat.hpp" #include "magiceffects.hpp" +#include "spells.hpp" namespace MWMechanics { @@ -14,7 +15,7 @@ namespace MWMechanics Stat mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; - std::set mAbilities; + Spells mSpells; MagicEffects mMagicEffects; }; } diff --git a/apps/openmw/mwmechanics/drawstate.hpp b/apps/openmw/mwmechanics/drawstate.hpp new file mode 100644 index 000000000..ded25f8d5 --- /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/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanager.cpp index 3c93857ef..f5711e78e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.cpp @@ -23,7 +23,7 @@ namespace MWMechanics // reset creatureStats.mLevel = player->npdt52.level; - creatureStats.mAbilities.clear(); + creatureStats.mSpells.clear(); creatureStats.mMagicEffects = MagicEffects(); for (int i=0; i<27; ++i) @@ -71,7 +71,7 @@ namespace MWMechanics for (std::vector::const_iterator iter (race->powers.list.begin()); iter!=race->powers.list.end(); ++iter) { - insertSpell (*iter, ptr); + creatureStats.mSpells.add (*iter); } } @@ -85,7 +85,7 @@ namespace MWMechanics for (std::vector::const_iterator iter (sign->powers.list.begin()); iter!=sign->powers.list.end(); ++iter) { - insertSpell (*iter, ptr); + creatureStats.mSpells.add (*iter); } } @@ -159,59 +159,14 @@ namespace MWMechanics creatureStats.mDynamic[i].setCurrent (creatureStats.mDynamic[i].getModified()); } - void MechanicsManager::insertSpell (const std::string& id, MWWorld::Ptr& creature) - { - MWMechanics::CreatureStats& creatureStats = - MWWorld::Class::get (creature).getCreatureStats (creature); - - const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (id); - - switch (spell->data.type) - { - case ESM::Spell::ST_Ability: - - if (creatureStats.mAbilities.find (id)==creatureStats.mAbilities.end()) - { - creatureStats.mAbilities.insert (id); - } - - break; - - // TODO ST_SPELL, ST_Blight, ST_Disease, ST_Curse, ST_Power - - default: - - std::cout - << "adding unsupported spell type (" << spell->data.type - << ") to creature: " << id << std::endl; - } - } - void MechanicsManager::adjustMagicEffects (MWWorld::Ptr& creature) { MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (creature).getCreatureStats (creature); - MagicEffects now; - - for (std::set::const_iterator iter (creatureStats.mAbilities.begin()); - iter!=creatureStats.mAbilities.end(); ++iter) - { - const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (*iter); - - for (std::vector::const_iterator iter = spell->effects.list.begin(); - iter!=spell->effects.list.end(); ++iter) - { - if (iter->range==0) // self - { - EffectParam param; - param.mMagnitude = iter->magnMax; // TODO calculate magnitude - now.add (EffectKey (*iter), param); - } - } - } + MagicEffects now = creatureStats.mSpells.getMagicEffects (mEnvironment); - // TODO add effects from other spell types, active spells and equipment + /// \todo add effects from active spells and equipment MagicEffects diff = MagicEffects::diff (creatureStats.mMagicEffects, now); diff --git a/apps/openmw/mwmechanics/mechanicsmanager.hpp b/apps/openmw/mwmechanics/mechanicsmanager.hpp index a7defe178..a121507ce 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.hpp @@ -37,8 +37,6 @@ namespace MWMechanics ///< build player according to stored class/race/birthsign information. Will /// default to the values of the ESM::NPC object, if no explicit information is given. - void insertSpell (const std::string& id, MWWorld::Ptr& creature); - void adjustMagicEffects (MWWorld::Ptr& creature); public: diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index aeb5f56d5..feac5d4d3 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -2,14 +2,19 @@ #define GAME_MWMECHANICS_NPCSTATS_H #include +#include #include "stat.hpp" +#include "drawstate.hpp" namespace MWMechanics { /// \brief Additional stats for NPCs /// /// For non-NPC-specific stats, see the CreatureStats struct. + /// + /// \note For technical reasons the spell list and the currently selected spell is also handled by + /// CreatureStats, even though they are actually NPC stats. struct NpcStats { @@ -24,9 +29,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/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp new file mode 100644 index 000000000..916239a84 --- /dev/null +++ b/apps/openmw/mwmechanics/spells.cpp @@ -0,0 +1,81 @@ + +#include "spells.hpp" + +#include + +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" + +#include "magiceffects.hpp" + +namespace MWMechanics +{ + void Spells::addSpell (const ESM::Spell *spell, MagicEffects& effects) const + { + for (std::vector::const_iterator iter = spell->effects.list.begin(); + iter!=spell->effects.list.end(); ++iter) + { + EffectParam param; + param.mMagnitude = iter->magnMax; /// \todo calculate magnitude + effects.add (EffectKey (*iter), param); + } + } + + Spells::TIterator Spells::begin() const + { + return mSpells.begin(); + } + + Spells::TIterator Spells::end() const + { + return mSpells.end(); + } + + void Spells::add (const std::string& spellId) + { + if (std::find (mSpells.begin(), mSpells.end(), spellId)!=mSpells.end()) + mSpells.push_back (spellId); + } + + void Spells::remove (const std::string& spellId) + { + TContainer::iterator iter = std::find (mSpells.begin(), mSpells.end(), spellId); + + if (iter!=mSpells.end()) + mSpells.erase (iter); + + if (spellId==mSelectedSpell) + mSelectedSpell.clear(); + } + + MagicEffects Spells::getMagicEffects (const MWWorld::Environment& environment) const + { + MagicEffects effects; + + for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) + { + const ESM::Spell *spell = environment.mWorld->getStore().spells.find (*iter); + + if (spell->data.type==ESM::Spell::ST_Ability || spell->data.type==ESM::Spell::ST_Blight || + spell->data.type==ESM::Spell::ST_Disease || spell->data.type==ESM::Spell::ST_Curse) + addSpell (spell, effects); + } + + return effects; + } + + void Spells::clear() + { + mSpells.clear(); + } + + void Spells::setSelectedSpell (const std::string& spellId) + { + mSelectedSpell = spellId; + } + + const std::string Spells::getSelectedSpell() const + { + return mSelectedSpell; + } +} diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp new file mode 100644 index 000000000..f7606c4ac --- /dev/null +++ b/apps/openmw/mwmechanics/spells.hpp @@ -0,0 +1,66 @@ +#ifndef GAME_MWMECHANICS_SPELLS_H +#define GAME_MWMECHANICS_SPELLS_H + +#include +#include + +namespace ESM +{ + struct Spell; +} + +namespace MWWorld +{ + struct Environment; +} + +namespace MWMechanics +{ + class MagicEffects; + + /// \brief Spell list + /// + /// This class manages known spells as well as abilities, powers and permanent negative effects like + /// diseaes. + class Spells + { + public: + + typedef std::vector TContainer; + typedef TContainer::const_iterator TIterator; + + private: + + std::vector mSpells; + std::string mSelectedSpell; + + void addSpell (const ESM::Spell *, MagicEffects& effects) const; + + public: + + TIterator begin() const; + + TIterator end() const; + + void add (const std::string& spell); + ///< Adding a spell that is already listed in *this is a no-op. + + void remove (const std::string& spell); + ///< If the spell to be removed is the selected spell, the selected spell will be changed to + /// no spell (empty string). + + MagicEffects getMagicEffects (const MWWorld::Environment& environment) const; + ///< Return sum of magic effects resulting from abilities, blights, deseases and curses. + + void clear(); + ///< Remove all spells of al types. + + void setSelectedSpell (const std::string& spellId); + ///< This function does not verify, if the spell is available. + + const std::string getSelectedSpell() const; + ///< May return an empty string. + }; +} + +#endif diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 4de6453aa..e0eb5ccc2 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 6a94551de..2cc233a01 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,10 @@ 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); + + // use fallback techniques without shadows and without mrt + vp->setMaterialScheme("Fallback"); rtt->update(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c6fe023d6..9de5705e3 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 feb6cb890..5cd013fcf 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; @@ -114,17 +115,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()) { @@ -132,7 +165,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()]; @@ -160,6 +193,12 @@ 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->setCastShadows(true); + + sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + mRenderer.getScene()->destroyEntity(ent); } } @@ -253,6 +292,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); @@ -270,6 +316,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 5911aa4cc..0c19f9f33 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 0c917cda1..80b804dce 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); @@ -62,25 +60,28 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode(); mBBQueryTotal = mRendering->getScene()->createBillboardSet(1); + mBBQueryTotal->setCastShadows(false); 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->setCastShadows(false); 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); /// \todo ideally this should occupy exactly 1 pixel on the screen + mBBQuerySingleObject->setCastShadows(false); 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 +154,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 000000000..2c7f9e9ac --- /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 a08fc276a..ee60407b2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -14,6 +14,10 @@ #include #include +#include "shadows.hpp" +#include "shaderhelper.hpp" +#include "localmap.hpp" +#include "water.hpp" using namespace MWRender; using namespace Ogre; @@ -21,11 +25,11 @@ using namespace Ogre; namespace MWRender { RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment) - :mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0) + :mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mSunEnabled(0) { mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5); - 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 @@ -39,14 +43,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 @@ -64,13 +88,17 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode(); cameraPitchNode->attachObject(mRendering.getCamera()); + mShadows = new Shadows(&mRendering); + mShaderHelper = new ShaderHelper(this); + + mTerrainManager = new TerrainManager(mRendering.getScene(), this, + environment); + //mSkyManager = 0; mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment); mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - mWater = 0; - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mSun = 0; @@ -122,8 +150,7 @@ void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store) void RenderingManager::removeWater () { if(mWater){ - delete mWater; - mWater = 0; + mWater->setActive(false); } } @@ -139,6 +166,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 +223,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 +290,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 +317,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 +366,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 ); } } @@ -371,7 +420,9 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr) void RenderingManager::setSunColour(const Ogre::ColourValue& colour) { + if (!mSunEnabled) return; mSun->setDiffuseColour(colour); + mSun->setSpecularColour(colour); mTerrainManager->setDiffuse(colour); } @@ -383,12 +434,21 @@ void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) void RenderingManager::sunEnable() { - if (mSun) mSun->setVisible(true); + // Don't disable the light, as the shaders assume the first light to be directional. + //if (mSun) mSun->setVisible(true); + mSunEnabled = true; } void RenderingManager::sunDisable() { - if (mSun) mSun->setVisible(false); + // Don't disable the light, as the shaders assume the first light to be directional. + //if (mSun) mSun->setVisible(false); + mSunEnabled = false; + if (mSun) + { + mSun->setDiffuseColour(ColourValue(0,0,0)); + mSun->setSpecularColour(ColourValue(0,0,0)); + } } void RenderingManager::setSunDirection(const Ogre::Vector3& direction) @@ -428,4 +488,14 @@ void RenderingManager::enableLights() mObjects.enableLights(); } +const bool RenderingManager::useMRT() +{ + return Settings::Manager::getBool("shader", "Water"); +} + +Shadows* RenderingManager::getShadows() +{ + return mShadows; +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ff9cd168e..a563d78c6 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -25,17 +25,12 @@ #include "objects.hpp" #include "actors.hpp" #include "player.hpp" -#include "water.hpp" -#include "localmap.hpp" #include "occlusionquery.hpp" namespace Ogre { - class Camera; - class Viewport; class SceneManager; class SceneNode; - class RaySceneQuery; class Quaternion; class Vector3; } @@ -48,7 +43,10 @@ namespace MWWorld namespace MWRender { - + class Shadows; + class ShaderHelper; + class LocalMap; + class Water; class RenderingManager: private RenderingInterface { @@ -84,6 +82,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 @@ -115,6 +115,8 @@ class RenderingManager: private RenderingInterface { bool occlusionQuerySupported() { return mOcclusionQuery->supported(); }; OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; }; + Shadows* getShadows(); + void setGlare(bool glare); void skyEnable (); void skyDisable (); @@ -150,6 +152,8 @@ class RenderingManager: private RenderingInterface { void setAmbientMode(); + bool mSunEnabled; + SkyManager* mSkyManager; OcclusionQuery* mOcclusionQuery; @@ -181,6 +185,10 @@ class RenderingManager: private RenderingInterface { MWRender::Debugging *mDebugging; MWRender::LocalMap* mLocalMap; + + MWRender::Shadows* mShadows; + + MWRender::ShaderHelper* mShaderHelper; }; } diff --git a/apps/openmw/mwrender/shaderhelper.cpp b/apps/openmw/mwrender/shaderhelper.cpp new file mode 100644 index 000000000..5354251f8 --- /dev/null +++ b/apps/openmw/mwrender/shaderhelper.cpp @@ -0,0 +1,308 @@ +#include "shaderhelper.hpp" +#include "renderingmanager.hpp" +#include "shadows.hpp" + +#include +#include +#include + +#include + +using namespace Ogre; +using namespace MWRender; + +ShaderHelper::ShaderHelper(RenderingManager* rend) +{ + mRendering = rend; + applyShaders(); +} + +void ShaderHelper::applyShaders() +{ + if (!Settings::Manager::getBool("shaders", "Objects")) return; + + bool mrt = RenderingManager::useMRT(); + bool shadows = Settings::Manager::getBool("enabled", "Shadows"); + bool split = Settings::Manager::getBool("split", "Shadows"); + + // shader for normal rendering + createShader(mrt, shadows, split, "main"); + + // fallback shader without mrt and without shadows + // (useful for reflection and for minimap) + createShader(false, false, false, "main_fallback"); +} + +void ShaderHelper::createShader(const bool mrt, const bool shadows, const bool split, const std::string& name) +{ + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + + const int numsplits = 3; + + // the number of lights to support. + // when rendering an object, OGRE automatically picks the lights that are + // closest to the object being rendered. unfortunately this mechanism does + // not work perfectly for objects batched together (they will all use the same + // lights). to work around this, we are simply pushing the maximum number + // of lights here in order to minimize disappearing lights. + int num_lights = Settings::Manager::getInt("num lights", "Objects"); + + { + // vertex + HighLevelGpuProgramPtr vertex; + if (!mgr.getByName(name+"_vp").isNull()) + mgr.remove(name+"_vp"); + + vertex = mgr.createProgram(name+"_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_VERTEX_PROGRAM); + vertex->setParameter("profiles", "vs_4_0 vs_2_x vp40 arbvp1"); + vertex->setParameter("entry_point", "main_vp"); + StringUtil::StrStreamType outStream; + outStream << + "void main_vp( \n" + " float4 position : POSITION, \n" + " float4 normal : NORMAL, \n" + " float4 colour : COLOR, \n" + " in float2 uv : TEXCOORD0, \n" + " out float2 oUV : TEXCOORD0, \n" + " out float4 oPosition : POSITION, \n" + " out float4 oPositionObjSpace : TEXCOORD1, \n" + " out float4 oNormal : TEXCOORD2, \n" + " out float oDepth : TEXCOORD3, \n" + " out float4 oVertexColour : TEXCOORD4, \n"; + if (shadows && !split) outStream << + " out float4 oLightSpacePos0 : TEXCOORD5, \n" + " uniform float4x4 worldMatrix, \n" + " uniform float4x4 texViewProjMatrix0, \n"; + else + { + for (int i=0; isetSource(outStream.str()); + vertex->load(); + vertex->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); + if (shadows) + { + vertex->getDefaultParameters()->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX); + if (!split) + vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix0", GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, 0); + else + { + for (int i=0; igetDefaultParameters()->setNamedAutoConstant("texViewProjMatrix"+StringConverter::toString(i), GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i); + } + } + } + } + + { + // fragment + HighLevelGpuProgramPtr fragment; + if (!mgr.getByName(name+"_fp").isNull()) + mgr.remove(name+"_fp"); + + fragment = mgr.createProgram(name+"_fp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_FRAGMENT_PROGRAM); + fragment->setParameter("profiles", "ps_4_0 ps_2_x fp40 arbfp1"); + fragment->setParameter("entry_point", "main_fp"); + StringUtil::StrStreamType outStream; + + if (shadows) outStream << + "float depthShadow(sampler2D shadowMap, float4 shadowMapPos, float2 offset) \n" + "{ \n" + " shadowMapPos /= shadowMapPos.w; \n" + " float3 o = float3(offset.xy, -offset.x) * 0.3f; \n" + " float c = (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left \n" + " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right \n" + " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left \n" + " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right \n" + " return c / 4; \n" + "} \n"; + + outStream << + "void main_fp( \n" + " in float2 uv : TEXCOORD0, \n" + " out float4 oColor : COLOR, \n" + " uniform sampler2D texture : register(s0), \n" + " float4 positionObjSpace : TEXCOORD1, \n" + " float4 normal : TEXCOORD2, \n" + " float iDepth : TEXCOORD3, \n" + " float4 vertexColour : TEXCOORD4, \n" + " uniform float4 fogColour, \n" + " uniform float4 fogParams, \n"; + + if (shadows) outStream << + " uniform float4 shadowFar_fadeStart, \n"; + + if (shadows && !split) outStream << + " uniform sampler2D shadowMap : register(s1), \n" + " float4 lightSpacePos0 : TEXCOORD5, \n" + " uniform float4 invShadowmapSize0, \n"; + else + { + outStream << + " uniform float4 pssmSplitPoints, \n"; + for (int i=0; i shadowFar_fadeStart.x) ? 1 : ((iDepth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); \n" + " lightColour.xyz += shadow * lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<setSource(outStream.str()); + fragment->load(); + + for (int i=0; igetDefaultParameters()->setNamedAutoConstant("lightPositionObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i); + fragment->getDefaultParameters()->setNamedAutoConstant("lightDiffuse"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i); + fragment->getDefaultParameters()->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); + } + fragment->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); + fragment->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); + 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 (shadows) + { + fragment->getDefaultParameters()->setNamedConstant("shadowFar_fadeStart", Vector4(mRendering->getShadows()->getShadowFar(), mRendering->getShadows()->getFadeStart()*mRendering->getShadows()->getShadowFar(), 0, 0)); + for (int i=0; i < (split ? numsplits : 1); ++i) + { + fragment->getDefaultParameters()->setNamedAutoConstant("invShadowmapSize" + StringConverter::toString(i), GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i+1); + } + if (split) + { + Vector4 splitPoints; + const PSSMShadowCameraSetup::SplitPointList& splitPointList = mRendering->getShadows()->getPSSMSetup()->getSplitPoints(); + // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) + for (int i = 1; i < numsplits; ++i) + { + splitPoints[i-1] = splitPointList[i]; + } + fragment->getDefaultParameters()->setNamedConstant("pssmSplitPoints", splitPoints); + } + } + + if (mrt) + fragment->getDefaultParameters()->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE); + } +} diff --git a/apps/openmw/mwrender/shaderhelper.hpp b/apps/openmw/mwrender/shaderhelper.hpp new file mode 100644 index 000000000..356d345de --- /dev/null +++ b/apps/openmw/mwrender/shaderhelper.hpp @@ -0,0 +1,29 @@ +#ifndef GAME_SHADERHELPER_H +#define GAME_SHADERHELPER_H + +#include + +namespace MWRender +{ + class RenderingManager; + + /// + /// \brief manages the main shader + /// + class ShaderHelper + { + public: + ShaderHelper(RenderingManager* rend); + + void applyShaders(); + ///< apply new settings + + private: + RenderingManager* mRendering; + + void createShader(const bool mrt, const bool shadows, const bool split, const std::string& name); + }; + +} + +#endif diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp new file mode 100644 index 000000000..bf5602f43 --- /dev/null +++ b/apps/openmw/mwrender/shadows.cpp @@ -0,0 +1,173 @@ +#include "shadows.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "renderconst.hpp" + +using namespace Ogre; +using namespace MWRender; + +Shadows::Shadows(OEngine::Render::OgreRenderer* rend) : + mShadowFar(1000), mFadeStart(0.9) +{ + mRendering = rend; + mSceneMgr = mRendering->getScene(); + recreate(); +} + +void Shadows::recreate() +{ + bool enabled = Settings::Manager::getBool("enabled", "Shadows"); + + // Split shadow maps are currently disabled because the terrain cannot cope with them + // (Too many texture units) Solution would be a multi-pass terrain material + bool split = Settings::Manager::getBool("split", "Shadows"); + //const bool split = false; + + if (!enabled) + { + mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE); + return; + } + + int texsize = Settings::Manager::getInt("texture size", "Shadows"); + mSceneMgr->setShadowTextureSize(texsize); + + mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE_INTEGRATED); + + // no point light shadows, i'm afraid. might revisit this with Deferred Shading + mSceneMgr->setShadowTextureCountPerLightType(Light::LT_POINT, 0); + + mSceneMgr->setShadowTextureCountPerLightType(Light::LT_DIRECTIONAL, split ? 3 : 1); + mSceneMgr->setShadowTextureCount(split ? 3 : 1); + + mSceneMgr->setShadowTextureSelfShadow(true); + mSceneMgr->setShadowCasterRenderBackFaces(true); + mSceneMgr->setShadowTextureCasterMaterial("depth_shadow_caster"); + mSceneMgr->setShadowTexturePixelFormat(PF_FLOAT32_R); + mSceneMgr->setShadowDirectionalLightExtrusionDistance(1000000); + + mShadowFar = split ? Settings::Manager::getInt("split shadow distance", "Shadows") : Settings::Manager::getInt("shadow distance", "Shadows"); + mSceneMgr->setShadowFarDistance(mShadowFar); + + mFadeStart = Settings::Manager::getFloat("fade start", "Shadows"); + + ShadowCameraSetupPtr shadowCameraSetup; + if (split) + { + mPSSMSetup = new PSSMShadowCameraSetup(); + mPSSMSetup->setSplitPadding(5); + mPSSMSetup->calculateSplitPoints(3, mRendering->getCamera()->getNearClipDistance(), mShadowFar); + + const Real adjustFactors[3] = {64, 64, 64}; + for (int i=0; i < 3; ++i) + { + mPSSMSetup->setOptimalAdjustFactor(i, adjustFactors[i]); + /*if (i==0) + mSceneMgr->setShadowTextureConfig(i, texsize, texsize, Ogre::PF_FLOAT32_R); + else if (i ==1) + mSceneMgr->setShadowTextureConfig(i, texsize/2, texsize/2, Ogre::PF_FLOAT32_R); + else if (i ==2) + mSceneMgr->setShadowTextureConfig(i, texsize/4, texsize/4, Ogre::PF_FLOAT32_R);*/ + } + + shadowCameraSetup = ShadowCameraSetupPtr(mPSSMSetup); + } + else + { + LiSPSMShadowCameraSetup* lispsmSetup = new LiSPSMShadowCameraSetup(); + lispsmSetup->setOptimalAdjustFactor(2); + //lispsmSetup->setCameraLightDirectionThreshold(Degree(0)); + //lispsmSetup->setUseAggressiveFocusRegion(false); + shadowCameraSetup = ShadowCameraSetupPtr(lispsmSetup); + } + mSceneMgr->setShadowCameraSetup(shadowCameraSetup); + + // Set visibility mask for the shadow render textures + int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows") + + (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows") + + RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows"); + + for (int i = 0; i < (split ? 3 : 1); ++i) + { + TexturePtr shadowTexture = mSceneMgr->getShadowTexture(i); + Viewport* vp = shadowTexture->getBuffer()->getRenderTarget()->getViewport(0); + vp->setVisibilityMask(visibilityMask); + } + + // -------------------------------------------------------------------------------------------------------------------- + // --------------------------- Debug overlays to display the content of shadow maps ----------------------------------- + // -------------------------------------------------------------------------------------------------------------------- + /* + OverlayManager& mgr = OverlayManager::getSingleton(); + Overlay* overlay; + + // destroy if already exists + if (overlay = mgr.getByName("DebugOverlay")) + mgr.destroy(overlay); + + overlay = mgr.create("DebugOverlay"); + for (size_t i = 0; i < (split ? 3 : 1); ++i) { + TexturePtr tex = mRendering->getScene()->getShadowTexture(i); + + // Set up a debug panel to display the shadow + + if (MaterialManager::getSingleton().resourceExists("Ogre/DebugTexture" + StringConverter::toString(i))) + MaterialManager::getSingleton().remove("Ogre/DebugTexture" + StringConverter::toString(i)); + MaterialPtr debugMat = MaterialManager::getSingleton().create( + "Ogre/DebugTexture" + StringConverter::toString(i), + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + + debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false); + TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName()); + t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + + OverlayContainer* debugPanel; + + // destroy container if exists + try + { + if (debugPanel = + static_cast( + mgr.getOverlayElement("Ogre/DebugTexPanel" + StringConverter::toString(i) + ))) + mgr.destroyOverlayElement(debugPanel); + } + catch (Ogre::Exception&) {} + + debugPanel = (OverlayContainer*) + (OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/DebugTexPanel" + StringConverter::toString(i))); + debugPanel->_setPosition(0.8, i*0.25); + debugPanel->_setDimensions(0.2, 0.24); + debugPanel->setMaterialName(debugMat->getName()); + debugPanel->show(); + overlay->add2D(debugPanel); + overlay->show(); + } + */ +} + +PSSMShadowCameraSetup* Shadows::getPSSMSetup() +{ + return mPSSMSetup; +} + +float Shadows::getShadowFar() const +{ + return mShadowFar; +} + +float Shadows::getFadeStart() const +{ + return mFadeStart; +} diff --git a/apps/openmw/mwrender/shadows.hpp b/apps/openmw/mwrender/shadows.hpp new file mode 100644 index 000000000..bc2b141f7 --- /dev/null +++ b/apps/openmw/mwrender/shadows.hpp @@ -0,0 +1,39 @@ +#ifndef GAME_SHADOWS_H +#define GAME_SHADOWS_H + +// forward declares +namespace Ogre +{ + class SceneManager; + class PSSMShadowCameraSetup; +} +namespace OEngine{ + namespace Render{ + class OgreRenderer; + } +} + +namespace MWRender +{ + class Shadows + { + public: + Shadows(OEngine::Render::OgreRenderer* rend); + + void recreate(); + + Ogre::PSSMShadowCameraSetup* getPSSMSetup(); + float getShadowFar() const; + float getFadeStart() const; + + protected: + OEngine::Render::OgreRenderer* mRendering; + Ogre::SceneManager* mSceneMgr; + + Ogre::PSSMShadowCameraSetup* mPSSMSetup; + float mShadowFar; + float mFadeStart; + }; +} + +#endif diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 781f7abd5..859da2dc1 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,13 +95,14 @@ 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); mBBSet->createBillboard(0,0,0); + mBBSet->setCastShadows(false); mMaterial = MaterialManager::getSingleton().create("BillboardMaterial"+StringConverter::toString(bodyCount), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); mMaterial->removeAllTechniques(); @@ -109,6 +116,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 +223,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 +234,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 +430,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 +449,9 @@ 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); + night1_ent->setCastShadows(false); mAtmosphereNight = mRootNode->createChildSceneNode(); mAtmosphereNight->attachObject(night1_ent); @@ -413,7 +489,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 +500,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(); @@ -445,10 +527,12 @@ void SkyManager::create() // Atmosphere (day) mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); Entity* atmosphere_ent = mSceneMgr->createEntity("meshes\\sky_atmosphere.nif"); + atmosphere_ent->setCastShadows(false); 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,29 +550,56 @@ 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(); + clouds_ent->setCastShadows(false); // Clouds vertex shader HighLevelGpuProgramPtr vshader2 = mgr.createProgram("Clouds_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -525,8 +636,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 +652,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(); @@ -564,6 +681,8 @@ void SkyManager::create() mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mCloudMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates(); + mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("textures\\tx_sky_cloudy.dds"); mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); mCreated = true; @@ -653,12 +772,14 @@ void SkyManager::disable() void SkyManager::setMoonColour (bool red) { + if (!mCreated) return; mSecunda->setColour( red ? ColourValue(1.0, 0.0784, 0.0784) : ColourValue(1.0, 1.0, 1.0)); } void SkyManager::setCloudsOpacity(float opacity) { + if (!mCreated) return; mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(opacity)); } @@ -734,7 +855,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); } @@ -813,11 +935,13 @@ void SkyManager::setThunder(const float factor) void SkyManager::setMasserFade(const float fade) { + if (!mCreated) return; mMasser->setVisibility(fade); } void SkyManager::setSecundaFade(const float fade) { + if (!mCreated) return; mSecunda->setVisibility(fade); } @@ -837,3 +961,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 baf5933cb..64d5c16a0 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 0b8f933a2..f9b43655b 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -6,7 +6,9 @@ #include "terrainmaterial.hpp" #include "terrain.hpp" - +#include "renderconst.hpp" +#include "shadows.hpp" +#include using namespace Ogre; @@ -15,8 +17,8 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - TerrainManager::TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& evn) : - mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)) + TerrainManager::TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend, const MWWorld::Environment& evn) : + mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)), mRendering(rend) { TerrainMaterialGeneratorPtr matGen; @@ -47,9 +49,19 @@ namespace MWRender mActiveProfile->setLayerSpecularMappingEnabled(false); mActiveProfile->setLayerNormalMappingEnabled(false); mActiveProfile->setLayerParallaxMappingEnabled(false); - mActiveProfile->setReceiveDynamicShadowsEnabled(false); - //composite maps lead to a drastic reduction in loading time so are + bool shadows = Settings::Manager::getBool("enabled", "Shadows"); + mActiveProfile->setReceiveDynamicShadowsEnabled(shadows); + mActiveProfile->setReceiveDynamicShadowsDepth(shadows); + if (Settings::Manager::getBool("split", "Shadows")) + mActiveProfile->setReceiveDynamicShadowsPSSM(mRendering->getShadows()->getPSSMSetup()); + else + mActiveProfile->setReceiveDynamicShadowsPSSM(0); + + mActiveProfile->setShadowFar(mRendering->getShadows()->getShadowFar()); + mActiveProfile->setShadowFadeStart(mRendering->getShadows()->getFadeStart()); + + //composite maps lead to a drastic increase in loading time so are //disabled mActiveProfile->setCompositeMapEnabled(false); @@ -162,6 +174,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/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 29a4ba36b..dc4a2388c 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -24,7 +24,7 @@ namespace MWRender{ */ class TerrainManager{ public: - TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& env); + TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend, const MWWorld::Environment& env); virtual ~TerrainManager(); void setDiffuse(const Ogre::ColourValue& diffuse); @@ -37,6 +37,7 @@ namespace MWRender{ Ogre::TerrainGroup mTerrainGroup; const MWWorld::Environment& mEnvironment; + RenderingManager* mRendering; Ogre::TerrainMaterialGeneratorB::SM2Profile* mActiveProfile; diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 922ea2280..57bea5388 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 { @@ -85,6 +86,7 @@ namespace Ogre , mPSSM(0) , mDepthShadows(false) , mLowLodShadows(false) + , mShadowFar(1300) { } @@ -101,6 +103,24 @@ namespace Ogre terrain->_setLightMapRequired(mLightmapEnabled, true); terrain->_setCompositeMapRequired(mCompositeMapEnabled); } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setShadowFar(float far) + { + if (mShadowFar != far) + { + mShadowFar = far; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setShadowFadeStart(float fadestart) + { + if (mShadowFadeStart != fadestart) + { + mShadowFadeStart = fadestart; + mParent->_markChanged(); + } + } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setLayerNormalMappingEnabled(bool enabled) { @@ -461,6 +481,7 @@ namespace Ogre StringUtil::StrStreamType sourceStr; generateFragmentProgramSource(prof, terrain, tt, sourceStr); + ret->setSource(sourceStr.str()); ret->load(); defaultFpParams(prof, terrain, tt, ret); @@ -532,8 +553,8 @@ namespace Ogre GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i); if (prof->getReceiveDynamicShadowsDepth()) { - params->setNamedAutoConstant("depthRange" + StringConverter::toString(i), - GpuProgramParameters::ACT_SHADOW_SCENE_DEPTH_RANGE, i); + //params->setNamedAutoConstant("depthRange" + StringConverter::toString(i), + //GpuProgramParameters::ACT_SHADOW_SCENE_DEPTH_RANGE, i); } } } @@ -557,12 +578,16 @@ 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); if (prof->isShadowingEnabled(tt, terrain)) { + params->setNamedConstant("shadowFar_fadeStart", Vector4(prof->mShadowFar, prof->mShadowFadeStart * prof->mShadowFar, 0, 0)); uint numTextures = 1; if (prof->getReceiveDynamicShadowsPSSM()) { @@ -728,7 +753,7 @@ namespace Ogre ret->unload(); } - ret->setParameter("profiles", "vs_3_0 vs_2_0 arbvp1"); + ret->setParameter("profiles", "vs_3_0 vs_2_0 vp40 arbvp1"); ret->setParameter("entry_point", "main_vp"); return ret; @@ -753,12 +778,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; @@ -791,9 +811,9 @@ namespace Ogre outStream << "out float4 oPos : POSITION,\n" - "out float4 oPosObj : TEXCOORD0 \n"; + "out float4 oPosObj : COLOR \n"; - uint texCoordSet = 1; + uint texCoordSet = 0; outStream << ", out float4 oUVMisc : TEXCOORD" << texCoordSet++ <<" // xy = uv, z = camDepth\n"; @@ -819,8 +839,8 @@ namespace Ogre if (fog) { outStream << - ", uniform float4 fogParams\n" - ", out float fogVal : COLOR\n"; + ", uniform float4 fogParams\n"; + //", out float fogVal : COLOR\n"; } if (prof->isShadowingEnabled(tt, terrain)) @@ -832,7 +852,7 @@ namespace Ogre if (texCoordSet > 8) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, - "Requested options require too many texture coordinate sets! Try reducing the number of layers.", + "Requested options require too many texture coordinate sets! Try reducing the number of layers. requested: " + StringConverter::toString(texCoordSet), __FUNCTION__); } @@ -917,10 +937,10 @@ namespace Ogre outStream << - "float4 main_fp(\n" - "float4 position : TEXCOORD0,\n"; + "void main_fp(\n" + "float4 position : COLOR,\n"; - uint texCoordSet = 1; + uint texCoordSet = 0; outStream << "float4 uvMisc : TEXCOORD" << texCoordSet++ << ",\n"; @@ -949,8 +969,8 @@ namespace Ogre if (fog) { outStream << - "uniform float3 fogColour, \n" - "float fogVal : COLOR,\n"; + "uniform float3 fogColour, \n"; + //"float fogVal : COLOR,\n"; } uint currentSamplerIdx = 0; @@ -1034,12 +1054,20 @@ 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" " float2 uv = uvMisc.xy;\n" + " float fogVal = position.w; \n" // base colour " outputCol = float4(0,0,0,1);\n"; @@ -1241,19 +1269,25 @@ 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) { if (terrain->getSceneManager()->getFogMode() == FOG_LINEAR) { outStream << - " fogVal = saturate((oPos.z - fogParams.y) * fogParams.w);\n"; + " float fogVal = saturate((oPos.z - fogParams.y) * fogParams.w);\n"; } else { outStream << - " fogVal = saturate(1 / (exp(oPos.z * fogParams.x)));\n"; + " float fogVal = saturate(1 / (exp(oPos.z * fogParams.x)));\n"; } + outStream << + " oPosObj.w = fogVal; \n"; } if (prof->isShadowingEnabled(tt, terrain)) @@ -1337,7 +1371,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"; } @@ -1349,7 +1388,7 @@ namespace Ogre outStream << "// Simple PCF \n" "// Number of samples in one dimension (square for total samples) \n" - "#define NUM_SHADOW_SAMPLES_1D 2.0 \n" + "#define NUM_SHADOW_SAMPLES_1D 1.0 \n" "#define SHADOW_FILTER_SCALE 1 \n" "#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D \n" @@ -1362,28 +1401,18 @@ namespace Ogre if (prof->getReceiveDynamicShadowsDepth()) { outStream << - "float calcDepthShadow(sampler2D shadowMap, float4 uv, float invShadowMapSize) \n" - "{ \n" - " // 4-sample PCF \n" - - " float shadow = 0.0; \n" - " float offset = (NUM_SHADOW_SAMPLES_1D/2 - 0.5) * SHADOW_FILTER_SCALE; \n" - " for (float y = -offset; y <= offset; y += SHADOW_FILTER_SCALE) \n" - " for (float x = -offset; x <= offset; x += SHADOW_FILTER_SCALE) \n" - " { \n" - " float4 newUV = offsetSample(uv, float2(x, y), invShadowMapSize);\n" - " // manually project and assign derivatives \n" - " // to avoid gradient issues inside loops \n" - " newUV = newUV / newUV.w; \n" - " float depth = tex2D(shadowMap, newUV.xy, 1, 1).x; \n" - " if (depth >= 1 || depth >= uv.z)\n" - " shadow += 1.0;\n" - " } \n" - - " shadow /= SHADOW_SAMPLES; \n" - - " return shadow; \n" - "} \n"; + "float calcDepthShadow(sampler2D shadowMap, float4 shadowMapPos, float2 offset) \n" + " { \n" + " shadowMapPos = shadowMapPos / shadowMapPos.w; \n" + " float2 uv = shadowMapPos.xy; \n" + " float3 o = float3(offset, -offset.x) * 0.3f; \n" + " // Note: We using 2x2 PCF. Good enough and is alot faster. \n" + " float c = (shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.xy).r) ? 1 : 0; // top left \n" + " c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.xy).r) ? 1 : 0; // bottom right \n" + " c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.zy).r) ? 1 : 0; // bottom left \n" + " c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.zy).r) ? 1 : 0; // top right \n" + " return c / 4; \n" + " } \n"; } else { @@ -1421,7 +1450,7 @@ namespace Ogre { outStream << "\n "; for (uint i = 0; i < numTextures; ++i) - outStream << "float invShadowmapSize" << i << ", "; + outStream << "float2 invShadowmapSize" << i << ", "; } outStream << "\n" " float4 pssmSplitPoints, float camDepth) \n" @@ -1443,7 +1472,7 @@ namespace Ogre if (prof->getReceiveDynamicShadowsDepth()) { outStream << - " shadow = calcDepthShadow(shadowMap" << i << ", lsPos" << i << ", invShadowmapSize" << i << "); \n"; + " shadow = calcDepthShadow(shadowMap" << i << ", lsPos" << i << ", invShadowmapSize" << i << ".xy); \n"; } else { @@ -1505,20 +1534,12 @@ namespace Ogre if (prof->getReceiveDynamicShadowsDepth()) { // make linear - outStream << - "oLightSpacePos" << i << ".z = (oLightSpacePos" << i << ".z - depthRange" << i << ".x) * depthRange" << i << ".w;\n"; + //outStream << + // "oLightSpacePos" << i << ".z = (oLightSpacePos" << i << ".z - depthRange" << i << ".x) * depthRange" << i << ".w;\n"; } } - - if (prof->getReceiveDynamicShadowsPSSM()) - { - outStream << - " // pass cam depth\n" - " oUVMisc.z = oPos.z;\n"; - } - } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsParams( @@ -1531,6 +1552,8 @@ namespace Ogre // in semantics & params uint numTextures = 1; + outStream << + ", uniform float4 shadowFar_fadeStart \n"; if (prof->getReceiveDynamicShadowsPSSM()) { numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); @@ -1547,7 +1570,7 @@ namespace Ogre if (prof->getReceiveDynamicShadowsDepth()) { outStream << - ", uniform float inverseShadowmapSize" << i << " \n"; + ", uniform float4 inverseShadowmapSize" << i << " \n"; } } @@ -1582,7 +1605,7 @@ namespace Ogre { outStream << "\n "; for (uint i = 0; i < numTextures; ++i) - outStream << "inverseShadowmapSize" << i << ", "; + outStream << "inverseShadowmapSize" << i << ".xy, "; } outStream << "\n" << " pssmSplitPoints, camDepth);\n"; @@ -1593,7 +1616,7 @@ namespace Ogre if (prof->getReceiveDynamicShadowsDepth()) { outStream << - " float rtshadow = calcDepthShadow(shadowMap0, lightSpacePos0, inverseShadowmapSize0);"; + " float rtshadow = calcDepthShadow(shadowMap0, lightSpacePos0, inverseShadowmapSize0.xy);"; } else { @@ -1602,7 +1625,11 @@ namespace Ogre } } - outStream << + outStream << + " float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; \n" + " float fade = 1-((uvMisc.z - shadowFar_fadeStart.y) / fadeRange); \n" + " rtshadow = (uvMisc.z > shadowFar_fadeStart.x) ? 1 : ((uvMisc.z > shadowFar_fadeStart.y) ? 1-((1-rtshadow)*fade) : rtshadow); \n" + " rtshadow = (1-(1-rtshadow)*0.6); \n" // make the shadow a little less intensive " shadow = min(shadow, rtshadow);\n"; } diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index 3cb316347..db916bf25 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -73,6 +73,9 @@ namespace Ogre void updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain); void requestOptions(Terrain* terrain); + void setShadowFar(float far); + void setShadowFadeStart(float fadestart); + /** Whether to support normal mapping per layer in the shader (default true). */ bool isLayerNormalMappingEnabled() const { return mLayerNormalMappingEnabled; } @@ -245,6 +248,8 @@ namespace Ogre bool mDepthShadows; bool mLowLodShadows; bool mSM3Available; + float mShadowFar; + float mShadowFadeStart; bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const; diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 9de55e3a5..71cf56dfd 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -1,50 +1,151 @@ #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), mToggled(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->setCastShadows(false); - 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); + mReflectionCamera = mSceneManager->createCamera("ReflectionCamera"); + if(!(cell->data.flags & cell->Interior)) { mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY)); } mWaterNode->attachObject(mWater); + + // Create rendertarget for reflection + int rttsize = Settings::Manager::getInt("rtt size", "Water"); + + TexturePtr tex; + if (Settings::Manager::getBool("shader", "Water")) + { + tex = TextureManager::getSingleton().createManual("WaterReflection", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, rttsize, rttsize, 0, PF_FLOAT16_RGBA, TU_RENDERTARGET); + + RenderTarget* rtt = tex->getBuffer()->getRenderTarget(); + Viewport* vp = rtt->addViewport(mReflectionCamera); + vp->setOverlaysEnabled(false); + vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f)); + vp->setShadowsEnabled(false); + vp->setVisibilityMask( mVisibilityFlags ); + // use fallback techniques without shadows and without mrt (currently not implemented for sky and terrain) + //vp->setMaterialScheme("Fallback"); + rtt->addListener(this); + rtt->setActive(true); + + mReflectionTarget = rtt; + } + + mCompositorName = RenderingManager::useMRT() ? "Underwater" : "UnderwaterNoMRT"; + + createMaterial(); + mWater->setMaterial(mMaterial); + + mUnderwaterEffect = Settings::Manager::getBool("underwater effect", "Water"); + + + // ---------------------------------------------------------------------------------------------- + // ---------------------------------- reflection debug overlay ---------------------------------- + // ---------------------------------------------------------------------------------------------- + /* + if (Settings::Manager::getBool("shader", "Water")) + { + OverlayManager& mgr = OverlayManager::getSingleton(); + Overlay* overlay; + // destroy if already exists + if (overlay = mgr.getByName("ReflectionDebugOverlay")) + mgr.destroy(overlay); + + overlay = mgr.create("ReflectionDebugOverlay"); + + if (MaterialManager::getSingleton().resourceExists("Ogre/ReflectionDebugTexture")) + MaterialManager::getSingleton().remove("Ogre/ReflectionDebugTexture"); + MaterialPtr debugMat = MaterialManager::getSingleton().create( + "Ogre/ReflectionDebugTexture", + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + + debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false); + TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName()); + t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + + OverlayContainer* debugPanel; + + // destroy container if exists + try + { + if (debugPanel = + static_cast( + mgr.getOverlayElement("Ogre/ReflectionDebugTexPanel" + ))) + mgr.destroyOverlayElement(debugPanel); + } + catch (Ogre::Exception&) {} + + debugPanel = (OverlayContainer*) + (OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/ReflectionDebugTexPanel")); + debugPanel->_setPosition(0, 0.55); + debugPanel->_setDimensions(0.3, 0.3); + debugPanel->setMaterialName(debugMat->getName()); + debugPanel->show(); + overlay->add2D(debugPanel); + overlay->show(); + } + */ } +void Water::setActive(bool active) +{ + mActive = active; + updateVisible(); +} 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 +166,128 @@ void Water::setHeight(const float height) void Water::toggle() { - mWater->setVisible(!mWater->getVisible()); + mToggled = !mToggled; + updateVisible(); } 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)); + + 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)); + + mWater->setRenderQueueGroup(RQG_UnderWater); + mIsUnderwater = true; } + + updateVisible(); +} + +Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY) +{ + 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) + { + mReflectionCamera->setOrientation(mCamera->getDerivedOrientation()); + mReflectionCamera->setPosition(mCamera->getDerivedPosition()); + mReflectionCamera->setNearClipDistance(mCamera->getNearClipDistance()); + mReflectionCamera->setFarClipDistance(mCamera->getFarClipDistance()); + mReflectionCamera->setAspectRatio(mCamera->getAspectRatio()); + mReflectionCamera->setFOVy(mCamera->getFOVy()); + + // 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); + + mReflectionCamera->enableCustomNearClipPlane(Plane(Vector3::UNIT_Y, mTop)); + mReflectionCamera->enableReflection(Plane(Vector3::UNIT_Y, mTop)); + } +} + +void Water::postRenderTargetUpdate(const RenderTargetEvent& evt) +{ + if (evt.source == mReflectionTarget) + { + mSky->resetSkyPosition(); + mSky->scaleSky(1); + mReflectionCamera->disableCustomNearClipPlane(); + mReflectionCamera->disableReflection(); + } +} + +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); } -Ogre::Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY) +void Water::updateVisible() { - return Ogre::Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2)); + mWater->setVisible(mToggled && mActive); + if (mReflectionTarget) + mReflectionTarget->setActive(mToggled && mActive && !mIsUnderwater); } } // namespace diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 5a5d1cca0..f14482e2b 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,41 @@ namespace MWRender { Ogre::Entity *mWater; bool mIsUnderwater; + bool mActive; + bool mToggled; int mTop; Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY); + protected: + void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); + void updateVisible(); + + SkyManager* mSky; + + std::string mCompositorName; + + void createMaterial(); + Ogre::MaterialPtr mMaterial; + + Ogre::Camera* mReflectionCamera; + + 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/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index df955ec19..1317794fa 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -129,4 +129,10 @@ op 0x2000143: ModWaterLevel op 0x2000144: ToggleWater, twa op 0x2000145: ToggleFogOfWar (tfow) op 0x2000146: TogglePathgrid -opcodes 0x2000147-0x3ffffff unused +op 0x2000147: AddSpell +op 0x2000148: AddSpell, explicit reference +op 0x2000149: RemoveSpell +op 0x200014a: RemoveSpell, explicit reference +op 0x200014b: GetSpell +op 0x200014c: GetSpell, explicit reference +opcodes 0x200014d-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 0e97a39cf..011b8b010 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -280,6 +280,65 @@ namespace MWScript } }; + template + class OpAddSpell : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string id = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.add (id); + } + }; + + template + class OpRemoveSpell : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string id = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.remove (id); + } + }; + + template + class OpGetSpell : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string id = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer value = 0; + + for (MWMechanics::Spells::TIterator iter ( + MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.begin()); + iter!=MWWorld::Class::get (ptr).getCreatureStats (ptr).mSpells.end(); ++iter) + if (*iter==id) + { + value = 1; + break; + } + + runtime.push (value); + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; @@ -311,6 +370,13 @@ namespace MWScript const int opcodeModSkill = 0x20000fa; const int opcodeModSkillExplicit = 0x2000115; + const int opcodeAddSpell = 0x2000147; + const int opcodeAddSpellExplicit = 0x2000148; + const int opcodeRemoveSpell = 0x2000149; + const int opcodeRemoveSpellExplicit = 0x200014a; + const int opcodeGetSpell = 0x200014b; + const int opcodeGetSpellExplicit = 0x200014c; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -381,6 +447,11 @@ namespace MWScript extensions.registerInstruction (mod + skills[i], "l", opcodeModSkill+i, opcodeModSkillExplicit+i); } + + extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); + extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, + opcodeRemoveSpellExplicit); + extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -436,6 +507,14 @@ namespace MWScript interpreter.installSegment5 (opcodeModSkill+i, new OpModSkill (i)); interpreter.installSegment5 (opcodeModSkillExplicit+i, new OpModSkill (i)); } + + interpreter.installSegment5 (opcodeAddSpell, new OpAddSpell); + interpreter.installSegment5 (opcodeAddSpellExplicit, new OpAddSpell); + interpreter.installSegment5 (opcodeRemoveSpell, new OpRemoveSpell); + interpreter.installSegment5 (opcodeRemoveSpellExplicit, + new OpRemoveSpell); + interpreter.installSegment5 (opcodeGetSpell, new OpGetSpell); + interpreter.installSegment5 (opcodeGetSpellExplicit, new OpGetSpell); } } } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index d3a0a34ae..d49b98d0f 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 + { } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index ca733b084..60b89b0b4 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -10,7 +10,7 @@ namespace MWMechanics namespace MWWorld { - struct Environment; + class Environment; ///< \brief Variant of the ContainerStore for NPCs class InventoryStore : public ContainerStore diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 884a72c3a..ed72a4589 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(); @@ -97,5 +104,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 da74fe6de..e199f17e9 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 377e60b39..83d379cf6 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -221,7 +221,6 @@ namespace MWWorld mWorld->adjustSky(); mCellChanged = true; - mRendering.waterAdded(mCurrentCell); } //We need the ogre renderer and a scene node. @@ -274,8 +273,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 67f274b70..f9c6b2fa0 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -331,14 +331,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 { @@ -352,6 +354,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) @@ -469,6 +472,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; @@ -517,23 +521,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"; @@ -581,8 +585,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/CMakeLists.txt b/components/CMakeLists.txt index b48c50640..8a1875d0f 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -6,6 +6,10 @@ add_component_dir (settings settings ) +add_component_dir (nifoverrides + nifoverrides + ) + add_component_dir (bsa bsa_archive bsa_file ) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 7fa45815f..ab6a708c0 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -26,6 +26,7 @@ #include "ogre_nif_loader.hpp" #include +#include typedef unsigned char ubyte; @@ -282,15 +283,61 @@ void NIFLoader::createMaterial(const String &name, // other values. 237 basically means normal transparencly. if (alphaFlags == 237) { - // Enable transparency - pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); + NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); + if (result.first) + { + pass->setAlphaRejectFunction(CMPF_GREATER_EQUAL); + pass->setAlphaRejectValue(result.second); + } + else + { + // Enable transparency + pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); - //pass->setDepthCheckEnabled(false); - pass->setDepthWriteEnabled(false); + //pass->setDepthCheckEnabled(false); + pass->setDepthWriteEnabled(false); + //std::cout << "alpha 237; material: " << name << " texName: " << texName << std::endl; + } } else warn("Unhandled alpha setting for texture " + texName); } + else + { + material->getTechnique(0)->setShadowCasterMaterial("depth_shadow_caster_noalpha"); + } + } + + if (Settings::Manager::getBool("enabled", "Shadows")) + { + bool split = Settings::Manager::getBool("split", "Shadows"); + const int numsplits = 3; + for (int i = 0; i < (split ? numsplits : 1); ++i) + { + TextureUnitState* tu = material->getTechnique(0)->getPass(0)->createTextureUnitState(); + tu->setName("shadowMap" + StringConverter::toString(i)); + tu->setContentType(TextureUnitState::CONTENT_SHADOW); + tu->setTextureAddressingMode(TextureUnitState::TAM_BORDER); + tu->setTextureBorderColour(ColourValue::White); + } + } + + if (Settings::Manager::getBool("shaders", "Objects")) + { + material->getTechnique(0)->getPass(0)->setVertexProgram("main_vp"); + material->getTechnique(0)->getPass(0)->setFragmentProgram("main_fp"); + } + + // Create a fallback technique without shadows and without mrt + Technique* tech2 = material->createTechnique(); + tech2->setSchemeName("Fallback"); + Pass* pass2 = tech2->createPass(); + pass2->createTextureUnitState(texName); + pass2->setVertexColourTracking(TVC_DIFFUSE); + if (Settings::Manager::getBool("shaders", "Objects")) + { + pass2->setVertexProgram("main_fallback_vp"); + pass2->setFragmentProgram("main_fallback_fp"); } // Add material bells and whistles @@ -299,137 +346,6 @@ void NIFLoader::createMaterial(const String &name, material->setSpecular(specular.array[0], specular.array[1], specular.array[2], alpha); material->setSelfIllumination(emissive.array[0], emissive.array[1], emissive.array[2]); material->setShininess(glossiness); - - if (Settings::Manager::getBool("shaders", "Objects")) - { - // Create shader for the material - // vertex - HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); - - HighLevelGpuProgramPtr vertex; - if (mgr.getByName("main_vp").isNull()) - { - vertex = mgr.createProgram("main_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_VERTEX_PROGRAM); - vertex->setParameter("profiles", "vs_4_0 vs_2_x vp40 arbvp1"); - vertex->setParameter("entry_point", "main_vp"); - StringUtil::StrStreamType outStream; - outStream << - "void main_vp( \n" - " float4 position : POSITION, \n" - " float4 normal : NORMAL, \n" - " float4 colour : COLOR, \n" - " in float2 uv : TEXCOORD0, \n" - " out float2 oUV : TEXCOORD0, \n" - " out float4 oPosition : POSITION, \n" - " out float4 oPositionObjSpace : TEXCOORD1, \n" - " out float4 oNormal : TEXCOORD2, \n" - " out float oFogValue : TEXCOORD3, \n" - " out float4 oVertexColour : TEXCOORD4, \n" - " uniform float4 fogParams, \n" - " uniform float4x4 worldViewProj \n" - ") \n" - "{ \n" - " oVertexColour = colour; \n" - " oUV = uv; \n" - " oNormal = normal; \n" - " oPosition = mul( worldViewProj, position ); \n" - " oFogValue = saturate((oPosition.z - fogParams.y) * fogParams.w); \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"); - material->getTechnique(0)->getPass(0)->setVertexProgram(vertex->getName()); - - // the number of lights to support. - // when rendering an object, OGRE automatically picks the lights that are - // closest to the object being rendered. unfortunately this mechanism does - // not work perfectly for objects batched together (they will all use the same - // lights). to work around this, we are simply pushing the maximum number - // of lights here in order to minimize disappearing lights. - int num_lights = Settings::Manager::getInt("num lights", "Objects"); - - // fragment - HighLevelGpuProgramPtr fragment; - if (mgr.getByName("main_fp").isNull()) - { - fragment = mgr.createProgram("main_fp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - "cg", GPT_FRAGMENT_PROGRAM); - fragment->setParameter("profiles", "ps_4_0 ps_2_x fp40 arbfp1"); - fragment->setParameter("entry_point", "main_fp"); - StringUtil::StrStreamType outStream; - outStream << - "void main_fp( \n" - " in float2 uv : TEXCOORD0, \n" - " out float4 oColor : COLOR, \n" - " uniform sampler2D texture : TEXUNIT0, \n" - " float4 positionObjSpace : TEXCOORD1, \n" - " float4 normal : TEXCOORD2, \n" - " float fogValue : TEXCOORD3, \n" - " float4 vertexColour : TEXCOORD4, \n" - " uniform float4 fogColour, \n"; - - for (int i=0; isetSource(outStream.str()); - fragment->load(); - - for (int i=0; igetDefaultParameters()->setNamedAutoConstant("lightPositionObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i); - fragment->getDefaultParameters()->setNamedAutoConstant("lightDiffuse"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i); - fragment->getDefaultParameters()->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); - } - fragment->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - fragment->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - 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); - } - else - fragment = mgr.getByName("main_fp"); - material->getTechnique(0)->getPass(0)->setFragmentProgram(fragment->getName()); - } } // Takes a name and adds a unique part to it. This is just used to diff --git a/components/nifoverrides/nifoverrides.cpp b/components/nifoverrides/nifoverrides.cpp new file mode 100644 index 000000000..1c8fefd24 --- /dev/null +++ b/components/nifoverrides/nifoverrides.cpp @@ -0,0 +1,37 @@ +#include "nifoverrides.hpp" + +#include + +#include + +using namespace NifOverrides; + +Ogre::ConfigFile Overrides::mTransparencyOverrides = Ogre::ConfigFile(); + +void Overrides::loadTransparencyOverrides (const std::string& file) +{ + mTransparencyOverrides.load(file); +} + +TransparencyResult Overrides::getTransparencyOverride(const std::string& texture) +{ + TransparencyResult result; + result.first = false; + + std::string tex = texture; + boost::to_lower(tex); + + Ogre::ConfigFile::SectionIterator seci = mTransparencyOverrides.getSectionIterator(); + while (seci.hasMoreElements()) + { + Ogre::String sectionName = seci.peekNextKey(); + if (sectionName == tex) + { + result.first = true; + result.second = Ogre::StringConverter::parseInt(mTransparencyOverrides.getSetting("alphaRejectValue", sectionName)); + break; + } + seci.getNext(); + } + return result; +} diff --git a/components/nifoverrides/nifoverrides.hpp b/components/nifoverrides/nifoverrides.hpp new file mode 100644 index 000000000..c9b711df6 --- /dev/null +++ b/components/nifoverrides/nifoverrides.hpp @@ -0,0 +1,23 @@ +#ifndef COMPONENTS_NIFOVERRIDES_H +#define COMPONENTS_NIFOVERRIDES_H + +#include + +namespace NifOverrides +{ + + typedef std::pair TransparencyResult; + + /// \brief provide overrides for some model / texture properties that bethesda has chosen poorly + class Overrides + { + public: + static Ogre::ConfigFile mTransparencyOverrides; + void loadTransparencyOverrides (const std::string& file); + + static TransparencyResult getTransparencyOverride(const std::string& texture); + }; + +} + +#endif diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index 203b40681..8ab3d5b51 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -1,13 +1,16 @@ 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) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/shadows/depthshadowcaster.material "${OpenMW_BINARY_DIR}/resources/shadows/depthshadowcaster.material" COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/shadows/depthshadowcaster.cg "${OpenMW_BINARY_DIR}/resources/shadows/depthshadowcaster.cg" COPYONLY) diff --git a/files/gbuffer/gbuffer.cg b/files/gbuffer/gbuffer.cg new file mode 100644 index 000000000..c7f2fe678 --- /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 000000000..316003af6 --- /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 000000000..faa8dd498 --- /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/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index dbc20f3f8..e3a7b9999 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -8,7 +8,6 @@ configure_file("${SDIR}/bigbars.png" "${DDIR}/bigbars.png" COPYONLY) configure_file("${SDIR}/black.png" "${DDIR}/black.png" COPYONLY) configure_file("${SDIR}/core.skin" "${DDIR}/core.skin" COPYONLY) configure_file("${SDIR}/core.xml" "${DDIR}/core.xml" COPYONLY) -configure_file("${SDIR}/mwpointer.png" "${DDIR}/mwpointer.png" COPYONLY) configure_file("${SDIR}/mwgui.png" "${DDIR}/mwgui.png" COPYONLY) configure_file("${SDIR}/openmw_images.xml" "${DDIR}/openmw_images.xml" COPYONLY) configure_file("${SDIR}/openmw_settings.xml" "${DDIR}/openmw_settings.xml" COPYONLY) diff --git a/files/mygui/mwpointer.png b/files/mygui/mwpointer.png deleted file mode 100644 index 90bc19b5e..000000000 Binary files a/files/mygui/mwpointer.png and /dev/null differ diff --git a/files/mygui/openmw_images.xml b/files/mygui/openmw_images.xml index 6487742c3..e149273e2 100644 --- a/files/mygui/openmw_images.xml +++ b/files/mygui/openmw_images.xml @@ -2,37 +2,37 @@ - + - + - + - + - + - + - + - + - + diff --git a/files/mygui/test.skin b/files/mygui/test.skin deleted file mode 100644 index 5f198ab4a..000000000 --- a/files/mygui/test.skin +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 878b9b095..4d2d46fca 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 @@ -14,6 +18,32 @@ anisotropy = 4 # Number of texture mipmaps to generate num mipmaps = 5 +[Shadows] +# Shadows are only supported when object shaders are on! +enabled = false + +# Split the shadow maps, allows for a larger shadow distance +# Warning: enabling this will cause some terrain textures to disappear due to +# hitting the texture unit limit of the terrain material +split = false + +# Increasing shadow distance will lower the shadow quality. +# Uses "shadow distance" or "split shadow distance" depending on "split" setting. +shadow distance = 1300 +# This one shouldn't be too low, otherwise you'll see artifacts. Use at least 2x max viewing distance. +split shadow distance = 14000 + +# Size of the shadow textures, higher means higher quality +texture size = 1024 + +# Turn on/off various shadow casters +actor shadows = true +misc shadows = true +statics shadows = true + +# Fraction of the total shadow distance after which the shadow starts to fade out +fade start = 0.8 + [HUD] # FPS counter # 0: not visible @@ -54,6 +84,21 @@ 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/shadows/depthshadowcaster.cg b/files/shadows/depthshadowcaster.cg new file mode 100644 index 000000000..3457a4f8d --- /dev/null +++ b/files/shadows/depthshadowcaster.cg @@ -0,0 +1,51 @@ +void main_vp( + float4 position : POSITION, + float2 uv : TEXCOORD0, + + out float4 oPosition : POSITION, + out float2 oDepth : TEXCOORD0, + out float2 oUv : TEXCOORD1, + + uniform float4x4 wvpMat) +{ + // this is the view space position + oPosition = mul(wvpMat, position); + + // depth info for the fragment. + oDepth.x = oPosition.z; + oDepth.y = oPosition.w; + + // clamp z to zero. seem to do the trick. :-/ + oPosition.z = max(oPosition.z, 0); + + oUv = uv; +} + +void main_fp( + float2 depth : TEXCOORD0, + float2 uv : TEXCOORD1, + uniform sampler2D texture1 : register(s0), + + out float4 oColour : COLOR) +{ + float finalDepth = depth.x / depth.y; + + // use alpha channel of the first texture + float alpha = tex2D(texture1, uv).a; + + // discard if alpha is less than 0.5 + clip((alpha >= 0.5) ? 1 : -1); + + oColour = float4(finalDepth, finalDepth, finalDepth, 1); +} + +void main_fp_noalpha( + float2 depth : TEXCOORD0, + float2 uv : TEXCOORD1, + + out float4 oColour : COLOR) +{ + float finalDepth = depth.x / depth.y; + + oColour = float4(finalDepth, finalDepth, finalDepth, 1); +} diff --git a/files/shadows/depthshadowcaster.material b/files/shadows/depthshadowcaster.material new file mode 100644 index 000000000..9ff51c5b1 --- /dev/null +++ b/files/shadows/depthshadowcaster.material @@ -0,0 +1,67 @@ +vertex_program depth_shadow_caster_vs cg +{ + source depthshadowcaster.cg + profiles vs_1_1 arbvp1 + entry_point main_vp + + default_params + { + param_named_auto wvpMat worldviewproj_matrix + } +} + +fragment_program depth_shadow_caster_ps cg +{ + source depthshadowcaster.cg + profiles ps_2_0 arbfp1 + entry_point main_fp + + default_params + { + } +} + +fragment_program depth_shadow_caster_ps_noalpha cg +{ + source depthshadowcaster.cg + profiles ps_2_0 arbfp1 + entry_point main_fp_noalpha + + default_params + { + } +} + +material depth_shadow_caster +{ + technique + { + pass + { + vertex_program_ref depth_shadow_caster_vs + { + } + + fragment_program_ref depth_shadow_caster_ps + { + } + } + } +} + +material depth_shadow_caster_noalpha +{ + technique + { + pass + { + vertex_program_ref depth_shadow_caster_vs + { + } + + fragment_program_ref depth_shadow_caster_ps_noalpha + { + } + } + } +} diff --git a/files/transparency-overrides.cfg b/files/transparency-overrides.cfg new file mode 100644 index 000000000..299792be1 --- /dev/null +++ b/files/transparency-overrides.cfg @@ -0,0 +1,574 @@ +# Bethesda has used wrong transparency settings for many textures +# (who would have guessed) +# This is very unfortunate because objects with real transparency: +# - cannot cast shadows +# - cannot receive advanced framebuffer effects like depth of field or ambient occlusion +# - cannot cover lens flare effects (the lens flare will just shine through) + +# This file lists textures that should be using alpha rejection instead of transparency +# basically these are textures that are not translucent (i.e. at one spot on the texture, either transparent or opaque) + +# Note: all the texture names here have to be lowercase + +# fauna +[textures\tx_wickwheat_01.dds] + alphaRejectValue = 128 + +[textures\tx_wickwheat_03.dds] + alphaRejectValue = 128 + +[textures\tx_red_lichen_01.dds] + alphaRejectValue = 128 + +[textures\tx_stone_flower_01.dds] + alphaRejectValue = 128 + +[textures\tx_ivy_02.dds] + alphaRejectValue = 128 + +[textures\tx_ivy_01.dds] + alphaRejectValue = 128 + +[textures\tx_saltrice_04.dds] + alphaRejectValue = 128 + +[textures\tx_black_lichen_01.dds] + alphaRejectValue = 128 + +[textures\tx_leaves_01.dds] + alphaRejectValue = 128 + +[textures\tx_leaves_02.dds] + alphaRejectValue = 128 + +[textures\tx_leaves_03.dds] + alphaRejectValue = 128 + +[textures\tx_leaves_04.dds] + alphaRejectValue = 128 + +[textures\tx_leaves_06.dds] + alphaRejectValue = 128 + +[textures\tx_leaves_07.dds] + alphaRejectValue = 128 + +[textures\tx_ai_heather_01.dds] + alphaRejectValue = 96 + +[textures\tx_goldkanet_01.dds] + alphaRejectValue = 128 + +[textures\tx_goldkanet_02.dds] + alphaRejectValue = 128 + +[textures\tx_plant_tails00.dds] + alphaRejectValue = 128 + +[textures\tx_vine_01.dds] + alphaRejectValue = 128 + +[textures\tx_comberry_01.dds] + alphaRejectValue = 128 + +[textures\tx_willow_flower_02.dds] + alphaRejectValue = 128 + +[textures\tx_cork_bulb_02.dds] + alphaRejectValue = 128 + +[textures\tx_green_lichen_01.dds] + alphaRejectValue = 128 + +[textures\tx_roobrush_01.dds] + alphaRejectValue = 128 + +[textures\tx_bittergreen_02.dds] + alphaRejectValue = 128 + +[textures\tx_chokeweed_01.dds] + alphaRejectValue = 128 + +[textures\tx_branches_01.dds] + alphaRejectValue = 128 + +[textures\tx_branches_02.dds] + alphaRejectValue = 128 + +[textures\tx_guarskin_hut_03.dds] + alphaRejectValue = 128 + +[textures\tx_hackle-lo_02.dds] + alphaRejectValue = 128 + +[textures\tx_bc_fern_01.dds] + alphaRejectValue = 128 + +[textures\tx_bc_fern_02.dds] + alphaRejectValue = 128 + +[textures\tx_bc_leaves_02.dds] + alphaRejectValue = 128 + +[textures\tx_marshmerrow_03.dds] + alphaRejectValue = 128 + +[textures\tx_bc_moss_01.dds] + alphaRejectValue = 128 + +[textures\tx_bc_moss_02.dds] + alphaRejectValue = 128 + +[textures\tx_bc_lilypad_01.dds] + alphaRejectValue = 128 + +[textures\tx_bc_lilypad_02.dds] + alphaRejectValue = 128 + +[textures\tx_bc_lilypad_03.dds] + alphaRejectValue = 128 + +[textures\tx_fire_fern_01.dds] + alphaRejectValue = 128 + +# banners and flags +[textures\tx_flag_imp_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_arena_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_comfort_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_child_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_count_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_faith_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_walk_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_imp_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_redoran_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_avs_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_serving_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_speak_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_stdeyln_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_stolms_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_thin_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_vivec_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_vivec_02.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_banner_01.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_banner_02.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_banner_04.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_banner_05.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_banner_06.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_banner_07.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_a_banner.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_e_banner.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_u_banner.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_z_banner.dds] + alphaRejectValue = 128 + +[textures\tx_banner_6th.dds] + alphaRejectValue = 128 + +[textures\tx_banner_6th_tall.dds] + alphaRejectValue = 128 + +[textures\tx_banner_gnisis_01.dds] + alphaRejectValue = 128 + +[textures\tx_banner_gnisis_02.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_bhm_01.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_02.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_03.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_04.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_05.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_06.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_07.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_08.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_08.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_09.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_10.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_11.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_12.dds] + alphaRejectValue = 128 + +[textures\tx_de_tapestry_13.dds] + alphaRejectValue = 128 + +[textures\tx_de_lutestrings_01.dds] + alphaRejectValue = 128 + +[textures\tx_fabric_imp_altar_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_akatosh_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_apprentice_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_arkay_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_dibella_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_golem_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_julianos_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_kynareth_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_lady_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_lord_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_lover_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_mara_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_ritual_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_shadow_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_steed_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_stendarr_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_thief_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_tower_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_warrior_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_wizard_01.dds] + alphaRejectValue = 128 + +[textures\tx_c_t_zenithar_01.dds] + alphaRejectValue = 128 + +[textures\tx_banner_dagoth_01.dds] + alphaRejectValue = 128 + +[textures\tx_bannerd_tavern_01.dds] + alphaRejectValue = 128 + +[textures\tx_bannerd_goods_01.dds] + alphaRejectValue = 128 + +[textures\tx_bannerd_danger_01.dds] + alphaRejectValue = 128 + +[textures\tx_bannerd_welcome_01.dds] + alphaRejectValue = 128 + +[textures\tx_bannerd_clothing_01.dds] + alphaRejectValue = 128 + +[textures\tx_bannerd_alchemy_01.dds] + alphaRejectValue = 128 + +[textures\tx_banner_hlaalu_01.dds] + alphaRejectValue = 128 + +[textures\tx_banner_redoran_01.dds] + alphaRejectValue = 128 + +[textures\tx_banner_temple_01.dds] + alphaRejectValue = 128 + +[textures\tx_banner_temple_03.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_book_01.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_ald_velothi.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_gnaar_mok.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_hla_oad.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_khull.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_pawn_01.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_sadrith_mora.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_tel_aruhn.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_tel_branora.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_tel_fyr.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_tel_mora.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_telvani_01.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_tel_vos.dds] + alphaRejectValue = 128 + +[textures\tx_de_banner_vos.dds] + alphaRejectValue = 128 + +[textures\tx_bannerd_w_a_shop_01.dds] + alphaRejectValue = 128 + +[textures\tx_banner_temple_02.dds] + alphaRejectValue = 128 + +[textures\tx_mural1_00.dds] + alphaRejectValue = 128 + +[textures\tx_mural1_01.dds] + alphaRejectValue = 128 + +[textures\tx_mural4_00.dds] + alphaRejectValue = 128 + +[textures\tx_mural4_01.dds] + alphaRejectValue = 128 + +[textures\tx_mural5_00.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_telvanni_01.dds] + alphaRejectValue = 128 + +[textures\tx_v_b_hlaalu_01.dds] + alphaRejectValue = 128 + +[textures\tx_fabric_tapestry.dds] + alphaRejectValue = 128 + +[textures\tx_fabric_tapestry_01.dds] + alphaRejectValue = 128 + +[textures\tx_fabric_tapestry_02.dds] + alphaRejectValue = 128 + +[textures\tx_fabric_tapestry_03.dds] + alphaRejectValue = 128 + +[textures\tx_fabric_tapestry_04.dds] + alphaRejectValue = 128 + +# characters +[textures\tx_netchgod00.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_argonian_f_hair02.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_argonian_f_hair03.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_argonian_m_hair01.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_argonian_m_hair04.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_argonian_m_hair05.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_khajiit_f_hair01.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_khajiit_f_hair02.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_khajiit_m_hair01.dds] + alphaRejectValue = 128 + +[textures\tx_corprus_stalker12.dds] + alphaRejectValue = 128 + +[textures\tx_a_clavicus02.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_dark elf_m_hair11.dds] + alphaRejectValue = 128 + +[textures\tx_b_n_dark elf_f_hair10.dds] + alphaRejectValue = 128 + +# misc items +[textures\tx_sail.dds] + alphaRejectValue = 128 + +[textures\tx_longboatsail01.dds] + alphaRejectValue = 128 + +[textures\tx_longboatsail01a.dds] + alphaRejectValue = 128 + +[textures\tx_longboatsail01b.dds] + alphaRejectValue = 128 + +[textures\tx_longboatsail02.dds] + alphaRejectValue = 128 + +[textures\tx_quill.dds] + alphaRejectValue = 128 + +[textures\tx_note_01.dds] + alphaRejectValue = 128 + +[textures\tx_note_02.dds] + alphaRejectValue = 128 + +[textures\tx_parchment_02.dds] + alphaRejectValue = 128 + +[textures\tx_parchment_03.dds] + alphaRejectValue = 128 + +[textures\tx_scroll_01.dds] + alphaRejectValue = 128 + +[textures\tx_scroll_02.dds] + alphaRejectValue = 128 + +[textures\tx_scroll_03.dds] + alphaRejectValue = 128 + +[textures\tx_alpha_small_edge.dds] + alphaRejectValue = 128 + +[textures\tx_alpha_shadow_circular.dds] + alphaRejectValue = 128 + +# building materials +[textures\tx_shack_thatch_strip.dds] + alphaRejectValue = 128 + +[textures\tx_rug00.dds] + alphaRejectValue = 128 + +[textures\tx_rug_02.dds] + alphaRejectValue = 128 + +[textures\tx_rug_edge_01.dds] + alphaRejectValue = 128 + +[textures\tx_awning_thatch_02.dds] + alphaRejectValue = 128 + +[textures\tx_awning_woven_01.dds] + alphaRejectValue = 128 + +[textures\tx_bridgeropes.dds] + alphaRejectValue = 128 + +[textures\tx_rope_woven_01.dds] + alphaRejectValue = 128 + +[textures\tx_rope_woven_02.dds] + alphaRejectValue = 128 + +[textures\tx_ashl_tent_06.dds] + alphaRejectValue = 128 + +[textures\tx_guar_tarp.dds] + alphaRejectValue = 128 + +[textures\tx_velothi_glyph00.dds] + alphaRejectValue = 128 diff --git a/files/water/Example_Fresnel.cg b/files/water/Example_Fresnel.cg deleted file mode 100644 index e091fc587..000000000 --- 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 2de078ef5..000000000 --- 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 2b46d6e08..000000000 --- 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 eb18885d2..000000000 --- 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 71153769c..000000000 --- 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 3efda7b6a..000000000 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 a9ca11b7e..000000000 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 000000000..771d15041 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 000000000..b853dd535 --- /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 000000000..bf6d04c5c --- /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 67bf90896..8d9c3cb39 100644 --- a/files/water/water.compositor +++ b/files/water/water.compositor @@ -1,4 +1,4 @@ -compositor Water +compositor UnderwaterNoMRT { technique { @@ -6,6 +6,30 @@ compositor Water target rt0 { input previous } + target_output + { + // Start with clear output + input none + + pass render_quad + { + 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 @@ -15,7 +39,8 @@ compositor Water { 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 000000000..8b4ff96f5 --- /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 Fallback + 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 c379886fa..000000000 Binary files a/files/water/waves2.dds and /dev/null differ diff --git a/libs/openengine/ogre/imagerotate.cpp b/libs/openengine/ogre/imagerotate.cpp new file mode 100644 index 000000000..1147559d6 --- /dev/null +++ b/libs/openengine/ogre/imagerotate.cpp @@ -0,0 +1,74 @@ +#include "imagerotate.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Ogre; +using namespace OEngine::Render; + +void ImageRotate::rotate(const std::string& sourceImage, const std::string& destImage, const float angle) +{ + Root* root = Ogre::Root::getSingletonPtr(); + + SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); + Camera* camera = sceneMgr->createCamera("ImageRotateCamera"); + + MaterialPtr material = MaterialManager::getSingleton().create("ImageRotateMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + material->getTechnique(0)->getPass(0)->setLightingEnabled(false); + material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + TextureUnitState* tus = material->getTechnique(0)->getPass(0)->createTextureUnitState(sourceImage); + Degree deg(angle); + tus->setTextureRotate(Radian(deg.valueRadians())); + tus->setTextureAddressingMode(TextureUnitState::TAM_BORDER); + tus->setTextureBorderColour(ColourValue(0, 0, 0, 0)); + + Rectangle2D* rect = new Rectangle2D(true); + rect->setCorners(-1.0, 1.0, 1.0, -1.0); + rect->setMaterial("ImageRotateMaterial"); + // Render the background before everything else + rect->setRenderQueueGroup(RENDER_QUEUE_BACKGROUND); + + // Use infinite AAB to always stay visible + AxisAlignedBox aabInf; + aabInf.setInfinite(); + rect->setBoundingBox(aabInf); + + // Attach background to the scene + SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(rect); + + // retrieve image width and height + TexturePtr sourceTexture = TextureManager::getSingleton().getByName(sourceImage); + unsigned int width = sourceTexture->getWidth(); + unsigned int height = sourceTexture->getHeight(); + + TexturePtr destTexture = TextureManager::getSingleton().createManual( + destImage, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + width, height, + 0, + PF_A8R8G8B8, + TU_RENDERTARGET); + + RenderTarget* rtt = destTexture->getBuffer()->getRenderTarget(); + rtt->setAutoUpdated(false); + Viewport* vp = rtt->addViewport(camera); + vp->setOverlaysEnabled(false); + vp->setShadowsEnabled(false); + vp->setBackgroundColour(ColourValue(0,0,0,0)); + vp->setClearEveryFrame(true, FBT_DEPTH); + + rtt->update(); + + // remove all the junk we've created + MaterialManager::getSingleton().remove("ImageRotateMaterial"); + root->destroySceneManager(sceneMgr); + delete rect; +} diff --git a/libs/openengine/ogre/imagerotate.hpp b/libs/openengine/ogre/imagerotate.hpp new file mode 100644 index 000000000..a3f6d662f --- /dev/null +++ b/libs/openengine/ogre/imagerotate.hpp @@ -0,0 +1,27 @@ +#ifndef OENGINE_OGRE_IMAGEROTATE_HPP +#define OENGINE_OGRE_IMAGEROTATE_HPP + +#include + +namespace OEngine +{ +namespace Render +{ + + /// Rotate an image by certain degrees and save as file, uses the GPU + /// Make sure Ogre Root is initialised before calling + class ImageRotate + { + public: + /** + * @param source image (file name - has to exist in an resource group) + * @param name of the destination texture to save to (in memory) + * @param angle in degrees to turn + */ + static void rotate(const std::string& sourceImage, const std::string& destImage, const float angle); + }; + +} +} + +#endif diff --git a/readme.txt b/readme.txt index 17806172f..e1c24ab52 100644 --- a/readme.txt +++ b/readme.txt @@ -95,6 +95,7 @@ Allowed options: CREDITS Current Developers: +Aleksandar Jovanov Alexander “Ace” Olofsson athile BrotherBrick