From c92592493ed8cf454a83034eb100181f0d7865a2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Apr 2015 15:34:50 +0200 Subject: [PATCH] OpenMW: create a window and render the starting cell(s) --- apps/nifosgtest/test.cpp | 3 +- apps/openmw/CMakeLists.txt | 5 +- apps/openmw/engine.cpp | 69 +- apps/openmw/engine.hpp | 32 +- apps/openmw/mwclass/activator.cpp | 5 +- apps/openmw/mwclass/container.cpp | 5 +- apps/openmw/mwclass/creature.cpp | 8 +- apps/openmw/mwclass/door.cpp | 5 +- apps/openmw/mwclass/light.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 4 +- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 2 +- apps/openmw/mwmechanics/levelledlist.hpp | 2 + .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 13 +- apps/openmw/mwrender/actors.hpp | 2 - apps/openmw/mwrender/animation.cpp | 1542 +---------------- apps/openmw/mwrender/animation.hpp | 198 +-- apps/openmw/mwrender/objects.cpp | 245 ++- apps/openmw/mwrender/objects.hpp | 57 +- apps/openmw/mwrender/renderingmanager.cpp | 1060 +---------- apps/openmw/mwrender/renderingmanager.hpp | 267 +-- .../mwscript/transformationextensions.cpp | 4 +- apps/openmw/mwworld/cellfunctors.hpp | 4 +- apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/physicssystem.cpp | 12 +- apps/openmw/mwworld/refdata.cpp | 15 +- apps/openmw/mwworld/refdata.hpp | 19 +- apps/openmw/mwworld/scene.cpp | 83 +- apps/openmw/mwworld/scene.hpp | 9 +- apps/openmw/mwworld/worldimp.cpp | 49 +- apps/openmw/mwworld/worldimp.hpp | 21 +- components/sceneutil/lightmanager.cpp | 42 +- components/sceneutil/lightmanager.hpp | 7 + files/settings-default.cfg | 9 - 36 files changed, 636 insertions(+), 3173 deletions(-) diff --git a/apps/nifosgtest/test.cpp b/apps/nifosgtest/test.cpp index 332867be5..487a91890 100644 --- a/apps/nifosgtest/test.cpp +++ b/apps/nifosgtest/test.cpp @@ -127,8 +127,7 @@ int main(int argc, char** argv) osg::Group* newNode = new osg::Group; NifOsg::Loader loader; Resource::TextureManager texMgr(&resourceMgr); - loader.mTextureManager = &texMgr; - newNode->addChild(loader.load(nif)); + newNode->addChild(loader.load(nif, &texMgr)); osg::PositionAttitudeTransform* trans = new osg::PositionAttitudeTransform; root->addChild(trans); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 50aeafc2a..e10688149 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,8 +20,8 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender -actors objects -# renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation + actors objects renderingmanager animation +# debugging sky camera npcanimation creatureanimation activatoranimation # renderinginterface localmap occlusionquery water shadows # characterpreview globalmap ripplesimulation refraction # terrainstorage renderconst effectmanager weaponanimation @@ -129,6 +129,7 @@ target_link_libraries(openmw ${OENGINE_LIBRARY} ${OGRE_LIBRARIES} ${OGRE_STATIC_PLUGINS} + ${OPENSCENEGRAPH_LIBRARIES} ${Boost_LIBRARIES} ${OPENAL_LIBRARY} ${SOUND_INPUT_LIBRARY} diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c7a92e52e..0d0294fa7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -3,15 +3,23 @@ #include #include -#include -#include +#include +#include + +#include #include #include +// TODO: move to component #include +#include +#include + +#include + #include #include @@ -172,8 +180,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) } OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) - : mOgre (0) - , mVerboseScripts (false) + : mVerboseScripts (false) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) @@ -220,10 +227,6 @@ void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path) { } -void OMW::Engine::addZipResource (const boost::filesystem::path& path) -{ -} - void OMW::Engine::enableFSStrict(bool fsStrict) { mFSStrict = fsStrict; @@ -318,21 +321,27 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) addResourcesDirectory(mResDir / "shadows"); addResourcesDirectory(mResDir / "materials"); - OEngine::Render::WindowSettings windowSettings; - windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); - windowSettings.window_border = settings.getBool("window border", "Video"); - windowSettings.window_x = settings.getInt("resolution x", "Video"); - windowSettings.window_y = settings.getInt("resolution y", "Video"); - windowSettings.screen = settings.getInt("screen", "Video"); - windowSettings.vsync = settings.getBool("vsync", "Video"); - windowSettings.icon = "openmw.png"; - std::string aa = settings.getString("antialiasing", "Video"); - windowSettings.fsaa = (aa.substr(0, 4) == "MSAA") ? aa.substr(5, aa.size()-5) : "0"; + //OEngine::Render::WindowSettings windowSettings; + //windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); + //windowSettings.window_border = settings.getBool("window border", "Video"); + //windowSettings.vsync = settings.getBool("vsync", "Video"); + //windowSettings.icon = "openmw.png"; + //std::string aa = settings.getString("antialiasing", "Video"); + //windowSettings.fsaa = (aa.substr(0, 4) == "MSAA") ? aa.substr(5, aa.size()-5) : "0"; SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, settings.getBool("minimize on focus loss", "Video") ? "1" : "0"); - //Bsa::registerResources (mFileCollections, mArchives, true, mFSStrict); + // not handling fullscreen yet, we should figure this out when adding SDL to the mix + mViewer.setUpViewInWindow(0, 0, settings.getInt("resolution x", "Video"), settings.getInt("resolution y", "Video"), settings.getInt("screen", "Video")); + osg::ref_ptr rootNode (new osg::Group); + mViewer.setSceneData(rootNode); + + mVFS.reset(new VFS::Manager(mFSStrict)); + + VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true); + + mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get())); // Create input and UI first to set up a bootstrapping environment for // showing a loading screen and keeping the window responsive while doing so @@ -378,8 +387,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) } // Create the world - mEnvironment.setWorld( new MWWorld::World (mFileCollections, mContentFiles, - mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, + mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), + mFileCollections, mContentFiles, mEncoder, mFallbackMap, mActivationDistanceOverride, mCellName, mStartupScript)); MWBase::Environment::get().getWorld()->setupPlayer(); //input->setPlayer(&mEnvironment.getWorld()->getPlayer()); @@ -460,7 +469,7 @@ void OMW::Engine::go() { MWBase::Environment::get().getStateManager()->loadGame(mSaveGameFile); } - else if (!mSkipMenu) + else if (0)// !mSkipMenu) { // start in main menu MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); @@ -481,6 +490,22 @@ void OMW::Engine::go() } // Start the main rendering loop + mViewer.setCameraManipulator(new osgGA::TrackballManipulator); + mViewer.addEventHandler(new osgViewer::StatsHandler); + + osg::Timer timer; + //osgUtil::IncrementalCompileOperation* ico = new osgUtil::IncrementalCompileOperation; + //ico->compileAllForNextFrame(1); + //mViewer.setRealizeOperation(ico); + mViewer.realize(); + std::cout << "realize took " << timer.time_m() << std::endl; + while (!mViewer.done()) + { + MWBase::Environment::get().getWorld()->update(0.f, false); + + mViewer.frame(/*simulationTime*/); + } + /* Ogre::Timer timer; while (!MWBase::Environment::get().getStateManager()->hasQuitRequest()) diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index ef26ceb3a..de09082d8 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -9,11 +9,23 @@ #include #include +#include + #include "mwbase/environment.hpp" #include "mwworld/ptr.hpp" +namespace Resource +{ + class ResourceSystem; +} + +namespace VFS +{ + class Manager; +} + namespace Compiler { class Context; @@ -39,19 +51,6 @@ namespace MWGui class WindowManager; } -namespace OEngine -{ - namespace GUI - { - class MyGUIManager; - } - - namespace Render - { - class OgreRenderer; - } -} - namespace Files { struct ConfigurationManager; @@ -62,13 +61,15 @@ namespace OMW /// \brief Main engine class, that brings together all the components of OpenMW class Engine : private Ogre::FrameListener { + std::auto_ptr mVFS; + std::auto_ptr mResourceSystem; MWBase::Environment mEnvironment; ToUTF8::FromType mEncoding; ToUTF8::Utf8Encoder* mEncoder; Files::PathContainer mDataDirs; std::vector mArchives; boost::filesystem::path mResDir; - OEngine::Render::OgreRenderer *mOgre; + osgViewer::Viewer mViewer; std::string mCellName; std::vector mContentFiles; bool mVerboseScripts; @@ -108,9 +109,6 @@ namespace OMW /// \note This function works recursively. void addResourcesDirectory (const boost::filesystem::path& path); - /// add a .zip resource - void addZipResource (const boost::filesystem::path& path); - void executeLocalScripts(); virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt); diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 457b0cec1..2621f5e52 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -16,7 +16,7 @@ #include "../mwworld/failedaction.hpp" #include "../mwworld/nullaction.hpp" -#include "../mwrender/actors.hpp" +#include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" @@ -34,8 +34,7 @@ namespace MWClass void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { - MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model, true); } } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 5f49a74b6..ac23744e8 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -23,7 +23,7 @@ #include "../mwgui/tooltips.hpp" -#include "../mwrender/actors.hpp" +#include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwmechanics/npcstats.hpp" @@ -93,8 +93,7 @@ namespace MWClass void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { - MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model, true); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index ac99fa4b4..c1b8177f7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -29,7 +29,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwrender/renderinginterface.hpp" -#include "../mwrender/actors.hpp" +#include "../mwrender/objects.hpp" #include "../mwgui/tooltips.hpp" @@ -163,10 +163,10 @@ namespace MWClass void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - //MWWorld::LiveCellRef *ref = ptr.get(); + MWWorld::LiveCellRef *ref = ptr.get(); - //MWRender::Actors& actors = renderingInterface.getActors(); - //actors.insertCreature(ptr, model, (ref->mBase->mFlags & ESM::Creature::Weapon) != 0); + MWRender::Objects& objects = renderingInterface.getObjects(); + objects.insertCreature(ptr, model, (ref->mBase->mFlags & ESM::Creature::Weapon) != 0); } void Creature::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 2d39881b1..f46ede730 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -24,7 +24,7 @@ #include "../mwgui/tooltips.hpp" -#include "../mwrender/actors.hpp" +#include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" namespace @@ -52,8 +52,7 @@ namespace MWClass void Door::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { if (!model.empty()) { - MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr, model); + renderingInterface.getObjects().insertModel(ptr, model, true); } } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 90c708f97..2cf31d996 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -23,7 +23,6 @@ #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" -#include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" namespace MWClass @@ -39,8 +38,7 @@ namespace MWClass ptr.get(); // Insert even if model is empty, so that the light is added - MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertActivator(ptr, model, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); + renderingInterface.getObjects().insertModel(ptr, model, true, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault)); } void Light::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3dcb57fbb..506852a90 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -35,7 +35,7 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/cellstore.hpp" -#include "../mwrender/actors.hpp" +#include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" @@ -409,7 +409,7 @@ namespace MWClass void Npc::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - renderingInterface.getActors().insertNPC(ptr); + renderingInterface.getObjects().insertNPC(ptr); } void Npc::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index bc7bb0bb5..319e1d7ec 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1702,6 +1702,7 @@ namespace MWGui void WindowManager::playVideo(const std::string &name, bool allowSkipping) { + return; mVideoWidget->playVideo("video\\" + name); mVideoWidget->eventKeyButtonPressed.clear(); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index d277b1249..685051860 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -474,7 +474,7 @@ namespace MWMechanics Ogre::Vector3 dir = playerPos - actorPos; Ogre::Radian faceAngle = Ogre::Math::ATan2(dir.x,dir.y); - Ogre::Radian actorAngle = actor.getRefData().getBaseNode()->getOrientation().getRoll(); + Ogre::Radian actorAngle = actor.getRefData().getBaseNodeOld()->getOrientation().getRoll(); // an attempt at reducing the turning animation glitch if( Ogre::Math::Abs( faceAngle - actorAngle ) >= Ogre::Degree(5) ) // TODO: is there a better way? { diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index c5fc34507..64456dd11 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -75,7 +75,7 @@ namespace MWMechanics return false; Ogre::Degree angle = signedAngle (Ogre::Vector3(attacker.getRefData().getPosition().pos) - Ogre::Vector3(blocker.getRefData().getPosition().pos), - blocker.getRefData().getBaseNode()->getOrientation().yAxis(), Ogre::Vector3(0,0,1)); + blocker.getRefData().getBaseNodeOld()->getOrientation().yAxis(), Ogre::Vector3(0,0,1)); const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); if (angle.valueDegrees() < gmst.find("fCombatBlockLeftAngle")->getFloat()) diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index 20b87a3a9..76c472001 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -3,6 +3,8 @@ #include +#include + #include "../mwworld/ptr.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/manualref.hpp" diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index abfc793de..0b3390271 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1385,7 +1385,7 @@ namespace MWMechanics static float fSneakViewMult = store.find("fSneakViewMult")->getFloat(); float y = 0; Ogre::Vector3 vec = pos1 - pos2; - Ogre::Radian angle = observer.getRefData().getBaseNode()->getOrientation().yAxis().angleBetween(vec); + Ogre::Radian angle = observer.getRefData().getBaseNodeOld()->getOrientation().yAxis().angleBetween(vec); if (angle < Ogre::Degree(90)) y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult; else diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index a7d3545b3..5f4d986b4 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -418,7 +418,7 @@ namespace MWMechanics absorbed = (OEngine::Misc::Rng::roll0to99() < absorb); if (absorbed) { - const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); + //const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Absorb"); //MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( // "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, ""); // Magicka is increased by cost of spell @@ -466,7 +466,7 @@ namespace MWMechanics bool isReflected = (OEngine::Misc::Rng::roll0to99() < reflect); if (isReflected) { - const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Reflect"); + //const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_Reflect"); //MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect( // "meshes\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, ""); reflectedEffects.mList.push_back(*effectIt); @@ -565,7 +565,7 @@ namespace MWMechanics } // Add VFX - const ESM::Static* castStatic; + /*const ESM::Static* castStatic; if (!magicEffect->mHit.empty()) castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); else @@ -574,9 +574,10 @@ namespace MWMechanics // TODO: VFX are no longer active after saving/reloading the game bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; // Note: in case of non actor, a free effect should be fine as well - //MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); - //if (anim) - // anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); + if (anim) + anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, ""); + */ } } } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 887ea68d1..931c5bbd5 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -1,8 +1,6 @@ #ifndef GAME_RENDER_ACTORS_H #define GAME_RENDER_ACTORS_H -//#include - #include namespace OEngine diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 871561bdc..85d9546aa 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1,1551 +1,73 @@ #include "animation.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/soundmanager.hpp" -#include "../mwbase/world.hpp" - -#include "../mwmechanics/character.hpp" -#include "../mwmechanics/creaturestats.hpp" - -#include "../mwworld/class.hpp" -#include "../mwworld/fallback.hpp" -#include "../mwworld/cellstore.hpp" -#include "../mwworld/esmstore.hpp" - -#include "renderconst.hpp" +#include +#include +#include namespace MWRender { -Ogre::Real Animation::AnimationTime::getValue() const -{ - AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName); - if(iter != mAnimation->mStates.end()) - return iter->second.mTime; - return 0.0f; -} - -void Animation::AnimationTime::setValue(Ogre::Real) -{ -} - -Ogre::Real Animation::EffectAnimationTime::getValue() const -{ - return mTime; -} - -void Animation::EffectAnimationTime::setValue(Ogre::Real) -{ -} - -Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) - : mPtr(ptr) - , mInsert(node) - , mSkelBase(NULL) - , mAccumRoot(NULL) - , mNonAccumRoot(NULL) - , mNonAccumCtrl(NULL) - , mAccumulate(0.0f) - , mNullAnimationTimePtr(OGRE_NEW NullAnimationTime) - , mGlowLight(NULL) -{ - for(size_t i = 0;i < sNumGroups;i++) - mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this)); -} - -Animation::~Animation() -{ - setLightEffect(0); - - mEffects.clear(); - - mAnimSources.clear(); -} - -std::string Animation::getObjectRootName() const -{ - if (mSkelBase) - return mSkelBase->getMesh()->getName(); - return std::string(); -} - -void Animation::setObjectRoot(const std::string &model, bool baseonly) -{ - OgreAssert(mAnimSources.empty(), "Setting object root while animation sources are set!"); - - mSkelBase = NULL; - mObjectRoot.setNull(); - - if(model.empty()) - return; - - mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : - NifOgre::Loader::createObjectBase(mInsert, model)); - - if(mObjectRoot->mSkelBase) - { - mSkelBase = mObjectRoot->mSkelBase; - - Ogre::AnimationStateSet *aset = mObjectRoot->mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); - while(asiter.hasMoreElements()) - { - Ogre::AnimationState *state = asiter.getNext(); - state->setEnabled(false); - state->setLoop(false); - } - - // Set the bones as manually controlled since we're applying the - // transformations manually - Ogre::SkeletonInstance *skelinst = mObjectRoot->mSkelBase->getSkeleton(); - Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); - while(boneiter.hasMoreElements()) - boneiter.getNext()->setManuallyControlled(true); - - // Reattach any objects that have been attached to this one - ObjectAttachMap::iterator iter = mAttachedObjects.begin(); - while(iter != mAttachedObjects.end()) - { - if(!skelinst->hasBone(iter->second)) - mAttachedObjects.erase(iter++); - else - { - mSkelBase->attachObjectToBone(iter->second, iter->first); - ++iter; - } - } - } - else - mAttachedObjects.clear(); -} - -struct AddGlow -{ - Ogre::Vector3* mColor; - NifOgre::MaterialControllerManager* mMaterialControllerMgr; - AddGlow(Ogre::Vector3* col, NifOgre::MaterialControllerManager* materialControllerMgr) - : mColor(col) - , mMaterialControllerMgr(materialControllerMgr) - {} - - void operator()(Ogre::Entity* entity) const - { - if (!entity->getNumSubEntities()) - return; - Ogre::MaterialPtr writableMaterial = mMaterialControllerMgr->getWritableMaterial(entity); - sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(writableMaterial->getName()); - - instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true))); - instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z))); - // Workaround for crash in Ogre (https://bitbucket.org/sinbad/ogre/pull-request/447/fix-shadows-crash-for-textureunitstates/diff) - // Remove when the fix is merged - instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha"); - } -}; - -class VisQueueSet -{ - Ogre::uint32 mVisFlags; - Ogre::uint8 mSolidQueue, mTransQueue; - Ogre::Real mDist; - -public: - VisQueueSet(Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist) - : mVisFlags(visflags), mSolidQueue(solidqueue), mTransQueue(transqueue), mDist(dist) - { } - - void operator()(Ogre::Entity *entity) const - { - if(mVisFlags != 0) - entity->setVisibilityFlags(mVisFlags); - entity->setRenderingDistance(mDist); - - unsigned int numsubs = entity->getNumSubEntities(); - for(unsigned int i = 0;i < numsubs;++i) - { - Ogre::SubEntity* subEnt = entity->getSubEntity(i); - sh::Factory::getInstance()._ensureMaterial(subEnt->getMaterial()->getName(), "Default"); - subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? mTransQueue : mSolidQueue); - } - } - - void operator()(Ogre::ParticleSystem *psys) const - { - if(mVisFlags != 0) - psys->setVisibilityFlags(mVisFlags); - psys->setRenderingDistance(mDist); - // TODO: Check particle material for actual transparency - psys->setRenderQueueGroup(mTransQueue); - } -}; - -void Animation::setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist, bool enchantedGlow, Ogre::Vector3* glowColor) -{ - std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(), - VisQueueSet(visflags, solidqueue, transqueue, dist)); - std::for_each(objlist->mParticles.begin(), objlist->mParticles.end(), - VisQueueSet(visflags, solidqueue, transqueue, dist)); - - if (enchantedGlow) - std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(), - AddGlow(glowColor, &objlist->mMaterialControllerMgr)); -} - - -size_t Animation::detectAnimGroup(const Ogre::Node *node) -{ - static const char sGroupRoots[sNumGroups][32] = { - "", /* Lower body / character root */ - "Bip01 Spine1", /* Torso */ - "Bip01 L Clavicle", /* Left arm */ - "Bip01 R Clavicle", /* Right arm */ - }; - - while(node) - { - const Ogre::String &name = node->getName(); - for(size_t i = 1;i < sNumGroups;i++) - { - if(name == sGroupRoots[i]) - return i; - } - - node = node->getParent(); - } - - return 0; -} - - -void Animation::addAnimSource(const std::string &model) -{ - OgreAssert(mInsert, "Object is missing a root!"); - if(!mSkelBase) - return; - - std::string kfname = model; - Misc::StringUtils::toLower(kfname); - - if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) - kfname.replace(kfname.size()-4, 4, ".kf"); - - if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname)) - return; - - std::vector > ctrls; - Ogre::SharedPtr animsrc(OGRE_NEW AnimSource); - NifOgre::Loader::createKfControllers(mSkelBase, kfname, animsrc->mTextKeys, ctrls); - if(animsrc->mTextKeys.empty() || ctrls.empty()) - return; - - mAnimSources.push_back(animsrc); - - std::vector > *grpctrls = animsrc->mControllers; - for(size_t i = 0;i < ctrls.size();i++) - { - NifOgre::NodeTargetValue *dstval; - dstval = static_cast*>(ctrls[i].getDestination().getPointer()); - - size_t grp = detectAnimGroup(dstval->getNode()); - - if(!mAccumRoot && grp == 0) - { - mNonAccumRoot = dstval->getNode(); - mAccumRoot = mNonAccumRoot->getParent(); - if(!mAccumRoot) - { - std::cerr<< "Non-Accum root for "<getNode()->getName() == "Bip01" || dstval->getNode()->getName() == "Root Bone")) - { - mNonAccumRoot = dstval->getNode(); - mAccumRoot = mNonAccumRoot->getParent(); - if(!mAccumRoot) - { - std::cerr<< "Non-Accum root for "<mControllers.size(); ++i) - { - if (mObjectRoot->mControllers[i].getSource().isNull()) - mObjectRoot->mControllers[i].setSource(mAnimationTimePtr[0]); - } -} - -void Animation::clearAnimSources() -{ - mStates.clear(); - - for(size_t i = 0;i < sNumGroups;i++) - mAnimationTimePtr[i]->setAnimName(std::string()); - - mNonAccumCtrl = NULL; - - mAccumRoot = NULL; - mNonAccumRoot = NULL; - - mAnimSources.clear(); -} - - -void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light) -{ - const MWWorld::Fallback *fallback = MWBase::Environment::get().getWorld()->getFallback(); - - const unsigned int clr = light->mData.mColor; - Ogre::ColourValue color(((clr >> 0) & 0xFF) / 255.0f, - ((clr >> 8) & 0xFF) / 255.0f, - ((clr >> 16) & 0xFF) / 255.0f); - const float radius = float(light->mData.mRadius); - - if((light->mData.mFlags&ESM::Light::Negative)) - color *= -1; - - objlist->mLights.push_back(sceneMgr->createLight()); - Ogre::Light *olight = objlist->mLights.back(); - olight->setDiffuseColour(color); - - Ogre::ControllerValueRealPtr src(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); - Ogre::ControllerValueRealPtr dest(OGRE_NEW OEngine::Render::LightValue(olight, color)); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW OEngine::Render::LightFunction( - (light->mData.mFlags&ESM::Light::Flicker) ? OEngine::Render::LT_Flicker : - (light->mData.mFlags&ESM::Light::FlickerSlow) ? OEngine::Render::LT_FlickerSlow : - (light->mData.mFlags&ESM::Light::Pulse) ? OEngine::Render::LT_Pulse : - (light->mData.mFlags&ESM::Light::PulseSlow) ? OEngine::Render::LT_PulseSlow : - OEngine::Render::LT_Normal - )); - objlist->mControllers.push_back(Ogre::Controller(src, dest, func)); - - bool interior = !(mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior()); - - static bool outQuadInLin = fallback->getFallbackBool("LightAttenuation_OutQuadInLin"); - static bool useQuadratic = fallback->getFallbackBool("LightAttenuation_UseQuadratic"); - static float quadraticValue = fallback->getFallbackFloat("LightAttenuation_QuadraticValue"); - static float quadraticRadiusMult = fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); - static bool useLinear = fallback->getFallbackBool("LightAttenuation_UseLinear"); - static float linearRadiusMult = fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); - static float linearValue = fallback->getFallbackFloat("LightAttenuation_LinearValue"); - - bool quadratic = useQuadratic && (!outQuadInLin || !interior); - - - // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, - // so we ignore lights if their attenuation falls below this factor. - const float threshold = 0.03f; - - float quadraticAttenuation = 0; - float linearAttenuation = 0; - float activationRange = 0; - if (quadratic) - { - float r = radius * quadraticRadiusMult; - quadraticAttenuation = quadraticValue / std::pow(r, 2); - activationRange = std::sqrt(1.0f / (threshold * quadraticAttenuation)); - } - if (useLinear) - { - float r = radius * linearRadiusMult; - linearAttenuation = linearValue / r; - activationRange = std::max(activationRange, 1.0f / (threshold * linearAttenuation)); - } - - olight->setAttenuation(activationRange, 0, linearAttenuation, quadraticAttenuation); - - // If there's an AttachLight bone, attach the light to that, otherwise put it in the center, - if(objlist->mSkelBase && objlist->mSkelBase->getSkeleton()->hasBone("AttachLight")) - objlist->mSkelBase->attachObjectToBone("AttachLight", olight); - else - { - Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - for(size_t i = 0;i < objlist->mEntities.size();i++) - { - Ogre::Entity *ent = objlist->mEntities[i]; - bounds.merge(ent->getBoundingBox()); - } - - Ogre::SceneNode *node = bounds.isFinite() ? mInsert->createChildSceneNode(bounds.getCenter()) - : mInsert->createChildSceneNode(); - node->attachObject(olight); - } -} - - -Ogre::Node* Animation::getNode(const std::string &name) -{ - if(mSkelBase) - { - Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); - if(skel->hasBone(name)) - return skel->getBone(name); - } - return NULL; -} - -Ogre::Node* Animation::getNode(int handle) -{ - if (mSkelBase) - { - Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); - return skel->getBone(handle); - } - return NULL; -} - -NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname) -{ - NifOgre::TextKeyMap::const_iterator iter(keys.begin()); - for(;iter != keys.end();++iter) - { - if(iter->second.compare(0, groupname.size(), groupname) == 0 && - iter->second.compare(groupname.size(), 2, ": ") == 0) - break; - } - return iter; -} - - -bool Animation::hasAnimation(const std::string &anim) -{ - AnimSourceList::const_iterator iter(mAnimSources.begin()); - for(;iter != mAnimSources.end();++iter) - { - const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; - if(findGroupStart(keys, anim) != keys.end()) - return true; - } - - return false; -} - - -void Animation::setAccumulation(const Ogre::Vector3 &accum) -{ - mAccumulate = accum; -} - - -void Animation::updatePtr(const MWWorld::Ptr &ptr) -{ - mPtr = ptr; -} - - -float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname) -{ - const std::string start = groupname+": start"; - const std::string loopstart = groupname+": loop start"; - const std::string loopstop = groupname+": loop stop"; - const std::string stop = groupname+": stop"; - float starttime = std::numeric_limits::max(); - float stoptime = 0.0f; - - // Pick the last Loop Stop key and the last Loop Start key. - // This is required because of broken text keys in AshVampire.nif. - // It has *two* WalkForward: Loop Stop keys at different times, the first one is used for stopping playback - // but the animation velocity calculation uses the second one. - // As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated, - // because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough. - NifOgre::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin()); - while(keyiter != keys.rend()) - { - if(keyiter->second == start || keyiter->second == loopstart) - { - starttime = keyiter->first; - break; - } - ++keyiter; - } - keyiter = keys.rbegin(); - while(keyiter != keys.rend()) - { - if (keyiter->second == stop) - stoptime = keyiter->first; - else if (keyiter->second == loopstop) - { - stoptime = keyiter->first; - break; - } - ++keyiter; - } - - if(stoptime > starttime) - { - Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum; - Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum; - - return startpos.distance(endpos) / (stoptime - starttime); - } - - return 0.0f; -} - -float Animation::getVelocity(const std::string &groupname) const -{ - /* Look in reverse; last-inserted source has priority. */ - AnimSourceList::const_reverse_iterator animsrc(mAnimSources.rbegin()); - for(;animsrc != mAnimSources.rend();++animsrc) - { - const NifOgre::TextKeyMap &keys = (*animsrc)->mTextKeys; - if(findGroupStart(keys, groupname) != keys.end()) - break; - } - if(animsrc == mAnimSources.rend()) - return 0.0f; - - float velocity = 0.0f; - const NifOgre::TextKeyMap &keys = (*animsrc)->mTextKeys; - const std::vector >&ctrls = (*animsrc)->mControllers[0]; - for(size_t i = 0;i < ctrls.size();i++) - { - NifOgre::NodeTargetValue *dstval; - dstval = static_cast*>(ctrls[i].getDestination().getPointer()); - if(dstval->getNode() == mNonAccumRoot) - { - velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); - break; - } - } - - // If there's no velocity, keep looking - if(!(velocity > 1.0f)) - { - AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); - while(*animiter != *animsrc) - ++animiter; - - while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend()) - { - const NifOgre::TextKeyMap &keys = (*animiter)->mTextKeys; - const std::vector >&ctrls = (*animiter)->mControllers[0]; - for(size_t i = 0;i < ctrls.size();i++) - { - NifOgre::NodeTargetValue *dstval; - dstval = static_cast*>(ctrls[i].getDestination().getPointer()); - if(dstval->getNode() == mNonAccumRoot) - { - velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); - break; - } - } - } - } - - return velocity; -} - - -static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) -{ - if(bone->getName() != " " // really should be != "", but see workaround in skeleton.cpp for empty node names - && skelsrc->hasBone(bone->getName())) - { - Ogre::Bone *srcbone = skelsrc->getBone(bone->getName()); - if(!srcbone->getParent() || !bone->getParent()) - { - bone->setOrientation(srcbone->getOrientation()); - bone->setPosition(srcbone->getPosition()); - bone->setScale(srcbone->getScale()); - } - else - { - bone->_setDerivedOrientation(srcbone->_getDerivedOrientation()); - bone->_setDerivedPosition(srcbone->_getDerivedPosition()); - bone->setScale(Ogre::Vector3::UNIT_SCALE); - } - } - - Ogre::Node::ChildNodeIterator boneiter = bone->getChildIterator(); - while(boneiter.hasMoreElements()) - updateBoneTree(skelsrc, static_cast(boneiter.getNext())); -} - -void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel) -{ - Ogre::Skeleton::BoneIterator boneiter = skel->getRootBoneIterator(); - while(boneiter.hasMoreElements()) - updateBoneTree(skelsrc, boneiter.getNext()); -} - - -void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &position) -{ - /* Get the non-accumulation root's difference from the last update, and move the position - * accordingly. - */ - Ogre::Vector3 off = mNonAccumCtrl->getTranslation(newtime)*mAccumulate; - position += off - mNonAccumCtrl->getTranslation(oldtime)*mAccumulate; - - /* Translate the accumulation root back to compensate for the move. */ - mAccumRoot->setPosition(-off); -} - -bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback) -{ - // Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two - // separate walkforward keys, and the last one is supposed to be used. - NifOgre::TextKeyMap::const_reverse_iterator groupend(keys.rbegin()); - for(;groupend != keys.rend();++groupend) - { - if(groupend->second.compare(0, groupname.size(), groupname) == 0 && - groupend->second.compare(groupname.size(), 2, ": ") == 0) - break; - } - - std::string starttag = groupname+": "+start; - NifOgre::TextKeyMap::const_reverse_iterator startkey(groupend); - while(startkey != keys.rend() && startkey->second != starttag) - ++startkey; - if(startkey == keys.rend() && start == "loop start") - { - starttag = groupname+": start"; - startkey = groupend; - while(startkey != keys.rend() && startkey->second != starttag) - ++startkey; - } - if(startkey == keys.rend()) - return false; - - const std::string stoptag = groupname+": "+stop; - NifOgre::TextKeyMap::const_reverse_iterator stopkey(groupend); - while(stopkey != keys.rend() - // We have to ignore extra garbage at the end. - // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". - // Why, just why? :( - && (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag)) - ++stopkey; - if(stopkey == keys.rend()) - return false; - - if(startkey->first > stopkey->first) - return false; - - state.mStartTime = startkey->first; - if (loopfallback) - { - state.mLoopStartTime = startkey->first; - state.mLoopStopTime = stopkey->first; - } - else - { - state.mLoopStartTime = startkey->first; - state.mLoopStopTime = std::numeric_limits::max(); - } - state.mStopTime = stopkey->first; - - state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint); - - // mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation - // (see handleTextKey). But if startpoint is already past these keys, we need to assign them now. - if(state.mTime > state.mStartTime) - { - const std::string loopstarttag = groupname+": loop start"; - const std::string loopstoptag = groupname+": loop stop"; - - NifOgre::TextKeyMap::const_reverse_iterator key(groupend); - for (; key != startkey && key != keys.rend(); ++key) - { - if (key->first > state.mTime) - continue; - - if (key->second == loopstarttag) - state.mLoopStartTime = key->first; - else if (key->second == loopstoptag) - state.mLoopStopTime = key->first; - } - } - - return true; -} - -void split(const std::string &s, char delim, std::vector &elems) { - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } -} - -void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key, - const NifOgre::TextKeyMap& textkeys) -{ - //float time = key->first; - const std::string &evt = key->second; - - if(evt.compare(0, 7, "sound: ") == 0) - { - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); - return; - } - if(evt.compare(0, 10, "soundgen: ") == 0) - { - std::string soundgen = evt.substr(10); - - // The event can optionally contain volume and pitch modifiers - float volume=1.f, pitch=1.f; - if (soundgen.find(" ") != std::string::npos) - { - std::vector tokens; - split(soundgen, ' ', tokens); - soundgen = tokens[0]; - if (tokens.size() >= 2) - volume = Ogre::StringConverter::parseReal(tokens[1]); - if (tokens.size() >= 3) - pitch = Ogre::StringConverter::parseReal(tokens[2]); - } - - std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); - if(!sound.empty()) - { - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx; - if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) - type = MWBase::SoundManager::Play_TypeFoot; - sndMgr->playSound3D(mPtr, sound, volume, pitch, type); - } - return; - } - - if(evt.compare(0, groupname.size(), groupname) != 0 || - evt.compare(groupname.size(), 2, ": ") != 0) - { - // Not ours, skip it - return; - } - size_t off = groupname.size()+2; - size_t len = evt.size() - off; - - if(evt.compare(off, len, "loop start") == 0) - state.mLoopStartTime = key->first; - else if(evt.compare(off, len, "loop stop") == 0) - state.mLoopStopTime = key->first; - else if(evt.compare(off, len, "equip attach") == 0) - showWeapons(true); - else if(evt.compare(off, len, "unequip detach") == 0) - showWeapons(false); - else if(evt.compare(off, len, "chop hit") == 0) - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); - else if(evt.compare(off, len, "slash hit") == 0) - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); - else if(evt.compare(off, len, "thrust hit") == 0) - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); - else if(evt.compare(off, len, "hit") == 0) - { - if (groupname == "attack1") - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); - else if (groupname == "attack2") - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); - else if (groupname == "attack3") - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); - else - mPtr.getClass().hit(mPtr); - } - else if (!groupname.empty() && groupname.compare(0, groupname.size()-1, "attack") == 0 - && evt.compare(off, len, "start") == 0) - { - NifOgre::TextKeyMap::const_iterator hitKey = key; - - // Not all animations have a hit key defined. If there is none, the hit happens with the start key. - bool hasHitKey = false; - while (hitKey != textkeys.end()) - { - if (hitKey->second == groupname + ": hit") - { - hasHitKey = true; - break; - } - if (hitKey->second == groupname + ": stop") - break; - ++hitKey; - } - if (!hasHitKey) - { - if (groupname == "attack1") - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); - else if (groupname == "attack2") - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); - else if (groupname == "attack3") - mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); - } - } - else if (evt.compare(off, len, "shoot attach") == 0) - attachArrow(); - else if (evt.compare(off, len, "shoot release") == 0) - releaseArrow(); - else if (evt.compare(off, len, "shoot follow attach") == 0) - attachArrow(); - - else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") - { - // Make sure this key is actually for the RangeType we are casting. The flame atronach has - // the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type. - // FIXME: This logic should really be in the CharacterController - const std::string& spellid = mPtr.getClass().getCreatureStats(mPtr).getSpells().getSelectedSpell(); - const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellid); - const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); - int range = 0; - if (evt.compare(off, len, "self release") == 0) - range = 0; - else if (evt.compare(off, len, "touch release") == 0) - range = 1; - else if (evt.compare(off, len, "target release") == 0) - range = 2; - if (effectentry.mRange == range) - { - MWBase::Environment::get().getWorld()->castSpell(mPtr); - } - } - - else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0) - mPtr.getClass().block(mPtr); -} - -void Animation::changeGroups(const std::string &groupname, int groups) -{ - AnimStateMap::iterator stateiter = mStates.find(groupname); - if(stateiter != mStates.end()) - { - if(stateiter->second.mGroups != groups) - { - stateiter->second.mGroups = groups; - resetActiveGroups(); - } - return; - } -} - -void Animation::stopLooping(const std::string& groupname) -{ - AnimStateMap::iterator stateiter = mStates.find(groupname); - if(stateiter != mStates.end()) - { - stateiter->second.mLoopCount = 0; - return; - } -} - -void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback) -{ - if(!mSkelBase || mAnimSources.empty()) - return; - - if(groupname.empty()) - { - resetActiveGroups(); - return; - } - - priority = std::max(0, priority); - - AnimStateMap::iterator stateiter = mStates.begin(); - while(stateiter != mStates.end()) - { - if(stateiter->second.mPriority == priority) - mStates.erase(stateiter++); - else - ++stateiter; - } - - stateiter = mStates.find(groupname); - if(stateiter != mStates.end()) - { - stateiter->second.mPriority = priority; - resetActiveGroups(); - return; - } - - /* Look in reverse; last-inserted source has priority. */ - AnimState state; - AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); - for(;iter != mAnimSources.rend();++iter) + Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr node, Resource::ResourceSystem* resourceSystem) + : mPtr(ptr) + , mInsert(node) + , mResourceSystem(resourceSystem) { - const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys; - if(reset(state, textkeys, groupname, start, stop, startpoint, loopfallback)) - { - state.mSource = *iter; - state.mSpeedMult = speedmult; - state.mLoopCount = loops; - state.mPlaying = (state.mTime < state.mStopTime); - state.mPriority = priority; - state.mGroups = groups; - state.mAutoDisable = autodisable; - mStates[groupname] = state; - - NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); - if (state.mPlaying) - { - while(textkey != textkeys.end() && textkey->first <= state.mTime) - { - handleTextKey(state, groupname, textkey, textkeys); - ++textkey; - } - } - - if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) - { - state.mLoopCount--; - state.mTime = state.mLoopStartTime; - state.mPlaying = true; - if(state.mTime >= state.mLoopStopTime) - break; - - NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); - while(textkey != textkeys.end() && textkey->first <= state.mTime) - { - handleTextKey(state, groupname, textkey, textkeys); - ++textkey; - } - } - - break; - } - } - if(iter == mAnimSources.rend()) - std::cerr<< "Failed to find animation "<setPosition(-mNonAccumCtrl->getTranslation(state.mTime)*mAccumulate); } -} - -void Animation::adjustSpeedMult(const std::string &groupname, float speedmult) -{ - AnimStateMap::iterator state(mStates.find(groupname)); - if(state != mStates.end()) - state->second.mSpeedMult = speedmult; -} - -bool Animation::isPlaying(const std::string &groupname) const -{ - AnimStateMap::const_iterator state(mStates.find(groupname)); - if(state != mStates.end()) - return state->second.mPlaying; - return false; -} -void Animation::resetActiveGroups() -{ - for(size_t grp = 0;grp < sNumGroups;grp++) + Animation::~Animation() { - AnimStateMap::const_iterator active = mStates.end(); - - AnimStateMap::const_iterator state = mStates.begin(); - for(;state != mStates.end();++state) - { - if(!(state->second.mGroups&(1<second.mPriority < state->second.mPriority) - active = state; - } - - mAnimationTimePtr[grp]->setAnimName((active == mStates.end()) ? - std::string() : active->first); + if (mObjectRoot) + mInsert->removeChild(mObjectRoot); } - mNonAccumCtrl = NULL; - - if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) - return; - AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName()); - if(state == mStates.end()) + osg::Vec3f Animation::runAnimation(float duration) { - if (mAccumRoot && mNonAccumRoot) - mAccumRoot->setPosition(-mNonAccumRoot->getPosition()*mAccumulate); - return; + return osg::Vec3f(); } - const Ogre::SharedPtr &animsrc = state->second.mSource; - const std::vector >&ctrls = animsrc->mControllers[0]; - for(size_t i = 0;i < ctrls.size();i++) + void Animation::setObjectRoot(const std::string &model) { - NifOgre::NodeTargetValue *dstval; - dstval = static_cast*>(ctrls[i].getDestination().getPointer()); - if(dstval->getNode() == mNonAccumRoot) + if (mObjectRoot) { - mNonAccumCtrl = dstval; - break; + mObjectRoot->getParent(0)->removeChild(mObjectRoot); } - } - - if (mAccumRoot && mNonAccumCtrl) - mAccumRoot->setPosition(-mNonAccumCtrl->getTranslation(state->second.mTime)*mAccumulate); -} - - -bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const -{ - AnimStateMap::const_iterator iter = mStates.find(groupname); - if(iter == mStates.end()) - { - if(complete) *complete = 0.0f; - if(speedmult) *speedmult = 0.0f; - return false; - } - if(complete) - { - if(iter->second.mStopTime > iter->second.mStartTime) - *complete = (iter->second.mTime - iter->second.mStartTime) / - (iter->second.mStopTime - iter->second.mStartTime); - else - *complete = (iter->second.mPlaying ? 0.0f : 1.0f); + mObjectRoot = mResourceSystem->getSceneManager()->createInstance(model, mInsert); } - if(speedmult) *speedmult = iter->second.mSpeedMult; - return true; -} -float Animation::getStartTime(const std::string &groupname) const -{ - for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) + osg::Group* Animation::getObjectRoot() { - const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; - - NifOgre::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); - if(found != keys.end()) - return found->first; + return static_cast(mObjectRoot.get()); } - return -1.f; -} -float Animation::getTextKeyTime(const std::string &textKey) const -{ - for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) + osg::Group* Animation::getOrCreateObjectRoot() { - const NifOgre::TextKeyMap &keys = (*iter)->mTextKeys; + if (mObjectRoot) + return static_cast(mObjectRoot.get()); - for(NifOgre::TextKeyMap::const_iterator iterKey(keys.begin()); iterKey != keys.end(); ++iterKey) - { - if(iterKey->second.compare(0, textKey.size(), textKey) == 0) - return iterKey->first; - } + mObjectRoot = new osg::Group; + mInsert->addChild(mObjectRoot); + return static_cast(mObjectRoot.get()); } - return -1.f; -} - -float Animation::getCurrentTime(const std::string &groupname) const -{ - AnimStateMap::const_iterator iter = mStates.find(groupname); - if(iter == mStates.end()) - return -1.f; - - return iter->second.mTime; -} - -void Animation::disable(const std::string &groupname) -{ - AnimStateMap::iterator iter = mStates.find(groupname); - if(iter != mStates.end()) - mStates.erase(iter); - resetActiveGroups(); -} - + // -------------------------------------------------------------------------------- -Ogre::Vector3 Animation::runAnimation(float duration) -{ - Ogre::Vector3 movement(0.0f); - AnimStateMap::iterator stateiter = mStates.begin(); - while(stateiter != mStates.end()) + ObjectAnimation::ObjectAnimation(const MWWorld::Ptr &ptr, const std::string &model, Resource::ResourceSystem* resourceSystem) + : Animation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), resourceSystem) { - AnimState &state = stateiter->second; - const NifOgre::TextKeyMap &textkeys = state.mSource->mTextKeys; - NifOgre::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.mTime)); - - float timepassed = duration * state.mSpeedMult; - while(state.mPlaying) - { - float targetTime; - - if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) - goto handle_loop; - - targetTime = state.mTime + timepassed; - if(textkey == textkeys.end() || textkey->first > targetTime) - { - if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) - updatePosition(state.mTime, targetTime, movement); - state.mTime = std::min(targetTime, state.mStopTime); - } - else - { - if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) - updatePosition(state.mTime, textkey->first, movement); - state.mTime = textkey->first; - } - - state.mPlaying = (state.mTime < state.mStopTime); - timepassed = targetTime - state.mTime; - - while(textkey != textkeys.end() && textkey->first <= state.mTime) - { - handleTextKey(state, stateiter->first, textkey, textkeys); - ++textkey; - } - - if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) - { - handle_loop: - state.mLoopCount--; - state.mTime = state.mLoopStartTime; - state.mPlaying = true; - - textkey = textkeys.lower_bound(state.mTime); - while(textkey != textkeys.end() && textkey->first <= state.mTime) - { - handleTextKey(state, stateiter->first, textkey, textkeys); - ++textkey; - } - - if(state.mTime >= state.mLoopStopTime) - break; - } - - if(timepassed <= 0.0f) - break; - } - - if(!state.mPlaying && state.mAutoDisable) + if (!model.empty()) { - mStates.erase(stateiter++); - - resetActiveGroups(); + setObjectRoot(model); } else - ++stateiter; - } - - for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) - { - if(!mObjectRoot->mControllers[i].getSource().isNull()) - mObjectRoot->mControllers[i].update(); - } - - // Apply group controllers - for(size_t grp = 0;grp < sNumGroups;grp++) - { - const std::string &name = mAnimationTimePtr[grp]->getAnimName(); - if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end()) - { - const Ogre::SharedPtr &src = stateiter->second.mSource; - for(size_t i = 0;i < src->mControllers[grp].size();i++) - src->mControllers[grp][i].update(); - } - } - - if(mSkelBase) - { - // HACK: Dirty the animation state set so that Ogre will apply the - // transformations to entities this skeleton instance is shared with. - mSkelBase->getAllAnimationStates()->_notifyDirty(); - } - - updateEffects(duration); - - return movement; -} - -void Animation::showWeapons(bool showWeapon) -{ -} - - -class ToggleLight { - bool mEnable; - -public: - ToggleLight(bool enable) : mEnable(enable) { } - - void operator()(Ogre::Light *light) const - { light->setVisible(mEnable); } -}; - -void Animation::enableLights(bool enable) -{ - std::for_each(mObjectRoot->mLights.begin(), mObjectRoot->mLights.end(), ToggleLight(enable)); -} - - -class MergeBounds { - Ogre::AxisAlignedBox *mBounds; - -public: - MergeBounds(Ogre::AxisAlignedBox *bounds) : mBounds(bounds) { } - - void operator()(Ogre::MovableObject *obj) - { - mBounds->merge(obj->getWorldBoundingBox(true)); - } -}; - -Ogre::AxisAlignedBox Animation::getWorldBounds() -{ - Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - std::for_each(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(), MergeBounds(&bounds)); - return bounds; -} - - -Ogre::TagPoint *Animation::attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj) -{ - Ogre::TagPoint *tag = NULL; - Ogre::SkeletonInstance *skel = (mSkelBase ? mSkelBase->getSkeleton() : NULL); - if(skel && skel->hasBone(bonename)) - { - tag = mSkelBase->attachObjectToBone(bonename, obj); - mAttachedObjects[obj] = bonename; - } - return tag; -} - -void Animation::detachObjectFromBone(Ogre::MovableObject *obj) -{ - ObjectAttachMap::iterator iter = mAttachedObjects.find(obj); - if(iter != mAttachedObjects.end()) - mAttachedObjects.erase(iter); - mSkelBase->detachObjectFromBone(obj); -} - -bool Animation::upperBodyReady() const -{ - for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) - { - if((stateiter->second.mPriority > MWMechanics::Priority_Movement - && stateiter->second.mPriority < MWMechanics::Priority_Torch) - || stateiter->second.mPriority == MWMechanics::Priority_Death) - return false; - } - return true; -} - -void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture) -{ - // Early out if we already have this effect - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) - if (it->mLoop && loop && it->mEffectId == effectId && it->mBoneName == bonename) - return; - - std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture); - - EffectParams params; - params.mModelName = model; - if (bonename.empty()) - params.mObjects = NifOgre::Loader::createObjects(mInsert, model); - else - { - if (!mSkelBase) - return; - params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, "", mInsert, model); - } - - setRenderProperties(params.mObjects, RV_Effects, - RQG_Main, RQG_Alpha, 0.f, false, NULL); - - params.mLoop = loop; - params.mEffectId = effectId; - params.mBoneName = bonename; - - for(size_t i = 0;i < params.mObjects->mControllers.size();i++) - { - if(params.mObjects->mControllers[i].getSource().isNull()) - params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationTime())); - } - - - // Do some manual adjustments on the created entities/particle systems - - // It looks like vanilla MW totally ignores lighting settings for effects attached to characters. - // If we don't do this, some effects will look way too dark depending on the environment - // (e.g. magic_cast_dst.nif). They were clearly meant to use emissive lighting. - // We used to have this hack in the NIF material loader, but for effects not attached to characters - // (e.g. ash storms) the lighting settings do seem to be in use. Is there maybe a flag we have missed? - Ogre::ColourValue ambient = Ogre::ColourValue(0.f, 0.f, 0.f); - Ogre::ColourValue diffuse = Ogre::ColourValue(0.f, 0.f, 0.f); - Ogre::ColourValue specular = Ogre::ColourValue(0.f, 0.f, 0.f); - Ogre::ColourValue emissive = Ogre::ColourValue(1.f, 1.f, 1.f); - for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) - { - Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; - - Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); - - for (int t=0; tgetNumTechniques(); ++t) - { - Ogre::Technique* tech = mat->getTechnique(t); - for (int p=0; pgetNumPasses(); ++p) - { - Ogre::Pass* pass = tech->getPass(p); - - pass->setAmbient(ambient); - pass->setDiffuse(diffuse); - pass->setSpecular(specular); - pass->setEmissive(emissive); - - if (!texture.empty()) - { - for (int tex=0; texgetNumTextureUnitStates(); ++tex) - { - Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); - tus->setTextureName(correctedTexture); - } - } - } - } - } - for(size_t i = 0;i < params.mObjects->mEntities.size(); ++i) - { - Ogre::Entity* ent = params.mObjects->mEntities[i]; - if (ent == params.mObjects->mSkelBase) - continue; - Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(ent); - - for (int t=0; tgetNumTechniques(); ++t) - { - Ogre::Technique* tech = mat->getTechnique(t); - for (int p=0; pgetNumPasses(); ++p) - { - Ogre::Pass* pass = tech->getPass(p); - - pass->setAmbient(ambient); - pass->setDiffuse(diffuse); - pass->setSpecular(specular); - pass->setEmissive(emissive); - - if (!texture.empty()) - { - for (int tex=0; texgetNumTextureUnitStates(); ++tex) - { - Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); - tus->setTextureName(correctedTexture); - } - } - } - } - } - - mEffects.push_back(params); -} - -void Animation::removeEffect(int effectId) -{ - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) - { - if (it->mEffectId == effectId) - { - mEffects.erase(it); - return; - } - } -} - -void Animation::getLoopingEffects(std::vector &out) -{ - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) - { - if (it->mLoop) - out.push_back(it->mEffectId); - } -} - -void Animation::updateEffects(float duration) -{ - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ) - { - NifOgre::ObjectScenePtr objects = it->mObjects; - for(size_t i = 0; i < objects->mControllers.size() ;i++) - { - EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); - if (value) - value->addTime(duration); - - objects->mControllers[i].update(); - } - - if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength) - { - if (it->mLoop) - { - // Start from the beginning again; carry over the remainder - float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength; - for(size_t i = 0; i < objects->mControllers.size() ;i++) - { - EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); - if (value) - value->resetTime(remainder); - } - } - else - { - it = mEffects.erase(it); - continue; - } - } - ++it; - } -} - -void Animation::preRender(Ogre::Camera *camera) -{ - for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ++it) - { - NifOgre::ObjectScenePtr objects = it->mObjects; - objects->rotateBillboardNodes(camera); - } - mObjectRoot->rotateBillboardNodes(camera); -} - -// TODO: Should not be here -Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item) -{ - Ogre::Vector3 result(1,1,1); - std::string enchantmentName = item.getClass().getEnchantment(item); - if (enchantmentName.empty()) - return result; - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(enchantmentName); - assert (enchantment->mEffects.mList.size()); - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find( - enchantment->mEffects.mList.front().mEffectID); - result.x = magicEffect->mData.mRed / 255.f; - result.y = magicEffect->mData.mGreen / 255.f; - result.z = magicEffect->mData.mBlue / 255.f; - return result; -} - -void Animation::setLightEffect(float effect) -{ - if (effect == 0) - { - if (mGlowLight) - { - mInsert->getCreator()->destroySceneNode(mGlowLight->getParentSceneNode()); - mInsert->getCreator()->destroyLight(mGlowLight); - mGlowLight = NULL; - } - } - else - { - if (!mGlowLight) - { - mGlowLight = mInsert->getCreator()->createLight(); - - Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - for(size_t i = 0;i < mObjectRoot->mEntities.size();i++) - { - Ogre::Entity *ent = mObjectRoot->mEntities[i]; - bounds.merge(ent->getBoundingBox()); - } - mInsert->createChildSceneNode(bounds.getCenter())->attachObject(mGlowLight); - } - mGlowLight->setType(Ogre::Light::LT_POINT); - effect += 3; - mGlowLight->setAttenuation(1.0f / (0.03f * (0.5f/effect)), 0, 0.5f/effect, 0); - } -} - - -ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model) - : Animation(ptr, ptr.getRefData().getBaseNode()) -{ - if (!model.empty()) - { - setObjectRoot(model, false); - - Ogre::Vector3 extents = getWorldBounds().getSize(); - float size = std::max(std::max(extents.x, extents.y), extents.z); - - bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && - Settings::Manager::getBool("limit small object distance", "Viewing distance"); - // do not fade out doors. that will cause holes and look stupid - if(ptr.getTypeName().find("Door") != std::string::npos) - small = false; - - float dist = small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0.0f; - Ogre::Vector3 col = getEnchantmentColor(ptr); - setRenderProperties(mObjectRoot, (mPtr.getTypeName() == typeid(ESM::Static).name()) ? - (small ? RV_StaticsSmall : RV_Statics) : RV_Misc, - RQG_Main, RQG_Alpha, dist, !ptr.getClass().getEnchantment(ptr).empty(), &col); - } - else - { - // No model given. Create an object root anyway, so that lights can be added to it if needed. - mObjectRoot = NifOgre::ObjectScenePtr (new NifOgre::ObjectScene(mInsert->getCreator())); - } -} - - -class FindEntityTransparency { -public: - bool operator()(Ogre::Entity *ent) const - { - unsigned int numsubs = ent->getNumSubEntities(); - for(unsigned int i = 0;i < numsubs;++i) { - sh::Factory::getInstance()._ensureMaterial(ent->getSubEntity(i)->getMaterial()->getName(), "Default"); - if(ent->getSubEntity(i)->getMaterial()->isTransparent()) - return true; + // No model given. Create an object root anyway, so that lights can be added to it if needed. + //mObjectRoot = NifOgre::ObjectScenePtr (new NifOgre::ObjectScene(mInsert->getCreator())); } - return false; } -}; - -bool ObjectAnimation::canBatch() const -{ - if(!mObjectRoot->mParticles.empty() || !mObjectRoot->mLights.empty() || !mObjectRoot->mControllers.empty()) - return false; - if (!mObjectRoot->mBillboardNodes.empty()) - return false; - return std::find_if(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(), - FindEntityTransparency()) == mObjectRoot->mEntities.end(); -} - -void ObjectAnimation::fillBatch(Ogre::StaticGeometry *sg) -{ - std::vector::reverse_iterator iter = mObjectRoot->mEntities.rbegin(); - for(;iter != mObjectRoot->mEntities.rend();++iter) - { - Ogre::Node *node = (*iter)->getParentNode(); - if ((*iter)->isVisible()) - sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); - } -} } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index dab8cfebb..cbded364a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -4,15 +4,20 @@ #include #include -#include - #include "../mwworld/ptr.hpp" +#include + namespace ESM { struct Light; } +namespace Resource +{ + class ResourceSystem; +} + namespace MWRender { class Camera; @@ -69,20 +74,18 @@ protected: virtual void setValue(Ogre::Real value); }; - - - class NullAnimationTime : public Ogre::ControllerValue + class NullAnimationTime : public NifOsg::ControllerSource { public: - virtual Ogre::Real getValue() const - { return 0.0f; } - virtual void setValue(Ogre::Real value) - { } + virtual float getValue(osg::NodeVisitor *nv) + { + return 0.f; + } }; struct AnimSource : public Ogre::AnimationAlloc { - NifOgre::TextKeyMap mTextKeys; + //NifOgre::TextKeyMap mTextKeys; std::vector > mControllers[sNumGroups]; }; typedef std::vector< Ogre::SharedPtr > AnimSourceList; @@ -113,106 +116,71 @@ protected: typedef std::map ObjectAttachMap; - struct EffectParams - { - std::string mModelName; // Just here so we don't add the same effect twice - NifOgre::ObjectScenePtr mObjects; - int mEffectId; - bool mLoop; - std::string mBoneName; - }; + osg::ref_ptr mInsert; - std::vector mEffects; + osg::ref_ptr mObjectRoot; MWWorld::Ptr mPtr; - Ogre::Light* mGlowLight; - - Ogre::SceneNode *mInsert; - Ogre::Entity *mSkelBase; - NifOgre::ObjectScenePtr mObjectRoot; - AnimSourceList mAnimSources; - Ogre::Node *mAccumRoot; - Ogre::Node *mNonAccumRoot; - NifOgre::NodeTargetValue *mNonAccumCtrl; - Ogre::Vector3 mAccumulate; - - AnimStateMap mStates; - - Ogre::SharedPtr mAnimationTimePtr[sNumGroups]; - Ogre::SharedPtr mNullAnimationTimePtr; - - ObjectAttachMap mAttachedObjects; - + Resource::ResourceSystem* mResourceSystem; /* Sets the appropriate animations on the bone groups based on priority. */ - void resetActiveGroups(); + //void resetActiveGroups(); - static size_t detectAnimGroup(const Ogre::Node *node); + //static size_t detectAnimGroup(const Ogre::Node *node); + /* static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname); - - /* Updates a skeleton instance so that all bones matching the source skeleton (based on - * bone names) are positioned identically. */ - void updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel); + */ /* Updates the position of the accum root node for the given time, and * returns the wanted movement vector from the previous time. */ - void updatePosition(float oldtime, float newtime, Ogre::Vector3 &position); + //void updatePosition(float oldtime, float newtime, Ogre::Vector3 &position); - static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname); + //static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname); /* Resets the animation to the time of the specified start marker, without * moving anything, and set the end time to the specified stop marker. If * the marker is not found, or if the markers are the same, it returns * false. */ - bool reset(AnimState &state, const NifOgre::TextKeyMap &keys, - const std::string &groupname, const std::string &start, const std::string &stop, - float startpoint, bool loopfallback); + //bool reset(AnimState &state, const NifOgre::TextKeyMap &keys, + // const std::string &groupname, const std::string &start, const std::string &stop, + // float startpoint, bool loopfallback); - void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key, - const NifOgre::TextKeyMap& map); + //void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key, + // const NifOgre::TextKeyMap& map); - /* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle - * systems in the model are ignored (useful for NPCs, where only the skeleton is needed for - * the root). + /* Sets the root model of the object. * * Note that you must make sure all animation sources are cleared before reseting the object * root. All nodes previously retrieved with getNode will also become invalidated. */ - void setObjectRoot(const std::string &model, bool baseonly); + void setObjectRoot(const std::string &model); /* Adds the keyframe controllers in the specified model as a new animation source. Note that * the filename portion of the provided model name will be prepended with 'x', and the .nif * extension will be replaced with .kf. */ - void addAnimSource(const std::string &model); + //void addAnimSource(const std::string &model); /** Adds an additional light to the given object list using the specified ESM record. */ - void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light); - - void clearAnimSources(); + //void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light); - // TODO: Should not be here - Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item); + //void clearAnimSources(); public: - // FIXME: Move outside of this class - static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, - Ogre::uint8 transqueue, Ogre::Real dist=0.0f, - bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); - /// Returns the name of the .nif file that makes up this animation's base skeleton. - /// If there is no skeleton, returns "". - std::string getObjectRootName() const; - - Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); + Animation(const MWWorld::Ptr &ptr, osg::ref_ptr node, Resource::ResourceSystem* resourceSystem); virtual ~Animation(); + osg::Group* getOrCreateObjectRoot(); + + osg::Group* getObjectRoot(); + /** * @brief Add an effect mesh attached to a bone or the insert scene node * @param model @@ -223,25 +191,18 @@ public: * @param texture override the texture specified in the model's materials * @note Will not add an effect twice. */ - void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", std::string texture = ""); - void removeEffect (int effectId); - void getLoopingEffects (std::vector& out); + //void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", std::string texture = ""); + //void removeEffect (int effectId); + //void getLoopingEffects (std::vector& out); - /// Prepare this animation for being rendered with \a camera (rotates billboard nodes) - virtual void preRender (Ogre::Camera* camera); + //void updatePtr(const MWWorld::Ptr &ptr); - virtual void setAlpha(float alpha) {} - virtual void setVampire(bool vampire) {} - -public: - void updatePtr(const MWWorld::Ptr &ptr); - - bool hasAnimation(const std::string &anim); + //bool hasAnimation(const std::string &anim); // Specifies the axis' to accumulate on. Non-accumulated axis will just // move visually, but not affect the actual movement. Each x/y/z value // should be on the scale of 0 to 1. - void setAccumulation(const Ogre::Vector3 &accum); + //void setAccumulation(const Ogre::Vector3 &accum); /** Plays an animation. * \param groupname Name of the animation group to play. @@ -263,23 +224,23 @@ public: * \param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use * the "start" and "stop" keys for looping? */ - void play(const std::string &groupname, int priority, int groups, bool autodisable, - float speedmult, const std::string &start, const std::string &stop, - float startpoint, size_t loops, bool loopfallback=false); + //void play(const std::string &groupname, int priority, int groups, bool autodisable, + // float speedmult, const std::string &start, const std::string &stop, + // float startpoint, size_t loops, bool loopfallback=false); /** If the given animation group is currently playing, set its remaining loop count to '0'. */ - void stopLooping(const std::string& groupName); + //void stopLooping(const std::string& groupName); /** Adjust the speed multiplier of an already playing animation. */ - void adjustSpeedMult (const std::string& groupname, float speedmult); + //void adjustSpeedMult (const std::string& groupname, float speedmult); /** Returns true if the named animation group is playing. */ - bool isPlaying(const std::string &groupname) const; + //bool isPlaying(const std::string &groupname) const; /// Returns true if no important animations are currently playing on the upper body. - bool upperBodyReady() const; + //bool upperBodyReady() const; /** Gets info about the given animation group. * \param groupname Animation group to check. @@ -287,71 +248,32 @@ public: * \param speedmult Stores the animation speed multiplier * \return True if the animation is active, false otherwise. */ - bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + //bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; /// Get the absolute position in the animation track of the first text key with the given group. - float getStartTime(const std::string &groupname) const; + //float getStartTime(const std::string &groupname) const; /// Get the absolute position in the animation track of the text key - float getTextKeyTime(const std::string &textKey) const; + //float getTextKeyTime(const std::string &textKey) const; /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. - float getCurrentTime(const std::string& groupname) const; + //float getCurrentTime(const std::string& groupname) const; /** Disables the specified animation group; * \param groupname Animation group to disable. */ - void disable(const std::string &groupname); - void changeGroups(const std::string &groupname, int group); - - virtual void setWeaponGroup(const std::string& group) {} + //void disable(const std::string &groupname); + //void changeGroups(const std::string &groupname, int group); /** Retrieves the velocity (in units per second) that the animation will move. */ - float getVelocity(const std::string &groupname) const; - - /// A relative factor (0-1) that decides if and how much the skeleton should be pitched - /// to indicate the facing orientation of the character. - virtual void setPitchFactor(float factor) {} - virtual void setHeadPitch(Ogre::Radian factor) {} - virtual void setHeadYaw(Ogre::Radian factor) {} - virtual Ogre::Radian getHeadPitch() const { return Ogre::Radian(0.f); } - virtual Ogre::Radian getHeadYaw() const { return Ogre::Radian(0.f); } - - virtual Ogre::Vector3 runAnimation(float duration); - - /// This is typically called as part of runAnimation, but may be called manually if needed. - void updateEffects(float duration); - - // TODO: move outside of this class - /// Makes this object glow, by placing a Light in its center. - /// @param effect Controls the radius and intensity of the light. - void setLightEffect(float effect); - - virtual void showWeapons(bool showWeapon); - virtual void showCarriedLeft(bool show) {} - virtual void attachArrow() {} - virtual void releaseArrow() {} - void enableLights(bool enable); - virtual void enableHeadAnimation(bool enable) {} - - Ogre::AxisAlignedBox getWorldBounds(); - - Ogre::Node *getNode(const std::string &name); - Ogre::Node *getNode(int handle); - - // Attaches the given object to a bone on this object's base skeleton. If the bone doesn't - // exist, the object isn't attached and NULL is returned. The returned TagPoint is only - // valid until the next setObjectRoot call. - Ogre::TagPoint *attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj); - void detachObjectFromBone(Ogre::MovableObject *obj); + //float getVelocity(const std::string &groupname) const; + + virtual osg::Vec3f runAnimation(float duration); }; class ObjectAnimation : public Animation { public: - ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model); - - bool canBatch() const; - void fillBatch(Ogre::StaticGeometry *sg); + ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, Resource::ResourceSystem* resourceSystem); }; } diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 3c6a507e7..6d37fa1e8 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -2,110 +2,204 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include -#include +#include +#include -//#include -#include +// light +#include + +#include + +#include +#include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" #include "renderconst.hpp" -//#include "animation.hpp" +#include "animation.hpp" + +namespace +{ + + /// Removes all particle systems and related nodes in a subgraph. + class RemoveParticlesVisitor : public osg::NodeVisitor + { + public: + RemoveParticlesVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { } + + virtual void apply(osg::Node &node) + { + if (dynamic_cast(&node) || dynamic_cast(&node)) + mToRemove.push_back(&node); + + traverse(node); + } -using namespace MWRender; + void remove() + { + for (std::vector >::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) + { + osg::Node* node = *it; + if (node->getNumParents()) + node->getParent(0)->removeChild(node); + } + mToRemove.clear(); + } -int Objects::uniqueID = 0; + private: + std::vector > mToRemove; + }; + +} -void Objects::setRootNode(Ogre::SceneNode* root) + +namespace MWRender +{ + +Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode) + : mResourceSystem(resourceSystem) + , mRootNode(rootNode) { - mRootNode = root; +} + +Objects::~Objects() +{ + for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();++iter) + delete iter->second; + mObjects.clear(); + + for (CellMap::iterator iter = mCellSceneNodes.begin(); iter != mCellSceneNodes.end(); ++iter) + iter->second->getParent(0)->removeChild(iter->second); + mCellSceneNodes.clear(); } void Objects::insertBegin(const MWWorld::Ptr& ptr) { - Ogre::SceneNode* root = mRootNode; - Ogre::SceneNode* cellnode; - if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) + osg::ref_ptr cellnode; + + CellMap::iterator found = mCellSceneNodes.find(ptr.getCell()); + if (found == mCellSceneNodes.end()) { - //Create the scenenode and put it in the map - cellnode = root->createChildSceneNode(); + cellnode = new osg::Group; + mRootNode->addChild(cellnode); mCellSceneNodes[ptr.getCell()] = cellnode; } else - { - cellnode = mCellSceneNodes[ptr.getCell()]; - } + cellnode = found->second; - Ogre::SceneNode* insert = cellnode->createChildSceneNode(); - const float *f = ptr.getRefData().getPosition().pos; + osg::ref_ptr insert (new osg::PositionAttitudeTransform); + cellnode->addChild(insert); - insert->setPosition(f[0], f[1], f[2]); - insert->setScale(ptr.getCellRef().getScale(), ptr.getCellRef().getScale(), ptr.getCellRef().getScale()); + const float *f = ptr.getRefData().getPosition().pos; + insert->setPosition(osg::Vec3(f[0], f[1], f[2])); + insert->setScale(osg::Vec3(ptr.getCellRef().getScale(), ptr.getCellRef().getScale(), ptr.getCellRef().getScale())); // Convert MW rotation to a quaternion: f = ptr.getCellRef().getPosition().rot; // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); + osg::Quat xr(-f[0], osg::Vec3(1,0,0)); // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y); + osg::Quat yr(-f[1], osg::Vec3(0,1,0)); // Rotate around Z axis - Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); + osg::Quat zr(-f[2], osg::Vec3(0,0,1)); // Rotates first around z, then y, then x - insert->setOrientation(xr*yr*zr); + insert->setAttitude(zr*yr*xr); + + // TODO: actors rotate around z only ptr.getRefData().setBaseNode(insert); } -void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch) +void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool animated, bool allowLight) { insertBegin(ptr); -/* - std::auto_ptr anim(new ObjectAnimation(ptr, mesh)); - if (!mesh.empty()) + std::auto_ptr anim (new ObjectAnimation(ptr, mesh, mResourceSystem)); + + if (ptr.getTypeName() == typeid(ESM::Light).name() && allowLight) { - Ogre::AxisAlignedBox bounds = anim->getWorldBounds(); - Ogre::Vector3 extents = bounds.getSize(); - extents *= ptr.getRefData().getBaseNode()->getScale(); - float size = std::max(std::max(extents.x, extents.y), extents.z); + SceneUtil::FindByNameVisitor visitor("AttachLight"); + ptr.getRefData().getBaseNode()->accept(visitor); + + osg::Vec3f lightOffset (0.f, 0.f, 0.f); + + osg::Group* attachTo = NULL; + if (visitor.mFoundNode) + { + attachTo = visitor.mFoundNode; + } + else + { + osg::ComputeBoundsVisitor computeBound; + osg::Group* objectRoot = anim->getOrCreateObjectRoot(); + objectRoot->accept(computeBound); - bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && - Settings::Manager::getBool("limit small object distance", "Viewing distance"); - // do not fade out doors. that will cause holes and look stupid - if(ptr.getTypeName().find("Door") != std::string::npos) - small = false; + lightOffset = computeBound.getBoundingBox().center(); - if (mBounds.find(ptr.getCell()) == mBounds.end()) - mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; - mBounds[ptr.getCell()].merge(bounds); + attachTo = objectRoot; + } + + const ESM::Light* esmLight = ptr.get()->mBase; + + osg::ref_ptr lightSource = new SceneUtil::LightSource; + osg::Light* light = new osg::Light; + lightSource->setLight(light); + light->setPosition(osg::Vec4f(lightOffset.x(), lightOffset.y(), lightOffset.z(), 1.f)); + + float realRadius = esmLight->mData.mRadius * 2; + + lightSource->setRadius(realRadius); + light->setLinearAttenuation(10.f/realRadius); + //light->setLinearAttenuation(0.05); + light->setConstantAttenuation(0.f); + light->setDiffuse(SceneUtil::colourFromRGB(esmLight->mData.mColor)); + light->setAmbient(osg::Vec4f(0,0,0,1)); + light->setSpecular(osg::Vec4f(0,0,0,0)); + + attachTo->addChild(lightSource); + } + if (!allowLight) + { + RemoveParticlesVisitor visitor; + anim->getObjectRoot()->accept(visitor); + visitor.remove(); } - if(anim.get() != NULL) - mObjects.insert(std::make_pair(ptr, anim.release())); - */ + mObjects.insert(std::make_pair(ptr, anim.release())); +} + +void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, bool weaponsShields) +{ + insertBegin(ptr); + + // CreatureAnimation + std::auto_ptr anim (new ObjectAnimation(ptr, mesh, mResourceSystem)); + mObjects.insert(std::make_pair(ptr, anim.release())); +} + +void Objects::insertNPC(const MWWorld::Ptr &ptr) +{ + } bool Objects::deleteObject (const MWWorld::Ptr& ptr) { - /* if(!ptr.getRefData().getBaseNode()) return true; @@ -115,18 +209,16 @@ bool Objects::deleteObject (const MWWorld::Ptr& ptr) delete iter->second; mObjects.erase(iter); - mRenderer.getScene()->destroySceneNode(ptr.getRefData().getBaseNode()); - ptr.getRefData().setBaseNode(0); + ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); + ptr.getRefData().setBaseNode(NULL); return true; } -*/ return false; } -void Objects::removeCell(MWWorld::CellStore* store) +void Objects::removeCell(const MWWorld::CellStore* store) { - /* for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();) { if(iter->first.getCell() == store) @@ -138,46 +230,19 @@ void Objects::removeCell(MWWorld::CellStore* store) ++iter; } - std::map::iterator geom = mStaticGeometry.find(store); - if(geom != mStaticGeometry.end()) - { - Ogre::StaticGeometry *sg = geom->second; - mStaticGeometry.erase(geom); - mRenderer.getScene()->destroyStaticGeometry(sg); - } - - geom = mStaticGeometrySmall.find(store); - if(geom != mStaticGeometrySmall.end()) - { - Ogre::StaticGeometry *sg = geom->second; - mStaticGeometrySmall.erase(store); - mRenderer.getScene()->destroyStaticGeometry(sg); - } - - mBounds.erase(store); - - std::map::iterator cell = mCellSceneNodes.find(store); + CellMap::iterator cell = mCellSceneNodes.find(store); if(cell != mCellSceneNodes.end()) { - cell->second->removeAndDestroyAllChildren(); - mRenderer.getScene()->destroySceneNode(cell->second); + cell->second->getParent(0)->removeChild(cell->second); mCellSceneNodes.erase(cell); } - */ -} - -Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::CellStore* cell) -{ - return mBounds[cell]; } void Objects::update(float dt, Ogre::Camera* camera) { - /* PtrAnimationMap::const_iterator it = mObjects.begin(); for(;it != mObjects.end();++it) it->second->runAnimation(dt); - */ } void Objects::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) @@ -206,13 +271,13 @@ void Objects::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) */ } -ObjectAnimation* Objects::getAnimation(const MWWorld::Ptr &ptr) +Animation* Objects::getAnimation(const MWWorld::Ptr &ptr) { - /* PtrAnimationMap::const_iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) return iter->second; - */ + return NULL; } +} diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index b2f07ab0f..44334e444 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -1,10 +1,16 @@ #ifndef GAME_RENDER_OBJECTS_H #define GAME_RENDER_OBJECTS_H -#include #include -#include +#include + +#include + +namespace osg +{ + class Group; +} namespace MWWorld { @@ -14,53 +20,52 @@ namespace MWWorld namespace MWRender{ -class ObjectAnimation; +class Animation; -class Objects{ - typedef std::map PtrAnimationMap; - OEngine::Render::OgreRenderer &mRenderer; +class Objects{ + typedef std::map PtrAnimationMap; - std::map mCellSceneNodes; - std::map mStaticGeometry; - std::map mStaticGeometrySmall; - std::map mBounds; + typedef std::map > CellMap; + CellMap mCellSceneNodes; PtrAnimationMap mObjects; - Ogre::SceneNode* mRootNode; - - static int uniqueID; + osg::ref_ptr mRootNode; void insertBegin(const MWWorld::Ptr& ptr); - + Resource::ResourceSystem* mResourceSystem; public: - Objects(OEngine::Render::OgreRenderer &renderer) - : mRenderer(renderer) - , mRootNode(NULL) - {} - ~Objects(){} - void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false); + Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr rootNode); + ~Objects(); + + /// @param animated Attempt to load separate keyframes from a .kf file matching the model file? + /// @param allowLight If false, no lights will be created, and particles systems will be cleared then frozen. + void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool animated=false, bool allowLight=true); - ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr); + void insertNPC(const MWWorld::Ptr& ptr); + void insertCreature (const MWWorld::Ptr& ptr, const std::string& model, bool weaponsShields); + + Animation* getAnimation(const MWWorld::Ptr &ptr); void update (float dt, Ogre::Camera* camera); ///< per-frame update - Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*); + //Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*); ///< get a bounding box that encloses all objects in the specified cell bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? - void removeCell(MWWorld::CellStore* store); - void setRootNode(Ogre::SceneNode* root); - - void rebuildStaticGeometry(); + void removeCell(const MWWorld::CellStore* store); /// Updates containing cell for object rendering data void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); + +private: + void operator = (const Objects&); + Objects(const Objects&); }; } #endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c64a265ac..4e5b33c64 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1,1046 +1,92 @@ #include "renderingmanager.hpp" -#include +#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include -#include +#include -#include -#include +#include -#include +#include -#include -#include -#include +#include -#include "../mwworld/esmstore.hpp" -#include "../mwworld/class.hpp" -#include "../mwworld/cellstore.hpp" - -#include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone -#include "../mwbase/environment.hpp" -#include "../mwbase/windowmanager.hpp" // FIXME -#include "../mwbase/statemanager.hpp" - -#include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/npcstats.hpp" - -#include "../mwworld/ptr.hpp" - -#include "localmap.hpp" -#include "water.hpp" -#include "npcanimation.hpp" -#include "globalmap.hpp" -#include "terrainstorage.hpp" -#include "effectmanager.hpp" - -using namespace MWRender; -using namespace Ogre; - -namespace MWRender { - -RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, - const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine, - MWWorld::Fallback* fallback) - : mRendering(_rend) - , mFallback(fallback) - , mPlayerAnimation(NULL) - , mSunEnabled(0) - , mPhysicsEngine(engine) - , mTerrain(NULL) - , mEffectManager(NULL) - , mRenderWorld(true) -{ - mActors = new MWRender::Actors(mRendering, this); - mObjects = new MWRender::Objects(mRendering); - mEffectManager = new EffectManager(mRendering.getScene()); - // select best shader mode - bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); - bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos); - - // glsl is only supported in opengl mode and hlsl only in direct3d mode. - std::string currentMode = Settings::Manager::getString("shader mode", "General"); - if (currentMode == "" - || (openGL && currentMode == "hlsl") - || (!openGL && currentMode == "glsl") - || (glES && currentMode != "glsles")) - { - Settings::Manager::setString("shader mode", "General", openGL ? (glES ? "glsles" : "glsl") : "hlsl"); - } - - mRendering.adjustCamera(Settings::Manager::getFloat("field of view", "General"), 5); - - mRendering.getWindow()->addListener(this); - mRendering.setWindowListener(this); - - mWater = 0; - - // material system - sh::OgrePlatform* platform = new sh::OgrePlatform("General", (resDir / "materials").string()); - if (!boost::filesystem::exists (cacheDir)) - boost::filesystem::create_directories (cacheDir); - platform->setCacheFolder (cacheDir.string()); - mFactory = new sh::Factory(platform); - - sh::Language lang; - std::string l = Settings::Manager::getString("shader mode", "General"); - if (l == "glsl") - lang = sh::Language_GLSL; - else if (l == "glsles") - lang = sh::Language_GLSLES; - else if (l == "hlsl") - lang = sh::Language_HLSL; - else - lang = sh::Language_CG; - mFactory->setCurrentLanguage (lang); - mFactory->setWriteSourceCache (true); - mFactory->setReadSourceCache (true); - mFactory->setReadMicrocodeCache (true); - mFactory->setWriteMicrocodeCache (true); - - mFactory->loadAllFiles(); - - TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General")); - - // Set default texture filtering options - TextureFilterOptions tfo; - 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 == "none")*/ tfo = TFO_NONE; - - MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); - MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); - - Ogre::TextureManager::getSingleton().setMemoryBudget(126*1024*1024); - Ogre::MeshManager::getSingleton().setMemoryBudget(64*1024*1024); - - Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); - - // disable unsupported effects - if (!Settings::Manager::getBool("shaders", "Objects")) - Settings::Manager::setBool("enabled", "Shadows", false); - - sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); - - sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); - sh::Factory::getInstance ().setGlobalSetting ("num_lights", Settings::Manager::getString ("num lights", "Objects")); - sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); - sh::Factory::getInstance ().setGlobalSetting ("render_refraction", "false"); - - sh::Factory::getInstance ().setSharedParameter ("waterEnabled", sh::makeProperty (new sh::FloatValue(0.0))); - sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(0))); - sh::Factory::getInstance ().setSharedParameter ("waterTimer", sh::makeProperty(new sh::FloatValue(0))); - sh::Factory::getInstance ().setSharedParameter ("windDir_windSpeed", sh::makeProperty(new sh::Vector3(0.5f, -0.8f, 0.2f))); - sh::Factory::getInstance ().setSharedParameter ("waterSunFade_sunHeight", sh::makeProperty(new sh::Vector2(1, 0.6f))); - sh::Factory::getInstance ().setGlobalSetting ("refraction", Settings::Manager::getBool("refraction", "Water") ? "true" : "false"); - sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false"); - sh::Factory::getInstance ().setSharedParameter ("vpRow2Fix", sh::makeProperty (new sh::Vector4(0,0,0,0))); - - mRootNode = mRendering.getScene()->getRootSceneNode(); - mRootNode->createChildSceneNode("player"); - - mObjects->setRootNode(mRootNode); - mActors->setRootNode(mRootNode); - - mCamera = new MWRender::Camera(mRendering.getCamera()); - - mSkyManager = new SkyManager(mRootNode, mRendering.getCamera()); - - mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - - mSun = 0; - - mLocalMap = new MWRender::LocalMap(&mRendering, this); - - mWater = new MWRender::Water(mRendering.getCamera(), this, mFallback); - - setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); -} - -RenderingManager::~RenderingManager () -{ - mRendering.getWindow()->removeListener(this); - - delete mPlayerAnimation; - delete mCamera; - delete mSkyManager; - delete mTerrain; - delete mLocalMap; - delete mOcclusionQuery; - delete mWater; - delete mActors; - delete mObjects; - delete mEffectManager; - delete mFactory; -} - -MWRender::SkyManager* RenderingManager::getSkyManager() -{ - return mSkyManager; -} - -MWRender::Objects& RenderingManager::getObjects(){ - return *mObjects; -} -MWRender::Actors& RenderingManager::getActors(){ - return *mActors; -} - -MWRender::Camera* RenderingManager::getCamera() const -{ - return mCamera; -} - -void RenderingManager::removeCell (MWWorld::CellStore *store) -{ - if (store->isExterior()) - mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); - - mLocalMap->saveFogOfWar(store); - mObjects->removeCell(store); - mActors->removeCell(store); -} - -void RenderingManager::removeWater () -{ - mWater->setActive(false); -} - -bool RenderingManager::toggleWater() -{ - return mWater->toggle(); -} - -bool RenderingManager::toggleWorld() -{ - mRenderWorld = !mRenderWorld; - - int visibilityMask = mRenderWorld ? ~int(0) : 0; - mRendering.getViewport()->setVisibilityMask(visibilityMask); - return mRenderWorld; -} - -void RenderingManager::cellAdded (MWWorld::CellStore *store) -{ - if (store->isExterior()) - mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); - - mObjects->buildStaticGeometry (*store); - sh::Factory::getInstance().unloadUnreferencedMaterials(); -} - -void RenderingManager::addObject (const MWWorld::Ptr& ptr, const std::string& model){ - const MWWorld::Class& class_ = - ptr.getClass(); - class_.insertObjectRendering(ptr, model, *this); -} - -void RenderingManager::removeObject (const MWWorld::Ptr& ptr) -{ - if (!mObjects->deleteObject (ptr)) - mActors->deleteObject (ptr); -} - -void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position) -{ - /// \todo move this to the rendering-subsystems - ptr.getRefData().getBaseNode()->setPosition(position); -} - -void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale) -{ - ptr.getRefData().getBaseNode()->setScale(scale); -} - -void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) -{ - Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - - if(ptr.getRefData().getHandle() == mCamera->getHandle() && - !mCamera->isVanityOrPreviewModeEnabled()) - mCamera->rotateCamera(-rot, false); - - Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(rot.z), Ogre::Vector3::NEGATIVE_UNIT_Z); - if(!ptr.getClass().isActor()) - newo = Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::NEGATIVE_UNIT_X) * - Ogre::Quaternion(Ogre::Radian(rot.y), Ogre::Vector3::NEGATIVE_UNIT_Y) * newo; - ptr.getRefData().getBaseNode()->setOrientation(newo); -} - -void -RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) -{ - if (!old.getRefData().getBaseNode()) - return; - Ogre::SceneNode *child = - mRendering.getScene()->getSceneNode(old.getRefData().getHandle()); - - Ogre::SceneNode *parent = child->getParentSceneNode(); - parent->removeChild(child); - - if (old.getClass().isActor()) { - mActors->updateObjectCell(old, cur); - } else { - mObjects->updateObjectCell(old, cur); - } -} - -void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) -{ - if(mPlayerAnimation) - mPlayerAnimation->updatePtr(ptr); - if(mCamera->getHandle() == ptr.getRefData().getHandle()) - attachCameraTo(ptr); -} - -void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) -{ - NpcAnimation *anim = NULL; - if(ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) - anim = mPlayerAnimation; - else if(ptr.getClass().isActor()) - anim = dynamic_cast(mActors->getAnimation(ptr)); - if(anim) - { - anim->rebuild(); - if(mCamera->getHandle() == ptr.getRefData().getHandle()) - { - attachCameraTo(ptr); - mCamera->setAnimation(anim); - } - } -} - -void RenderingManager::update (float duration, bool paused) -{ - if (MWBase::Environment::get().getStateManager()->getState()== - MWBase::StateManager::State_NoGame) - return; - - MWBase::World *world = MWBase::Environment::get().getWorld(); - - MWWorld::Ptr player = world->getPlayerPtr(); - - int blind = static_cast(player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude()); - MWBase::Environment::get().getWindowManager()->setBlindness(std::max(0, std::min(100, blind))); - setAmbientMode(); - - if (player.getClass().getNpcStats(player).isWerewolf()) - MWBase::Environment::get().getWindowManager()->setWerewolfOverlay(mCamera->isFirstPerson()); - - // player position - MWWorld::RefData &data = player.getRefData(); - Ogre::Vector3 playerPos(data.getPosition().pos); - - mCamera->setCameraDistance(); - if(!mCamera->isFirstPerson()) - { - Ogre::Vector3 orig, dest; - mCamera->getPosition(orig, dest); - - btVector3 btOrig(orig.x, orig.y, orig.z); - btVector3 btDest(dest.x, dest.y, dest.z); - std::pair test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5f, btOrig, btDest); - if(test.first) - mCamera->setCameraDistance(test.second * orig.distance(dest), false, false); - } - - // Sink the camera while sneaking - bool isSneaking = player.getClass().getCreatureStats(player).getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool isInAir = !world->isOnGround(player); - bool isSwimming = world->isSwimming(player); - - static const float i1stPersonSneakDelta = MWBase::Environment::get().getWorld()->getStore().get() - .find("i1stPersonSneakDelta")->getFloat(); - if(!paused && isSneaking && !(isSwimming || isInAir)) - mCamera->setSneakOffset(i1stPersonSneakDelta); - - mOcclusionQuery->update(duration); - - mRendering.update(duration); - - Ogre::ControllerManager::getSingleton().setTimeFactor(paused ? 0.f : 1.f); - - Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); - - applyFog(world->isUnderwater(player.getCell(), cam)); - - mCamera->update(duration, paused); - - Ogre::SceneNode *node = data.getBaseNode(); - Ogre::Quaternion orient = node->_getDerivedOrientation(); - mLocalMap->updatePlayer(playerPos, orient); - - if(paused) - return; - - mEffectManager->update(duration, mRendering.getCamera()); - - mActors->update (mRendering.getCamera()); - mPlayerAnimation->preRender(mRendering.getCamera()); - mObjects->update (duration, mRendering.getCamera()); - - mSkyManager->update(duration); - - mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); - - mWater->changeCell(player.getCell()->getCell()); - - mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam)); - - mWater->update(duration, playerPos); -} - -void RenderingManager::preRenderTargetUpdate(const RenderTargetEvent &evt) -{ - mOcclusionQuery->setActive(true); -} - -void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) -{ - // deactivate queries to make sure we aren't getting false results from several misc render targets - // (will be reactivated at the bottom of this method) - mOcclusionQuery->setActive(false); -} - -void RenderingManager::setWaterEnabled(bool enable) -{ - mWater->setActive(enable); -} - -void RenderingManager::setWaterHeight(float height) -{ - mWater->setHeight(height); -} - -void RenderingManager::skyEnable () -{ - mSkyManager->enable(); - mOcclusionQuery->setSunNode(mSkyManager->getSunNode()); -} - -void RenderingManager::skyDisable () -{ - mSkyManager->disable(); -} - -void RenderingManager::skySetHour (double hour) -{ - mSkyManager->setHour(hour); -} - - -void RenderingManager::skySetDate (int day, int month) -{ - mSkyManager->setDate(day, month); -} - -int RenderingManager::skyGetMasserPhase() const -{ - - return mSkyManager->getMasserPhase(); -} - -int RenderingManager::skyGetSecundaPhase() const -{ - return mSkyManager->getSecundaPhase(); -} - -void RenderingManager::skySetMoonColour (bool red){ - mSkyManager->setMoonColour(red); -} - -bool RenderingManager::toggleRenderMode(int mode) -{ -#if 0 - if (mode == MWBase::World::Render_CollisionDebug || mode == MWBase::World::Render_Pathgrid) - return mDebugging->toggleRenderMode(mode); - if (mode == MWBase::World::Render_Wireframe) - { - if (mRendering.getCamera()->getPolygonMode() == PM_SOLID) - { - mRendering.getCamera()->setPolygonMode(PM_WIREFRAME); - return true; - } - else - { - mRendering.getCamera()->setPolygonMode(PM_SOLID); - return false; - } - } - else //if (mode == MWBase::World::Render_BoundingBoxes) - { - bool show = !mRendering.getScene()->getShowBoundingBoxes(); - mRendering.getScene()->showBoundingBoxes(show); - return show; - } -#endif - return 0; -} - -void RenderingManager::configureFog(const MWWorld::CellStore &mCell) -{ - Ogre::ColourValue color; - color.setAsABGR (mCell.getCell()->mAmbi.mFog); - - configureFog (mCell.getCell()->mAmbi.mFogDensity, color); -} - -void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) +namespace MWRender { - mFogColour = colour; - float max = Settings::Manager::getFloat("viewing distance", "Viewing distance"); - if (density == 0) - { - mFogStart = 0; - mFogEnd = std::numeric_limits::max(); - mRendering.getCamera()->setFarClipDistance (max); - } - else + RenderingManager::RenderingManager(osgViewer::Viewer &viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem) + : mViewer(viewer) + , mRootNode(rootNode) + , mResourceSystem(resourceSystem) { - mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); - mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); - mRendering.getCamera()->setFarClipDistance (max / density); - } + osg::ref_ptr lightRoot = new SceneUtil::LightManager; + lightRoot->setStartLight(1); -} + mRootNode->addChild(lightRoot); -void RenderingManager::applyFog (bool underwater) -{ - if (!underwater) - { - mRendering.getScene()->setFog (FOG_LINEAR, mFogColour, 0, mFogStart, mFogEnd); - mRendering.getViewport()->setBackgroundColour (mFogColour); - mWater->setViewportBackground (mFogColour); - } - else - { - Ogre::ColourValue clv(0.090195f, 0.115685f, 0.12745f); - mRendering.getScene()->setFog (FOG_LINEAR, Ogre::ColourValue(clv), 0, 0, 1000); - mRendering.getViewport()->setBackgroundColour (Ogre::ColourValue(clv)); - mWater->setViewportBackground (Ogre::ColourValue(clv)); - } -} + mObjects.reset(new Objects(mResourceSystem, lightRoot)); -void RenderingManager::setAmbientMode() -{ - setAmbientColour(mAmbientColor); -} + mViewer.setLightingMode(osgViewer::View::NO_LIGHT); -void RenderingManager::configureAmbient(MWWorld::CellStore &mCell) -{ - if (mCell.getCell()->mData.mFlags & ESM::Cell::Interior) - mAmbientColor.setAsABGR (mCell.getCell()->mAmbi.mAmbient); - setAmbientMode(); + osg::ref_ptr source = new osg::LightSource; + mSunLight = new osg::Light; + source->setLight(mSunLight); + mSunLight->setDiffuse(osg::Vec4f(0,0,0,1)); + mSunLight->setAmbient(osg::Vec4f(0,0,0,1)); + mSunLight->setConstantAttenuation(1.f); + source->setStateSetModes(*rootNode->getOrCreateStateSet(), osg::StateAttribute::ON); + lightRoot->addChild(source); - // Create a "sun" that shines light downwards. It doesn't look - // completely right, but leave it for now. - if(!mSun) - { - mSun = mRendering.getScene()->createLight(); - mSun->setType(Ogre::Light::LT_DIRECTIONAL); - } - if (mCell.getCell()->mData.mFlags & ESM::Cell::Interior) - { - Ogre::ColourValue colour; - colour.setAsABGR (mCell.getCell()->mAmbi.mSunlight); - mSun->setDiffuseColour (colour); - mSun->setDirection(1,-1,-1); - sunEnable(false); - } -} + rootNode->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON); + rootNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON); + rootNode->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON); -void RenderingManager::setSunColour(const Ogre::ColourValue& colour) -{ - if (!mSunEnabled) return; - mSun->setDiffuseColour(colour); - mSun->setSpecularColour(colour); -} - -void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) -{ - mAmbientColor = colour; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - int nightEye = static_cast(player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::NightEye).getMagnitude()); - Ogre::ColourValue final = colour; - final += Ogre::ColourValue(0.7f,0.7f,0.7f,0) * std::min(1.f, (nightEye/100.f)); - - mRendering.getScene()->setAmbientLight(final); -} - -void RenderingManager::sunEnable(bool real) -{ - if (real && mSun) mSun->setVisible(true); - else - { - // Don't disable the light, as the shaders assume the first light to be directional. - mSunEnabled = true; + // for consistent benchmarks against the ogre branch. remove later + osg::CullStack::CullingMode cullingMode = viewer.getCamera()->getCullingMode(); + cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING); + viewer.getCamera()->setCullingMode( cullingMode ); } -} - -void RenderingManager::sunDisable(bool real) -{ - if (real && mSun) mSun->setVisible(false); - else - { - // Don't disable the light, as the shaders assume the first light to be directional. - mSunEnabled = false; - if (mSun) - { - mSun->setDiffuseColour(ColourValue(0,0,0)); - mSun->setSpecularColour(ColourValue(0,0,0)); - } - } -} - -void RenderingManager::setSunDirection(const Ogre::Vector3& direction, bool is_night) -{ - // direction * -1 (because 'direction' is camera to sun vector and not sun to camera), - if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.y, -direction.z)); - - mSkyManager->setSunDirection(direction, is_night); -} - -void RenderingManager::setGlare(bool glare) -{ - mSkyManager->setGlare(glare); -} -void RenderingManager::updateTerrain() -{ - if (mTerrain) + MWRender::Objects& RenderingManager::getObjects() { - // Avoid updating with dims.getCenter for each cell. Player position should be good enough - mTerrain->update(mRendering.getCamera()->getRealPosition()); - mTerrain->syncLoad(); - // need to update again so the chunks that were just loaded can be made visible - mTerrain->update(mRendering.getCamera()->getRealPosition()); + return *mObjects.get(); } -} -void RenderingManager::requestMap(MWWorld::CellStore* cell) -{ - if (cell->getCell()->isExterior()) + MWRender::Actors& RenderingManager::getActors() { - assert(mTerrain); - - Ogre::AxisAlignedBox dims = mObjects->getDimensions(cell); - Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5f, cell->getCell()->getGridY() + 0.5f); - dims.merge(mTerrain->getWorldBoundingBox(center)); - - mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z); + throw std::runtime_error("unimplemented"); } - else - mLocalMap->requestMap(cell, mObjects->getDimensions(cell)); -} - -void RenderingManager::writeFog(MWWorld::CellStore* cell) -{ - mLocalMap->saveFogOfWar(cell); -} - -void RenderingManager::disableLights(bool sun) -{ - mActors->disableLights(); - sunDisable(sun); -} - -void RenderingManager::enableLights(bool sun) -{ - mActors->enableLights(); - sunEnable(sun); -} - -void RenderingManager::notifyWorldSpaceChanged() -{ - mEffectManager->clear(); - mWater->clearRipples(); -} - -Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds) -{ - Ogre::Matrix4 mat = mRendering.getCamera()->getViewMatrix(); - - const Ogre::Vector3* corners = bounds.getAllCorners(); - - float min_x = 1.0f, max_x = 0.0f, min_y = 1.0f, max_y = 0.0f; - // expand the screen-space bounding-box so that it completely encloses - // the object's AABB - for (int i=0; i<8; i++) + Resource::ResourceSystem* RenderingManager::getResourceSystem() { - Ogre::Vector3 corner = corners[i]; - - // multiply the AABB corner vertex by the view matrix to - // get a camera-space vertex - corner = mat * corner; - - // make 2D relative/normalized coords from the view-space vertex - // by dividing out the Z (depth) factor -- this is an approximation - float x = corner.x / corner.z + 0.5f; - float y = corner.y / corner.z + 0.5f; - - if (x < min_x) - min_x = x; - - if (x > max_x) - max_x = x; - - if (y < min_y) - min_y = y; - - if (y > max_y) - max_y = y; + return mResourceSystem; } - return Vector4(min_x, min_y, max_x, max_y); -} - -void RenderingManager::processChangedSettings(const Settings::CategorySettingVector& settings) -{ - bool changeRes = false; - bool rebuild = false; // rebuild static geometry (necessary after any material changes) - for (Settings::CategorySettingVector::const_iterator it=settings.begin(); - it != settings.end(); ++it) + void RenderingManager::configureAmbient(const ESM::Cell *cell) { - if (it->second == "menu transparency" && it->first == "GUI") - { - setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); - } - else if (it->second == "viewing distance" && it->first == "Viewing distance") - { - if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior() - && MWBase::Environment::get().getWorld()->getPlayerPtr().mCell) - configureFog(*MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()); - } - else if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y" - || it->second == "fullscreen")) - changeRes = true; - else if (it->first == "Video" && it->second == "window border") - changeRes = true; - else if (it->second == "field of view" && it->first == "General") - mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); - else if (it->second == "gamma" && it->first == "General") - { - mRendering.setWindowGammaContrast(Settings::Manager::getFloat("gamma", "General"), Settings::Manager::getFloat("contrast", "General")); - } - else if ((it->second == "texture filtering" && it->first == "General") - || (it->second == "anisotropy" && it->first == "General")) - { - TextureFilterOptions tfo; - 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 == "none")*/ tfo = TFO_NONE; + osg::ref_ptr lightmodel = new osg::LightModel; + lightmodel->setAmbientIntensity(SceneUtil::colourFromRGB(cell->mAmbi.mAmbient)); + mRootNode->getOrCreateStateSet()->setAttributeAndModes(lightmodel, osg::StateAttribute::ON); - MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); - MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); - } - else if (it->second == "shader" && it->first == "Water") - { - sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); - rebuild = true; - mRendering.getViewport ()->setClearEveryFrame (true); - } - else if (it->second == "refraction" && it->first == "Water") - { - sh::Factory::getInstance ().setGlobalSetting ("refraction", Settings::Manager::getBool("refraction", "Water") ? "true" : "false"); - rebuild = true; - } - else if (it->second == "shaders" && it->first == "Objects") - { - sh::Factory::getInstance ().setShadersEnabled (Settings::Manager::getBool("shaders", "Objects")); - rebuild = true; - } - else if (it->second == "shader mode" && it->first == "General") - { - sh::Language lang; - std::string l = Settings::Manager::getString("shader mode", "General"); - if (l == "glsl") - lang = sh::Language_GLSL; - else if (l == "hlsl") - lang = sh::Language_HLSL; - else - lang = sh::Language_CG; - sh::Factory::getInstance ().setCurrentLanguage (lang); - rebuild = true; - } + mSunLight->setDiffuse(SceneUtil::colourFromRGB(cell->mAmbi.mSunlight)); + mSunLight->setDirection(osg::Vec3f(1.f,-1.f,-1.f)); } - if (changeRes) + osg::Vec3f RenderingManager::getEyePos() { - unsigned int x = Settings::Manager::getInt("resolution x", "Video"); - unsigned int y = Settings::Manager::getInt("resolution y", "Video"); - bool fullscreen = Settings::Manager::getBool("fullscreen", "Video"); - bool windowBorder = Settings::Manager::getBool("window border", "Video"); - - SDL_Window* window = mRendering.getSDLWindow(); - - SDL_SetWindowFullscreen(window, 0); - - if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) - SDL_RestoreWindow(window); - - if (fullscreen) - { - SDL_DisplayMode mode; - SDL_GetWindowDisplayMode(window, &mode); - mode.w = x; - mode.h = y; - SDL_SetWindowDisplayMode(window, &mode); - SDL_SetWindowFullscreen(window, fullscreen); - } - else - { - SDL_SetWindowSize(window, x, y); - SDL_SetWindowBordered(window, windowBorder ? SDL_TRUE : SDL_FALSE); - } + osg::Vec3d eye; + //mViewer.getCamera()->getViewMatrixAsLookAt(eye, center, up); + eye = mViewer.getCameraManipulator()->getMatrix().getTrans(); + return eye; } - mWater->processChangedSettings(settings); - - if (rebuild) + void RenderingManager::removeCell(const MWWorld::CellStore *store) { - mObjects->rebuildStaticGeometry(); - if (mTerrain) - mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), - Settings::Manager::getBool("split", "Shadows")); + mObjects->removeCell(store); } -} - -void RenderingManager::setMenuTransparency(float val) -{ - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().getByName("transparent.png"); std::vector buffer; - buffer.resize(1); - buffer[0] = (int(255*val) << 24) | (255 << 16) | (255 << 8) | 255; - memcpy(tex->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], 1*4); - tex->getBuffer()->unlock(); -} -void RenderingManager::windowResized(int x, int y) -{ - Settings::Manager::setInt("resolution x", "Video", x); - Settings::Manager::setInt("resolution y", "Video", y); - mRendering.adjustViewport(); - - MWBase::Environment::get().getWindowManager()->windowResized(x,y); } - -void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned int &batches) -{ - batches = mRendering.getWindow()->getBatchCount(); - triangles = mRendering.getWindow()->getTriangleCount(); -} - -void RenderingManager::setupPlayer(const MWWorld::Ptr &ptr) -{ - ptr.getRefData().setBaseNode(mRendering.getScene()->getSceneNode("player")); - attachCameraTo(ptr); -} - -void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) -{ - Ogre::SceneNode* cameraNode = mCamera->attachTo(ptr); - mSkyManager->attachToNode(cameraNode); -} - -void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) -{ - if(!mPlayerAnimation) - { - mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), RV_Actors); - } - else - { - // Reconstruct the NpcAnimation in-place - mPlayerAnimation->~NpcAnimation(); - new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(), RV_Actors); - } - - mCamera->setAnimation(mPlayerAnimation); - mWater->removeEmitter(ptr); - mWater->addEmitter(ptr); - // apply race height - MWBase::Environment::get().getWorld()->scaleObject(ptr, 1.f); -} - -bool RenderingManager::vanityRotateCamera(const float *rot) -{ - if(!mCamera->isVanityOrPreviewModeEnabled()) - return false; - - Ogre::Vector3 vRot(rot); - mCamera->rotateCamera(vRot, true); - return true; -} - -void RenderingManager::setCameraDistance(float dist, bool adjust, bool override) -{ - if(!mCamera->isVanityOrPreviewModeEnabled() && !mCamera->isFirstPerson()) - { - if(mCamera->isNearest() && dist > 0.f) - mCamera->toggleViewMode(); - else - mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override); - } - else if(mCamera->isFirstPerson() && dist < 0.f) - { - mCamera->toggleViewMode(); - mCamera->setCameraDistance(0.f, false, override); - } -} - -void RenderingManager::worldToInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y) -{ - return mLocalMap->worldToInteriorMapPosition (position, nX, nY, x, y); -} - -Ogre::Vector2 RenderingManager::interiorMapToWorldPosition(float nX, float nY, int x, int y) -{ - return mLocalMap->interiorMapToWorldPosition(nX, nY, x, y); -} - -bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, bool interior) -{ - return mLocalMap->isPositionExplored(nX, nY, x, y, interior); -} - -Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) -{ - Animation *anim = mActors->getAnimation(ptr); - - if(!anim && ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) - anim = mPlayerAnimation; - - if (!anim) - anim = mObjects->getAnimation(ptr); - - return anim; -} - -void RenderingManager::screenshot(Image &image, int w, int h) -{ - // Create a temporary render target. We do not use the RenderWindow since we want a specific size. - // Also, the GUI should not be visible (and it is only rendered on the RenderWindow's primary viewport) - const std::string tempName = "@temp"; - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(tempName, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, w, h, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET); - - float oldAspect = mRendering.getCamera()->getAspectRatio(); - - mRendering.getCamera()->setAspectRatio(w / static_cast(h)); - - Ogre::RenderTarget* rt = texture->getBuffer()->getRenderTarget(); - Ogre::Viewport* vp = rt->addViewport(mRendering.getCamera()); - vp->setBackgroundColour(mRendering.getViewport()->getBackgroundColour()); - vp->setOverlaysEnabled(false); - vp->setVisibilityMask(mRendering.getViewport()->getVisibilityMask()); - rt->update(); - - Ogre::PixelFormat pf = rt->suggestPixelFormat(); - - image.loadDynamicImage( - OGRE_ALLOC_T(Ogre::uchar, w * h * Ogre::PixelUtil::getNumElemBytes(pf), Ogre::MEMCATEGORY_GENERAL), - w, h, 1, pf, true // autoDelete=true, frees memory we allocate - ); - rt->copyContentsToMemory(image.getPixelBox()); // getPixelBox returns a box sharing the same memory as the image - - Ogre::TextureManager::getSingleton().remove(tempName); - mRendering.getCamera()->setAspectRatio(oldAspect); -} - -void RenderingManager::addWaterRippleEmitter (const MWWorld::Ptr& ptr, float scale, float force) -{ - mWater->addEmitter (ptr, scale, force); -} - -void RenderingManager::removeWaterRippleEmitter (const MWWorld::Ptr& ptr) -{ - mWater->removeEmitter (ptr); -} - -void RenderingManager::updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr) -{ - mWater->updateEmitterPtr(old, ptr); -} - -void RenderingManager::frameStarted(float dt, bool paused) -{ - if (mTerrain) - mTerrain->update(mRendering.getCamera()->getRealPosition()); - - if (!paused) - mWater->frameStarted(dt); -} - -void RenderingManager::resetCamera() -{ - mCamera->reset(); -} - -float RenderingManager::getTerrainHeightAt(Ogre::Vector3 worldPos) -{ - if (!mTerrain || !mTerrain->getVisible()) - return -std::numeric_limits::max(); - return mTerrain->getHeightAt(worldPos); -} - -void RenderingManager::enableTerrain(bool enable) -{ - if (enable) - { - if (!mTerrain) - { - if (Settings::Manager::getBool("distant land", "Terrain")) - mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(true), RV_Terrain, - Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); - else - mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(false), RV_Terrain, - Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY); - mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), - Settings::Manager::getBool("split", "Shadows")); - mTerrain->update(mRendering.getCamera()->getRealPosition()); - } - mTerrain->setVisible(true); - } - else if (mTerrain) - mTerrain->setVisible(false); -} - -float RenderingManager::getCameraDistance() const -{ - return mCamera->getCameraDistance(); -} - -void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale) -{ - mEffectManager->addEffect(model, texture, worldPosition, scale); -} - -void RenderingManager::clear() -{ - mLocalMap->clear(); - notifyWorldSpaceChanged(); -} - -} // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 5bfd8ae77..f0c5040f2 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -1,270 +1,61 @@ -#ifndef GAME_RENDERING_MANAGER_H -#define GAME_RENDERING_MANAGER_H +#ifndef OPENMW_MWRENDER_RENDERINGMANAGER_H +#define OPENMW_MWRENDER_RENDERINGMANAGER_H -#include "sky.hpp" -#include "debugging.hpp" +#include +#include -#include - -#include - -#include +#include "objects.hpp" #include "renderinginterface.hpp" -#include "objects.hpp" -#include "actors.hpp" -#include "camera.hpp" -#include "occlusionquery.hpp" - -namespace Ogre +namespace osg { - class SceneNode; + class Group; } -namespace MWWorld +namespace Resource { - class Ptr; - class CellStore; + class ResourceSystem; } -namespace sh +namespace osgViewer { - class Factory; + class Viewer; } -namespace Terrain +namespace ESM { - class World; + struct Cell; } namespace MWRender { - class Shadows; - class LocalMap; - class Water; - class GlobalMap; - class Animation; - class EffectManager; - -class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener -{ -private: - virtual MWRender::Objects& getObjects(); - virtual MWRender::Actors& getActors(); - -public: - RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, - const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine, - MWWorld::Fallback* fallback); - virtual ~RenderingManager(); - - void togglePOV() - { mCamera->toggleViewMode(); } - void togglePreviewMode(bool enable) - { mCamera->togglePreviewMode(enable); } - - bool toggleVanityMode(bool enable) - { return mCamera->toggleVanityMode(enable); } - - void allowVanityMode(bool allow) - { mCamera->allowVanityMode(allow); } - - void togglePlayerLooking(bool enable) - { mCamera->togglePlayerLooking(enable); } - - void changeVanityModeScale(float factor) + class RenderingManager : public MWRender::RenderingInterface { - if(mCamera->isVanityOrPreviewModeEnabled()) - mCamera->setCameraDistance(-factor/120.f*10, true, true); - } - - void resetCamera(); - - bool vanityRotateCamera(const float *rot); - void setCameraDistance(float dist, bool adjust = false, bool override = true); - float getCameraDistance() const; - - void setupPlayer(const MWWorld::Ptr &ptr); - void renderPlayer(const MWWorld::Ptr &ptr); - - SkyManager* getSkyManager(); - - MWRender::Camera* getCamera() const; - - bool toggleRenderMode(int mode); - - void removeCell (MWWorld::CellStore *store); - - /// \todo this function should be removed later. Instead the rendering subsystems should track - /// when rebatching is needed and update automatically at the end of each frame. - void cellAdded (MWWorld::CellStore *store); - - /// Clear all savegame-specific data (i.e. fog of war textures) - void clear(); - - void enableTerrain(bool enable); - - void removeWater(); - - /// Write current fog of war for this cell to the CellStore - void writeFog (MWWorld::CellStore* store); - - void addObject (const MWWorld::Ptr& ptr, const std::string& model); - void removeObject (const MWWorld::Ptr& ptr); - - void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); - void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); - - /// Updates an object's rotation - void rotateObject (const MWWorld::Ptr& ptr); - - void setWaterHeight(float height); - void setWaterEnabled(bool enabled); - bool toggleWater(); - bool toggleWorld(); - - /// Updates object rendering after cell change - /// \param old Object reference in previous cell - /// \param cur Object reference in new cell - void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); - - /// Specifies an updated Ptr object for the player (used on cell change). - void updatePlayerPtr(const MWWorld::Ptr &ptr); - - /// Currently for NPCs only. Rebuilds the NPC, updating their root model, animation sources, - /// and equipment. - void rebuildPtr(const MWWorld::Ptr &ptr); - - void update (float duration, bool paused); - - void setAmbientColour(const Ogre::ColourValue& colour); - void setSunColour(const Ogre::ColourValue& colour); - void setSunDirection(const Ogre::Vector3& direction, bool is_night); - void sunEnable(bool real); ///< @param real whether or not to really disable the sunlight (otherwise just set diffuse to 0) - void sunDisable(bool real); - - void disableLights(bool sun); ///< @param sun whether or not to really disable the sunlight (otherwise just set diffuse to 0) - void enableLights(bool sun); - - - void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); - void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt); - - bool occlusionQuerySupported() { return mOcclusionQuery->supported(); } - OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; } - - float getTerrainHeightAt (Ogre::Vector3 worldPos); - - void notifyWorldSpaceChanged(); - - void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches); - - void setGlare(bool glare); - void skyEnable (); - void skyDisable (); - void skySetHour (double hour); - void skySetDate (int day, int month); - int skyGetMasserPhase() const; - int skyGetSecundaPhase() const; - void skySetMoonColour (bool red); - void configureAmbient(MWWorld::CellStore &mCell); - - void addWaterRippleEmitter (const MWWorld::Ptr& ptr, float scale = 1.f, float force = 1.f); - void removeWaterRippleEmitter (const MWWorld::Ptr& ptr); - void updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); - - void updateTerrain (); - ///< update the terrain according to the player position. Usually done automatically, but should be done manually - /// before calling requestMap - - void requestMap (MWWorld::CellStore* cell); - ///< request the local map for a cell - - /// configure fog according to cell - void configureFog(const MWWorld::CellStore &mCell); - - /// configure fog manually - void configureFog(const float density, const Ogre::ColourValue& colour); - - Ogre::Vector4 boundingBoxToScreen(Ogre::AxisAlignedBox bounds); - ///< transform the specified bounding box (in world coordinates) into screen coordinates. - /// @return packed vector4 (min_x, min_y, max_x, max_y) - - void processChangedSettings(const Settings::CategorySettingVector& settings); - - Ogre::Viewport* getViewport() { return mRendering.getViewport(); } - - void worldToInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); - ///< see MWRender::LocalMap::worldToInteriorMapPosition - - Ogre::Vector2 interiorMapToWorldPosition (float nX, float nY, int x, int y); - ///< see MWRender::LocalMap::interiorMapToWorldPosition - - bool isPositionExplored (float nX, float nY, int x, int y, bool interior); - ///< see MWRender::LocalMap::isPositionExplored - - Animation* getAnimation(const MWWorld::Ptr &ptr); - - void frameStarted(float dt, bool paused); - void screenshot(Ogre::Image& image, int w, int h); - - void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition, float scale=1.f); - -protected: - virtual void windowResized(int x, int y); - -private: - sh::Factory* mFactory; - - void setAmbientMode(); - void applyFog(bool underwater); - - void attachCameraTo(const MWWorld::Ptr& ptr); - - void setMenuTransparency(float val); - - bool mSunEnabled; - - MWWorld::Fallback* mFallback; - - SkyManager* mSkyManager; - - OcclusionQuery* mOcclusionQuery; - - Terrain::World* mTerrain; - - MWRender::Water *mWater; - - GlobalMap* mGlobalMap; - - OEngine::Render::OgreRenderer &mRendering; - - MWRender::Objects* mObjects; - MWRender::Actors* mActors; - - MWRender::EffectManager* mEffectManager; + public: + RenderingManager(osgViewer::Viewer& viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem); - MWRender::NpcAnimation *mPlayerAnimation; + MWRender::Objects& getObjects(); + MWRender::Actors& getActors(); - Ogre::ColourValue mAmbientColor; - Ogre::Light* mSun; + Resource::ResourceSystem* getResourceSystem(); - Ogre::SceneNode *mRootNode; + void configureAmbient(const ESM::Cell* cell); - Ogre::ColourValue mFogColour; - float mFogStart; - float mFogEnd; + void removeCell(const MWWorld::CellStore* store); - OEngine::Physic::PhysicEngine* mPhysicsEngine; + osg::Vec3f getEyePos(); - MWRender::Camera *mCamera; + private: + osgViewer::Viewer& mViewer; + osg::ref_ptr mRootNode; + Resource::ResourceSystem* mResourceSystem; - MWRender::LocalMap* mLocalMap; + osg::ref_ptr mSunLight; - bool mRenderWorld; -}; + std::auto_ptr mObjects; + }; } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index f87983ce8..e71a4a34b 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -686,10 +686,10 @@ namespace MWScript else throw std::runtime_error ("invalid movement axis: " + axis); - if (!ptr.getRefData().getBaseNode()) + if (!ptr.getRefData().getBaseNodeOld()) return; - Ogre::Vector3 diff = ptr.getRefData().getBaseNode()->getOrientation() * posChange; + Ogre::Vector3 diff = ptr.getRefData().getBaseNodeOld()->getOrientation() * posChange; Ogre::Vector3 worldPos(ptr.getRefData().getPosition().pos); worldPos += diff; MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z); diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp index 5115fa02d..cf6f7bf56 100644 --- a/apps/openmw/mwworld/cellfunctors.hpp +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -20,11 +20,11 @@ namespace MWWorld bool operator() (MWWorld::Ptr ptr) { - Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNodeOld(); if (handle) mHandles.push_back (handle); - ptr.getRefData().setBaseNode(0); + ptr.getRefData().setBaseNodeOld(0); return true; } }; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index d4aadc6c7..371543f2e 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -242,7 +242,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr MWWorld::Ptr item = *it; // we may have copied an item from the world, so reset a few things first - item.getRefData().setBaseNode(NULL); // Especially important, otherwise scripts on the item could think that it's actually in a cell + item.getRefData().setBaseNodeOld(NULL); // Especially important, otherwise scripts on the item could think that it's actually in a cell ESM::Position pos; pos.rot[0] = 0; pos.rot[1] = 0; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index a59efecfe..b8e4a06a8 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -653,7 +653,7 @@ namespace MWWorld std::vector PhysicsSystem::getCollisions(const Ptr &ptr, int collisionGroup, int collisionMask) { - return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName(), collisionGroup, collisionMask); + return mEngine->getCollisions(ptr.getRefData().getBaseNodeOld()->getName(), collisionGroup, collisionMask); } Ogre::Vector3 PhysicsSystem::traceDown(const MWWorld::Ptr &ptr, float maxHeight) @@ -675,7 +675,7 @@ namespace MWWorld void PhysicsSystem::addObject (const Ptr& ptr, const std::string& mesh, bool placeable) { - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* node = ptr.getRefData().getBaseNodeOld(); handleToMesh[node->getName()] = mesh; mEngine->createAndAdjustRigidBody( mesh, node->getName(), ptr.getCellRef().getScale(), node->getPosition(), node->getOrientation(), 0, 0, false, placeable); @@ -685,7 +685,7 @@ namespace MWWorld void PhysicsSystem::addActor (const Ptr& ptr, const std::string& mesh) { - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* node = ptr.getRefData().getBaseNodeOld(); //TODO:optimize this. Searching the std::map isn't very efficient i think. mEngine->addCharacter(node->getName(), mesh, node->getPosition(), node->getScale().x, node->getOrientation()); } @@ -699,7 +699,7 @@ namespace MWWorld void PhysicsSystem::moveObject (const Ptr& ptr) { - Ogre::SceneNode *node = ptr.getRefData().getBaseNode(); + Ogre::SceneNode *node = ptr.getRefData().getBaseNodeOld(); const std::string &handle = node->getName(); const Ogre::Vector3 &position = node->getPosition(); @@ -722,7 +722,7 @@ namespace MWWorld void PhysicsSystem::rotateObject (const Ptr& ptr) { - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* node = ptr.getRefData().getBaseNodeOld(); const std::string &handle = node->getName(); const Ogre::Quaternion &rotation = node->getOrientation(); @@ -751,7 +751,7 @@ namespace MWWorld void PhysicsSystem::scaleObject (const Ptr& ptr) { - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* node = ptr.getRefData().getBaseNodeOld(); const std::string &handle = node->getName(); if(handleToMesh.find(handle) != handleToMesh.end()) { diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 14a315a81..7e83fda5e 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -134,14 +134,23 @@ namespace MWWorld return mBaseNode->getName(); } - Ogre::SceneNode* RefData::getBaseNode() + Ogre::SceneNode* RefData::getBaseNodeOld() { return mBaseNode; } - void RefData::setBaseNode(Ogre::SceneNode* base) + void RefData::setBaseNodeOld(Ogre::SceneNode* base) { - mBaseNode = base; + } + + void RefData::setBaseNode(osg::PositionAttitudeTransform *base) + { + mBase = base; + } + + osg::PositionAttitudeTransform* RefData::getBaseNode() + { + return mBase; } int RefData::getCount() const diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index e90b44f9c..2099b859f 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -10,6 +10,11 @@ namespace Ogre class SceneNode; } +namespace osg +{ + class PositionAttitudeTransform; +} + namespace ESM { class Script; @@ -28,7 +33,7 @@ namespace MWWorld class RefData { Ogre::SceneNode* mBaseNode; - + osg::PositionAttitudeTransform* mBase; MWScript::Locals mLocals; // if we find the overhead of heaving a locals // object in the refdata of refs without a script, @@ -78,10 +83,18 @@ namespace MWWorld const std::string &getHandle(); /// Return OGRE base node (can be a null pointer). - Ogre::SceneNode* getBaseNode(); + /// obsolete + Ogre::SceneNode* getBaseNodeOld(); + + /// Return base node (can be a null pointer). + osg::PositionAttitudeTransform* getBaseNode(); /// Set OGRE base node (can be a null pointer). - void setBaseNode (Ogre::SceneNode* base); + /// obsolete + void setBaseNodeOld (Ogre::SceneNode* base); + + /// Set base node (can be a null pointer). + void setBaseNode (osg::PositionAttitudeTransform* base); int getCount() const; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 6d7408607..1084ae03a 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -6,12 +6,16 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwrender/renderingmanager.hpp" + //#include "physicssystem.hpp" #include "player.hpp" #include "localscripts.hpp" @@ -20,19 +24,20 @@ #include "cellfunctors.hpp" #include "cellstore.hpp" +#include + namespace { -#if 0 - void addObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, + void addObject(const MWWorld::Ptr& ptr, //MWWorld::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { - std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); + std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr), rendering.getResourceSystem()->getVFS()); std::string id = ptr.getClass().getId(ptr); if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player - rendering.addObject(ptr, model); - ptr.getClass().insertObject (ptr, model, physics); + ptr.getClass().insertObjectRendering(ptr, model, rendering); + //ptr.getClass().insertObject (ptr, model, physics); } void updateObjectLocalRotation (const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, @@ -40,22 +45,21 @@ namespace { if (ptr.getRefData().getBaseNode() != NULL) { - Ogre::Quaternion worldRotQuat(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z); + osg::Quat worldRotQuat(ptr.getRefData().getPosition().rot[2], osg::Vec3(0,0,-1)); if (!ptr.getClass().isActor()) - worldRotQuat = Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* - Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* worldRotQuat; + worldRotQuat = worldRotQuat * osg::Quat(ptr.getRefData().getPosition().rot[1], osg::Vec3(0,-1,0)) * + osg::Quat(ptr.getRefData().getPosition().rot[0], osg::Vec3(-1,0,0)); float x = ptr.getRefData().getLocalRotation().rot[0]; float y = ptr.getRefData().getLocalRotation().rot[1]; float z = ptr.getRefData().getLocalRotation().rot[2]; - Ogre::Quaternion rot(Ogre::Radian(z), Ogre::Vector3::NEGATIVE_UNIT_Z); + osg::Quat rot(z, osg::Vec3(0,0,-1)); if (!ptr.getClass().isActor()) - rot = Ogre::Quaternion(Ogre::Radian(x), Ogre::Vector3::NEGATIVE_UNIT_X)* - Ogre::Quaternion(Ogre::Radian(y), Ogre::Vector3::NEGATIVE_UNIT_Y)*rot; + rot = rot * osg::Quat(y, osg::Vec3(0,-1,0)) * osg::Quat(x, osg::Vec3(-1,0,0)); - ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); - physics.rotateObject(ptr); + ptr.getRefData().getBaseNode()->setAttitude(rot * worldRotQuat); + //physics.rotateObject(ptr); } } @@ -64,20 +68,21 @@ namespace MWWorld::CellStore& mCell; bool mRescale; Loading::Listener& mLoadingListener; - MWWorld::PhysicsSystem& mPhysics; + //MWWorld::PhysicsSystem& mPhysics; MWRender::RenderingManager& mRendering; InsertFunctor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, - MWWorld::PhysicsSystem& physics, MWRender::RenderingManager& rendering); + /*MWWorld::PhysicsSystem& physics, */MWRender::RenderingManager& rendering); bool operator() (const MWWorld::Ptr& ptr); }; InsertFunctor::InsertFunctor (MWWorld::CellStore& cell, bool rescale, - Loading::Listener& loadingListener, MWWorld::PhysicsSystem& physics, + Loading::Listener& loadingListener, /*MWWorld::PhysicsSystem& physics,*/ MWRender::RenderingManager& rendering) : mCell (cell), mRescale (rescale), mLoadingListener (loadingListener), - mPhysics (physics), mRendering (rendering) + //mPhysics (physics), + mRendering (rendering) {} bool InsertFunctor::operator() (const MWWorld::Ptr& ptr) @@ -94,13 +99,13 @@ namespace { try { - addObject(ptr, mPhysics, mRendering); - updateObjectLocalRotation(ptr, mPhysics, mRendering); + addObject(ptr, /*mPhysics, */mRendering); + //updateObjectLocalRotation(ptr, mPhysics, mRendering); if (ptr.getRefData().getBaseNode()) { float scale = ptr.getCellRef().getScale(); ptr.getClass().adjustScale(ptr, scale); - mRendering.scaleObject(ptr, Ogre::Vector3(scale)); + //mRendering.scaleObject(ptr, Ogre::Vector3(scale)); } ptr.getClass().adjustPosition (ptr, false); } @@ -115,7 +120,6 @@ namespace return true; } -#endif } @@ -129,7 +133,7 @@ namespace MWWorld void Scene::updateObjectRotation (const Ptr& ptr) { - if(ptr.getRefData().getBaseNode() != 0) + if(ptr.getRefData().getBaseNodeOld() != 0) { //mRendering.rotateObject(ptr); //mPhysics->rotateObject(ptr); @@ -190,23 +194,24 @@ namespace MWWorld for (std::vector::const_iterator iter2 (functor.mHandles.begin()); iter2!=functor.mHandles.end(); ++iter2) { - Ogre::SceneNode* node = *iter2; + //Ogre::SceneNode* node = *iter2; //mPhysics->removeObject (node->getName()); } } if ((*iter)->getCell()->isExterior()) { - ESM::Land* land = + /*ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search( (*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY() ); - //if (land && land->mDataTypes&ESM::Land::DATA_VHGT) - //mPhysics->removeHeightField ((*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY()); + if (land && land->mDataTypes&ESM::Land::DATA_VHGT) + mPhysics->removeHeightField ((*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY()); + */ } - //mRendering.removeCell(*iter); + mRendering.removeCell(*iter); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); @@ -224,8 +229,8 @@ namespace MWWorld { std::cout << "loading cell " << cell->getCell()->getDescription() << std::endl; - float verts = ESM::Land::LAND_SIZE; - float worldsize = ESM::Land::REAL_SIZE; + //float verts = ESM::Land::LAND_SIZE; + //float worldsize = ESM::Land::REAL_SIZE; #if 0 // Load terrain physics first... @@ -271,9 +276,9 @@ namespace MWWorld } else mPhysics->disableWater(); - - mRendering.configureAmbient(*cell); #endif + if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) + mRendering.configureAmbient(cell->getCell()); } // register local scripts @@ -290,7 +295,7 @@ namespace MWWorld mCurrentCell = NULL; } - void Scene::playerMoved(const Ogre::Vector3 &pos) + void Scene::playerMoved(const osg::Vec3f &pos) { if (!mCurrentCell || !mCurrentCell->isExterior()) return; @@ -301,12 +306,14 @@ namespace MWWorld float centerX, centerY; MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); const float maxDistance = 8192/2 + 1024; // 1/2 cell size + threshold - float distance = std::max(std::abs(centerX-pos.x), std::abs(centerY-pos.y)); + float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y())); if (distance > maxDistance) { int newX, newY; - MWBase::Environment::get().getWorld()->positionToIndex(pos.x, pos.y, newX, newY); + MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newX, newY); + osg::Timer timer; changeCellGrid(newX, newY); + std::cout << "changeCellGrid took " << timer.time_m() << std::endl; //mRendering.updateTerrain(); } } @@ -435,7 +442,7 @@ namespace MWWorld //We need the ogre renderer and a scene node. Scene::Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics) - : mCurrentCell (0), mCellChanged (false), /*mPhysics(physics), mRendering(rendering),*/ mNeedMapUpdate(false) + : mCurrentCell (0), mCellChanged (false), /*mPhysics(physics),*/ mRendering(rendering), mNeedMapUpdate(false) { } @@ -545,15 +552,15 @@ namespace MWWorld void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { - //InsertFunctor functor (cell, rescale, *loadingListener, *mPhysics, mRendering); - //cell.forEach (functor); + InsertFunctor functor (cell, rescale, *loadingListener, /* *mPhysics, */mRendering); + cell.forEach (functor); } void Scene::addObjectToScene (const Ptr& ptr) { try { - //addObject(ptr, *mPhysics, mRendering); + addObject(ptr, /* *mPhysics, */mRendering); //MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true); //MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 341a89a78..4e8f6a11b 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -8,6 +8,11 @@ #include +namespace osg +{ + class Vec3f; +} + namespace Ogre { class Vector3; @@ -59,7 +64,7 @@ namespace MWWorld CellStoreCollection mActiveCells; bool mCellChanged; //PhysicsSystem *mPhysics; - //MWRender::RenderingManager& mRendering; + MWRender::RenderingManager& mRendering; bool mNeedMapUpdate; @@ -80,7 +85,7 @@ namespace MWWorld void loadCell (CellStore *cell, Loading::Listener* loadingListener); - void playerMoved (const Ogre::Vector3& pos); + void playerMoved (const osg::Vec3f& pos); void changePlayerCell (CellStore* newCell, const ESM::Position& position, bool adjustPlayerPos); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8b2035fe9..994a0c9dd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -11,6 +11,8 @@ #include "../mwscript/globalscripts.hpp" #include +#include + #include #include @@ -37,6 +39,7 @@ #include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors //#include "../mwrender/animation.hpp" +#include "../mwrender/renderingmanager.hpp" #include "../mwscript/interpretercontext.hpp" @@ -143,9 +146,11 @@ namespace MWWorld } World::World ( + osgViewer::Viewer& viewer, + osg::ref_ptr rootNode, + Resource::ResourceSystem* resourceSystem, const Files::Collections& fileCollections, const std::vector& contentFiles, - const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int activationDistanceOverride, const std::string& startCell, const std::string& startupScript) : mPlayer (0), mLocalScripts (mStore), @@ -162,7 +167,7 @@ namespace MWWorld #if 0 mProjectileManager.reset(new ProjectileManager(renderer.getScene(), *mPhysEngine)); #endif - //mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback); + mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem); //mPhysEngine->setSceneManager(renderer.getScene()); @@ -476,7 +481,7 @@ namespace MWWorld #endif //delete mWeatherManager; delete mWorldScene; - //delete mRendering; + delete mRendering; //delete mPhysics; delete mPlayer; @@ -1184,7 +1189,7 @@ namespace MWWorld MWWorld::Ptr newPtr = ptr.getClass() .copyToCell(ptr, *newCell); - newPtr.getRefData().setBaseNode(0); + newPtr.getRefData().setBaseNodeOld(0); } else if (!currCellActive && !newCellActive) ptr.getClass().copyToCell(ptr, *newCell); @@ -1194,7 +1199,7 @@ namespace MWWorld ptr.getClass().copyToCell(ptr, *newCell, pos); //mRendering->updateObjectCell(ptr, copy); - ptr.getRefData().setBaseNode(NULL); + ptr.getRefData().setBaseNodeOld(NULL); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, copy); MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); @@ -1213,14 +1218,14 @@ namespace MWWorld ptr.getRefData().setCount(0); } } - if (haveToMove && ptr.getRefData().getBaseNode()) + if (haveToMove && ptr.getRefData().getBaseNodeOld()) { //mRendering->moveObject(ptr, vec); //mPhysics->moveObject (ptr); } if (isPlayer) { - mWorldScene->playerMoved (vec); + //mWorldScene->playerMoved (vec); } } @@ -1252,7 +1257,7 @@ namespace MWWorld ptr.getCellRef().setScale(scale); ptr.getClass().adjustScale(ptr,scale); - if(ptr.getRefData().getBaseNode() == 0) + if(ptr.getRefData().getBaseNodeOld() == 0) return; //mRendering->scaleObject(ptr, Vector3(scale,scale,scale)); //mPhysics->scaleObject(ptr); @@ -1303,7 +1308,7 @@ namespace MWWorld ptr.getRefData().setPosition(pos); - if(ptr.getRefData().getBaseNode() == 0) + if(ptr.getRefData().getBaseNodeOld() == 0) return; if (ptr.getClass().isActor()) @@ -1325,7 +1330,7 @@ namespace MWWorld ptr.getRefData().setLocalRotation(rot); - if (ptr.getRefData().getBaseNode() != 0) + if (ptr.getRefData().getBaseNodeOld() != 0) { mWorldScene->updateObjectLocalRotation(ptr); } @@ -1335,7 +1340,7 @@ namespace MWWorld { ESM::Position pos (ptr.getRefData().getPosition()); - if(!ptr.getRefData().getBaseNode()) + if(!ptr.getRefData().getBaseNodeOld()) { // will be adjusted when Ptr's cell becomes active return; @@ -1352,9 +1357,9 @@ namespace MWWorld if (force || !isFlying(ptr)) { - Ogre::Vector3 traced;// = mPhysics->traceDown(ptr, 500); - if (traced.z < pos.pos[2]) - pos.pos[2] = traced.z; + //Ogre::Vector3 traced = mPhysics->traceDown(ptr, 500); + //if (traced.z < pos.pos[2]) + // pos.pos[2] = traced.z; } moveObject(ptr, ptr.getCell(), pos.pos[0], pos.pos[1], pos.pos[2]); @@ -1599,13 +1604,14 @@ namespace MWWorld void World::update (float duration, bool paused) { + /* if (mGoToJail && !paused) goToJail(); updateWeather(duration, paused); - //if (!paused) - // doPhysics (duration); + if (!paused) + doPhysics (duration); mWorldScene->update (duration, paused); @@ -1620,11 +1626,14 @@ namespace MWWorld ESM::Position pos = mPlayer->getPlayer().getRefData().getPosition(); mPlayer->setLastKnownExteriorPosition(Ogre::Vector3(pos.pos)); } + */ + + //mWorldScene->playerMoved(mRendering->getEyePos()); } void World::updateSoundListener() { - Ogre::Vector3 playerPos = mPlayer->getPlayer().getRefData().getBaseNode()->getPosition(); + Ogre::Vector3 playerPos = mPlayer->getPlayer().getRefData().getBaseNodeOld()->getPosition(); const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(getPlayerPtr().getRefData().getHandle()); if(actor) playerPos.z += 1.85f * actor->getHalfExtents().z; Ogre::Quaternion playerOrient = Ogre::Quaternion(Ogre::Radian(getPlayerPtr().getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * @@ -1644,7 +1653,7 @@ namespace MWWorld // retrieve object dimensions so we know where to place the floating label if (!object.isEmpty ()) { - Ogre::SceneNode* node = object.getRefData().getBaseNode(); + Ogre::SceneNode* node = object.getRefData().getBaseNodeOld(); Ogre::AxisAlignedBox bounds = node->_getWorldAABB(); if (bounds.isFinite()) { @@ -2341,7 +2350,7 @@ namespace MWWorld bool operator() (Ptr ptr) { - Ogre::SceneNode* handle = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* handle = ptr.getRefData().getBaseNodeOld(); if (handle) mHandles.push_back(handle->getName()); return true; @@ -2366,7 +2375,7 @@ namespace MWWorld { if (!targetActor.getRefData().isEnabled() || !actor.getRefData().isEnabled()) return false; // cannot get LOS unless both NPC's are enabled - if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode()) + if (!targetActor.getRefData().getBaseNodeOld() || !targetActor.getRefData().getBaseNodeOld()) return false; // not in active cell OEngine::Physic::PhysicActor* actor1 = mPhysEngine->getCharacter(actor.getRefData().getHandle()); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 07698aae5..f77bb4760 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -5,6 +5,8 @@ #include +#include + #include "ptr.hpp" #include "scene.hpp" #include "esmstore.hpp" @@ -21,6 +23,21 @@ #include +namespace osg +{ + class Group; +} + +namespace osgViewer +{ + class Viewer; +} + +namespace Resource +{ + class ResourceSystem; +} + namespace Ogre { class Vector3; @@ -155,9 +172,11 @@ namespace MWWorld public: World ( + osgViewer::Viewer& viewer, + osg::ref_ptr rootNode, + Resource::ResourceSystem* resourceSystem, const Files::Collections& fileCollections, const std::vector& contentFiles, - const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int activationDistanceOverride, const std::string& startCell, const std::string& startupScript); diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index c49cf77b8..60315ba7d 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -74,6 +74,16 @@ namespace SceneUtil if (lights.size()) { + + static std::map > statesets; + std::map >::iterator found = statesets.find(node); + osg::ref_ptr stateset; + if (found != statesets.end()) + { + stateset = found->second; + } + else{ + // we do the intersections in view space osg::BoundingSphere nodeBound = node->getBound(); osg::Matrixf mat = *cv->getModelViewMatrix(); @@ -89,26 +99,33 @@ namespace SceneUtil if (lightList.empty()) { + statesets[node] = NULL; traverse(node, nv); return; } - if (lightList.size() > 8) + unsigned int maxLights = static_cast (8 - mLightManager->getStartLight()); + + if (lightList.size() > maxLights) { //std::cerr << "More than 8 lights!" << std::endl; // TODO: sort lights by certain criteria - while (lightList.size() > 8) + while (lightList.size() > maxLights) lightList.pop_back(); } - osg::ref_ptr stateset = mLightManager->getLightListStateSet(lightList); + stateset = mLightManager->getLightListStateSet(lightList); + statesets[node] = stateset; + } + if (stateset) cv->pushStateSet(stateset); traverse(node, nv); + if (stateset) cv->popStateSet(); } else @@ -174,7 +191,7 @@ namespace SceneUtil throw std::runtime_error("can't find parent LightManager"); } - mLightManager->addLight(static_cast(node), osg::computeLocalToWorld(nv->getNodePath())); + //mLightManager->addLight(static_cast(node), osg::computeLocalToWorld(nv->getNodePath())); traverse(node, nv); } @@ -208,6 +225,7 @@ namespace SceneUtil LightManager::LightManager() : mLightsInViewSpace(false) , mDecorated(false) + , mStartLight(0) { setUpdateCallback(new LightManagerUpdateCallback); } @@ -216,6 +234,7 @@ namespace SceneUtil : osg::Group(copy, copyop) , mLightsInViewSpace(false) , mDecorated(copy.mDecorated) + , mStartLight(copy.mStartLight) { } @@ -284,11 +303,14 @@ namespace SceneUtil osg::ref_ptr clonedLight = new LightStateAttribute(*light, osg::CopyOp::DEEP_COPY_ALL); clonedLight->setPosition(mLights[lightIndex].mWorldMatrix.preMult(light->getPosition())); - clonedLight->setLightNum(i); + clonedLight->setLightNum(i+mStartLight); // don't use setAttributeAndModes, that does not support light indices! stateset->setAttribute(clonedLight, osg::StateAttribute::ON); stateset->setAssociatedModes(clonedLight, osg::StateAttribute::ON); + + //stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + } mStateSetCache.insert(std::make_pair(hash, stateset)); return stateset; @@ -300,6 +322,16 @@ namespace SceneUtil return mLights; } + void LightManager::setStartLight(int start) + { + mStartLight = start; + } + + int LightManager::getStartLight() const + { + return mStartLight; + } + LightSource::LightSource() : mRadius(0.f) { diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index ccb6603a6..fe5dc5082 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -87,6 +87,11 @@ namespace SceneUtil osg::ref_ptr getLightListStateSet(const LightList& lightList); + /// Set the first light index that should be used by this manager, typically the number of directional lights in the scene. + void setStartLight(int start); + + int getStartLight() const; + private: // Lights collected from the scene graph. Only valid during the cull traversal. std::vector mLights; @@ -98,6 +103,8 @@ namespace SceneUtil LightStateSetMap mStateSetCache; bool mDecorated; + + int mStartLight; }; } diff --git a/files/settings-default.cfg b/files/settings-default.cfg index de22e1b56..32c3861c0 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -129,15 +129,6 @@ local map hud widget size = 256 exterior grid size = 3 [Viewing distance] -# Limit the rendering distance of small objects -limit small object distance = false - -# Size below which an object is considered as small -small object size = 250 - -# Rendering distance for small objects -small object distance = 3500 - # Viewing distance at normal weather conditions # The maximum distance with no pop-in will be: (see RenderingManager::configureFog) # viewing distance / minimum weather fog depth (.69) * view frustum factor <= cell size (8192) - loading threshold (1024)