diff --git a/.gitmodules b/.gitmodules index f53c97677..d2a4cf0d3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "extern/shiny"] path = extern/shiny - url = git@github.com:scrawl/shiny.git + url = git://github.com/scrawl/shiny.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f2decb8f7..543d9cb98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,9 @@ if (UNIX AND NOT APPLE) find_package (Threads) endif() +# find boost without components so we can use Boost_VERSION +find_package(Boost REQUIRED) + set(BOOST_COMPONENTS system filesystem program_options thread) if (Boost_VERSION LESS 104900) @@ -256,14 +259,13 @@ endif (APPLE) # Set up Ogre plugin folder & debug suffix -if (DEFINED CMAKE_BUILD_TYPE) - # Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) - if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") - else() - add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") - endif() +# Ogre on OS X doesn't use "_d" suffix (see Ogre's CMakeLists.txt) +if (DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT APPLE) + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d") +else() + add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="") endif() + add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") if (APPLE AND OPENMW_OSX_DEPLOYMENT) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8e424ad56..556b9a1ba 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -39,7 +39,7 @@ add_openmw_dir (mwscript locals scriptmanager compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions - animationextensions transformationextensions + animationextensions transformationextensions consoleextensions userextensions ) add_openmw_dir (mwsound diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 96fbeb9e2..5e9aedf66 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -56,11 +56,8 @@ void OMW::Engine::executeLocalScripts() localScripts.setIgnore (MWWorld::Ptr()); } -void OMW::Engine::setAnimationVerbose(bool animverbose){ - if(animverbose){ - NifOgre::NIFLoader::getSingletonPtr()->setOutputAnimFiles(true); - NifOgre::NIFLoader::getSingletonPtr()->setVerbosePath(mCfgMgr.getLogPath().string()); - } +void OMW::Engine::setAnimationVerbose(bool animverbose) +{ } bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) @@ -132,6 +129,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mCompileAll (false) , mScriptContext (0) , mFSStrict (false) + , mScriptConsoleMode (false) , mCfgMgr(configurationManager) { std::srand ( std::time(NULL) ); @@ -329,7 +327,8 @@ void OMW::Engine::go() MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"))); + mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mScriptConsoleMode)); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -391,6 +390,9 @@ void OMW::Engine::go() << std::endl; } + if (!mStartupScript.empty()) + MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); + // Start the main rendering loop mOgre->start(); @@ -493,3 +495,13 @@ void OMW::Engine::setFallbackValues(std::map fallbackMa { mFallbackMap = fallbackMap; } + +void OMW::Engine::setScriptConsoleMode (bool enabled) +{ + mScriptConsoleMode = enabled; +} + +void OMW::Engine::setStartupScript (const std::string& path) +{ + mStartupScript = path; +} diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 031cae551..57402c91e 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -73,6 +73,8 @@ namespace OMW bool mCompileAll; std::string mFocusName; std::map mFallbackMap; + bool mScriptConsoleMode; + std::string mStartupScript; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; @@ -158,6 +160,12 @@ namespace OMW void setFallbackValues(std::map map); + /// Enable console-only script functionality + void setScriptConsoleMode (bool enabled); + + /// Set path for a script that is run on startup in the console. + void setStartupScript (const std::string& path); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 993ec6623..0ca9dd6d9 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -124,12 +124,20 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-verbose", bpo::value()->implicit_value(true) ->default_value(false), "verbose script output") - ("new-game", bpo::value()->implicit_value(true) - ->default_value(false), "activate char gen/new game mechanics") - ("script-all", bpo::value()->implicit_value(true) ->default_value(false), "compile all scripts (excluding dialogue scripts) at startup") + ("script-console", bpo::value()->implicit_value(true) + ->default_value(false), "enable console-only script functionality") + + ("script-run", bpo::value()->default_value(""), + "select a file that is executed in the console on startup\n\n" + "Note: The file contains a list of script lines, but not a complete scripts. " + "That means no begin/end and no variable declarations.") + + ("new-game", bpo::value()->implicit_value(true) + ->default_value(false), "activate char gen/new game mechanics") + ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -249,6 +257,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setCompileAll(variables["script-all"].as()); engine.setAnimationVerbose(variables["anim-verbose"].as()); engine.setFallbackValues(variables["fallback"].as().mMap); + engine.setScriptConsoleMode (variables["script-console"].as()); + engine.setStartupScript (variables["script-run"].as()); return true; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6937cbf3b..f257b723e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -54,6 +54,14 @@ namespace MWBase World& operator= (const World&); ///< not implemented + protected: + + virtual void + placeObject( + const MWWorld::Ptr &ptr, + MWWorld::CellStore &cell, + const ESM::Position &pos) = 0; + public: enum RenderMode diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 81a47ccb0..9b0082efc 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -19,32 +19,33 @@ namespace MWClass { void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Activator::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Activator::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Activator::getName (const MWWorld::Ptr& ptr) const @@ -93,4 +94,14 @@ namespace MWClass return info; } + + MWWorld::Ptr + Activator::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.activators.insert(*ref), &cell); + } } + diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 223dd0a36..4165fbc08 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -7,6 +7,10 @@ namespace MWClass { class Activator : public MWWorld::Class { + + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -28,6 +32,8 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 7e3c3b8f9..9814b140c 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -22,34 +22,35 @@ namespace MWClass { - void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Apparatus::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Apparatus::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Apparatus::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Apparatus::getName (const MWWorld::Ptr& ptr) const @@ -148,4 +149,13 @@ namespace MWClass { return boost::shared_ptr(new MWWorld::ActionAlchemy()); } + + MWWorld::Ptr + Apparatus::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.appas.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index f33f92e2c..7045f62d6 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -7,6 +7,10 @@ namespace MWClass { class Apparatus : public MWWorld::Class { + + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -48,6 +52,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 380c596d7..4624b94d9 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -27,31 +27,33 @@ namespace MWClass { void Armor::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Armor::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Armor::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Armor::getName (const MWWorld::Ptr& ptr) const @@ -274,4 +276,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Armor::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.armors.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index a63806162..51c0ea21c 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Armor : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -66,6 +69,7 @@ namespace MWClass const; ///< Generate action for using via inventory menu + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index a37da0fd7..d8166347e 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -23,32 +23,33 @@ namespace MWClass { void Book::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Book::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Book::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Book::getName (const MWWorld::Ptr& ptr) const @@ -156,4 +157,12 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionRead(ptr)); } + MWWorld::Ptr + Book::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.books.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index ee3aac8d8..acb1aac06 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Book : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -50,6 +53,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 6c34b5e56..f55d6ed88 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -25,32 +25,33 @@ namespace MWClass { void Clothing::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Clothing::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Clothing::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Clothing::getName (const MWWorld::Ptr& ptr) const @@ -226,4 +227,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Clothing::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.clothes.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index aba317be0..f7801848f 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Clothing : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -59,6 +62,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 8dd27db42..c6d22b3a5 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -54,32 +54,33 @@ namespace MWClass void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Container::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Container::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } boost::shared_ptr Container::activate (const MWWorld::Ptr& ptr, @@ -206,4 +207,13 @@ namespace MWClass { ptr.getCellRef().lockLevel = 0; } + + MWWorld::Ptr + Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.containers.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 739c75c77..006e4bd22 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -9,6 +9,10 @@ namespace MWClass { void ensureCustomData (const MWWorld::Ptr& ptr) const; + + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -51,6 +55,8 @@ namespace MWClass ///< Unlock object static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 83370478f..0f3141f5c 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -86,17 +86,25 @@ namespace MWClass } void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()){ + physics.insertActorPhysics(ptr, model); + } + MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + } + + std::string Creature::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert (ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertActorPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - - MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + return ""; } std::string Creature::getName (const MWWorld::Ptr& ptr) const @@ -187,4 +195,13 @@ namespace MWClass return weight; } + + MWWorld::Ptr + Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.creatures.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 1274be09a..f7a5e5874 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -11,6 +11,9 @@ namespace MWClass { void ensureCustomData (const MWWorld::Ptr& ptr) const; + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -54,6 +57,8 @@ namespace MWClass /// effects). Throws an exception, if the object can't hold other objects. static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index b0bba2c03..6939c356e 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -25,30 +25,33 @@ namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Door::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { - MWWorld::LiveCellRef *ref = + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Door::getModel(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } + return ""; } std::string Door::getName (const MWWorld::Ptr& ptr) const @@ -94,18 +97,18 @@ namespace MWClass if (ref->ref.teleport) { // teleport door + /// \todo remove this if clause once ActionTeleport can also support other actors if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()==actor) { // the player is using the door // The reason this is not 3D is that it would get interrupted when you teleport MWBase::Environment::get().getSoundManager()->playSound(openSound, 1.0, 1.0); return boost::shared_ptr ( - new MWWorld::ActionTeleportPlayer (ref->ref.destCell, ref->ref.doorDest)); + new MWWorld::ActionTeleport (ref->ref.destCell, ref->ref.doorDest)); } else { // another NPC or a creature is using the door - // TODO return action for teleporting other NPC/creature return boost::shared_ptr (new MWWorld::NullAction); } } @@ -206,4 +209,13 @@ namespace MWClass return info; } + + MWWorld::Ptr + Door::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.doors.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 63d1c1ab8..b0f86f12d 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Door : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -38,6 +41,8 @@ namespace MWClass ///< Return name of the script attached to ptr static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 01146fe67..d8c8b4b3b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -23,30 +23,33 @@ namespace MWClass { void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Ingredient::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Ingredient::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } + return ""; } std::string Ingredient::getName (const MWWorld::Ptr& ptr) const @@ -153,4 +156,13 @@ namespace MWClass return info; } + + MWWorld::Ptr + Ingredient::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.ingreds.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 4c45bd69c..1365c4a71 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Ingredient : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -44,6 +47,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 15cd89ac2..f09d3ce36 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -28,8 +28,8 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); - assert (ref->base != NULL); + const std::string &model = ref->base->model; MWRender::Objects& objects = renderingInterface.getObjects(); @@ -50,20 +50,31 @@ namespace MWClass { MWWorld::LiveCellRef *ref = ptr.get(); - assert (ref->base != NULL); + const std::string &model = ref->base->model; - if(!model.empty()){ + if(!model.empty()) { physics.insertObjectPhysics(ptr, "meshes\\" + model); } - - if (!ref->base->sound.empty()) - { - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop); + if (!ref->base->sound.empty()) { + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop); } } + std::string Light::getModel(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + assert (ref->base != NULL); + + const std::string &model = ref->base->model; + if (!model.empty()) { + return "meshes\\" + model; + } + return ""; + } + std::string Light::getName (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -185,4 +196,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Light::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.lights.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 91193dfdc..640e1705b 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Light : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -52,6 +55,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index d3d60315f..20441b520 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -25,35 +25,35 @@ namespace MWClass { void Lockpick::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Lockpick::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Lockpick::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } - std::string Lockpick::getName (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -165,4 +165,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Lockpick::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.lockpicks.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 26aab584c..0961b55b2 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Lockpick : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -52,6 +55,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 8484a5dd1..cb6d40c43 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -12,6 +12,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/manualref.hpp" #include "../mwgui/window_manager.hpp" #include "../mwgui/tooltips.hpp" @@ -27,32 +28,33 @@ namespace MWClass { void Miscellaneous::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Miscellaneous::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const @@ -182,4 +184,39 @@ namespace MWClass return info; } + + MWWorld::Ptr + Miscellaneous::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::Ptr newPtr; + + const ESMS::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + if (MWWorld::Class::get(ptr).getName(ptr) == store.gameSettings.search("sGold")->str) { + int goldAmount = ptr.getRefData().getCount(); + + std::string base = "Gold_001"; + if (goldAmount >= 100) + base = "Gold_100"; + else if (goldAmount >= 25) + base = "Gold_025"; + else if (goldAmount >= 10) + base = "Gold_010"; + else if (goldAmount >= 5) + base = "Gold_005"; + + // Really, I have no idea why moving ref out of conditional + // scope causes list::push_back throwing std::bad_alloc + MWWorld::ManualRef newRef(store, base); + MWWorld::LiveCellRef *ref = + newRef.getPtr().get(); + newPtr = MWWorld::Ptr(&cell.miscItems.insert(*ref), &cell); + } else { + MWWorld::LiveCellRef *ref = + ptr.get(); + newPtr = MWWorld::Ptr(&cell.miscItems.insert(*ref), &cell); + } + return newPtr; + } } diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index da5f0df96..a5a79a8f6 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Miscellaneous : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -44,6 +47,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 80bff73fa..81c0c85f5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -125,25 +125,29 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { + physics.insertActorPhysics(ptr, getModel(ptr)); + MWBase::Environment::get().getMechanicsManager()->addActor(ptr); + } + std::string Npc::getModel(const MWWorld::Ptr &ptr) const + { MWWorld::LiveCellRef *ref = ptr.get(); - - assert (ref->base != NULL); - - + assert(ref->base != NULL); std::string headID = ref->base->head; - std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); - bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; + int end = headID.find_last_of("head_") - 4; + std::string bodyRaceID = headID.substr(0, end); - std::string smodel = "meshes\\base_anim.nif"; - if(beast) - smodel = "meshes\\base_animkna.nif"; - physics.insertActorPhysics(ptr, smodel); - - - MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + std::string model = "meshes\\base_anim.nif"; + if (bodyRaceID == "b_n_khajiit_m_" || + bodyRaceID == "b_n_khajiit_f_" || + bodyRaceID == "b_n_argonian_m_" || + bodyRaceID == "b_n_argonian_f_") + { + model = "meshes\\base_animkna.nif"; + } + return model; } std::string Npc::getName (const MWWorld::Ptr& ptr) const @@ -376,4 +380,13 @@ namespace MWClass y = 0; x = 0; } + + MWWorld::Ptr + Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.npcs.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index b32a162a1..e494fbaa7 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -9,6 +9,9 @@ namespace MWClass { void ensureCustomData (const MWWorld::Ptr& ptr) const; + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -88,6 +91,8 @@ namespace MWClass virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 45cb07840..d3f2912ea 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -25,32 +25,33 @@ namespace MWClass { void Potion::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Potion::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Potion::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Potion::getName (const MWWorld::Ptr& ptr) const @@ -156,7 +157,20 @@ namespace MWClass MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - return boost::shared_ptr ( - new MWWorld::ActionApply (actor, ref->base->mId, actor)); + boost::shared_ptr action ( + new MWWorld::ActionApply (actor, ref->base->mId)); + + action->setSound ("Drink"); + + return action; + } + + MWWorld::Ptr + Potion::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.potions.insert(*ref), &cell); } } diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 101f4cefa..d595f7e69 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Potion : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -47,6 +50,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index f3a8406f5..450016209 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -25,35 +25,35 @@ namespace MWClass { void Probe::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Probe::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Probe::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } - std::string Probe::getName (const MWWorld::Ptr& ptr) const { MWWorld::LiveCellRef *ref = @@ -164,4 +164,14 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Probe::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.probes.insert(*ref), &cell); + } } + diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 51b046fda..d9f90baf6 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -7,9 +7,12 @@ namespace MWClass { class Probe : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; @@ -52,6 +55,8 @@ namespace MWClass virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 464ba1091..829fe311a 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -23,32 +23,33 @@ namespace MWClass { void Repair::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Repair::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Repair::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Repair::getName (const MWWorld::Ptr& ptr) const @@ -145,4 +146,13 @@ namespace MWClass return info; } + + MWWorld::Ptr + Repair::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.repairs.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 1e935e154..c58e38f96 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -7,9 +7,12 @@ namespace MWClass { class Repair : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: - virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering virtual void insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const; @@ -44,6 +47,8 @@ namespace MWClass virtual std::string getInventoryIcon (const MWWorld::Ptr& ptr) const; ///< Return name of inventory icon. + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 9b166b076..e317b740c 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -13,31 +13,33 @@ namespace MWClass { void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), true); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Static::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Static::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); + assert(ref->base != NULL); - assert (ref->base != NULL); const std::string &model = ref->base->model; - - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } + return ""; } std::string Static::getName (const MWWorld::Ptr& ptr) const @@ -51,4 +53,13 @@ namespace MWClass registerClass (typeid (ESM::Static).name(), instance); } + + MWWorld::Ptr + Static::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.statics.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index c223df1ac..e36b3d142 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Static : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -19,6 +22,8 @@ namespace MWClass /// can return an empty string. static void registerSelf(); + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 099312d2c..b45953130 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -25,32 +25,33 @@ namespace MWClass { void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - MWWorld::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - { + const std::string model = getModel(ptr); + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, "meshes\\" + model); + objects.insertMesh(ptr, model); } } void Weapon::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const + { + const std::string model = getModel(ptr); + if(!model.empty()) { + physics.insertObjectPhysics(ptr, model); + } + } + + std::string Weapon::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); - + assert(ref->base != NULL); const std::string &model = ref->base->model; - assert (ref->base != NULL); - if(!model.empty()){ - physics.insertObjectPhysics(ptr, "meshes\\" + model); + if (!model.empty()) { + return "meshes\\" + model; } - + return ""; } std::string Weapon::getName (const MWWorld::Ptr& ptr) const @@ -364,4 +365,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::ActionEquip(ptr)); } + + MWWorld::Ptr + Weapon::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return MWWorld::Ptr(&cell.weapons.insert(*ref), &cell); + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 92d703b4a..06cf88c5f 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -7,6 +7,9 @@ namespace MWClass { class Weapon : public MWWorld::Class { + virtual MWWorld::Ptr + copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -66,6 +69,7 @@ namespace MWClass const; ///< Generate action for using via inventory menu + virtual std::string getModel(const MWWorld::Ptr &ptr) const; }; } diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 1ea3da839..1e0301d8e 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -3,9 +3,11 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwinput/inputmanager.hpp" #include "../mwsound/soundmanager.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/player.hpp" #include "formatting.hpp" #include "window_manager.hpp" @@ -99,7 +101,7 @@ void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); MWWorld::ActionTake take(mBook); - take.execute(); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); mWindowManager.removeGuiMode(GM_Book); } diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 4101165c1..86c8940a1 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -2,6 +2,7 @@ #include "console.hpp" #include +#include #include #include @@ -105,9 +106,10 @@ namespace MWGui } } - Console::Console(int w, int h, const Compiler::Extensions& extensions) + Console::Console(int w, int h, bool consoleOnlyScripts) : Layout("openmw_console.layout"), - mCompilerContext (MWScript::CompilerContext::Type_Console) + mCompilerContext (MWScript::CompilerContext::Type_Console), + mConsoleOnlyScripts (consoleOnlyScripts) { setCoord(10,10, w-10, h/2); @@ -126,7 +128,8 @@ namespace MWGui history->setVisibleVScroll(true); // compiler - mCompilerContext.setExtensions (&extensions); + MWScript::registerExtensions (mExtensions, mConsoleOnlyScripts); + mCompilerContext.setExtensions (&mExtensions); } void Console::enable() @@ -173,6 +176,47 @@ namespace MWGui print("#FF2222" + msg + "\n"); } + void Console::execute (const std::string& command) + { + // Log the command + print("#FFFFFF> " + command + "\n"); + + Compiler::Locals locals; + Compiler::Output output (locals); + + if (compile (command + "\n", output)) + { + try + { + ConsoleInterpreterContext interpreterContext (*this, mPtr); + Interpreter::Interpreter interpreter; + MWScript::installOpcodes (interpreter, mConsoleOnlyScripts); + std::vector code; + output.getCode (code); + interpreter.run (&code[0], code.size(), interpreterContext); + } + catch (const std::exception& error) + { + printError (std::string ("An exception has been thrown: ") + error.what()); + } + } + } + + void Console::executeFile (const std::string& path) + { + std::ifstream stream (path.c_str()); + + if (!stream.is_open()) + printError ("failed to open file: " + path); + else + { + std::string line; + + while (std::getline (stream, line)) + execute (line); + } + } + void Console::keyPress(MyGUI::WidgetPtr _sender, MyGUI::KeyCode key, MyGUI::Char _char) @@ -234,28 +278,7 @@ namespace MWGui current = command_history.end(); editString.clear(); - // Log the command - print("#FFFFFF> " + cm + "\n"); - - Compiler::Locals locals; - Compiler::Output output (locals); - - if (compile (cm + "\n", output)) - { - try - { - ConsoleInterpreterContext interpreterContext (*this, mPtr); - Interpreter::Interpreter interpreter; - MWScript::installOpcodes (interpreter); - std::vector code; - output.getCode (code); - interpreter.run (&code[0], code.size(), interpreterContext); - } - catch (const std::exception& error) - { - printError (std::string ("An exception has been thrown: ") + error.what()); - } - } + execute (cm); command->setCaption(""); } diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index eadf4aa4e..1893b0148 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "../mwscript/compilercontext.hpp" @@ -24,8 +25,10 @@ namespace MWGui { private: + Compiler::Extensions mExtensions; MWScript::CompilerContext mCompilerContext; std::vector mNames; + bool mConsoleOnlyScripts; bool compile (const std::string& cmd, Compiler::Output& output); @@ -62,7 +65,7 @@ namespace MWGui StringList::iterator current; std::string editString; - Console(int w, int h, const Compiler::Extensions& extensions); + Console(int w, int h, bool consoleOnlyScripts); void enable(); @@ -86,6 +89,10 @@ namespace MWGui /// Error message void printError(const std::string &msg); + void execute (const std::string& command); + + void executeFile (const std::string& command); + private: void keyPress(MyGUI::WidgetPtr _sender, diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 23a96b1d3..926c35172 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -175,7 +175,7 @@ namespace MWGui boost::shared_ptr action = MWWorld::Class::get(ptr).use(ptr); - action->execute(); + action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); // this is necessary for books/scrolls: if they are already in the player's inventory, // the "Take" button should not be visible. diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index e6ff71a14..00e5a01dc 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -1,8 +1,10 @@ #include "scrollwindow.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwinput/inputmanager.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/player.hpp" #include "../mwsound/soundmanager.hpp" #include "formatting.hpp" @@ -63,7 +65,7 @@ void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWSound::Play_NoTrack); MWWorld::ActionTake take(mScroll); - take.execute(); + take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); mWindowManager.removeGuiMode(GM_Scroll); } diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index aa0595b85..659af0447 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -41,7 +41,7 @@ using namespace MWGui; WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath) + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts) : mGuiManager(NULL) , mHud(NULL) , mMap(NULL) @@ -113,7 +113,7 @@ WindowManager::WindowManager( mMenu = new MainMenu(w,h); mMap = new MapWindow(*this); mStatsWindow = new StatsWindow(*this); - mConsole = new Console(w,h, extensions); + mConsole = new Console(w,h, consoleOnlyScripts); mJournal = new JournalWindow(*this); mMessageBoxManager = new MessageBoxManager(this); mInventoryWindow = new InventoryWindow(*this,mDragAndDrop); @@ -740,3 +740,8 @@ bool WindowManager::getWorldMouseOver() { return mHud->getWorldMouseOver(); } + +void WindowManager::executeInConsole (const std::string& path) +{ + mConsole->executeFile (path); +} diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index adab79942..3653615a6 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -94,7 +94,7 @@ namespace MWGui typedef std::vector FactionList; typedef std::vector SkillList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath); + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, bool consoleOnlyScripts); virtual ~WindowManager(); /** @@ -237,6 +237,8 @@ namespace MWGui void processChangedSettings(const Settings::CategorySettingVector& changed); + void executeInConsole (const std::string& path); + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index b37921b0c..a64397d49 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -122,15 +122,13 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store){ void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number){ if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->startScript(groupName, mode, number); + mAllActors[ptr]->playGroup(groupName, mode, number); } void Actors::skipAnimation (const MWWorld::Ptr& ptr){ if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->stopScript(); + mAllActors[ptr]->skipAnim(); } void Actors::update (float duration){ for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) - { - (iter->second)->runAnimation(duration); - } + iter->second->runAnimation(duration); } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 549dbf6b2..46f3bdc0d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -5,498 +5,171 @@ #include #include #include +#include + namespace MWRender { - std::map Animation::sUniqueIDs; - Animation::Animation(OEngine::Render::OgreRenderer& _rend) - : mInsert(NULL) - , mRend(_rend) - , mVecRotPos() - , mTime(0.0f) - , mStartTime(0.0f) - , mStopTime(0.0f) - , mAnimate(0) - , mRindexI() - , mTindexI() - , mShapeNumber(0) - , mShapeIndexI() - , mShapes(NULL) - , mTransformations(NULL) - , mTextmappings(NULL) - , mBase(NULL) +Animation::Animation(OEngine::Render::OgreRenderer& _rend) + : mInsert(NULL) + , mRend(_rend) + , mTime(0.0f) + , mSkipFrame(false) +{ +} + +Animation::~Animation() +{ + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) + sceneMgr->destroyEntity(mEntityList.mEntities[i]); + mEntityList.mEntities.clear(); +} + + +struct checklow { + bool operator()(const char &a, const char &b) const { + return ::tolower(a) == ::tolower(b); } +}; - Animation::~Animation() +bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTimes *times) +{ + const std::string &start = groupname+": start"; + const std::string &startloop = groupname+": loop start"; + const std::string &stop = groupname+": stop"; + const std::string &stoploop = groupname+": loop stop"; + + NifOgre::TextKeyMap::const_iterator iter; + for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++) { - } + if(times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f) + return true; - std::string Animation::getUniqueID(std::string mesh) - { - int counter; - std::string copy = mesh; - std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower); - - if(sUniqueIDs.find(copy) == sUniqueIDs.end()) + std::string::const_iterator strpos = iter->second.begin(); + std::string::const_iterator strend = iter->second.end(); + + while(strpos != strend) { - counter = sUniqueIDs[copy] = 0; - } - else - { - sUniqueIDs[copy] = sUniqueIDs[copy] + 1; - counter = sUniqueIDs[copy]; - } + size_t strlen = strend-strpos; + std::string::const_iterator striter; - std::stringstream out; - - if(counter > 99 && counter < 1000) - out << "0"; - else if(counter > 9) - out << "00"; - else - out << "000"; - out << counter; - - return out.str(); - } - - void Animation::startScript(std::string groupname, int mode, int loops) - { - //If groupname is recognized set animate to true - //Set the start time and stop time - //How many times to loop - if(groupname == "all") - { - mAnimate = loops; - mTime = mStartTime; - } - else if(mTextmappings) - { - - std::string startName = groupname + ": loop start"; - std::string stopName = groupname + ": loop stop"; - - bool first = false; - - if(loops > 1) + if(start.size() <= strlen && + ((striter=std::mismatch(strpos, strend, start.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) { - startName = groupname + ": loop start"; - stopName = groupname + ": loop stop"; - - for(std::map::iterator iter = mTextmappings->begin(); iter != mTextmappings->end(); iter++) - { - - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); - - if(current == startName) - { - mStartTime = iter->second; - mAnimate = loops; - mTime = mStartTime; - first = true; - } - if(current2 == stopName) - { - mStopTime = iter->second; - if(first) - break; - } - } + times->mStart = iter->first; + times->mLoopStart = iter->first; } - if(!first) + else if(startloop.size() <= strlen && + ((striter=std::mismatch(strpos, strend, startloop.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) { - startName = groupname + ": start"; - stopName = groupname + ": stop"; + times->mLoopStart = iter->first; + } + else if(stoploop.size() <= strlen && + ((striter=std::mismatch(strpos, strend, stoploop.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) + { + times->mLoopStop = iter->first; + } + else if(stop.size() <= strlen && + ((striter=std::mismatch(strpos, strend, stop.begin(), checklow()).first) == strend || + *striter == '\r' || *striter == '\n')) + { + times->mStop = iter->first; + if(times->mLoopStop < 0.0f) + times->mLoopStop = iter->first; + break; + } - for(std::map::iterator iter = mTextmappings->begin(); iter != mTextmappings->end(); iter++) - { + strpos = std::find(strpos+1, strend, '\n'); + while(strpos != strend && *strpos == '\n') + strpos++; + } + } - std::string current = iter->first.substr(0, startName.size()); - std::transform(current.begin(), current.end(), current.begin(), ::tolower); - std::string current2 = iter->first.substr(0, stopName.size()); - std::transform(current2.begin(), current2.end(), current2.begin(), ::tolower); + return (times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f); +} - if(current == startName) - { - mStartTime = iter->second; - mAnimate = loops; - mTime = mStartTime; - first = true; - } - if(current2 == stopName) - { - mStopTime = iter->second; - if(first) - break; - } - } + +void Animation::playGroup(std::string groupname, int mode, int loops) +{ + GroupTimes times; + times.mLoops = loops; + + if(groupname == "all") + { + times.mStart = times.mLoopStart = 0.0f; + times.mLoopStop = times.mStop = 0.0f; + + if(mEntityList.mSkelBase) + { + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) + { + Ogre::AnimationState *state = as.getNext(); + times.mLoopStop = times.mStop = state->getLength(); + break; } } } - - void Animation::stopScript() + else if(!findGroupTimes(groupname, ×)) + throw std::runtime_error("Failed to find animation group "+groupname); + + if(mode == 0 && mCurGroup.mLoops > 0) + mNextGroup = times; + else { - mAnimate = 0; + mCurGroup = times; + mNextGroup = GroupTimes(); + mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart); } - - void Animation::handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel) +} + +void Animation::skipAnim() +{ + mSkipFrame = true; +} + +void Animation::runAnimation(float timepassed) +{ + if(mCurGroup.mLoops > 0 && !mSkipFrame) { - mShapeNumber = 0; - - if (allshapes == NULL || creaturemodel == NULL || skel == NULL) - return; - - std::vector::iterator allshapesiter; - for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++) + mTime += timepassed; + if(mTime >= mCurGroup.mLoopStop) { - //std::map vecPosRot; - - Nif::NiTriShapeCopy& copy = *allshapesiter; - std::vector* allvertices = ©.vertices; - - //std::set vertices; - //std::set normals; - //std::vector boneinfovector = copy.boneinfo; - std::map >* verticesToChange = ©.vertsToWeights; - - //std::cout << "Name " << copy.sname << "\n"; - Ogre::HardwareVertexBufferSharedPtr vbuf = creaturemodel->getMesh()->getSubMesh(copy.sname)->vertexData->vertexBufferBinding->getBuffer(0); - Ogre::Real* pReal = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_NORMAL)); - - - std::vector initialVertices = copy.morph.getInitialVertices(); - //Each shape has multiple indices - if(initialVertices.size() ) + if(mCurGroup.mLoops > 1) { - if(copy.vertices.size() == initialVertices.size()) - { - //Create if it doesn't already exist - if(mShapeIndexI.size() == static_cast (mShapeNumber)) - { - std::vector vec; - mShapeIndexI.push_back(vec); - } - if(mTime >= copy.morph.getStartTime() && mTime <= copy.morph.getStopTime()) - { - float x; - for (unsigned int i = 0; i < copy.morph.getAdditionalVertices().size(); i++) - { - int j = 0; - if(mShapeIndexI[mShapeNumber].size() <= i) - mShapeIndexI[mShapeNumber].push_back(0); - - if(timeIndex(mTime,copy.morph.getRelevantTimes()[i],(mShapeIndexI[mShapeNumber])[i], j, x)) - { - int indexI = (mShapeIndexI[mShapeNumber])[i]; - std::vector relevantData = (copy.morph.getRelevantData()[i]); - float v1 = relevantData[indexI].x; - float v2 = relevantData[j].x; - float t = v1 + (v2 - v1) * x; - - if ( t < 0 ) - t = 0; - if ( t > 1 ) - t = 1; - if( t != 0 && initialVertices.size() == copy.morph.getAdditionalVertices()[i].size()) - for (unsigned int v = 0; v < initialVertices.size(); v++) - initialVertices[v] += ((copy.morph.getAdditionalVertices()[i])[v]) * t; - } - - } - - allvertices = &initialVertices; - } - mShapeNumber++; - } + mCurGroup.mLoops--; + mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart; } - - - if(verticesToChange->size() > 0) + else if(mTime >= mCurGroup.mStop) { - - for(std::map >::iterator iter = verticesToChange->begin(); - iter != verticesToChange->end(); iter++) - { - std::vector inds = iter->second; - int verIndex = iter->first; - Ogre::Vector3 currentVertex = (*allvertices)[verIndex]; - Nif::NiSkinData::BoneInfoCopy* boneinfocopy = &(allshapesiter->boneinfo[inds[0].boneinfocopyindex]); - Ogre::Bone *bonePtr = 0; - - Ogre::Vector3 vecPos; - Ogre::Quaternion vecRot; - std::map::iterator result = mVecRotPos.find(boneinfocopy); - - if(result == mVecRotPos.end()) - { - bonePtr = skel->getBone(boneinfocopy->bonename); - - vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; - vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; - - PosAndRot both; - both.vecPos = vecPos; - both.vecRot = vecRot; - mVecRotPos[boneinfocopy] = both; - - } - else - { - PosAndRot both = result->second; - vecPos = both.vecPos; - vecRot = both.vecRot; - } - - Ogre::Vector3 absVertPos = (vecPos + vecRot * currentVertex) * inds[0].weight; - - for(std::size_t i = 1; i < inds.size(); i++) - { - boneinfocopy = &(allshapesiter->boneinfo[inds[i].boneinfocopyindex]); - result = mVecRotPos.find(boneinfocopy); - - if(result == mVecRotPos.end()) - { - bonePtr = skel->getBone(boneinfocopy->bonename); - vecPos = bonePtr->_getDerivedPosition() + bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.trans; - vecRot = bonePtr->_getDerivedOrientation() * boneinfocopy->trafo.rotation; - - PosAndRot both; - both.vecPos = vecPos; - both.vecRot = vecRot; - mVecRotPos[boneinfocopy] = both; - - } - else - { - PosAndRot both = result->second; - vecPos = both.vecPos; - vecRot = both.vecRot; - } - - absVertPos += (vecPos + vecRot * currentVertex) * inds[i].weight; - - } - Ogre::Real* addr = (pReal + 3 * verIndex); - *addr = absVertPos.x; - *(addr+1) = absVertPos.y; - *(addr+2) = absVertPos.z; - - } - } - else - { - //Ogre::Bone *bonePtr = creaturemodel->getSkeleton()->getBone(copy.bonename); - Ogre::Quaternion shaperot = copy.trafo.rotation; - Ogre::Vector3 shapetrans = copy.trafo.trans; - float shapescale = copy.trafo.scale; - std::vector boneSequence = copy.boneSequence; - - Ogre::Vector3 transmult; - Ogre::Quaternion rotmult; - float scale; - if(boneSequence.size() > 0) - { - std::vector::iterator boneSequenceIter = boneSequence.begin(); - if(skel->hasBone(*boneSequenceIter)) - { - Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); - - transmult = bonePtr->getPosition(); - rotmult = bonePtr->getOrientation(); - scale = bonePtr->getScale().x; - boneSequenceIter++; - - for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) - { - if(skel->hasBone(*boneSequenceIter)) - { - Ogre::Bone *bonePtr = skel->getBone(*boneSequenceIter); - // Computes C = B + AxC*scale - transmult = transmult + rotmult * bonePtr->getPosition(); - rotmult = rotmult * bonePtr->getOrientation(); - scale = scale * bonePtr->getScale().x; - } - //std::cout << "Bone:" << *boneSequenceIter << " "; - } - transmult = transmult + rotmult * shapetrans; - rotmult = rotmult * shaperot; - scale = shapescale * scale; - - //std::cout << "Position: " << transmult << "Rotation: " << rotmult << "\n"; - } - } + if(mNextGroup.mLoops > 0) + mTime = mTime - mCurGroup.mStop + mNextGroup.mStart; else - { - transmult = shapetrans; - rotmult = shaperot; - scale = shapescale; - } - - // Computes C = B + AxC*scale - // final_vector = old_vector + old_rotation*new_vector*old_scale/ - - for(unsigned int i = 0; i < allvertices->size(); i++) - { - Ogre::Vector3 current = transmult + rotmult * (*allvertices)[i]; - Ogre::Real* addr = pReal + i * 3; - *addr = current.x; - *(addr+1) = current.y; - *(addr + 2) = current.z; - - }/* - for(int i = 0; i < allnormals.size(); i++){ - Ogre::Vector3 current =rotmult * allnormals[i]; - Ogre::Real* addr = pRealNormal + i * 3; - *addr = current.x; - *(addr+1) = current.y; - *(addr + 2) = current.z; - - }*/ - + mTime = mCurGroup.mStop; + mCurGroup = mNextGroup; + mNextGroup = GroupTimes(); } - vbuf->unlock(); } - - } - - bool Animation::timeIndex( float time, const std::vector & times, int & i, int & j, float & x ) - { - int count; - if ( (count = times.size()) > 0 ) + + if(mEntityList.mSkelBase) { - if ( time <= times[0] ) + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) { - i = j = 0; - x = 0.0; - return true; - } - if ( time >= times[count - 1] ) - { - i = j = count - 1; - x = 0.0; - return true; - } - - if ( i < 0 || i >= count ) - i = 0; - - float tI = times[i]; - if ( time > tI ) - { - j = i + 1; - float tJ; - while ( time >= ( tJ = times[j]) ) - { - i = j++; - tI = tJ; - } - x = ( time - tI ) / ( tJ - tI ); - return true; - } - else if ( time < tI ) - { - j = i - 1; - float tJ; - while ( time <= ( tJ = times[j] ) ) - { - i = j--; - tI = tJ; - } - x = ( time - tI ) / ( tJ - tI ); - return true; - } - else - { - j = i; - x = 0.0; - return true; + Ogre::AnimationState *state = as.getNext(); + state->setTimePosition(mTime); } } - else - return false; - - } - - void Animation::handleAnimationTransforms() - { - Ogre::SkeletonInstance* skel = mBase->getSkeleton(); - - Ogre::Bone* b = skel->getRootBone(); - b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3)); //This is a trick - - skel->_updateTransforms(); - //skel->_notifyManualBonesDirty(); - - mBase->getAllAnimationStates()->_notifyDirty(); - //mBase->_updateAnimation(); - //mBase->_notifyMoved(); - - std::vector::iterator iter; - int slot = 0; - if(mTransformations) - { - for(iter = mTransformations->begin(); iter != mTransformations->end(); iter++) - { - if(mTime < iter->getStartTime() || mTime < mStartTime || mTime > iter->getStopTime()) - { - slot++; - continue; - } - - float x; - float x2; - - const std::vector & quats = iter->getQuat(); - - const std::vector & ttime = iter->gettTime(); - const std::vector & rtime = iter->getrTime(); - int rindexJ = mRindexI[slot]; - - timeIndex(mTime, rtime, mRindexI[slot], rindexJ, x2); - int tindexJ = mTindexI[slot]; - - const std::vector & translist1 = iter->getTranslist1(); - - timeIndex(mTime, ttime, mTindexI[slot], tindexJ, x); - - Ogre::Vector3 t; - Ogre::Quaternion r; - - bool bTrans = translist1.size() > 0; - - bool bQuats = quats.size() > 0; - - if(skel->hasBone(iter->getBonename())) - { - Ogre::Bone* bone = skel->getBone(iter->getBonename()); - - if(bTrans) - { - Ogre::Vector3 v1 = translist1[mTindexI[slot]]; - Ogre::Vector3 v2 = translist1[tindexJ]; - t = (v1 + (v2 - v1) * x); - bone->setPosition(t); - - } - - if(bQuats) - { - r = Ogre::Quaternion::Slerp(x2, quats[mRindexI[slot]], quats[rindexJ], true); - bone->setOrientation(r); - } - - } - - slot++; - } - skel->_updateTransforms(); - mBase->getAllAnimationStates()->_notifyDirty(); - } } + mSkipFrame = false; +} } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 30f8acb64..3611d35c0 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,6 +1,9 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H +#include + +#include #include #include "../mwworld/actiontalk.hpp" #include @@ -8,53 +11,47 @@ -namespace MWRender{ -struct PosAndRot{ - Ogre::Quaternion vecRot; - Ogre::Vector3 vecPos; -}; +namespace MWRender { -class Animation{ +class Animation { + struct GroupTimes { + float mStart; + float mStop; + float mLoopStart; + float mLoopStop; - protected: + size_t mLoops; + + GroupTimes() + : mStart(-1.0f), mStop(-1.0f), mLoopStart(-1.0f), mLoopStop(-1.0f), + mLoops(0) + { } + }; + +protected: Ogre::SceneNode* mInsert; OEngine::Render::OgreRenderer &mRend; - std::map mVecRotPos; - static std::map sUniqueIDs; float mTime; - float mStartTime; - float mStopTime; - int mAnimate; - //Represents a rotation index for each bone - std::vectormRindexI; - //Represents a translation index for each bone - std::vectormTindexI; + GroupTimes mCurGroup; + GroupTimes mNextGroup; - //Only shapes with morphing data will use a shape number - int mShapeNumber; - std::vector > mShapeIndexI; + bool mSkipFrame; - //Ogre::SkeletonInstance* skel; - std::vector* mShapes; //All the NiTriShapeData for a creature + NifOgre::EntityList mEntityList; + NifOgre::TextKeyMap mTextKeys; - std::vector* mTransformations; - std::map* mTextmappings; - Ogre::Entity* mBase; - void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); - void handleAnimationTransforms(); - bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); - std::string getUniqueID(std::string mesh); + bool findGroupTimes(const std::string &groupname, GroupTimes *times); - public: - Animation(OEngine::Render::OgreRenderer& _rend); - virtual void runAnimation(float timepassed) = 0; - void startScript(std::string groupname, int mode, int loops); - void stopScript(); - - virtual ~Animation(); +public: + Animation(OEngine::Render::OgreRenderer& _rend); + virtual ~Animation(); + void playGroup(std::string groupname, int mode, int loops); + void skipAnim(); + virtual void runAnimation(float timepassed); }; + } #endif diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 20037a773..7fb153bd1 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -14,7 +14,7 @@ namespace MWRender{ CreatureAnimation::~CreatureAnimation() { -} +} CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend) { @@ -24,75 +24,54 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::O assert (ref->base != NULL); if(!ref->base->model.empty()) { - const std::string &mesh = "meshes\\" + ref->base->model; - std::string meshNumbered = mesh + getUniqueID(mesh) + ">|"; - NifOgre::NIFLoader::load(meshNumbered); - mBase = mRend.getScene()->createEntity(meshNumbered); - mBase->setVisibilityFlags(RV_Actors); + std::string mesh = "meshes\\" + ref->base->model; - bool transparent = false; - for (unsigned int i=0; i < mBase->getNumSubEntities(); ++i) + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, mesh); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) { - Ogre::MaterialPtr mat = mBase->getSubEntity(i)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) - { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) - { - Ogre::Pass* pass = passIt.getNext(); + Ogre::Entity *ent = mEntityList.mEntities[i]; + ent->setVisibilityFlags(RV_Actors); - if (pass->getDepthWriteEnabled() == false) - transparent = true; + bool transparent = false; + for (unsigned int j=0;j < ent->getNumSubEntities() && !transparent; ++j) + { + Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements() && !transparent) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements() && !transparent) + { + Ogre::Pass* pass = passIt.getNext(); + + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } } } + ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } - mBase->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - std::string meshZero = mesh + "0000>|"; - - if((mTransformations = (NIFLoader::getSingletonPtr())->getAnim(meshZero))) + if(mEntityList.mSkelBase) { - for(std::size_t init = 0; init < mTransformations->size(); init++) + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) { - mRindexI.push_back(0); - mTindexI.push_back(0); + Ogre::AnimationState *state = as.getNext(); + state->setEnabled(true); + state->setLoop(false); } - mStopTime = mTransformations->begin()->getStopTime(); - mStartTime = mTransformations->begin()->getStartTime(); - mShapes = (NIFLoader::getSingletonPtr())->getShapes(meshZero); } - mTextmappings = NIFLoader::getSingletonPtr()->getTextIndices(meshZero); - mInsert->attachObject(mBase); } } void CreatureAnimation::runAnimation(float timepassed) { - mVecRotPos.clear(); - if(mAnimate > 0) - { - //Add the amount of time passed to time + // Placeholder - //Handle the animation transforms dependent on time - - //Handle the shapes dependent on animation transforms - mTime += timepassed; - if(mTime >= mStopTime) - { - mAnimate--; - //std::cout << "Stopping the animation\n"; - if(mAnimate == 0) - mTime = mStopTime; - else - mTime = mStartTime + (mTime - mStopTime); - } - - handleAnimationTransforms(); - handleShapes(mShapes, mBase, mBase->getSkeleton()); - - } + Animation::runAnimation(timepassed); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ef075b12b..4f98aebc4 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -13,256 +13,161 @@ using namespace Ogre; using namespace NifOgre; + namespace MWRender{ NpcAnimation::~NpcAnimation() { + removeEntities(head); + removeEntities(hair); + removeEntities(neck); + removeEntities(chest); + removeEntities(groin); + removeEntities(skirt); + removeEntities(rHand); + removeEntities(lHand); + removeEntities(rWrist); + removeEntities(lWrist); + removeEntities(rForearm); + removeEntities(lForearm); + removeEntities(rupperArm); + removeEntities(lupperArm); + removeEntities(rfoot); + removeEntities(lfoot); + removeEntities(rAnkle); + removeEntities(lAnkle); + removeEntities(rKnee); + removeEntities(lKnee); + removeEntities(rUpperLeg); + removeEntities(lUpperLeg); + removeEntities(rclavicle); + removeEntities(lclavicle); + removeEntities(tail); } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv): Animation(_rend), mStateID(-1), mInv(_inv), timeToChange(0), +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv) + : Animation(_rend), mStateID(-1), mInv(_inv), timeToChange(0), robe(mInv.end()), helmet(mInv.end()), shirt(mInv.end()), cuirass(mInv.end()), greaves(mInv.end()), leftpauldron(mInv.end()), rightpauldron(mInv.end()), boots(mInv.end()), leftglove(mInv.end()), rightglove(mInv.end()), skirtiter(mInv.end()), - pants(mInv.end()), - lclavicle(0), - rclavicle(0), - rupperArm(0), - lupperArm(0), - rUpperLeg(0), - lUpperLeg(0), - lForearm(0), - rForearm(0), - lWrist(0), - rWrist(0), - rKnee(0), - lKnee(0), - neck(0), - rAnkle(0), - lAnkle(0), - groin(0), - lfoot(0), - rfoot(0) + pants(mInv.end()) { MWWorld::LiveCellRef *ref = ptr.get(); - Ogre::Entity* blank = 0; - std::vector* blankshape = 0; - mZero = std::make_pair(blank, blankshape); - mChest = std::make_pair(blank, blankshape); - mTail = std::make_pair(blank, blankshape); - mLFreeFoot = std::make_pair(blank, blankshape); - mRFreeFoot = std::make_pair(blank, blankshape); - mRhand = std::make_pair(blank, blankshape); - mLhand = std::make_pair(blank, blankshape); - mSkirt = std::make_pair(blank, blankshape); + for (int init = 0; init < 27; init++) { mPartslots[init] = -1; //each slot is empty mPartPriorities[init] = 0; } - - //Part selection on last character of the file string - // " Tri Chest - // * Tri Tail - // : Tri Left Foot - // < Tri Right Foot - // > Tri Left Hand - // ? Tri Right Hand - // | Normal - - //Mirroring Parts on second to last character - //suffix == '*' - // vector = Ogre::Vector3(-1,1,1); - // suffix == '?' - // vector = Ogre::Vector3(1,-1,1); - // suffix == '<' - // vector = Ogre::Vector3(1,1,-1); - + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Race *race = store.races.find(ref->base->race); std::string hairID = ref->base->hair; std::string headID = ref->base->head; - headModel = "meshes\\" + - MWBase::Environment::get().getWorld()->getStore().bodyParts.find(headID)->model; - - hairModel = "meshes\\" + - MWBase::Environment::get().getWorld()->getStore().bodyParts.find(hairID)->model; + headModel = "meshes\\" + store.bodyParts.find(headID)->model; + hairModel = "meshes\\" + store.bodyParts.find(hairID)->model; npcName = ref->base->name; - //ESMStore::Races r = - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().races.find(ref->base->race); + isFemale = !!(ref->base->flags&ESM::NPC::Female); + isBeast = !!(race->data.flags&ESM::Race::Beast); - - bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); - char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2); - isFemale = tolower(secondtolast) == 'f'; + bodyRaceID = "b_n_"+ref->base->race; std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); - isBeast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; /*std::cout << "Race: " << ref->base->race ; - if(female){ - std::cout << " Sex: Female" << " Height: " << race->data.height.female << "\n"; - } - else{ - std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; - }*/ - - - std::string smodel = "meshes\\base_anim.nif"; - if(isBeast) - smodel = "meshes\\base_animkna.nif"; + if(female) + std::cout << " Sex: Female" << " Height: " << race->data.height.female << "\n"; + else + std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; + */ mInsert = ptr.getRefData().getBaseNode(); assert(mInsert); - - NifOgre::NIFLoader::load(smodel); - mBase = mRend.getScene()->createEntity(smodel); + std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - mBase->setVisibilityFlags(RV_Actors); - bool transparent = false; - for (unsigned int i=0; igetNumSubEntities(); ++i) + mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, smodel); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) { - Ogre::MaterialPtr mat = mBase->getSubEntity(i)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) - { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) - { - Ogre::Pass* pass = passIt.getNext(); + Ogre::Entity *base = mEntityList.mEntities[i]; - if (pass->getDepthWriteEnabled() == false) - transparent = true; + base->setVisibilityFlags(RV_Actors); + bool transparent = false; + for(unsigned int j=0;j < base->getNumSubEntities();++j) + { + Ogre::MaterialPtr mat = base->getSubEntity(j)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements()) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } } } + base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } - mBase->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - - mBase->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones - //stay in the same place when we skipanim, or open a gui window - - - if((mTransformations = (NIFLoader::getSingletonPtr())->getAnim(smodel))) + if(mEntityList.mSkelBase) { - - for(unsigned int init = 0; init < mTransformations->size(); init++) + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); + while(as.hasMoreElements()) { - mRindexI.push_back(0); - mTindexI.push_back(0); + Ogre::AnimationState *state = as.getNext(); + state->setEnabled(true); + state->setLoop(false); } - - mStopTime = mTransformations->begin()->getStopTime(); - mStartTime = mTransformations->begin()->getStartTime(); } - mTextmappings = NIFLoader::getSingletonPtr()->getTextIndices(smodel); - mInsert->attachObject(mBase); - if(isFemale) mInsert->scale(race->data.height.female, race->data.height.female, race->data.height.female); else mInsert->scale(race->data.height.male, race->data.height.male, race->data.height.male); updateParts(); - } void NpcAnimation::updateParts() { - bool apparelChanged = false; - - //mInv.getSlot(MWWorld::InventoryStore::Slot_Robe); - if(robe != mInv.getSlot(MWWorld::InventoryStore::Slot_Robe)) + const struct { + MWWorld::ContainerStoreIterator *iter; + int slot; + } slotlist[] = { + { &robe, MWWorld::InventoryStore::Slot_Robe }, + { &skirtiter, MWWorld::InventoryStore::Slot_Skirt }, + { &helmet, MWWorld::InventoryStore::Slot_Helmet }, + { &cuirass, MWWorld::InventoryStore::Slot_Cuirass }, + { &greaves, MWWorld::InventoryStore::Slot_Greaves }, + { &leftpauldron, MWWorld::InventoryStore::Slot_LeftPauldron }, + { &rightpauldron, MWWorld::InventoryStore::Slot_RightPauldron }, + { &boots, MWWorld::InventoryStore::Slot_Boots }, + { &leftglove, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { &rightglove, MWWorld::InventoryStore::Slot_RightGauntlet }, + { &shirt, MWWorld::InventoryStore::Slot_Shirt }, + { &pants, MWWorld::InventoryStore::Slot_Pants }, + }; + for(size_t i = 0;i < sizeof(slotlist)/sizeof(slotlist[0]);i++) { - //A robe was added or removed - removePartGroup(MWWorld::InventoryStore::Slot_Robe); - robe = mInv.getSlot(MWWorld::InventoryStore::Slot_Robe); - apparelChanged = true; - } - if(skirtiter != mInv.getSlot(MWWorld::InventoryStore::Slot_Skirt)) - { - //A robe was added or removed - removePartGroup(MWWorld::InventoryStore::Slot_Skirt); - skirtiter = mInv.getSlot(MWWorld::InventoryStore::Slot_Skirt); - apparelChanged = true; - } - if(helmet != mInv.getSlot(MWWorld::InventoryStore::Slot_Helmet)) - { - apparelChanged = true; - helmet = mInv.getSlot(MWWorld::InventoryStore::Slot_Helmet); - removePartGroup(MWWorld::InventoryStore::Slot_Helmet); - - } - if(cuirass != mInv.getSlot(MWWorld::InventoryStore::Slot_Cuirass)) - { - cuirass = mInv.getSlot(MWWorld::InventoryStore::Slot_Cuirass); - removePartGroup(MWWorld::InventoryStore::Slot_Cuirass); - apparelChanged = true; - - } - if(greaves != mInv.getSlot(MWWorld::InventoryStore::Slot_Greaves)) - { - greaves = mInv.getSlot(MWWorld::InventoryStore::Slot_Greaves); - removePartGroup(MWWorld::InventoryStore::Slot_Greaves); - apparelChanged = true; - } - if(leftpauldron != mInv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron)) - { - leftpauldron = mInv.getSlot(MWWorld::InventoryStore::Slot_LeftPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_LeftPauldron); - apparelChanged = true; - - } - if(rightpauldron != mInv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron)) - { - rightpauldron = mInv.getSlot(MWWorld::InventoryStore::Slot_RightPauldron); - removePartGroup(MWWorld::InventoryStore::Slot_RightPauldron); - apparelChanged = true; - - } - if(!isBeast && boots != mInv.getSlot(MWWorld::InventoryStore::Slot_Boots)) - { - boots = mInv.getSlot(MWWorld::InventoryStore::Slot_Boots); - removePartGroup(MWWorld::InventoryStore::Slot_Boots); - apparelChanged = true; - - } - if(leftglove != mInv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet)) - { - leftglove = mInv.getSlot(MWWorld::InventoryStore::Slot_LeftGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet); - apparelChanged = true; - - } - if(rightglove != mInv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet)) - { - rightglove = mInv.getSlot(MWWorld::InventoryStore::Slot_RightGauntlet); - removePartGroup(MWWorld::InventoryStore::Slot_RightGauntlet); - apparelChanged = true; - - } - if(shirt != mInv.getSlot(MWWorld::InventoryStore::Slot_Shirt)) - { - shirt = mInv.getSlot(MWWorld::InventoryStore::Slot_Shirt); - removePartGroup(MWWorld::InventoryStore::Slot_Shirt); - apparelChanged = true; - - } - if(pants != mInv.getSlot(MWWorld::InventoryStore::Slot_Pants)) - { - pants = mInv.getSlot(MWWorld::InventoryStore::Slot_Pants); - removePartGroup(MWWorld::InventoryStore::Slot_Pants); - apparelChanged = true; + MWWorld::ContainerStoreIterator iter = mInv.getSlot(slotlist[i].slot); + if(*slotlist[i].iter != iter) + { + *slotlist[i].iter = iter; + removePartGroup(slotlist[i].slot); + apparelChanged = true; + } } if(apparelChanged) { - if(robe != mInv.end()) { MWWorld::Ptr ptr = *robe; @@ -301,14 +206,12 @@ void NpcAnimation::updateParts() const ESM::Armor *armor = (helmet->get())->base; std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts); - } if(cuirass != mInv.end()) { const ESM::Armor *armor = (cuirass->get())->base; std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts); - } if(greaves != mInv.end()) { @@ -322,16 +225,14 @@ void NpcAnimation::updateParts() const ESM::Armor *armor = (leftpauldron->get())->base; std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts); - } if(rightpauldron != mInv.end()) { const ESM::Armor *armor = (rightpauldron->get())->base; std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); - } - if(!isBeast && boots != mInv.end()) + if(boots != mInv.end()) { if(boots->getTypeName() == typeid(ESM::Clothing).name()) { @@ -341,13 +242,13 @@ void NpcAnimation::updateParts() } else if(boots->getTypeName() == typeid(ESM::Armor).name()) { - const ESM::Armor *armor = (boots->get())->base; + const ESM::Armor *armor = (boots->get())->base; std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts); } - } - if(leftglove != mInv.end()){ + if(leftglove != mInv.end()) + { if(leftglove->getTypeName() == typeid(ESM::Clothing).name()) { const ESM::Clothing *clothes = (leftglove->get())->base; @@ -356,13 +257,13 @@ void NpcAnimation::updateParts() } else { - const ESM::Armor *armor = (leftglove->get())->base; + const ESM::Armor *armor = (leftglove->get())->base; std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts); } - } - if(rightglove != mInv.end()){ + if(rightglove != mInv.end()) + { if(rightglove->getTypeName() == typeid(ESM::Clothing).name()) { const ESM::Clothing *clothes = (rightglove->get())->base; @@ -371,7 +272,7 @@ void NpcAnimation::updateParts() } else { - const ESM::Armor *armor = (rightglove->get())->base; + const ESM::Armor *armor = (rightglove->get())->base; std::vector parts = armor->parts.parts; addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts); } @@ -393,541 +294,291 @@ void NpcAnimation::updateParts() } if(mPartPriorities[ESM::PRT_Head] < 1) - { - addOrReplaceIndividualPart(ESM::PRT_Head, -1,1,headModel); - } + addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, headModel); if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1) - { - addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1,hairModel); - } - if(mPartPriorities[ESM::PRT_Neck] < 1) - { - const ESM::BodyPart *neckPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "neck"); - if(neckPart) - addOrReplaceIndividualPart(ESM::PRT_Neck, -1,1,"meshes\\" + neckPart->model); - } - if(mPartPriorities[ESM::PRT_Cuirass] < 1) - { - const ESM::BodyPart *chestPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "chest"); - if(chestPart) - addOrReplaceIndividualPart(ESM::PRT_Cuirass, -1,1,"meshes\\" + chestPart->model); - } + addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, hairModel); - if(mPartPriorities[ESM::PRT_Groin] < 1) - { - const ESM::BodyPart *groinPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "groin"); - if(groinPart) - addOrReplaceIndividualPart(ESM::PRT_Groin, -1,1,"meshes\\" + groinPart->model); - } - if(mPartPriorities[ESM::PRT_RHand] < 1) - { - const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); - if(!handPart) - handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); - if(handPart) - addOrReplaceIndividualPart(ESM::PRT_RHand, -1,1,"meshes\\" + handPart->model); - } - if(mPartPriorities[ESM::PRT_LHand] < 1) - { - const ESM::BodyPart *handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hand"); - if(!handPart) - handPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "hands"); - if(handPart) - addOrReplaceIndividualPart(ESM::PRT_LHand, -1,1,"meshes\\" + handPart->model); - } + static const struct { + ESM::PartReferenceType type; + const char name[2][12]; + } PartTypeList[] = { + { ESM::PRT_Neck, { "neck", "" } }, + { ESM::PRT_Cuirass, { "chest", "" } }, + { ESM::PRT_Groin, { "groin", "" } }, + { ESM::PRT_RHand, { "hand", "hands" } }, + { ESM::PRT_LHand, { "hand", "hands" } }, + { ESM::PRT_RWrist, { "wrist", "" } }, + { ESM::PRT_LWrist, { "wrist", "" } }, + { ESM::PRT_RForearm, { "forearm", "" } }, + { ESM::PRT_LForearm, { "forearm", "" } }, + { ESM::PRT_RUpperarm, { "upper arm", "" } }, + { ESM::PRT_LUpperarm, { "upper arm", "" } }, + { ESM::PRT_RFoot, { "foot", "feet" } }, + { ESM::PRT_LFoot, { "foot", "feet" } }, + { ESM::PRT_RAnkle, { "ankle", "" } }, + { ESM::PRT_LAnkle, { "ankle", "" } }, + { ESM::PRT_RKnee, { "knee", "" } }, + { ESM::PRT_LKnee, { "knee", "" } }, + { ESM::PRT_RLeg, { "upper leg", "" } }, + { ESM::PRT_LLeg, { "upper leg", "" } }, + { ESM::PRT_Tail, { "tail", "" } } + }; - if(mPartPriorities[ESM::PRT_RWrist] < 1) + const ESMS::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) { - const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); - if(wristPart) - addOrReplaceIndividualPart(ESM::PRT_RWrist, -1,1,"meshes\\" + wristPart->model); - } - if(mPartPriorities[ESM::PRT_LWrist] < 1) - { - const ESM::BodyPart *wristPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "wrist"); - if(wristPart) - addOrReplaceIndividualPart(ESM::PRT_LWrist, -1,1,"meshes\\" + wristPart->model); - } - if(mPartPriorities[ESM::PRT_RForearm] < 1) - { - const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); - if(bodyRaceID == "b_n_argonian_f_") - forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); - if(forearmPart) - addOrReplaceIndividualPart(ESM::PRT_RForearm, -1,1,"meshes\\" + forearmPart->model); - } - if(mPartPriorities[ESM::PRT_LForearm] < 1) - { - const ESM::BodyPart *forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "forearm"); - if(bodyRaceID == "b_n_argonian_f_") - forearmPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search ("b_n_argonian_m_forearm"); - if(forearmPart) - addOrReplaceIndividualPart(ESM::PRT_LForearm, -1,1,"meshes\\" + forearmPart->model); - } - if(mPartPriorities[ESM::PRT_RUpperarm] < 1) - { - const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); - if(armPart) - addOrReplaceIndividualPart(ESM::PRT_RUpperarm, -1,1,"meshes\\" + armPart->model); - } - if(mPartPriorities[ESM::PRT_LUpperarm] < 1) - { - const ESM::BodyPart *armPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper arm"); - if(armPart) - addOrReplaceIndividualPart(ESM::PRT_LUpperarm, -1,1,"meshes\\" + armPart->model); - } - if(mPartPriorities[ESM::PRT_RFoot] < 1) - { - const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); - if(isBeast && !footPart) - footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); - if(footPart) - addOrReplaceIndividualPart(ESM::PRT_RFoot, -1,1,"meshes\\" + footPart->model); - } - if(mPartPriorities[ESM::PRT_LFoot] < 1) - { - const ESM::BodyPart *footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "foot"); - if(isBeast && !footPart) - footPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "feet"); - if(footPart) - addOrReplaceIndividualPart(ESM::PRT_LFoot, -1,1,"meshes\\" + footPart->model); - } - if(mPartPriorities[ESM::PRT_RAnkle] < 1) - { - const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); - if(anklePart) - addOrReplaceIndividualPart(ESM::PRT_RAnkle, -1,1,"meshes\\" + anklePart->model); - } - if(mPartPriorities[ESM::PRT_LAnkle] < 1) - { - const ESM::BodyPart *anklePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "ankle"); - if(anklePart) - addOrReplaceIndividualPart(ESM::PRT_LAnkle, -1,1,"meshes\\" + anklePart->model); - } - if(mPartPriorities[ESM::PRT_RKnee] < 1) - { - const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); - if(kneePart) - addOrReplaceIndividualPart(ESM::PRT_RKnee, -1,1,"meshes\\" + kneePart->model); - } - if(mPartPriorities[ESM::PRT_LKnee] < 1) - { - const ESM::BodyPart *kneePart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "knee"); - if(kneePart) - addOrReplaceIndividualPart(ESM::PRT_LKnee, -1,1,"meshes\\" + kneePart->model); - } - if(mPartPriorities[ESM::PRT_RLeg] < 1) - { - const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); - if(legPart) - addOrReplaceIndividualPart(ESM::PRT_RLeg, -1,1,"meshes\\" + legPart->model); - } - if(mPartPriorities[ESM::PRT_LLeg] < 1) - { - const ESM::BodyPart *legPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "upper leg"); - if(legPart) - addOrReplaceIndividualPart(ESM::PRT_LLeg, -1,1,"meshes\\" + legPart->model); - } - if(mPartPriorities[ESM::PRT_Tail] < 1) - { - const ESM::BodyPart *tailPart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (bodyRaceID + "tail"); - if(tailPart) - addOrReplaceIndividualPart(ESM::PRT_Tail, -1,1,"meshes\\" + tailPart->model); - } - -} - -Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, std::string bonename) -{ - NIFLoader::load(mesh); - Ogre::Entity* part = mRend.getScene()->createEntity(mesh); - part->setVisibilityFlags(RV_Actors); - - mBase->attachObjectToBone(bonename, part); - return part; -} -void NpcAnimation::insertFootPart(int type, const std::string &mesh) -{ - std::string meshAndSuffix = mesh; - if(type == ESM::PRT_LFoot) - meshAndSuffix += "*|"; - NIFLoader::load(meshAndSuffix); - Ogre::Entity* part = mRend.getScene()->createEntity(meshAndSuffix); - std::vector* shape = ((NIFLoader::getSingletonPtr())->getShapes(meshAndSuffix)); - if(shape == 0) - { - if(type == ESM::PRT_LFoot) + if(mPartPriorities[PartTypeList[i].type] < 1) { - mBase->attachObjectToBone("Left Foot", part); - lfoot = part; - } - else if (type == ESM::PRT_RFoot) - { - mBase->attachObjectToBone("Right Foot", part); - rfoot = part; + const ESM::BodyPart *part = NULL; + bool tryfemale = isFemale; + int ni = 0; + do { + part = store.bodyParts.search(bodyRaceID+(tryfemale?"_f_":"_m_")+PartTypeList[i].name[ni]); + if(part) break; + + ni ^= 1; + if(ni == 0) + { + if(!tryfemale) + break; + tryfemale = false; + } + } while(1); + + if(part) + addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->model); } } - else - { - if(type == ESM::PRT_LFoot) - mLFreeFoot = insertFreePart(mesh, "::"); - else if (type == ESM::PRT_RFoot) - mRFreeFoot = insertFreePart(mesh, ":<"); - } - } -std::pair*> NpcAnimation::insertFreePart(const std::string &mesh, const std::string& suffix) +NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) { - std::string meshNumbered = mesh + getUniqueID(mesh + suffix) + suffix; - NIFLoader::load(meshNumbered); - - Ogre::Entity* part = mRend.getScene()->createEntity(meshNumbered); - part->setVisibilityFlags(RV_Actors); - - mInsert->attachObject(part); - - std::vector* shape = ((NIFLoader::getSingletonPtr())->getShapes(mesh + "0000" + suffix)); - if(shape) - handleShapes(shape, part, mBase->getSkeleton()); - std::pair*> pair = std::make_pair(part, shape); - return pair; + NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, + mInsert, mesh); + std::vector &parts = entities.mEntities; + for(size_t i = 0;i < parts.size();i++) + parts[i]->setVisibilityFlags(RV_Actors); + return entities; } void NpcAnimation::runAnimation(float timepassed) { - - if(timeToChange > .2){ + if(timeToChange > .2) + { timeToChange = 0; updateParts(); } - timeToChange += timepassed; - //1. Add the amount of time passed to time - - //2. Handle the animation transforms dependent on time - - //3. Handle the shapes dependent on animation transforms - if(mAnimate > 0) - { - mTime += timepassed; - - if(mTime > mStopTime) - { - mAnimate--; - - if(mAnimate == 0) - mTime = mStopTime; - else - mTime = mStartTime + (mTime - mStopTime); - } - - handleAnimationTransforms(); - - mVecRotPos.clear(); - - if(mLFreeFoot.first) - handleShapes(mLFreeFoot.second, mLFreeFoot.first, mBase->getSkeleton()); - if(mRFreeFoot.first) - handleShapes(mRFreeFoot.second, mRFreeFoot.first, mBase->getSkeleton()); - - if(mChest.first) - handleShapes(mChest.second, mChest.first, mBase->getSkeleton()); - if(mTail.first) - handleShapes(mTail.second, mTail.first, mBase->getSkeleton()); - if(mSkirt.first) - handleShapes(mSkirt.second, mSkirt.first, mBase->getSkeleton()); - if(mLhand.first) - handleShapes(mLhand.second, mLhand.first, mBase->getSkeleton()); - if(mRhand.first) - handleShapes(mRhand.second, mRhand.first, mBase->getSkeleton()); - - } + Animation::runAnimation(timepassed); } -void NpcAnimation::removeIndividualPart(int type){ +void NpcAnimation::removeEntities(NifOgre::EntityList &entities) +{ + assert(&entities != &mEntityList); + + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + for(size_t i = 0;i < entities.mEntities.size();i++) + { + entities.mEntities[i]->detachFromParent(); + sceneMgr->destroyEntity(entities.mEntities[i]); + } + entities.mEntities.clear(); + entities.mSkelBase = NULL; +} + +void NpcAnimation::removeIndividualPart(int type) +{ mPartPriorities[type] = 0; mPartslots[type] = -1; - if(type == ESM::PRT_Head && head) //0 - { - mBase->detachObjectFromBone(head); - head = 0; - } - else if(type == ESM::PRT_Hair && hair) //1 - { - mBase->detachObjectFromBone(hair); - hair = 0; - } - else if(type == ESM::PRT_Neck && neck) //2 - { - mBase->detachObjectFromBone(neck); - neck = 0; - } - else if(type == ESM::PRT_Cuirass && mChest.first) //3 - { - mInsert->detachObject(mChest.first); - mChest = mZero; - } - else if(type == ESM::PRT_Groin && groin) //4 - { - mBase->detachObjectFromBone(groin); - groin = 0; - } - else if(type == ESM::PRT_Skirt && mSkirt.first) //5 - { - mInsert->detachObject(mSkirt.first); - mSkirt = mZero; - } - else if(type == ESM::PRT_RHand && mRhand.first) //6 - { - mInsert->detachObject(mRhand.first); - mRhand = mZero; - } - else if(type == ESM::PRT_LHand && mLhand.first) //7 - { - mInsert->detachObject(mLhand.first); - mLhand = mZero; - } - else if(type == ESM::PRT_RWrist && rWrist) //8 - { - mBase->detachObjectFromBone(rWrist); - rWrist = 0; - } - else if(type == ESM::PRT_LWrist && lWrist) //9 - { - mBase->detachObjectFromBone(lWrist); - lWrist = 0; - } - else if(type == ESM::PRT_Shield) //10 - { - - } - else if(type == ESM::PRT_RForearm && rForearm) //11 - { - mBase->detachObjectFromBone(rForearm); - rForearm = 0; - } - else if(type == ESM::PRT_LForearm && lForearm) //12 - { - mBase->detachObjectFromBone(lForearm); - lForearm = 0; - } - else if(type == ESM::PRT_RUpperarm && rupperArm) //13 - { - mBase->detachObjectFromBone(rupperArm); - rupperArm = 0; - } - else if(type == ESM::PRT_LUpperarm && lupperArm) //14 - { - mBase->detachObjectFromBone(lupperArm); - lupperArm = 0; - } - else if(type == ESM::PRT_RFoot) //15 - { - if(rfoot) - { - mBase->detachObjectFromBone(rfoot); - rfoot = 0; - } - else if(mRFreeFoot.first) - { - mInsert->detachObject(mRFreeFoot.first); - mRFreeFoot = mZero; - } - } - else if(type == ESM::PRT_LFoot) //16 - { - if(lfoot) - { - mBase->detachObjectFromBone(lfoot); - lfoot = 0; - } - else if(mLFreeFoot.first) - { - mInsert->detachObject(mLFreeFoot.first); - mLFreeFoot = mZero; - } - } - else if(type == ESM::PRT_RAnkle && rAnkle) //17 - { - mBase->detachObjectFromBone(rAnkle); - rAnkle = 0; - } - else if(type == ESM::PRT_LAnkle && lAnkle) //18 - { - mBase->detachObjectFromBone(lAnkle); - lAnkle = 0; - } - else if(type == ESM::PRT_RKnee && rKnee) //19 - { - mBase->detachObjectFromBone(rKnee); - rKnee = 0; - } - else if(type == ESM::PRT_LKnee && lKnee) //20 - { - mBase->detachObjectFromBone(lKnee); - lKnee = 0; - } - else if(type == ESM::PRT_RLeg && rUpperLeg) //21 - { - mBase->detachObjectFromBone(rUpperLeg); - rUpperLeg = 0; - } - else if(type == ESM::PRT_LLeg && lUpperLeg) //22 - { - mBase->detachObjectFromBone(lUpperLeg); - lUpperLeg = 0; - } - else if(type == ESM::PRT_RPauldron && rclavicle) //23 - { - mBase->detachObjectFromBone(rclavicle); - rclavicle = 0; - } - else if(type == ESM::PRT_LPauldron && lclavicle) //24 - { - mBase->detachObjectFromBone(lclavicle); - lclavicle = 0; - } - else if(type == ESM::PRT_Weapon) //25 - { - - } - else if(type == ESM::PRT_Tail && mTail.first) //26 - { - mInsert->detachObject(mTail.first); - mTail = mZero; - } - - } - - void NpcAnimation::reserveIndividualPart(int type, int group, int priority) + if(type == ESM::PRT_Head) //0 + removeEntities(head); + else if(type == ESM::PRT_Hair) //1 + removeEntities(hair); + else if(type == ESM::PRT_Neck) //2 + removeEntities(neck); + else if(type == ESM::PRT_Cuirass)//3 + removeEntities(chest); + else if(type == ESM::PRT_Groin)//4 + removeEntities(groin); + else if(type == ESM::PRT_Skirt)//5 + removeEntities(skirt); + else if(type == ESM::PRT_RHand)//6 + removeEntities(rHand); + else if(type == ESM::PRT_LHand)//7 + removeEntities(lHand); + else if(type == ESM::PRT_RWrist)//8 + removeEntities(rWrist); + else if(type == ESM::PRT_LWrist) //9 + removeEntities(lWrist); + else if(type == ESM::PRT_Shield) //10 { - if(priority > mPartPriorities[type]) - { - removeIndividualPart(type); - mPartPriorities[type] = priority; - mPartslots[type] = group; - } } - - void NpcAnimation::removePartGroup(int group) + else if(type == ESM::PRT_RForearm) //11 + removeEntities(rForearm); + else if(type == ESM::PRT_LForearm) //12 + removeEntities(lForearm); + else if(type == ESM::PRT_RUpperarm) //13 + removeEntities(rupperArm); + else if(type == ESM::PRT_LUpperarm) //14 + removeEntities(lupperArm); + else if(type == ESM::PRT_RFoot) //15 + removeEntities(rfoot); + else if(type == ESM::PRT_LFoot) //16 + removeEntities(lfoot); + else if(type == ESM::PRT_RAnkle) //17 + removeEntities(rAnkle); + else if(type == ESM::PRT_LAnkle) //18 + removeEntities(lAnkle); + else if(type == ESM::PRT_RKnee) //19 + removeEntities(rKnee); + else if(type == ESM::PRT_LKnee) //20 + removeEntities(lKnee); + else if(type == ESM::PRT_RLeg) //21 + removeEntities(rUpperLeg); + else if(type == ESM::PRT_LLeg) //22 + removeEntities(lUpperLeg); + else if(type == ESM::PRT_RPauldron) //23 + removeEntities(rclavicle); + else if(type == ESM::PRT_LPauldron) //24 + removeEntities(lclavicle); + else if(type == ESM::PRT_Weapon) //25 { - for(int i = 0; i < 27; i++) - if(mPartslots[i] == group) - removeIndividualPart(i); } - bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh) + else if(type == ESM::PRT_Tail) //26 + removeEntities(tail); +} + +void NpcAnimation::reserveIndividualPart(int type, int group, int priority) +{ + if(priority > mPartPriorities[type]) { - if(priority > mPartPriorities[type]) - { - removeIndividualPart(type); - mPartslots[type] = group; - mPartPriorities[type] = priority; - switch(type) - { - case ESM::PRT_Head: //0 - head = insertBoundedPart(mesh, "Head"); - break; - case ESM::PRT_Hair: //1 - hair = insertBoundedPart(mesh, "Head"); - break; - case ESM::PRT_Neck: //2 - neck = insertBoundedPart(mesh, "Neck"); - break; - case ESM::PRT_Cuirass: //3 - mChest = insertFreePart(mesh, ":\""); - break; - case ESM::PRT_Groin: //4 - groin = insertBoundedPart(mesh, "Groin"); - break; - case ESM::PRT_Skirt: //5 - mSkirt = insertFreePart(mesh, ":|"); - break; - case ESM::PRT_RHand: //6 - mRhand = insertFreePart(mesh, ":?"); - break; - case ESM::PRT_LHand: //7 - mLhand = insertFreePart(mesh, ":>"); - break; - case ESM::PRT_RWrist: //8 - rWrist = insertBoundedPart(mesh, "Right Wrist"); - break; - case ESM::PRT_LWrist: //9 - lWrist = insertBoundedPart(mesh + "*|", "Left Wrist"); - break; - case ESM::PRT_Shield: //10 - break; - case ESM::PRT_RForearm: //11 - rForearm = insertBoundedPart(mesh, "Right Forearm"); - break; - case ESM::PRT_LForearm: //12 - lForearm = insertBoundedPart(mesh + "*|", "Left Forearm"); - break; - case ESM::PRT_RUpperarm: //13 - rupperArm = insertBoundedPart(mesh, "Right Upper Arm"); - break; - case ESM::PRT_LUpperarm: //14 - lupperArm = insertBoundedPart(mesh + "*|", "Left Upper Arm"); - break; - case ESM::PRT_RFoot: //15 - insertFootPart(type, mesh); - break; - case ESM::PRT_LFoot: //16 - insertFootPart(type, mesh); - break; - case ESM::PRT_RAnkle: //17 - rAnkle = insertBoundedPart(mesh , "Right Ankle"); - break; - case ESM::PRT_LAnkle: //18 - lAnkle = insertBoundedPart(mesh + "*|", "Left Ankle"); - break; - case ESM::PRT_RKnee: //19 - rKnee = insertBoundedPart(mesh , "Right Knee"); - break; - case ESM::PRT_LKnee: //20 - lKnee = insertBoundedPart(mesh + "*|", "Left Knee"); - break; - case ESM::PRT_RLeg: //21 - rUpperLeg = insertBoundedPart(mesh, "Right Upper Leg"); - break; - case ESM::PRT_LLeg: //22 - lUpperLeg = insertBoundedPart(mesh + "*|", "Left Upper Leg"); - break; - case ESM::PRT_RPauldron: //23 - rclavicle = insertBoundedPart(mesh , "Right Clavicle"); - break; - case ESM::PRT_LPauldron: //24 - lclavicle = insertBoundedPart(mesh + "*|", "Left Clavicle"); - break; - case ESM::PRT_Weapon: //25 - break; - case ESM::PRT_Tail: //26 - mTail = insertFreePart(mesh, ":*"); - break; - } - return true; - } - return false; - } - - void NpcAnimation::addPartGroup(int group, int priority, std::vector& parts) - { - for(std::size_t i = 0; i < parts.size(); i++) - { - ESM::PartReference part = parts[i]; - - const ESM::BodyPart *bodypart = 0; - - if(isFemale) - bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.female); - if(!bodypart) - bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search (part.male); - if(bodypart) - addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); - else - reserveIndividualPart(part.part, group, priority); - } + removeIndividualPart(type); + mPartPriorities[type] = priority; + mPartslots[type] = group; } } + +void NpcAnimation::removePartGroup(int group) +{ + for(int i = 0; i < 27; i++) + { + if(mPartslots[i] == group) + removeIndividualPart(i); + } +} + +bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh) +{ + if(priority <= mPartPriorities[type]) + return false; + + removeIndividualPart(type); + mPartslots[type] = group; + mPartPriorities[type] = priority; + switch(type) + { + case ESM::PRT_Head: //0 + head = insertBoundedPart(mesh, "Head"); + break; + case ESM::PRT_Hair: //1 + hair = insertBoundedPart(mesh, "Head"); + break; + case ESM::PRT_Neck: //2 + neck = insertBoundedPart(mesh, "Neck"); + break; + case ESM::PRT_Cuirass: //3 + chest = insertBoundedPart(mesh, "Chest"); + break; + case ESM::PRT_Groin: //4 + groin = insertBoundedPart(mesh, "Groin"); + break; + case ESM::PRT_Skirt: //5 + skirt = insertBoundedPart(mesh, "Groin"); + break; + case ESM::PRT_RHand: //6 + rHand = insertBoundedPart(mesh, "Right Hand"); + break; + case ESM::PRT_LHand: //7 + lHand = insertBoundedPart(mesh, "Left Hand"); + break; + case ESM::PRT_RWrist: //8 + rWrist = insertBoundedPart(mesh, "Right Wrist"); + break; + case ESM::PRT_LWrist: //9 + lWrist = insertBoundedPart(mesh, "Left Wrist"); + break; + case ESM::PRT_Shield: //10 + break; + case ESM::PRT_RForearm: //11 + rForearm = insertBoundedPart(mesh, "Right Forearm"); + break; + case ESM::PRT_LForearm: //12 + lForearm = insertBoundedPart(mesh, "Left Forearm"); + break; + case ESM::PRT_RUpperarm: //13 + rupperArm = insertBoundedPart(mesh, "Right Upper Arm"); + break; + case ESM::PRT_LUpperarm: //14 + lupperArm = insertBoundedPart(mesh, "Left Upper Arm"); + break; + case ESM::PRT_RFoot: //15 + rfoot = insertBoundedPart(mesh, "Right Foot"); + break; + case ESM::PRT_LFoot: //16 + lfoot = insertBoundedPart(mesh, "Left Foot"); + break; + case ESM::PRT_RAnkle: //17 + rAnkle = insertBoundedPart(mesh, "Right Ankle"); + break; + case ESM::PRT_LAnkle: //18 + lAnkle = insertBoundedPart(mesh, "Left Ankle"); + break; + case ESM::PRT_RKnee: //19 + rKnee = insertBoundedPart(mesh, "Right Knee"); + break; + case ESM::PRT_LKnee: //20 + lKnee = insertBoundedPart(mesh, "Left Knee"); + break; + case ESM::PRT_RLeg: //21 + rUpperLeg = insertBoundedPart(mesh, "Right Upper Leg"); + break; + case ESM::PRT_LLeg: //22 + lUpperLeg = insertBoundedPart(mesh, "Left Upper Leg"); + break; + case ESM::PRT_RPauldron: //23 + rclavicle = insertBoundedPart(mesh , "Right Clavicle"); + break; + case ESM::PRT_LPauldron: //24 + lclavicle = insertBoundedPart(mesh, "Left Clavicle"); + break; + case ESM::PRT_Weapon: //25 + break; + case ESM::PRT_Tail: //26 + tail = insertBoundedPart(mesh, "Tail"); + break; + } + return true; +} + +void NpcAnimation::addPartGroup(int group, int priority, std::vector &parts) +{ + for(std::size_t i = 0; i < parts.size(); i++) + { + ESM::PartReference &part = parts[i]; + + const ESM::BodyPart *bodypart = 0; + if(isFemale) + bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search(part.female); + if(!bodypart) + bodypart = MWBase::Environment::get().getWorld()->getStore().bodyParts.search(part.male); + + if(bodypart) + addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); + else + reserveIndividualPart(part.part, group, priority); + } +} + +} diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 2f8cb97da..d4b2a5b9e 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -14,42 +14,37 @@ class NpcAnimation: public Animation{ private: MWWorld::InventoryStore& mInv; int mStateID; - //Free Parts - std::pair*> mChest; - std::pair*> mSkirt; - std::pair*> mLhand; - std::pair*> mRhand; - std::pair*> mTail; - std::pair*> mLFreeFoot; - std::pair*> mRFreeFoot; - int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty - int mPartPriorities[27]; - std::pair*> mZero; + int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty + int mPartPriorities[27]; //Bounded Parts - Ogre::Entity* lclavicle; - Ogre::Entity* rclavicle; - Ogre::Entity* rupperArm; - Ogre::Entity* lupperArm; - Ogre::Entity* rUpperLeg; - Ogre::Entity* lUpperLeg; - Ogre::Entity* lForearm; - Ogre::Entity* rForearm; - Ogre::Entity* lWrist; - Ogre::Entity* rWrist; - Ogre::Entity* rKnee; - Ogre::Entity* lKnee; - Ogre::Entity* neck; - Ogre::Entity* rAnkle; - Ogre::Entity* lAnkle; - Ogre::Entity* groin; - Ogre::Entity* lfoot; - Ogre::Entity* rfoot; - Ogre::Entity* hair; - Ogre::Entity* head; + NifOgre::EntityList lclavicle; + NifOgre::EntityList rclavicle; + NifOgre::EntityList rupperArm; + NifOgre::EntityList lupperArm; + NifOgre::EntityList rUpperLeg; + NifOgre::EntityList lUpperLeg; + NifOgre::EntityList lForearm; + NifOgre::EntityList rForearm; + NifOgre::EntityList lWrist; + NifOgre::EntityList rWrist; + NifOgre::EntityList rKnee; + NifOgre::EntityList lKnee; + NifOgre::EntityList neck; + NifOgre::EntityList rAnkle; + NifOgre::EntityList lAnkle; + NifOgre::EntityList groin; + NifOgre::EntityList skirt; + NifOgre::EntityList lfoot; + NifOgre::EntityList rfoot; + NifOgre::EntityList hair; + NifOgre::EntityList rHand; + NifOgre::EntityList lHand; + NifOgre::EntityList head; + NifOgre::EntityList chest; + NifOgre::EntityList tail; - Ogre::SceneNode* insert; bool isBeast; bool isFemale; std::string headModel; @@ -73,18 +68,17 @@ private: public: NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); virtual ~NpcAnimation(); - Ogre::Entity* insertBoundedPart(const std::string &mesh, std::string bonename); - std::pair*> insertFreePart(const std::string &mesh, const std::string& suffix); - void insertFootPart(int type, const std::string &mesh); + NifOgre::EntityList insertBoundedPart(const std::string &mesh, const std::string &bonename); virtual void runAnimation(float timepassed); void updateParts(); + void removeEntities(NifOgre::EntityList &entities); void removeIndividualPart(int type); void reserveIndividualPart(int type, int group, int priority); bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); void removePartGroup(int group); void addPartGroup(int group, int priority, std::vector& parts); - }; + } #endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index b3457a5fa..ecd1328c4 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -92,11 +92,16 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); - NifOgre::NIFLoader::load(mesh); - Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh); - - - Ogre::Vector3 extents = ent->getBoundingBox().getSize(); + Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, NULL, mesh); + for(size_t i = 0;i < entities.mEntities.size();i++) + { + const Ogre::AxisAlignedBox &tmp = entities.mEntities[i]->getBoundingBox(); + bounds.merge(Ogre::AxisAlignedBox(insert->_getDerivedPosition() + tmp.getMinimum(), + insert->_getDerivedPosition() + tmp.getMaximum()) + ); + } + Ogre::Vector3 extents = bounds.getSize(); extents *= insert->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); @@ -108,42 +113,41 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) if (mBounds.find(ptr.getCell()) == mBounds.end()) mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; - - Ogre::AxisAlignedBox bounds = ent->getBoundingBox(); - bounds = Ogre::AxisAlignedBox( - insert->_getDerivedPosition() + bounds.getMinimum(), - insert->_getDerivedPosition() + bounds.getMaximum() - ); - - bounds.scale(insert->getScale()); mBounds[ptr.getCell()].merge(bounds); bool transparent = false; - for (unsigned int i=0; igetNumSubEntities(); ++i) + for(size_t i = 0;i < entities.mEntities.size();i++) { - Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); - Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) + Ogre::Entity *ent = entities.mEntities[i]; + for (unsigned int i=0; igetNumSubEntities(); ++i) { - Ogre::Technique* tech = techIt.getNext(); - Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) + Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while (techIt.hasMoreElements()) { - Ogre::Pass* pass = passIt.getNext(); + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while (passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); - if (pass->getDepthWriteEnabled() == false) - transparent = true; + if (pass->getDepthWriteEnabled() == false) + transparent = true; + } } } } if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || transparent) { - insert->attachObject(ent); + for(size_t i = 0;i < entities.mEntities.size();i++) + { + Ogre::Entity *ent = entities.mEntities[i]; - ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); - ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); - ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0); + ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc); + ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + } } else { @@ -183,15 +187,20 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) // - there will be too many batches. sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500)); - sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); - sg->setVisibilityFlags(small ? RV_StaticsSmall : RV_Statics); sg->setCastShadows(true); sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - mRenderer.getScene()->destroyEntity(ent); + for(size_t i = 0;i < entities.mEntities.size();i++) + { + Ogre::Entity *ent = entities.mEntities[i]; + insert->detachObject(ent); + sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); + + mRenderer.getScene()->destroyEntity(ent); + } } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c5449cfba..ae0e57219 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -72,6 +72,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const else lang = sh::Language_CG; mFactory->setCurrentLanguage (lang); + mFactory->loadAllFiles(); //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog @@ -110,6 +111,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const sh::Factory::getInstance ().setGlobalSetting ("fog", "true"); sh::Factory::getInstance ().setGlobalSetting ("lighting", "true"); sh::Factory::getInstance ().setGlobalSetting ("num_lights", Settings::Manager::getString ("num lights", "Objects")); + sh::Factory::getInstance ().setGlobalSetting ("terrain_num_lights", Settings::Manager::getString ("num lights", "Terrain")); sh::Factory::getInstance ().setGlobalSetting ("underwater_effects", Settings::Manager::getString("underwater effect", "Water")); sh::Factory::getInstance ().setGlobalSetting ("simple_water", Settings::Manager::getBool("shader", "Water") ? "false" : "true"); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index aeefb95d1..9e551ba2a 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -287,6 +287,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) void SkyManager::create() { + assert(!mCreated); + sh::Factory::getInstance().setSharedParameter ("cloudBlendFactor", sh::makeProperty(new sh::FloatValue(0))); sh::Factory::getInstance().setSharedParameter ("cloudOpacity", @@ -302,7 +304,7 @@ void SkyManager::create() sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", ""); sh::Factory::getInstance().setTextureAlias ("cloud_texture_2", ""); - // Create overlay used for thunderstorm + // Create light used for thunderstorm mLightning = mSceneMgr->createLight(); mLightning->setType (Ogre::Light::LT_DIRECTIONAL); mLightning->setDirection (Ogre::Vector3(0.3, -0.7, 0.3)); @@ -324,52 +326,57 @@ void SkyManager::create() mSunGlare->setVisibilityFlags(RV_NoReflection); // Stars - MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif"); - Entity* night1_ent = mSceneMgr->createEntity("meshes\\sky_night_01.nif"); - night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); - night1_ent->setVisibilityFlags(RV_Sky); - night1_ent->setCastShadows(false); - mAtmosphereNight = mRootNode->createChildSceneNode(); - mAtmosphereNight->attachObject(night1_ent); - - for (unsigned int i=0; igetNumSubEntities(); ++i) + NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, NULL, "meshes\\sky_night_01.nif"); + for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++) { - std::string matName = "openmw_stars_" + boost::lexical_cast(i); - sh::MaterialInstance* m = sh::Factory::getInstance ().createMaterialInstance (matName, "openmw_stars"); + Entity* night1_ent = entities.mEntities[i]; + night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1); + night1_ent->setVisibilityFlags(RV_Sky); + night1_ent->setCastShadows(false); - std::string textureName = sh::retrieveValue( - sh::Factory::getInstance().getMaterialInstance(night1_ent->getSubEntity (i)->getMaterialName ())->getProperty("diffuseMap"), NULL).get(); + for (unsigned int j=0; jgetNumSubEntities(); ++j) + { + std::string matName = "openmw_stars_" + boost::lexical_cast(matidx++); + sh::MaterialInstance* m = sh::Factory::getInstance().createMaterialInstance(matName, "openmw_stars"); - m->setProperty ("texture", sh::makeProperty(new sh::StringValue(textureName))); + std::string textureName = sh::retrieveValue( + sh::Factory::getInstance().getMaterialInstance(night1_ent->getSubEntity(j)->getMaterialName())->getProperty("diffuseMap"), NULL).get(); - night1_ent->getSubEntity(i)->setMaterialName (matName); + m->setProperty("texture", sh::makeProperty(new sh::StringValue(textureName))); + + night1_ent->getSubEntity(j)->setMaterialName(matName); + } } + // Atmosphere (day) - mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif"); - Entity* atmosphere_ent = mSceneMgr->createEntity("meshes\\sky_atmosphere.nif"); - atmosphere_ent->setCastShadows(false); - - ModVertexAlpha(atmosphere_ent, 0); - - atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); - atmosphere_ent->setVisibilityFlags(RV_Sky); mAtmosphereDay = mRootNode->createChildSceneNode(); - mAtmosphereDay->attachObject(atmosphere_ent); - atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere"); + entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, NULL, "meshes\\sky_atmosphere.nif"); + for(size_t i = 0;i < entities.mEntities.size();i++) + { + Entity* atmosphere_ent = entities.mEntities[i]; + atmosphere_ent->setCastShadows(false); + atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); + atmosphere_ent->setVisibilityFlags(RV_Sky); + atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere"); + ModVertexAlpha(atmosphere_ent, 0); + } + // Clouds - NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif"); - Entity* clouds_ent = mSceneMgr->createEntity("meshes\\sky_clouds_01.nif"); - clouds_ent->setVisibilityFlags(RV_Sky); - clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); SceneNode* clouds_node = mRootNode->createChildSceneNode(); - clouds_node->attachObject(clouds_ent); - clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds"); - clouds_ent->setCastShadows(false); + entities = NifOgre::NIFLoader::createEntities(clouds_node, NULL, "meshes\\sky_clouds_01.nif"); + for(size_t i = 0;i < entities.mEntities.size();i++) + { + Entity* clouds_ent = entities.mEntities[i]; + clouds_ent->setVisibilityFlags(RV_Sky); + clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); + clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds"); + clouds_ent->setCastShadows(false); - ModVertexAlpha(clouds_ent, 1); + ModVertexAlpha(clouds_ent, 1); + } mCreated = true; } diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 9fdff3a01..09d56dbd9 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -1,6 +1,8 @@ #ifndef _GAME_RENDER_SKY_H #define _GAME_RENDER_SKY_H +#include + #include #include #include @@ -9,7 +11,7 @@ #include -#include "sky.hpp" + #include "../mwworld/weather.hpp" namespace Ogre diff --git a/apps/openmw/mwscript/consoleextensions.cpp b/apps/openmw/mwscript/consoleextensions.cpp new file mode 100644 index 000000000..00b4f74e5 --- /dev/null +++ b/apps/openmw/mwscript/consoleextensions.cpp @@ -0,0 +1,24 @@ + +#include "consoleextensions.hpp" + +#include + +#include +#include +#include + +namespace MWScript +{ + namespace Console + { + void registerExtensions (Compiler::Extensions& extensions) + { + + } + + void installOpcodes (Interpreter::Interpreter& interpreter) + { + + } + } +} diff --git a/apps/openmw/mwscript/consoleextensions.hpp b/apps/openmw/mwscript/consoleextensions.hpp new file mode 100644 index 000000000..b10bf06a8 --- /dev/null +++ b/apps/openmw/mwscript/consoleextensions.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_SCRIPT_CONSOLEEXTENSIONS_H +#define GAME_SCRIPT_CONSOLEEXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWScript +{ + /// \brief Script functionality limited to the console + namespace Console + { + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); + } +} + +#endif diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b979420dd..3ca93d4b2 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -169,5 +169,14 @@ op 0x2000164: SetScale op 0x2000165: SetScale, explicit reference op 0x2000166: SetAngle op 0x2000167: SetAngle, explicit reference -opcodes 0x2000168-0x3ffffff unused - +op 0x2000168: GetScale +op 0x2000169: GetScale, explicit reference +op 0x200016a: GetAngle +op 0x200016b: GetAngle, explicit reference +op 0x200016c: user1 (console only, requires --script-console switch) +op 0x200016d: user2 (console only, requires --script-console switch) +op 0x200016e: user3, explicit reference (console only, requires --script-console switch) +op 0x200016f: user3 (implicit reference, console only, requires --script-console switch) +op 0x2000170: user4, explicit reference (console only, requires --script-console switch) +op 0x2000171: user4 (implicit reference, console only, requires --script-console switch) +opcodes 0x2000172-0x3ffffff unused diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp index b7425aca0..1b1560820 100644 --- a/apps/openmw/mwscript/extensions.cpp +++ b/apps/openmw/mwscript/extensions.cpp @@ -16,10 +16,12 @@ #include "dialogueextensions.hpp" #include "animationextensions.hpp" #include "transformationextensions.hpp" +#include "consoleextensions.hpp" +#include "userextensions.hpp" namespace MWScript { - void registerExtensions (Compiler::Extensions& extensions) + void registerExtensions (Compiler::Extensions& extensions, bool consoleOnly) { Cell::registerExtensions (extensions); Misc::registerExtensions (extensions); @@ -33,9 +35,15 @@ namespace MWScript Dialogue::registerExtensions (extensions); Animation::registerExtensions (extensions); Transformation::registerExtensions (extensions); + + if (consoleOnly) + { + Console::registerExtensions (extensions); + User::registerExtensions (extensions); + } } - void installOpcodes (Interpreter::Interpreter& interpreter) + void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly) { Interpreter::installOpcodes (interpreter); Cell::installOpcodes (interpreter); @@ -50,5 +58,11 @@ namespace MWScript Dialogue::installOpcodes (interpreter); Animation::installOpcodes (interpreter); Transformation::installOpcodes (interpreter); + + if (consoleOnly) + { + Console::installOpcodes (interpreter); + User::installOpcodes (interpreter); + } } } diff --git a/apps/openmw/mwscript/extensions.hpp b/apps/openmw/mwscript/extensions.hpp index 9738367a0..cb1aaf9db 100644 --- a/apps/openmw/mwscript/extensions.hpp +++ b/apps/openmw/mwscript/extensions.hpp @@ -13,9 +13,11 @@ namespace Interpreter namespace MWScript { - void registerExtensions (Compiler::Extensions& extensions); - - void installOpcodes (Interpreter::Interpreter& interpreter); + void registerExtensions (Compiler::Extensions& extensions, bool consoleOnly = false); + ///< \param consoleOnly include console only extensions + + void installOpcodes (Interpreter::Interpreter& interpreter, bool consoleOnly = false); + ///< \param consoleOnly include console only opcodes } #endif diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 5da512778..3a824473e 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -10,6 +10,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "../mwgui/window_manager.hpp" @@ -236,7 +237,7 @@ namespace MWScript if (!mAction.get()) throw std::runtime_error ("activation failed, because no action to perform"); - mAction->execute(); + mAction->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); mActivationHandled = true; } diff --git a/apps/openmw/mwscript/userextensions.cpp b/apps/openmw/mwscript/userextensions.cpp new file mode 100644 index 000000000..9c9738815 --- /dev/null +++ b/apps/openmw/mwscript/userextensions.cpp @@ -0,0 +1,91 @@ + +#include "userextensions.hpp" + +#include + +#include +#include +#include +#include + +#include "ref.hpp" + +namespace MWScript +{ + /// Temporary script extensions. + /// + /// \attention Do not commit changes to this file to a git repository! + namespace User + { + class OpUser1 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.getContext().report ("user1: not in use"); + } + }; + + class OpUser2 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.getContext().report ("user2: not in use"); + } + }; + + template + class OpUser3 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { +// MWWorld::Ptr ptr = R()(runtime); + + runtime.getContext().report ("user3: not in use"); + } + }; + + template + class OpUser4 : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { +// MWWorld::Ptr ptr = R()(runtime); + + runtime.getContext().report ("user4: not in use"); + } + }; + + const int opcodeUser1 = 0x200016c; + const int opcodeUser2 = 0x200016d; + const int opcodeUser3 = 0x200016e; + const int opcodeUser3Explicit = 0x200016f; + const int opcodeUser4 = 0x2000170; + const int opcodeUser4Explicit = 0x2000171; + + void registerExtensions (Compiler::Extensions& extensions) + { + extensions.registerInstruction ("user1", "", opcodeUser1); + extensions.registerInstruction ("user2", "", opcodeUser2); + extensions.registerInstruction ("user3", "", opcodeUser3, opcodeUser3); + extensions.registerInstruction ("user4", "", opcodeUser4, opcodeUser4); + } + + void installOpcodes (Interpreter::Interpreter& interpreter) + { + interpreter.installSegment5 (opcodeUser1, new OpUser1); + interpreter.installSegment5 (opcodeUser2, new OpUser2); + interpreter.installSegment5 (opcodeUser3, new OpUser3); + interpreter.installSegment5 (opcodeUser3Explicit, new OpUser3); + interpreter.installSegment5 (opcodeUser4, new OpUser4); + interpreter.installSegment5 (opcodeUser4Explicit, new OpUser4); + } + } +} diff --git a/apps/openmw/mwscript/userextensions.hpp b/apps/openmw/mwscript/userextensions.hpp new file mode 100644 index 000000000..3642eb5f4 --- /dev/null +++ b/apps/openmw/mwscript/userextensions.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_SCRIPT_USEREXTENSIONS_H +#define GAME_SCRIPT_USEREXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWScript +{ + /// \brief Temporaty script functionality limited to the console + namespace User + { + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); + } +} + +#endif diff --git a/apps/openmw/mwworld/action.cpp b/apps/openmw/mwworld/action.cpp new file mode 100644 index 000000000..0a57d5f67 --- /dev/null +++ b/apps/openmw/mwworld/action.cpp @@ -0,0 +1,24 @@ + +#include "action.hpp" + +#include "../mwbase/environment.hpp" + +#include "../mwsound/soundmanager.hpp" + +MWWorld::Action::Action() {} + +MWWorld::Action::~Action() {} + +void MWWorld::Action::execute (const Ptr& actor) +{ + if (!mSoundId.empty()) + MWBase::Environment::get().getSoundManager()->playSound3D (actor, mSoundId, 1.0, 1.0, + MWSound::Play_NoTrack); + + executeImp (actor); +} + +void MWWorld::Action::setSound (const std::string& id) +{ + mSoundId = id; +} diff --git a/apps/openmw/mwworld/action.hpp b/apps/openmw/mwworld/action.hpp index d5cf12779..a00f67951 100644 --- a/apps/openmw/mwworld/action.hpp +++ b/apps/openmw/mwworld/action.hpp @@ -1,22 +1,32 @@ #ifndef GAME_MWWORLD_ACTION_H #define GAME_MWWORLD_ACTION_H +#include + namespace MWWorld { + class Ptr; + /// \brief Abstract base for actions class Action { + std::string mSoundId; + // not implemented Action (const Action& action); Action& operator= (const Action& action); + virtual void executeImp (const Ptr& actor) = 0; + public: - Action() {} + Action(); - virtual ~Action() {} + virtual ~Action(); - virtual void execute() = 0; + void execute (const Ptr& actor); + + void setSound (const std::string& id); }; } diff --git a/apps/openmw/mwworld/actionalchemy.cpp b/apps/openmw/mwworld/actionalchemy.cpp index eb91b6946..a7ee4fd0e 100644 --- a/apps/openmw/mwworld/actionalchemy.cpp +++ b/apps/openmw/mwworld/actionalchemy.cpp @@ -5,7 +5,7 @@ namespace MWWorld { - void ActionAlchemy::execute() + void ActionAlchemy::executeImp (const Ptr& actor) { MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Alchemy); } diff --git a/apps/openmw/mwworld/actionalchemy.hpp b/apps/openmw/mwworld/actionalchemy.hpp index 739de419b..e6d1a7976 100644 --- a/apps/openmw/mwworld/actionalchemy.hpp +++ b/apps/openmw/mwworld/actionalchemy.hpp @@ -7,8 +7,7 @@ namespace MWWorld { class ActionAlchemy : public Action { - public: - virtual void execute (); + virtual void executeImp (const Ptr& actor); }; } diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp index b330a70e7..595ee6cb3 100644 --- a/apps/openmw/mwworld/actionapply.cpp +++ b/apps/openmw/mwworld/actionapply.cpp @@ -5,24 +5,24 @@ namespace MWWorld { - ActionApply::ActionApply (const Ptr& target, const std::string& id, const Ptr& actor) - : mTarget (target), mId (id), mActor (actor) + ActionApply::ActionApply (const Ptr& target, const std::string& id) + : mTarget (target), mId (id) {} - void ActionApply::execute() + void ActionApply::executeImp (const Ptr& actor) { - MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor); + MWWorld::Class::get (mTarget).apply (mTarget, mId, actor); } ActionApplyWithSkill::ActionApplyWithSkill (const Ptr& target, const std::string& id, - const Ptr& actor, int skillIndex, int usageType) - : mTarget (target), mId (id), mActor (actor), mSkillIndex (skillIndex), mUsageType (usageType) + int skillIndex, int usageType) + : mTarget (target), mId (id), mSkillIndex (skillIndex), mUsageType (usageType) {} - void ActionApplyWithSkill::execute() + void ActionApplyWithSkill::executeImp (const Ptr& actor) { - if (MWWorld::Class::get (mTarget).apply (mTarget, mId, mActor) && mUsageType!=-1) - MWWorld::Class::get (mTarget).skillUsageSucceeded (mActor, mSkillIndex, mUsageType); + if (MWWorld::Class::get (mTarget).apply (mTarget, mId, actor) && mUsageType!=-1) + MWWorld::Class::get (mTarget).skillUsageSucceeded (actor, mSkillIndex, mUsageType); } } diff --git a/apps/openmw/mwworld/actionapply.hpp b/apps/openmw/mwworld/actionapply.hpp index 972417e02..523bf9373 100644 --- a/apps/openmw/mwworld/actionapply.hpp +++ b/apps/openmw/mwworld/actionapply.hpp @@ -13,29 +13,27 @@ namespace MWWorld { Ptr mTarget; std::string mId; - Ptr mActor; + + virtual void executeImp (const Ptr& actor); public: - ActionApply (const Ptr& target, const std::string& id, const Ptr& actor); - - virtual void execute(); + ActionApply (const Ptr& target, const std::string& id); }; class ActionApplyWithSkill : public Action { Ptr mTarget; std::string mId; - Ptr mActor; int mSkillIndex; int mUsageType; + virtual void executeImp (const Ptr& actor); + public: - ActionApplyWithSkill (const Ptr& target, const std::string& id, const Ptr& actor, + ActionApplyWithSkill (const Ptr& target, const std::string& id, int skillIndex, int usageType); - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 52b9437fd..20e0afb68 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -13,7 +13,7 @@ namespace MWWorld { } - void ActionEquip::execute () + void ActionEquip::executeImp (const Ptr& actor) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); diff --git a/apps/openmw/mwworld/actionequip.hpp b/apps/openmw/mwworld/actionequip.hpp index 6cf3640f8..5685a294a 100644 --- a/apps/openmw/mwworld/actionequip.hpp +++ b/apps/openmw/mwworld/actionequip.hpp @@ -10,11 +10,11 @@ namespace MWWorld { Ptr mObject; + virtual void executeImp (const Ptr& actor); + public: /// @param item to equip ActionEquip (const Ptr& object); - - virtual void execute (); }; } diff --git a/apps/openmw/mwworld/actionopen.cpp b/apps/openmw/mwworld/actionopen.cpp index a70773af9..c73ef9149 100644 --- a/apps/openmw/mwworld/actionopen.cpp +++ b/apps/openmw/mwworld/actionopen.cpp @@ -15,7 +15,7 @@ namespace MWWorld mContainer = container; } - void ActionOpen::execute () + void ActionOpen::executeImp (const MWWorld::Ptr& actor) { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return; diff --git a/apps/openmw/mwworld/actionopen.hpp b/apps/openmw/mwworld/actionopen.hpp index eff26c78c..5666ff293 100644 --- a/apps/openmw/mwworld/actionopen.hpp +++ b/apps/openmw/mwworld/actionopen.hpp @@ -12,10 +12,11 @@ namespace MWWorld { Ptr mContainer; + virtual void executeImp (const MWWorld::Ptr& actor); + public: ActionOpen (const Ptr& container); ///< \param The Container the Player has activated. - virtual void execute (); }; } diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 1e03230c7..c81d79e03 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -11,7 +11,7 @@ namespace MWWorld { } - void ActionRead::execute () + void ActionRead::executeImp (const MWWorld::Ptr& actor) { LiveCellRef *ref = mObject.get(); diff --git a/apps/openmw/mwworld/actionread.hpp b/apps/openmw/mwworld/actionread.hpp index a4b495f79..9bb74fb88 100644 --- a/apps/openmw/mwworld/actionread.hpp +++ b/apps/openmw/mwworld/actionread.hpp @@ -10,11 +10,11 @@ namespace MWWorld { Ptr mObject; // book or scroll to read + virtual void executeImp (const MWWorld::Ptr& actor); + public: /// @param book or scroll to read ActionRead (const Ptr& object); - - virtual void execute (); }; } diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 39544b35d..5207f1a10 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -13,7 +13,7 @@ namespace MWWorld { ActionTake::ActionTake (const MWWorld::Ptr& object) : mObject (object) {} - void ActionTake::execute() + void ActionTake::executeImp (const Ptr& actor) { if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) return; diff --git a/apps/openmw/mwworld/actiontake.hpp b/apps/openmw/mwworld/actiontake.hpp index f495fc3c4..52a114acf 100644 --- a/apps/openmw/mwworld/actiontake.hpp +++ b/apps/openmw/mwworld/actiontake.hpp @@ -10,11 +10,11 @@ namespace MWWorld { MWWorld::Ptr mObject; + virtual void executeImp (const Ptr& actor); + public: ActionTake (const MWWorld::Ptr& object); - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/actiontalk.cpp b/apps/openmw/mwworld/actiontalk.cpp index a0f9d8c4c..78171bbe5 100644 --- a/apps/openmw/mwworld/actiontalk.cpp +++ b/apps/openmw/mwworld/actiontalk.cpp @@ -9,7 +9,7 @@ namespace MWWorld { ActionTalk::ActionTalk (const Ptr& actor) : mActor (actor) {} - void ActionTalk::execute() + void ActionTalk::executeImp (const Ptr& actor) { MWBase::Environment::get().getDialogueManager()->startDialogue (mActor); } diff --git a/apps/openmw/mwworld/actiontalk.hpp b/apps/openmw/mwworld/actiontalk.hpp index 1b7b9b6d6..53adf9e53 100644 --- a/apps/openmw/mwworld/actiontalk.hpp +++ b/apps/openmw/mwworld/actiontalk.hpp @@ -10,12 +10,12 @@ namespace MWWorld { Ptr mActor; + virtual void executeImp (const Ptr& actor); + public: ActionTalk (const Ptr& actor); ///< \param actor The actor the player is talking to - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 8ae3244f8..9c87d37ae 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -6,12 +6,12 @@ namespace MWWorld { - ActionTeleportPlayer::ActionTeleportPlayer (const std::string& cellName, + ActionTeleport::ActionTeleport (const std::string& cellName, const ESM::Position& position) : mCellName (cellName), mPosition (position) {} - void ActionTeleportPlayer::execute() + void ActionTeleport::executeImp (const Ptr& actor) { if (mCellName.empty()) MWBase::Environment::get().getWorld()->changeToExteriorCell (mPosition); diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index 00efdc876..a13cb61b2 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -9,17 +9,17 @@ namespace MWWorld { - class ActionTeleportPlayer : public Action + class ActionTeleport : public Action { std::string mCellName; ESM::Position mPosition; + virtual void executeImp (const Ptr& actor); + public: - ActionTeleportPlayer (const std::string& cellName, const ESM::Position& position); + ActionTeleport (const std::string& cellName, const ESM::Position& position); ///< If cellName is empty, an exterior cell is asumed. - - virtual void execute(); }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 60a7eb6e1..eceb5ddc1 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1,10 +1,14 @@ - #include "cellstore.hpp" #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "ptr.hpp" + namespace MWWorld { CellStore::CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index de3ac12ae..0be0ef651 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -15,6 +15,8 @@ namespace ESMS namespace MWWorld { + class Ptr; + /// A reference to one object (of any type) in a cell. /// /// Constructing this with a CellRef instance in the constructor means that @@ -73,6 +75,11 @@ namespace MWWorld return 0; } + + LiveRef &insert(const LiveRef &item) { + list.push_back(item); + return list.back(); + } }; /// A storage struct for one single cell reference. @@ -106,9 +113,9 @@ namespace MWWorld CellRefList ingreds; CellRefList creatureLists; CellRefList itemLists; - CellRefList lights; + CellRefList lights; CellRefList lockpicks; - CellRefList miscItems; + CellRefList miscItems; CellRefList npcs; CellRefList probes; CellRefList repairs; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 65fa91666..5267e368d 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -5,7 +5,10 @@ #include +#include + #include "ptr.hpp" +#include "refdata.hpp" #include "nullaction.hpp" #include "containerstore.hpp" @@ -212,4 +215,32 @@ namespace MWWorld void Class::adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const { } + + std::string Class::getModel(const MWWorld::Ptr &ptr) const + { + return ""; + } + + MWWorld::Ptr + Class::copyToCellImpl(const Ptr &ptr, CellStore &cell) const + { + throw std::runtime_error("unable to move class to cell"); + } + + MWWorld::Ptr + Class::copyToCell(const Ptr &ptr, CellStore &cell) const + { + Ptr newPtr = copyToCellImpl(ptr, cell); + + return newPtr; + } + + MWWorld::Ptr + Class::copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const + { + Ptr newPtr = copyToCell(ptr, cell); + newPtr.getRefData().getPosition() = pos; + + return newPtr; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 3d3ed71ec..1bc592798 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -31,12 +31,18 @@ namespace MWGui struct ToolTipInfo; } +namespace ESM +{ + struct Position; +} + namespace MWWorld { class Ptr; class ContainerStore; class InventoryStore; class PhysicsSystem; + class CellStore; /// \brief Base class for referenceable esm records class Class @@ -51,6 +57,8 @@ namespace MWWorld Class(); + virtual Ptr copyToCellImpl(const Ptr &ptr, CellStore &cell) const; + public: /// NPC-stances. @@ -204,6 +212,14 @@ namespace MWWorld virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; virtual void adjustRotation(const MWWorld::Ptr& ptr,float& x,float& y,float& z) const; + + virtual std::string getModel(const MWWorld::Ptr &ptr) const; + + virtual Ptr + copyToCell(const Ptr &ptr, CellStore &cell) const; + + virtual Ptr + copyToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos) const; }; } diff --git a/apps/openmw/mwworld/nullaction.hpp b/apps/openmw/mwworld/nullaction.hpp index c8e3368e4..7ef8b4a06 100644 --- a/apps/openmw/mwworld/nullaction.hpp +++ b/apps/openmw/mwworld/nullaction.hpp @@ -8,9 +8,7 @@ namespace MWWorld /// \brief Action: do nothing class NullAction : public Action { - public: - - virtual void execute() {} + virtual void executeImp (const Ptr& actor) {} }; } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index bf5c001db..45cfdd123 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -14,6 +14,7 @@ #include "../mwbase/world.hpp" // FIXME #include "ptr.hpp" +#include "class.hpp" using namespace Ogre; namespace MWWorld @@ -121,6 +122,22 @@ namespace MWWorld return !(result.first == ""); } + std::pair + PhysicsSystem::castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len) + { + Ogre::Ray ray = Ogre::Ray(orig, dir); + Ogre::Vector3 to = ray.getPoint(len); + + btVector3 btFrom = btVector3(orig.x, orig.y, orig.z); + btVector3 btTo = btVector3(to.x, to.y, to.z); + + std::pair test = mEngine->rayTest(btFrom, btTo); + if (test.first == "") { + return std::make_pair(false, Ogre::Vector3()); + } + return std::make_pair(true, ray.getPoint(len * test.second)); + } + std::pair PhysicsSystem::castRay(float mouseX, float mouseY) { Ogre::Ray ray = mRender.getCamera()->getCameraToViewportRay( @@ -348,21 +365,41 @@ namespace MWWorld throw std::logic_error ("can't find player"); } - void PhysicsSystem::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string model){ + void PhysicsSystem::insertObjectPhysics(const MWWorld::Ptr& ptr, const std::string model){ - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - // unused - //Ogre::Vector3 objPos = node->getPosition(); + addObject( + node->getName(), + model, + node->getOrientation(), + node->getScale().x, + node->getPosition()); + } - addObject (node->getName(), model, node->getOrientation(), - node->getScale().x, node->getPosition()); - } + void PhysicsSystem::insertActorPhysics(const MWWorld::Ptr& ptr, const std::string model){ + Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); + addActor (node->getName(), model, node->getPosition()); + } - void PhysicsSystem::insertActorPhysics(const MWWorld::Ptr& ptr, const std::string model){ - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - // std::cout << "Adding node with name" << node->getName(); - addActor (node->getName(), model, node->getPosition()); - } + bool PhysicsSystem::getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max) + { + std::string model = MWWorld::Class::get(ptr).getModel(ptr); + if (model.empty()) { + return false; + } + btVector3 btMin, btMax; + float scale = ptr.getCellRef().scale; + mEngine->getObjectAABB(model, scale, btMin, btMax); + min.x = btMin.x(); + min.y = btMin.y(); + min.z = btMin.z(); + + max.x = btMax.x(); + max.y = btMax.y(); + max.z = btMax.z(); + + return true; + } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index a6b679833..e42fa536b 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -54,6 +54,9 @@ namespace MWWorld // cast ray, return true if it hit something bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); + std::pair + castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len); + std::pair castRay(float mouseX, float mouseY); ///< cast ray from the mouse, return true if it hit something and the first result (in OGRE coordinates) @@ -65,6 +68,8 @@ namespace MWWorld void setCurrentWater(bool hasWater, int waterHeight); + bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); + private: OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 33c67aad8..13e5ecb85 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -9,8 +9,6 @@ #include "../mwgui/window_manager.hpp" -#include "../mwworld/manualref.hpp" /// FIXME - #include "player.hpp" #include "localscripts.hpp" @@ -334,139 +332,12 @@ namespace MWWorld insertCellRefList(mRendering, cell.weapons, cell, *mPhysics); } - - /// \todo this whole code needs major clean up, and doesn't belong in this class. - void Scene::insertObject (const Ptr& ptr, CellStore* cell) - { - std::string type = ptr.getTypeName(); - - MWWorld::Ptr newPtr; - - // insert into the correct CellRefList - if (type == typeid(ESM::Potion).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->potions.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->potions.list.back(), cell); - } - else if (type == typeid(ESM::Apparatus).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->appas.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->appas.list.back(), cell); - } - else if (type == typeid(ESM::Armor).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->armors.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->armors.list.back(), cell); - } - else if (type == typeid(ESM::Book).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->books.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->books.list.back(), cell); - } - else if (type == typeid(ESM::Clothing).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->clothes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->clothes.list.back(), cell); - } - else if (type == typeid(ESM::Ingredient).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->ingreds.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->ingreds.list.back(), cell); - } - else if (type == typeid(ESM::Light).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lights.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lights.list.back(), cell); - } - else if (type == typeid(ESM::Tool).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->lockpicks.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->lockpicks.list.back(), cell); - } - else if (type == typeid(ESM::Repair).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->repairs.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->repairs.list.back(), cell); - } - else if (type == typeid(ESM::Probe).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->probes.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->probes.list.back(), cell); - } - else if (type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef* ref = ptr.get(); - cell->weapons.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->weapons.list.back(), cell); - } - else if (type == typeid(ESM::Miscellaneous).name()) - { - - // if this is gold, we need to fetch the correct mesh depending on the amount of gold. - if (MWWorld::Class::get(ptr).getName(ptr) == MWBase::Environment::get().getWorld()->getStore().gameSettings.search("sGold")->str) - { - int goldAmount = ptr.getRefData().getCount(); - - std::string base = "Gold_001"; - if (goldAmount >= 100) - base = "Gold_100"; - else if (goldAmount >= 25) - base = "Gold_025"; - else if (goldAmount >= 10) - base = "Gold_010"; - else if (goldAmount >= 5) - base = "Gold_005"; - - MWWorld::ManualRef newRef (MWBase::Environment::get().getWorld()->getStore(), base); - - MWWorld::LiveCellRef* ref = newRef.getPtr().get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - - ESM::Position& p = newPtr.getRefData().getPosition(); - p.pos[0] = ptr.getRefData().getPosition().pos[0]; - p.pos[1] = ptr.getRefData().getPosition().pos[1]; - p.pos[2] = ptr.getRefData().getPosition().pos[2]; - } - else - { - MWWorld::LiveCellRef* ref = ptr.get(); - - cell->miscItems.list.push_back( *ref ); - newPtr = MWWorld::Ptr(&cell->miscItems.list.back(), cell); - } - } - else - throw std::runtime_error("Trying to insert object of unhandled type"); - - - - newPtr.getRefData().setCount(ptr.getRefData().getCount()); - ptr.getRefData().setCount(0); - newPtr.getRefData().enable(); - - mRendering.addObject(newPtr); - MWWorld::Class::get(newPtr).insertObject(newPtr, *mPhysics); - - } - void Scene::addObjectToScene (const Ptr& ptr) { - mRendering.addObject (ptr); - MWWorld::Class::get (ptr).insertObject (ptr, *mPhysics); + mRendering.addObject(ptr); + MWWorld::Class::get(ptr).insertObject(ptr, *mPhysics); } - + void Scene::removeObjectFromScene (const Ptr& ptr) { MWBase::Environment::get().getMechanicsManager()->removeActor (ptr); @@ -474,4 +345,19 @@ namespace MWWorld mPhysics->removeObject (ptr.getRefData().getHandle()); mRendering.removeObject (ptr); } + + bool Scene::isCellActive(const CellStore &cell) + { + CellStoreCollection::iterator active = mActiveCells.begin(); + while (active != mActiveCells.end()) { + if ((*active)->cell->name == cell.cell->name && + (*active)->cell->data.gridX == cell.cell->data.gridX && + (*active)->cell->data.gridY == cell.cell->data.gridY) + { + return true; + } + ++active; + } + return false; + } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index c0b93796a..59e13dafe 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -88,10 +88,6 @@ namespace MWWorld void insertCell (Ptr::CellStore &cell); - /// this method is only meant for dropping objects into the gameworld from a container - /// and thus only handles object types that can be placed in a container - void insertObject (const Ptr& object, CellStore* cell); - void update (float duration); void addObjectToScene (const Ptr& ptr); @@ -99,6 +95,8 @@ namespace MWWorld void removeObjectFromScene (const Ptr& ptr); ///< Remove an object from the scene, but not from the world model. + + bool isCellActive(const CellStore &cell); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a68f08e34..67d8a7cec 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1016,14 +1016,13 @@ namespace MWWorld else cell = getPlayer().getPlayer().getCell(); - ESM::Position& pos = object.getRefData().getPosition(); + ESM::Position pos = getPlayer().getPlayer().getRefData().getPosition(); pos.pos[0] = result.second[0]; pos.pos[1] = -result.second[2]; pos.pos[2] = result.second[1]; - mWorldScene->insertObject(object, cell); - - /// \todo retrieve the bounds of the object and translate it accordingly + placeObject(object, *cell, pos); + object.getRefData().setCount(0); return true; } @@ -1039,18 +1038,52 @@ namespace MWWorld return true; } + void + World::placeObject(const Ptr &object, CellStore &cell, const ESM::Position &pos) + { + /// \todo add searching correct cell for position specified + MWWorld::Ptr dropped = + MWWorld::Class::get(object).copyToCell(object, cell, pos); + + Ogre::Vector3 min, max; + if (mPhysics->getObjectAABB(object, min, max)) { + float *pos = dropped.getRefData().getPosition().pos; + pos[0] -= (min.x + max.x) / 2; + pos[1] -= (min.y + max.y) / 2; + pos[2] -= min.z; + } + + if (mWorldScene->isCellActive(cell)) { + if (dropped.getRefData().isEnabled()) { + mWorldScene->addObjectToScene(dropped); + } + std::string script = MWWorld::Class::get(dropped).getScript(dropped); + if (!script.empty()) { + mLocalScripts.add(script, dropped); + } + } + } + void World::dropObjectOnGround (const Ptr& object) { MWWorld::Ptr::CellStore* cell = getPlayer().getPlayer().getCell(); - float* playerPos = getPlayer().getPlayer().getRefData().getPosition().pos; + ESM::Position pos = + getPlayer().getPlayer().getRefData().getPosition(); - ESM::Position& pos = object.getRefData().getPosition(); - pos.pos[0] = playerPos[0]; - pos.pos[1] = playerPos[1]; - pos.pos[2] = playerPos[2]; + Ogre::Vector3 orig = + Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); + Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); + + float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; + len += 100.0; - mWorldScene->insertObject(object, cell); + std::pair hit = + mPhysics->castRay(orig, dir, len); + pos.pos[2] = hit.second.z; + + placeObject(object, *cell, pos); + object.getRefData().setCount(0); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 43b178fe3..d39871c21 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -89,6 +89,9 @@ namespace MWWorld bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed + virtual void + placeObject(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + public: World (OEngine::Render::OgreRenderer& renderer, diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 07921da69..081207b0c 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -62,28 +62,13 @@ static bool fsstrict = false; /// An OGRE Archive wrapping a BSAFile archive class DirArchive: public Ogre::FileSystemArchive { - boost::filesystem::path currentdir; std::map, ciLessBoost> m; unsigned int cutoff; bool findFile(const String& filename, std::string& copy) const { - { - String passed = filename; - if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); - else if(filename.at(filename.length() - 2) == '"') - passed = filename.substr(0, filename.length() - 9); - else if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' - || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' - || filename.at(filename.length() - 1) == '|') - passed = filename.substr(0, filename.length() - 2); - - - copy = passed; - } - + copy = filename; std::replace(copy.begin(), copy.end(), '\\', '/'); if(copy.at(0) == '/') @@ -223,43 +208,20 @@ public: // OGRE's fault. You should NOT expect an open() command not to // have any side effects on the archive, and hence this function // should not have been declared const in the first place. - BSAFile *narc = (BSAFile*)&arc; + BSAFile *narc = const_cast(&arc); - String passed = filename; - if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); - else if(filename.at(filename.length() - 2) == '"') - passed = filename.substr(0, filename.length() - 9); - else if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' - || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' - || filename.at(filename.length() - 1) == '|') - passed = filename.substr(0, filename.length() - 2); - - // Open the file - return narc->getFile(passed.c_str()); + return narc->getFile(filename.c_str()); } -bool exists(const String& filename) { - return cexists(filename); -} + bool exists(const String& filename) { + return arc.exists(filename.c_str()); + } - // Check if the file exists. bool cexists(const String& filename) const { - String passed = filename; - if(filename.at(filename.length() - 2) == '>' || filename.at(filename.length() - 2) == ':') - passed = filename.substr(0, filename.length() - 6); - else if(filename.at(filename.length() - 2) == '"') - passed = filename.substr(0, filename.length() - 9); - else if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' - || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' - || filename.at(filename.length() - 1) == '|') - passed = filename.substr(0, filename.length() - 2); - - + return arc.exists(filename.c_str()); + } -return arc.exists(passed.c_str()); -} time_t getModifiedTime(const String&) { return 0; } // This is never called as far as I can see. diff --git a/components/nif/data.hpp b/components/nif/data.hpp index ad670bc5e..63df23b27 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -96,7 +96,9 @@ public: class ShapeData : public Record { public: - std::vector vertices, normals, colors, uvlist; + std::vector vertices, normals; + std::vector colors; + std::vector< std::vector > uvlist; Ogre::Vector3 center; float radius; @@ -105,16 +107,16 @@ public: int verts = nif->getUShort(); if(nif->getInt()) - nif->getFloats(vertices, verts*3); + nif->getVector3s(vertices, verts); if(nif->getInt()) - nif->getFloats(normals, verts*3); + nif->getVector3s(normals, verts); center = nif->getVector3(); radius = nif->getFloat(); if(nif->getInt()) - nif->getFloats(colors, verts*4); + nif->getVector4s(colors, verts); // Only the first 6 bits are used as a count. I think the rest are // flags of some sort. @@ -122,7 +124,11 @@ public: uvs &= 0x3f; if(nif->getInt()) - nif->getFloats(uvlist, uvs*verts*2); + { + uvlist.resize(uvs); + for(int i = 0;i < uvs;i++) + nif->getVector2s(uvlist[i], verts); + } } }; @@ -202,61 +208,34 @@ public: class NiPosData : public Record { public: + Vector3KeyList mKeyList; + void read(NIFFile *nif) { - int count = nif->getInt(); - int type = nif->getInt(); - if(type != 1 && type != 2) - nif->fail("Cannot handle NiPosData type"); - - // TODO: Could make structs of these. Seems to be identical to - // translation in NiKeyframeData. - for(int i=0; igetFloat(); - nif->getVector3(); // This isn't really shared between type 1 - // and type 2, most likely - if(type == 2) - { - nif->getVector3(); - nif->getVector3(); - } - } + mKeyList.read(nif); } }; class NiUVData : public Record { public: + FloatKeyList mKeyList[4]; + void read(NIFFile *nif) { - // TODO: This is claimed to be a "float animation key", which is - // also used in FloatData and KeyframeData. We could probably - // reuse and refactor a lot of this if we actually use it at some - // point. - for(int i=0; i<2; i++) - { - int count = nif->getInt(); - if(count) - { - nif->getInt(); // always 2 - nif->skip(count * (sizeof(float) + 3*sizeof(float))); // Really one time float + one vector - } - } - // Always 0 - nif->getInt(); - nif->getInt(); + for(int i = 0;i < 4;i++) + mKeyList[i].read(nif); } }; class NiFloatData : public Record { public: + FloatKeyList mKeyList; + void read(NIFFile *nif) { - int count = nif->getInt(); - nif->getInt(); // always 2 - nif->skip(count * (sizeof(float) + 3*sizeof(float))); // Really one time float + one vector + mKeyList.read(nif); } }; @@ -302,19 +281,11 @@ public: class NiColorData : public Record { public: - struct ColorData - { - float time; - Ogre::Vector4 rgba; - }; + Vector4KeyList mKeyList; void read(NIFFile *nif) { - int count = nif->getInt(); - nif->getInt(); // always 1 - - // Skip the data - nif->skip(count * 5*sizeof(float)); + mKeyList.read(nif); } }; @@ -361,12 +332,6 @@ public: Ogre::Vector3 trans; // Translation float scale; // Probably scale (always 1) }; - struct BoneTrafoCopy - { - Ogre::Quaternion rotation; - Ogre::Vector3 trans; - float scale; - }; struct VertWeight { @@ -374,26 +339,12 @@ public: float weight; }; - struct BoneInfo { BoneTrafo trafo; Ogre::Vector4 unknown; std::vector weights; }; - struct BoneInfoCopy - { - std::string bonename; - unsigned short bonehandle; - BoneTrafoCopy trafo; - Ogre::Vector4 unknown; - //std::vector weights; - }; - struct IndividualWeight - { - float weight; - unsigned int boneinfocopyindex; - }; BoneTrafo trafo; std::vector bones; @@ -428,378 +379,45 @@ public: } }; -class NiMorphData : public Record +struct NiMorphData : public Record { - float startTime; - float stopTime; - std::vector initialVertices; - std::vector > relevantTimes; - std::vector > relevantData; - std::vector > additionalVertices; - -public: - float getStartTime() const - { return startTime; } - float getStopTime() const - { return stopTime; } - - void setStartTime(float time) - { startTime = time; } - void setStopTime(float time) - { stopTime = time; } - - const std::vector& getInitialVertices() const - { return initialVertices; } - const std::vector >& getRelevantData() const - { return relevantData; } - const std::vector >& getRelevantTimes() const - { return relevantTimes; } - const std::vector >& getAdditionalVertices() const - { return additionalVertices; } + struct MorphData { + FloatKeyList mData; + std::vector mVertices; + }; + std::vector mMorphs; void read(NIFFile *nif) { int morphCount = nif->getInt(); int vertCount = nif->getInt(); nif->getChar(); - int magic = nif->getInt(); - /*int type =*/ nif->getInt(); - for(int i = 0; i < vertCount; i++) + mMorphs.resize(morphCount); + for(int i = 0;i < morphCount;i++) { - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - initialVertices.push_back(Ogre::Vector3(x, y, z)); - } + mMorphs[i].mData.read(nif, true); - for(int i=1; igetInt(); - /*type =*/ nif->getInt(); - std::vector current; - std::vector currentTime; - for(int i = 0; i < magic; i++) - { - // Time, data, forward, backward tangents - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - current.push_back(Ogre::Vector3(x,y,z)); - currentTime.push_back(time); - //nif->getFloatLen(4*magic); - } - - if(magic) - { - relevantData.push_back(current); - relevantTimes.push_back(currentTime); - } - - std::vector verts; - for(int i = 0; i < vertCount; i++) - { - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - verts.push_back(Ogre::Vector3(x, y, z)); - } - additionalVertices.push_back(verts); + mMorphs[i].mVertices.resize(vertCount); + for(int j = 0;j < vertCount;j++) + mMorphs[i].mVertices[j] = nif->getVector3(); } } }; -class NiKeyframeData : public Record +struct NiKeyframeData : public Record { - std::string bonename; - //Rotations - std::vector quats; - std::vector tbc; - std::vector rottime; - float startTime; - float stopTime; - int rtype; - - //Translations - std::vector translist1; - std::vector translist2; - std::vector translist3; - std::vector transtbc; - std::vector transtime; - int ttype; - - //Scalings - std::vector scalefactor; - std::vector scaletime; - std::vector forwards; - std::vector backwards; - std::vector tbcscale; - int stype; - -public: - void clone(const NiKeyframeData &c) - { - quats = c.getQuat(); - tbc = c.getrTbc(); - rottime = c.getrTime(); - - //types - ttype = c.getTtype(); - rtype = c.getRtype(); - stype = c.getStype(); - - - translist1 = c.getTranslist1(); - translist2 = c.getTranslist2(); - translist3 = c.getTranslist3(); - - transtime = c.gettTime(); - - bonename = c.getBonename(); - } - - void setBonename(std::string bone) - { bonename = bone; } - void setStartTime(float start) - { startTime = start; } - void setStopTime(float end) - { stopTime = end; } + QuaternionKeyList mRotations; + Vector3KeyList mTranslations; + FloatKeyList mScales; void read(NIFFile *nif) { - // Rotations first - int count = nif->getInt(); - //std::vector quat(count); - //std::vector rottime(count); - if(count) - { - //TYPE1 LINEAR_KEY - //TYPE2 QUADRATIC_KEY - //TYPE3 TBC_KEY - //TYPE4 XYZ_ROTATION_KEY - //TYPE5 UNKNOWN_KEY - rtype = nif->getInt(); - //std::cout << "Count: " << count << "Type: " << type << "\n"; - - if(rtype == 1) - { - //We need to actually read in these values instead of skipping them - //nif->skip(count*4*5); // time + quaternion - for (int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float w = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); - quats.push_back(quat); - rottime.push_back(time); - //if(time == 0.0 || time > 355.5) - // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; - } - } - else if(rtype == 3) - { - //Example - node 116 in base_anim.nif - for (int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float w = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - - float tbcx = nif->getFloat(); - float tbcy = nif->getFloat(); - float tbcz = nif->getFloat(); - - Ogre::Quaternion quat = Ogre::Quaternion(Ogre::Real(w), Ogre::Real(x), Ogre::Real(y), Ogre::Real(z)); - Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); - quats.push_back(quat); - rottime.push_back(time); - tbc.push_back(vec); - //if(time == 0.0 || time > 355.5) - // std::cout <<"Time:" << time << "W:" << w <<"X:" << x << "Y:" << y << "Z:" << z << "\n"; - } - } - else if(rtype == 4) - { - for(int j=0;jgetFloat(); // time - for(int i=0; i<3; i++) - { - int cnt = nif->getInt(); - int type = nif->getInt(); - if(type == 1) - nif->skip(cnt*4*2); // time + unknown - else if(type == 2) - nif->skip(cnt*4*4); // time + unknown vector - else - nif->fail("Unknown sub-rotation type"); - } - } - } - else - nif->fail("Unknown rotation type in NiKeyframeData"); - } - //first = false; - - // Then translation - count = nif->getInt(); - if(count) - { - ttype = nif->getInt(); - - //std::cout << "TransCount:" << count << " Type: " << type << "\n"; - if(ttype == 1) - { - for(int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - translist1.push_back(trans); - transtime.push_back(time); - } - //nif->getFloatLen(count*4); // time + translation - } - else if(ttype == 2) - { - //Example - node 116 in base_anim.nif - for(int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - float x2 = nif->getFloat(); - float y2 = nif->getFloat(); - float z2 = nif->getFloat(); - float x3 = nif->getFloat(); - float y3 = nif->getFloat(); - float z3 = nif->getFloat(); - - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - Ogre::Vector3 trans2 = Ogre::Vector3(x2, y2, z2); - Ogre::Vector3 trans3 = Ogre::Vector3(x3, y3, z3); - transtime.push_back(time); - translist1.push_back(trans); - translist2.push_back(trans2); - translist3.push_back(trans3); - } - - //nif->getFloatLen(count*10); // trans1 + forward + backward - } - else if(ttype == 3) - { - for(int i = 0; i < count; i++) - { - float time = nif->getFloat(); - float x = nif->getFloat(); - float y = nif->getFloat(); - float z = nif->getFloat(); - float t = nif->getFloat(); - float b = nif->getFloat(); - float c = nif->getFloat(); - Ogre::Vector3 trans = Ogre::Vector3(x, y, z); - Ogre::Vector3 tbc = Ogre::Vector3(t, b, c); - translist1.push_back(trans); - transtbc.push_back(tbc); - transtime.push_back(time); - } - //nif->getFloatLen(count*7); // trans1 + tension,bias,continuity - } - else nif->fail("Unknown translation type"); - } - - // Finally, scalings - count = nif->getInt(); - if(count) - { - stype = nif->getInt(); - - for(int i = 0; i < count; i++) - { - //int size = 0; - if(stype >= 1 && stype < 4) - { - float time = nif->getFloat(); - float scale = nif->getFloat(); - scaletime.push_back(time); - scalefactor.push_back(scale); - //size = 2; // time+scale - } - else - nif->fail("Unknown scaling type"); - - if(stype == 2) - { - //size = 4; // 1 + forward + backward (floats) - float forward = nif->getFloat(); - float backward = nif->getFloat(); - forwards.push_back(forward); - backwards.push_back(backward); - } - else if(stype == 3) - { - //size = 5; // 1 + tbc - float tbcx = nif->getFloat(); - float tbcy = nif->getFloat(); - float tbcz = nif->getFloat(); - Ogre::Vector3 vec = Ogre::Vector3(tbcx, tbcy, tbcz); - tbcscale.push_back(vec); - } - } - } - else - stype = 0; + mRotations.read(nif); + mTranslations.read(nif); + mScales.read(nif); } - - int getRtype() const - { return rtype; } - int getStype() const - { return stype; } - int getTtype() const - { return ttype; } - float getStartTime() const - { return startTime; } - float getStopTime() const - { return stopTime; } - const std::vector& getQuat() const - { return quats; } - const std::vector& getrTbc() const - { return tbc; } - const std::vector& getrTime() const - { return rottime; } - - const std::vector& getTranslist1() const - { return translist1; } - const std::vector& getTranslist2() const - { return translist2; } - const std::vector& getTranslist3() const - { return translist3; } - const std::vector& gettTime() const - { return transtime; } - const std::vector& getScalefactor() const - { return scalefactor; } - const std::vector& getForwards() const - { return forwards; } - const std::vector& getBackwards() const - { return backwards; } - const std::vector& getScaleTbc() const - { return tbcscale; } - - const std::vector& getsTime() const - { return scaletime; } - const std::string& getBonename() const - { return bonename; } }; } // Namespace diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 36badbf0d..3313d89ab 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -205,8 +205,22 @@ void NiSkinInstance::post(NIFFile *nif) for(size_t i=0; ifail("Oops: Missing bone! Don't know how to handle this."); - bones[i].makeBone(i, data->bones[i]); + bones[i]->makeBone(i, data->bones[i]); } } + +Ogre::Matrix4 Node::getLocalTransform() +{ + Ogre::Matrix4 mat4(Ogre::Matrix4::IDENTITY); + mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation)); + return mat4; +} + +Ogre::Matrix4 Node::getWorldTransform() +{ + if(parent != NULL) + return parent->getWorldTransform() * getLocalTransform(); + return getLocalTransform(); +} diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index 4072b4307..624066317 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -26,9 +26,12 @@ #include #include +#include #include #include #include +#include +#include #include #include @@ -97,6 +100,12 @@ public: throw std::runtime_error(err); } + void warn(const std::string &msg) + { + std::cerr<< "NIFFile Warning: "< &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector2(); + } + void getVector3s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector3(); + } + void getVector4s(std::vector &vec, size_t size) + { + vec.resize(size); + for(size_t i = 0;i < vec.size();i++) + vec[i] = getVector4(); + } }; + +template +struct KeyT { + float mTime; + T mValue; + T mForwardValue; // Only for Quadratic interpolation + T mBackwardValue; // Only for Quadratic interpolation + float mTension; // Only for TBC interpolation + float mBias; // Only for TBC interpolation + float mContinuity; // Only for TBC interpolation +}; +typedef KeyT FloatKey; +typedef KeyT Vector3Key; +typedef KeyT Vector4Key; +typedef KeyT QuaternionKey; + +template +struct KeyListT { + typedef std::vector< KeyT > VecType; + + static const int sLinearInterpolation = 1; + static const int sQuadraticInterpolation = 2; + static const int sTBCInterpolation = 3; + + int mInterpolationType; + VecType mKeys; + + void read(NIFFile *nif, bool force=false) + { + size_t count = nif->getInt(); + if(count == 0 && !force) + return; + + mInterpolationType = nif->getInt(); + mKeys.resize(count); + if(mInterpolationType == sLinearInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + } + } + else if(mInterpolationType == sQuadraticInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + key.mForwardValue = (nif->*getValue)(); + key.mBackwardValue = (nif->*getValue)(); + } + } + else if(mInterpolationType == sTBCInterpolation) + { + for(size_t i = 0;i < count;i++) + { + KeyT &key = mKeys[i]; + key.mTime = nif->getFloat(); + key.mValue = (nif->*getValue)(); + key.mTension = nif->getFloat(); + key.mBias = nif->getFloat(); + key.mContinuity = nif->getFloat(); + } + } + else + nif->warn("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); + } +}; +typedef KeyListT FloatKeyList; +typedef KeyListT Vector3KeyList; +typedef KeyListT Vector4KeyList; +typedef KeyListT QuaternionKeyList; + } // Namespace #endif diff --git a/components/nif/nif_types.hpp b/components/nif/nif_types.hpp index 705ed5994..a5fb61361 100644 --- a/components/nif/nif_types.hpp +++ b/components/nif/nif_types.hpp @@ -37,21 +37,12 @@ struct Transformation Ogre::Vector3 pos; Ogre::Matrix3 rotation; float scale; - Ogre::Vector3 velocity; static const Transformation& getIdentity() { - static Transformation identity; - static bool iset = false; - if (!iset) - { - identity.scale = 1.0f; - identity.rotation[0][0] = 1.0f; - identity.rotation[1][1] = 1.0f; - identity.rotation[2][2] = 1.0f; - iset = true; - } - + static const Transformation identity = { + Ogre::Vector3::ZERO, Ogre::Matrix3::IDENTITY, 1.0f + }; return identity; } }; diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 64ef1e3e9..f7d3c6e96 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -24,6 +24,8 @@ #ifndef _NIF_NODE_H_ #define _NIF_NODE_H_ +#include + #include "controlled.hpp" #include "data.hpp" #include "property.hpp" @@ -43,6 +45,7 @@ public: // Node flags. Interpretation depends somewhat on the type of node. int flags; Transformation trafo; + Ogre::Vector3 velocity; // Unused? Might be a run-time game state PropertyList props; // Bounding box info @@ -57,6 +60,7 @@ public: flags = nif->getUShort(); trafo = nif->getTrafo(); + velocity = nif->getVector3(); props.read(nif); hasBounds = !!nif->getInt(); @@ -106,20 +110,9 @@ public: boneTrafo = &bi.trafo; boneIndex = ind; } -}; -struct NiTriShapeCopy -{ - std::string sname; - std::vector boneSequence; - Nif::NiSkinData::BoneTrafoCopy trafo; - //Ogre::Quaternion initialBoneRotation; - //Ogre::Vector3 initialBoneTranslation; - std::vector vertices; - std::vector normals; - std::vector boneinfo; - std::map > vertsToWeights; - Nif::NiMorphData morph; + Ogre::Matrix4 getLocalTransform(); + Ogre::Matrix4 getWorldTransform(); }; struct NiNode : Node @@ -151,8 +144,8 @@ struct NiNode : Node for(size_t i = 0;i < children.length();i++) { // Why would a unique list of children contain empty refs? - if(children.has(i)) - children[i].parent = this; + if(!children[i].empty()) + children[i]->parent = this; } } }; @@ -182,28 +175,6 @@ struct NiTriShape : Node data.post(nif); skin.post(nif); } - - NiTriShapeCopy clone() - { - NiTriShapeCopy copy; - copy.sname = name; - float *ptr = (float*)&data->vertices[0]; - float *ptrNormals = (float*)&data->normals[0]; - int numVerts = data->vertices.size() / 3; - for(int i = 0; i < numVerts; i++) - { - float *current = (float*) (ptr + i * 3); - copy.vertices.push_back(Ogre::Vector3(*current, *(current + 1), *(current + 2))); - - if(ptrNormals) - { - float *currentNormals = (float*) (ptrNormals + i * 3); - copy.normals.push_back(Ogre::Vector3(*currentNormals, *(currentNormals + 1), *(currentNormals + 2))); - } - } - - return copy; - } }; struct NiCamera : Node diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 84f253eb8..5c4141fa9 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -24,7 +24,7 @@ #ifndef _NIF_RECORD_H_ #define _NIF_RECORD_H_ -#include +#include namespace Nif { diff --git a/components/nif/record_ptr.hpp b/components/nif/record_ptr.hpp index 755094147..ef5bb1dee 100644 --- a/components/nif/record_ptr.hpp +++ b/components/nif/record_ptr.hpp @@ -71,18 +71,21 @@ public: } /// Look up the actual object from the index - X* getPtr() + X* getPtr() const { assert(ptr != NULL); return ptr; } - X& get() { return *getPtr(); } + X& get() const + { return *getPtr(); } /// Syntactic sugar - X* operator->() { return getPtr(); } + X* operator->() const + { return getPtr(); } /// Pointers are allowed to be empty - bool empty() { return ptr == NULL; } + bool empty() const + { return ptr == NULL; } }; /** A list of references to other records. These are read as a list, @@ -111,17 +114,10 @@ public: list[i].post(nif); } - X& operator[](size_t index) - { - return list.at(index).get(); - } + const Ptr& operator[](size_t index) const + { return list.at(index); } - bool has(size_t index) - { - return !list.at(index).empty(); - } - - size_t length() + size_t length() const { return list.size(); } }; diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index ea94e7758..071f03630 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -79,7 +79,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) // of the early stages of development. Right now we WANT to catch // every error as early and intrusively as possible, as it's most // likely a sign of incomplete code rather than faulty input. - Nif::NIFFile nif(resourceName); + Nif::NIFFile nif(resourceName.substr(0, resourceName.length()-7)); if (nif.numRecords() < 1) { warn("Found no records in NIF."); @@ -138,9 +138,9 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) int n = list.length(); for (int i=0; ipos + trafo->rotation*final.pos*trafo->scale; - final.velocity = trafo->velocity + trafo->rotation*final.velocity*trafo->scale; // Merge the rotations together final.rotation = trafo->rotation * final.rotation; @@ -222,9 +221,9 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, int n = list.length(); for (int i=0; itrafo,hasCollisionNode,isCollisionNode,raycastingOnly); + handleNode(list[i].getPtr(), flags,&node->trafo,hasCollisionNode,isCollisionNode,raycastingOnly); } } } @@ -239,8 +238,8 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, int n = list.length(); for (int i=0; itrafo, hasCollisionNode,true,raycastingOnly); + if (!list[i].empty()) + handleNode(list[i].getPtr(), flags,&node->trafo, hasCollisionNode,true,raycastingOnly); } } } @@ -272,19 +271,16 @@ void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape *shape, int flags Nif::NiTriShapeData *data = shape->data.getPtr(); - float* vertices = &data->vertices[0]; - short* triangles = &data->triangles[0]; + const std::vector &vertices = data->vertices; const Ogre::Matrix3 &rot = shape->trafo.rotation; const Ogre::Vector3 &pos = shape->trafo.pos; - float scale = shape->trafo.scale; - for(unsigned int i=0; i < data->triangles.size(); i = i+3) + float scale = shape->trafo.scale * parentScale; + short* triangles = &data->triangles[0]; + for(size_t i = 0;i < data->triangles.size();i+=3) { - Ogre::Vector3 b1(vertices[triangles[i+0]*3]*parentScale,vertices[triangles[i+0]*3+1]*parentScale,vertices[triangles[i+0]*3+2]*parentScale); - Ogre::Vector3 b2(vertices[triangles[i+1]*3]*parentScale,vertices[triangles[i+1]*3+1]*parentScale,vertices[triangles[i+1]*3+2]*parentScale); - Ogre::Vector3 b3(vertices[triangles[i+2]*3]*parentScale,vertices[triangles[i+2]*3+1]*parentScale,vertices[triangles[i+2]*3+2]*parentScale); - b1 = pos + rot*b1*scale; - b2 = pos + rot*b2*scale; - b3 = pos + rot*b3*scale; + Ogre::Vector3 b1 = pos + rot*vertices[triangles[i+0]]*scale; + Ogre::Vector3 b2 = pos + rot*vertices[triangles[i+1]]*scale; + Ogre::Vector3 b3 = pos + rot*vertices[triangles[i+2]]*scale; mTriMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); } } diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index bde948970..4393749fb 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -25,6 +25,8 @@ #include "ogre_nif_loader.hpp" +#include + #include #include #include @@ -32,9 +34,12 @@ #include #include #include +#include +#include #include #include +#include #include @@ -45,31 +50,8 @@ typedef unsigned char ubyte; using namespace std; using namespace Nif; -using namespace Misc; using namespace NifOgre; -NIFLoader& NIFLoader::getSingleton() -{ - static NIFLoader instance; - return instance; -} - -NIFLoader* NIFLoader::getSingletonPtr() -{ - return &getSingleton(); -} - -void NIFLoader::warn(string msg) -{ - std::cerr << "NIFLoader: Warn:" << msg << "\n"; -} - -void NIFLoader::fail(string msg) -{ - std::cerr << "NIFLoader: Fail: "<< msg << std::endl; - assert(1); -} - // Helper class that computes the bounding box and of a mesh class BoundsFinder @@ -155,6 +137,264 @@ public: } }; + +class NIFSkeletonLoader : public Ogre::ManualResourceLoader { + +static void warn(const std::string &msg) +{ + std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl; +} + +static void fail(const std::string &msg) +{ + std::cerr << "NIFSkeletonLoader: Fail: "<< msg << std::endl; + abort(); +} + + +void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector &ctrls, Ogre::Bone *parent=NULL) +{ + Ogre::Bone *bone; + if(!skel->hasBone(node->name)) + bone = skel->createBone(node->name); + else + bone = skel->createBone(); + if(parent) parent->addChild(bone); + + bone->setOrientation(node->trafo.rotation); + bone->setPosition(node->trafo.pos); + bone->setScale(Ogre::Vector3(node->trafo.scale)); + bone->setBindingPose(); + bone->setInitialState(); + + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiKeyframeController) + ctrls.push_back(static_cast(ctrl.getPtr())); + ctrl = ctrl->next; + } + + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + buildBones(skel, children[i].getPtr(), ctrls, bone); + } + } +} + + +/* Comparitor to help sort Key<> vectors */ +template +struct KeyTimeSort +{ + bool operator()(const Nif::KeyT &lhs, const Nif::KeyT &rhs) const + { return lhs.mTime < rhs.mTime; } +}; + + +typedef std::map LoaderMap; +static LoaderMap sLoaders; + +public: +void loadResource(Ogre::Resource *resource) +{ + Ogre::Skeleton *skel = dynamic_cast(resource); + OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); + + Nif::NIFFile nif(skel->getName()); + const Nif::Node *node = dynamic_cast(nif.getRecord(0)); + + std::vector ctrls; + buildBones(skel, node, ctrls); + + std::vector targets; + // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file + if(ctrls.size() == 0) // No animations? Then we're done. + return; + + float maxtime = 0.0f; + for(size_t i = 0;i < ctrls.size();i++) + { + Nif::NiKeyframeController *ctrl = ctrls[i]; + maxtime = std::max(maxtime, ctrl->timeStop); + Nif::Named *target = dynamic_cast(ctrl->target.getPtr()); + if(target != NULL) + targets.push_back(target->name); + } + + if(targets.size() != ctrls.size()) + { + warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+ + Ogre::StringConverter::toString(ctrls.size())+" controllers)"); + return; + } + + Ogre::Animation *anim = skel->createAnimation(skel->getName(), maxtime); + /* HACK: Pre-create the node tracks by matching the track IDs with the + * bone IDs. Otherwise, Ogre animates the wrong bones. */ + size_t bonecount = skel->getNumBones(); + for(size_t i = 0;i < bonecount;i++) + anim->createNodeTrack(i, skel->getBone(i)); + + for(size_t i = 0;i < ctrls.size();i++) + { + Nif::NiKeyframeController *kfc = ctrls[i]; + Nif::NiKeyframeData *kf = kfc->data.getPtr(); + + /* Get the keyframes and make sure they're sorted first to last */ + QuaternionKeyList quatkeys = kf->mRotations; + Vector3KeyList trankeys = kf->mTranslations; + FloatKeyList scalekeys = kf->mScales; + std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort()); + std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort()); + std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort()); + + QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); + Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); + FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); + + Ogre::Bone *bone = skel->getBone(targets[i]); + const Ogre::Quaternion startquat = bone->getInitialOrientation(); + const Ogre::Vector3 starttrans = bone->getInitialPosition(); + const Ogre::Vector3 startscale = bone->getInitialScale(); + Ogre::NodeAnimationTrack *nodetrack = anim->getNodeTrack(bone->getHandle()); + + Ogre::Quaternion lastquat, curquat; + Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); + Ogre::Vector3 lastscale(1.0f), curscale(1.0f); + if(quatiter != quatkeys.mKeys.end()) + lastquat = curquat = startquat.Inverse() * quatiter->mValue; + if(traniter != trankeys.mKeys.end()) + lasttrans = curtrans = traniter->mValue - starttrans; + if(scaleiter != scalekeys.mKeys.end()) + lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + bool didlast = false; + while(!didlast) + { + float curtime = kfc->timeStop; + if(quatiter != quatkeys.mKeys.end()) + curtime = std::min(curtime, quatiter->mTime); + if(traniter != trankeys.mKeys.end()) + curtime = std::min(curtime, traniter->mTime); + if(scaleiter != scalekeys.mKeys.end()) + curtime = std::min(curtime, scaleiter->mTime); + + curtime = std::max(curtime, kfc->timeStart); + if(curtime >= kfc->timeStop) + { + didlast = true; + curtime = kfc->timeStop; + } + + // Get the latest quaternion, translation, and scale for the + // current time + while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) + { + lastquat = curquat; + curquat = startquat.Inverse() * quatiter->mValue; + quatiter++; + } + while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) + { + lasttrans = curtrans; + curtrans = traniter->mValue - starttrans; + traniter++; + } + while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) + { + lastscale = curscale; + curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + scaleiter++; + } + + Ogre::TransformKeyFrame *kframe; + kframe = nodetrack->createNodeKeyFrame(curtime); + if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) + kframe->setRotation(curquat); + else + { + QuaternionKeyList::VecType::const_iterator last = quatiter-1; + float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); + kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); + } + if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin()) + kframe->setTranslate(curtrans); + else + { + Vector3KeyList::VecType::const_iterator last = traniter-1; + float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); + kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); + } + if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin()) + kframe->setScale(curscale); + else + { + FloatKeyList::VecType::const_iterator last = scaleiter-1; + float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); + kframe->setScale(lastscale + ((curscale-lastscale)*diff)); + } + } + } + anim->optimise(); +} + +bool createSkeleton(const std::string &name, const std::string &group, TextKeyMap *textkeys, const Nif::Node *node) +{ + if(textkeys) + { + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + for(size_t i = 0;i < tk->list.size();i++) + (*textkeys)[tk->list[i].time] = tk->list[i].text; + } + e = e->extra; + } + } + + if(node->boneTrafo != NULL) + { + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + + Ogre::SkeletonPtr skel = skelMgr.getByName(name); + if(skel.isNull()) + { + NIFSkeletonLoader *loader = &sLoaders[name]; + skel = skelMgr.create(name, group, true, loader); + } + + if(!textkeys || textkeys->size() > 0) + return true; + } + + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + { + if(createSkeleton(name, group, textkeys, children[i].getPtr())) + return true; + } + } + } + return false; +} + +}; +NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; + + // Conversion of blend / test mode from NIF -> OGRE. // Not in use yet, so let's comment it out. /* @@ -199,25 +439,137 @@ static CompareFunction getTestMode(int mode) } */ -void NIFLoader::setOutputAnimFiles(bool output){ - mOutputAnimFiles = output; -} -void NIFLoader::setVerbosePath(std::string path){ - verbosePath = path; -} -void NIFLoader::createMaterial(const Ogre::String &name, - const Ogre::Vector3 &ambient, - const Ogre::Vector3 &diffuse, - const Ogre::Vector3 &specular, - const Ogre::Vector3 &emissive, - float glossiness, float alpha, - int alphaFlags, float alphaTest, - const Ogre::String &texName, bool vertexColor) -{ - if (texName.empty()) - return; - sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (name, "openmw_objects_base"); +class NIFMaterialLoader { + +static std::map MaterialMap; + +static void warn(const std::string &msg) +{ + std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; +} + +static void fail(const std::string &msg) +{ + std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + abort(); +} + + +public: +static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) +{ + Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); + Ogre::MaterialPtr material = matMgr.getByName(name); + if(!material.isNull()) + return name; + + Ogre::Vector3 ambient(1.0f); + Ogre::Vector3 diffuse(1.0f); + Ogre::Vector3 specular(0.0f); + Ogre::Vector3 emissive(0.0f); + float glossiness = 0.0f; + float alpha = 1.0f; + int alphaFlags = -1; + ubyte alphaTest = 0; + Ogre::String texName; + + bool vertexColour = (shape->data->colors.size() != 0); + + // These are set below if present + const NiTexturingProperty *t = NULL; + const NiMaterialProperty *m = NULL; + const NiAlphaProperty *a = NULL; + + // Scan the property list for material information + const PropertyList &list = shape->props; + for (size_t i = 0;i < list.length();i++) + { + // Entries may be empty + if (list[i].empty()) continue; + + const Property *pr = list[i].getPtr(); + if (pr->recType == RC_NiTexturingProperty) + t = static_cast(pr); + else if (pr->recType == RC_NiMaterialProperty) + m = static_cast(pr); + else if (pr->recType == RC_NiAlphaProperty) + a = static_cast(pr); + else + warn("Skipped property type: "+pr->recName); + } + + // Texture + if (t && t->textures[0].inUse) + { + NiSourceTexture *st = t->textures[0].texture.getPtr(); + if (st->external) + { + /* Bethesda at some at some point converted all their BSA + * textures from tga to dds for increased load speed, but all + * texture file name references were kept as .tga. + */ + texName = "textures\\" + st->filename; + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + { + Ogre::String::size_type pos = texName.rfind('.'); + texName.replace(pos, texName.length(), ".dds"); + } + } + else warn("Found internal texture, ignoring."); + } + + // Alpha modifiers + if (a) + { + alphaFlags = a->flags; + alphaTest = a->data.threshold; + } + + // Material + if(m) + { + ambient = m->data.ambient; + diffuse = m->data.diffuse; + specular = m->data.specular; + emissive = m->data.emissive; + glossiness = m->data.glossiness; + alpha = m->data.alpha; + } + + Ogre::String matname = name; + if (m || !texName.empty()) + { + // Generate a hash out of all properties that can affect the material. + size_t h = 0; + boost::hash_combine(h, ambient.x); + boost::hash_combine(h, ambient.y); + boost::hash_combine(h, ambient.z); + boost::hash_combine(h, diffuse.x); + boost::hash_combine(h, diffuse.y); + boost::hash_combine(h, diffuse.z); + boost::hash_combine(h, specular.x); + boost::hash_combine(h, specular.y); + boost::hash_combine(h, specular.z); + boost::hash_combine(h, emissive.x); + boost::hash_combine(h, emissive.y); + boost::hash_combine(h, emissive.z); + boost::hash_combine(h, texName); + boost::hash_combine(h, vertexColour); + boost::hash_combine(h, alphaFlags); + + std::map::iterator itr = MaterialMap.find(h); + if (itr != MaterialMap.end()) + { + // a suitable material exists already - use it + return itr->second; + } + // not found, create a new one + MaterialMap.insert(std::make_pair(h, matname)); + } + + // No existing material like this. Create a new one. + sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (matname, "openmw_objects_base"); instance->setProperty ("ambient", sh::makeProperty ( new sh::Vector3(ambient.x, ambient.y, ambient.z))); @@ -232,7 +584,7 @@ void NIFLoader::createMaterial(const Ogre::String &name, instance->setProperty ("diffuseMap", sh::makeProperty(texName)); - if (vertexColor) + if (vertexColour) instance->setProperty ("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); // Add transparency if NiAlphaProperty was present @@ -286,1078 +638,511 @@ void NIFLoader::createMaterial(const Ogre::String &name, pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1)); } - else - pass->setDepthWriteEnabled(true); */ +*/ + + return matname; } -// Takes a name and adds a unique part to it. This is just used to -// make sure that all materials are given unique names. -Ogre::String NIFLoader::getUniqueName(const Ogre::String &input) +}; +std::map NIFMaterialLoader::MaterialMap; + + +class NIFMeshLoader : Ogre::ManualResourceLoader { - static int addon = 0; - static char buf[8]; - snprintf(buf, 8, "_%d", addon++); + std::string mName; + std::string mGroup; + std::string mShapeName; + std::string mMaterialName; + std::string mSkelName; - // Don't overflow the buffer - if (addon > 999999) addon = 0; - - return input + buf; -} - -// Check if the given texture name exists in the real world. If it -// does not, change the string IN PLACE to say .dds instead and try -// that. The texture may still not exist, but no information of value -// is lost in that case. -void NIFLoader::findRealTexture(Ogre::String &texName) -{ - if(Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) - return; - - // Change texture extension to .dds - Ogre::String::size_type pos = texName.rfind('.'); - texName.replace(pos, texName.length(), ".dds"); -} - -//Handle node at top - -// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given -// mesh. -void NIFLoader::createOgreSubMesh(NiTriShape *shape, const Ogre::String &material, std::list &vertexBoneAssignments) -{ - // cout << "s:" << shape << "\n"; - NiTriShapeData *data = shape->data.getPtr(); - Ogre::SubMesh *sub = mesh->createSubMesh(shape->name); - - int nextBuf = 0; - - // This function is just one long stream of Ogre-barf, but it works - // great. - - // Add vertices - int numVerts = data->vertices.size() / 3; - sub->vertexData = new Ogre::VertexData(); - sub->vertexData->vertexCount = numVerts; - sub->useSharedVertices = false; - - Ogre::VertexDeclaration *decl = sub->vertexData->vertexDeclaration; - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - - Ogre::HardwareVertexBufferSharedPtr vbuf = - Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - numVerts, Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, false); - - if(flip) - { - float *datamod = new float[data->vertices.size()]; - //std::cout << "Shape" << shape->name.toString() << "\n"; - for(int i = 0; i < numVerts; i++) - { - int index = i * 3; - const float *pos = &data->vertices[index]; - Ogre::Vector3 original = Ogre::Vector3(*pos ,*(pos+1), *(pos+2)); - original = mTransform * original; - mBoundingBox.merge(original); - datamod[index] = original.x; - datamod[index+1] = original.y; - datamod[index+2] = original.z; - } - vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false); - delete [] datamod; - } - else - { - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->vertices[0], false); - } - - - Ogre::VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding; - bind->setBinding(nextBuf++, vbuf); - - if (data->normals.size()) + void warn(const std::string &msg) { - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); - vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); + std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl; + } - if(flip) - { - Ogre::Quaternion rotation = mTransform.extractQuaternion(); - rotation.normalise(); - - float *datamod = new float[data->normals.size()]; - for(int i = 0; i < numVerts; i++) - { - int index = i * 3; - const float *pos = &data->normals[index]; - Ogre::Vector3 original = Ogre::Vector3(*pos ,*(pos+1), *(pos+2)); - original = rotation * original; - if (mNormaliseNormals) - { - original.normalise(); - } - - - datamod[index] = original.x; - datamod[index+1] = original.y; - datamod[index+2] = original.z; - } - vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false); - delete [] datamod; - } - else - { - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->normals[0], false); - } - bind->setBinding(nextBuf++, vbuf); + void fail(const std::string &msg) + { + std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl; + abort(); } - // Vertex colors - if (data->colors.size()) + // Convert NiTriShape to Ogre::SubMesh + void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape *shape) { - const float *colors = &data->colors[0]; - Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); - std::vector colorsRGB(numVerts); - Ogre::RGBA *pColour = &colorsRGB.front(); - for (int i=0; idata.getPtr(); + const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); + std::vector srcVerts = data->vertices; + std::vector srcNorms = data->normals; + if(skin != NULL) { - rs->convertColourValue(Ogre::ColourValue(colors[0],colors[1],colors[2], - colors[3]),pColour++); - colors += 4; - } - decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); - vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), - numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); - vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB.front(), true); - bind->setBinding(nextBuf++, vbuf); - } + // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be + // explicitly attached later. + mesh->setSkeletonName(mSkelName); - if (data->uvlist.size()) - { + // Get the skeleton resource, so vertices can be transformed into the bones' initial state. + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + skel = skelMgr->getByName(mSkelName); + skel->touch(); - decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES); - vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), - numVerts, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,false); + // Convert vertices and normals to bone space from bind position. It would be + // better to transform the bones into bind position, but there doesn't seem to + // be a reliable way to do that. + std::vector newVerts(srcVerts.size(), Ogre::Vector3(0.0f)); + std::vector newNorms(srcNorms.size(), Ogre::Vector3(0.0f)); - if(flip) - { - float *datamod = new float[data->uvlist.size()]; - - for(unsigned int i = 0; i < data->uvlist.size(); i+=2){ - float x = data->uvlist[i]; - - float y = data->uvlist[i + 1]; - - datamod[i] =x; - datamod[i + 1] =y; - } - vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false); - delete [] datamod; - } - else - vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[0], false); - bind->setBinding(nextBuf++, vbuf); - } - - // Triangle faces - The total number of triangle points - int numFaces = data->triangles.size(); - if (numFaces) - { - - sub->indexData->indexCount = numFaces; - sub->indexData->indexStart = 0; - Ogre::HardwareIndexBufferSharedPtr ibuf = Ogre::HardwareBufferManager::getSingleton(). - createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, numFaces, - Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); - - if(flip && mFlipVertexWinding && sub->indexData->indexCount % 3 == 0){ - - sub->indexData->indexBuffer = ibuf; - - uint16_t *datamod = new uint16_t[numFaces]; - int index = 0; - for (size_t i = 0; i < sub->indexData->indexCount; i+=3) - { - - const short *pos = &data->triangles[index]; - uint16_t i0 = (uint16_t) *(pos+0); - uint16_t i1 = (uint16_t) *(pos+1); - uint16_t i2 = (uint16_t) *(pos+2); - - //std::cout << "i0: " << i0 << "i1: " << i1 << "i2: " << i2 << "\n"; - - - datamod[index] = i2; - datamod[index+1] = i1; - datamod[index+2] = i0; - - index += 3; - } - - ibuf->writeData(0, ibuf->getSizeInBytes(), datamod, false); - delete [] datamod; - - } - else - ibuf->writeData(0, ibuf->getSizeInBytes(), &data->triangles[0], false); - sub->indexData->indexBuffer = ibuf; - } - - // Set material if one was given - if (!material.empty()) sub->setMaterialName(material); - - //add vertex bone assignments - - for (std::list::iterator it = vertexBoneAssignments.begin(); - it != vertexBoneAssignments.end(); it++) - { - sub->addBoneAssignment(*it); - } - if(mSkel.isNull()) - needBoneAssignments.push_back(sub); -} - -// Helper math functions. Reinventing linear algebra for the win! - -// Computes C = B + AxC*scale -static void vectorMulAdd(const Ogre::Matrix3 &A, const Ogre::Vector3 &B, float *C, float scale) -{ - // Keep the original values - float a = C[0]; - float b = C[1]; - float c = C[2]; - - // Perform matrix multiplication, scaling and addition - for (int i=0;i<3;i++) - C[i] = B[i] + (a*A[i][0] + b*A[i][1] + c*A[i][2])*scale; -} - -// Computes B = AxB (matrix*vector) -static void vectorMul(const Ogre::Matrix3 &A, float *C) -{ - // Keep the original values - float a = C[0]; - float b = C[1]; - float c = C[2]; - - // Perform matrix multiplication, scaling and addition - for (int i=0;i<3;i++) - C[i] = a*A[i][0] + b*A[i][1] + c*A[i][2]; -} - - -void NIFLoader::handleNiTriShape(NiTriShape *shape, int flags, BoundsFinder &bounds, Transformation original, std::vector boneSequence) -{ - assert(shape != NULL); - - bool saveTheShape = inTheSkeletonTree; - // Interpret flags - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision - - // Bounding box collision isn't implemented, always use mesh for now. - if (bbcollide) - { - collide = true; - bbcollide = false; - } - - // If the object was marked "NCO" earlier, it shouldn't collide with - // anything. - if (flags & 0x800) - { - collide = false; - bbcollide = false; - } - - if (!collide && !bbcollide && hidden) - // This mesh apparently isn't being used for anything, so don't - // bother setting it up. - return; - - // Material name for this submesh, if any - Ogre::String material; - - // Skip the entire material phase for hidden nodes - if (!hidden) - { - // These are set below if present - NiTexturingProperty *t = NULL; - NiMaterialProperty *m = NULL; - NiAlphaProperty *a = NULL; - // can't make any sense of these values, so ignoring them for now - //NiVertexColorProperty *v = NULL; - - // Scan the property list for material information - PropertyList &list = shape->props; - int n = list.length(); - for (int i=0; irecType == RC_NiTexturingProperty) - t = static_cast(pr); - else if (pr->recType == RC_NiMaterialProperty) - m = static_cast(pr); - else if (pr->recType == RC_NiAlphaProperty) - a = static_cast(pr); - //else if (pr->recType == RC_NiVertexColorProperty) - //v = static_cast(pr); - } - - // Texture - Ogre::String texName; - if (t && t->textures[0].inUse) - { - NiSourceTexture *st = t->textures[0].texture.getPtr(); - if (st->external) + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t b = 0;b < bones.length();b++) { - /* findRealTexture checks if the file actually - exists. If it doesn't, and the name ends in .tga, it - will try replacing the extension with .dds instead - and search for that. Bethesda at some at some point - converted all their BSA textures from tga to dds for - increased load speed, but all texture file name - references were kept as .tga. + Ogre::Bone *bone = skel->getBone(bones[b]->name); + Ogre::Matrix4 mat, mat2; + mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), + Ogre::Quaternion(data->bones[b].trafo.rotation)); + mat2.makeTransform(bone->_getDerivedPosition(), bone->_getDerivedScale(), + bone->_getDerivedOrientation()); + mat = mat2 * mat; - The function replaces the name in place (that's why - we cast away the const modifier), but this is no - problem since all the nif data is stored in a local - throwaway buffer. - */ - texName = "textures\\" + st->filename; - findRealTexture(texName); - } - else warn("Found internal texture, ignoring."); - } - - // Alpha modifiers - int alphaFlags = -1; - ubyte alphaTest = 0; - if (a) - { - alphaFlags = a->flags; - alphaTest = a->data.threshold; - } - - // Material - if (m || !texName.empty()) - { - // If we're here, then this mesh has a material. Thus we - // need to calculate a snappy material name. It should - // contain the mesh name (mesh->getName()) but also has to - // be unique. One mesh may use many materials. - material = getUniqueName(mesh->getName()); - - if (m) - { - // Use NiMaterialProperty data to create the data - const S_MaterialProperty *d = &m->data; - - std::multimap::iterator itr = MaterialMap.find(texName); - std::multimap::iterator lastElement; - lastElement = MaterialMap.upper_bound(texName); - if (itr != MaterialMap.end()) + const std::vector &weights = data->bones[b].weights; + for(size_t i = 0;i < weights.size();i++) { - for ( ; itr != lastElement; ++itr) + size_t index = weights[i].vertex; + float weight = weights[i].weight; + + newVerts.at(index) += (mat*srcVerts[index]) * weight; + if(newNorms.size() > index) { - //std::cout << "OK!"; - //MaterialPtr mat = MaterialManager::getSingleton().getByName(itr->second,recourceGroup); - material = itr->second; - //if( mat->getA + Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f); + vec4 = mat*vec4 * weight; + newNorms[index] += Ogre::Vector3(&vec4[0]); } } - else + } + + srcVerts = newVerts; + srcNorms = newNorms; + } + else if(mSkelName.length() == 0) + { + // No skinning and no skeleton, so just transform the vertices and + // normals into position. + Ogre::Matrix4 mat4 = shape->getWorldTransform(); + for(size_t i = 0;i < srcVerts.size();i++) + { + Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f); + vec4 = mat4*vec4; + srcVerts[i] = Ogre::Vector3(&vec4[0]); + } + for(size_t i = 0;i < srcNorms.size();i++) + { + Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); + vec4 = mat4*vec4; + srcNorms[i] = Ogre::Vector3(&vec4[0]); + } + } + + // Set the bounding box first + BoundsFinder bounds; + bounds.add(&srcVerts[0][0], srcVerts.size()); + // No idea why this offset is needed. It works fine without it if the + // vertices weren't transformed first, but otherwise it fails later on + // when the object is being inserted into the scene. + mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f, + bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f)); + mesh->_setBoundingSphereRadius(bounds.getRadius()); + + // This function is just one long stream of Ogre-barf, but it works + // great. + Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareVertexBufferSharedPtr vbuf; + Ogre::HardwareIndexBufferSharedPtr ibuf; + Ogre::VertexBufferBinding *bind; + Ogre::VertexDeclaration *decl; + int nextBuf = 0; + + Ogre::SubMesh *sub = mesh->createSubMesh(shape->name); + + // Add vertices + sub->useSharedVertices = false; + sub->vertexData = new Ogre::VertexData(); + sub->vertexData->vertexStart = 0; + sub->vertexData->vertexCount = srcVerts.size(); + + decl = sub->vertexData->vertexDeclaration; + bind = sub->vertexData->vertexBufferBinding; + if(srcVerts.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcVerts.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, + true); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex normals + if(srcNorms.size()) + { + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + srcNorms.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, + true); + vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true); + + decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); + bind->setBinding(nextBuf++, vbuf); + } + + // Vertex colors + const std::vector &colors = data->colors; + if(colors.size()) + { + Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); + std::vector colorsRGB(colors.size()); + for(size_t i = 0;i < colorsRGB.size();i++) + { + Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]); + rs->convertColourValue(clr, &colorsRGB[i]); + } + vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, + true); + vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true); + decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); + bind->setBinding(nextBuf++, vbuf); + } + + // Texture UV coordinates + size_t numUVs = data->uvlist.size(); + if(numUVs) + { + size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2); + vbuf = hwBufMgr->createVertexBuffer(elemSize, srcVerts.size()*numUVs, + Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true); + for(size_t i = 0;i < numUVs;i++) + { + const std::vector &uvlist = data->uvlist[i]; + vbuf->writeData(i*srcVerts.size()*elemSize, elemSize*srcVerts.size(), &uvlist[0], true); + decl->addElement(nextBuf, i*srcVerts.size()*elemSize, Ogre::VET_FLOAT2, + Ogre::VES_TEXTURE_COORDINATES, i); + } + bind->setBinding(nextBuf++, vbuf); + } + + // Triangle faces + const std::vector &srcIdx = data->triangles; + if(srcIdx.size()) + { + ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(), + Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); + ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true); + sub->indexData->indexBuffer = ibuf; + sub->indexData->indexCount = srcIdx.size(); + sub->indexData->indexStart = 0; + } + + // Assign bone weights for this TriShape + if(skin != NULL) + { + const Nif::NiSkinData *data = skin->data.getPtr(); + const Nif::NodeList &bones = skin->bones; + for(size_t i = 0;i < bones.length();i++) + { + Ogre::VertexBoneAssignment boneInf; + boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle(); + + const std::vector &weights = data->bones[i].weights; + for(size_t j = 0;j < weights.size();j++) { - //std::cout << "new"; - createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive, - d->glossiness, d->alpha, alphaFlags, alphaTest, texName, shape->data->colors.size() != 0); - MaterialMap.insert(std::make_pair(texName,material)); + boneInf.vertexIndex = weights[j].vertex; + boneInf.weight = weights[j].weight; + sub->addBoneAssignment(boneInf); } } + } + + if(mMaterialName.length() > 0) + sub->setMaterialName(mMaterialName); + } + + bool findTriShape(Ogre::Mesh *mesh, Nif::Node *node) + { + if(node->recType == Nif::RC_NiTriShape && mShapeName == node->name) + { + handleNiTriShape(mesh, dynamic_cast(node)); + return true; + } + + Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + { + if(findTriShape(mesh, children[i].getPtr())) + return true; + } + } + } + return false; + } + + + typedef std::map LoaderMap; + static LoaderMap sLoaders; + +public: + NIFMeshLoader() + { } + NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName) + : mName(name), mGroup(group), mSkelName(skelName) + { } + + virtual void loadResource(Ogre::Resource *resource) + { + Ogre::Mesh *mesh = dynamic_cast(resource); + assert(mesh && "Attempting to load a mesh into a non-mesh resource!"); + + if(!mShapeName.length()) + { + if(mSkelName.length() > 0) + mesh->setSkeletonName(mSkelName); + return; + } + + Nif::NIFFile nif(mName); + Nif::Node *node = dynamic_cast(nif.getRecord(0)); + findTriShape(mesh, node); + } + + void createMeshes(const Nif::Node *node, MeshPairList &meshes, int flags=0) + { + flags |= node->flags; + + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + Nif::NiStringExtraData *sd; + Nif::NiTextKeyExtraData *td; + if((sd=dynamic_cast(e.getPtr())) != NULL) + { + // String markers may contain important information + // affecting the entire subtree of this obj + if(sd->string == "MRK") + { + // Marker objects. These are only visible in the + // editor. + flags |= 0x01; + } + } + else if((td=dynamic_cast(e.getPtr())) != NULL) + { + // TODO: Read and store text keys somewhere + } else - { - // We only have a texture name. Create a default - // material for it. - const Ogre::Vector3 zero(0.0f), one(1.0f); - createMaterial(material, one, one, zero, zero, 0.0f, 1.0f, - alphaFlags, alphaTest, texName, shape->data->colors.size() != 0); - } + warn("Unhandled extra data type "+e->recName); + e = e->extra; } - } // End of material block, if(!hidden) ... - /* Do in-place transformation of all the vertices and normals. This - is pretty messy stuff, but we need it to make the sub-meshes - appear in the correct place. Neither Ogre nor Bullet support - nested levels of sub-meshes with transformations applied to each - level. - */ - NiTriShapeData *data = shape->data.getPtr(); - int numVerts = data->vertices.size() / 3; - - float *ptr = (float*)&data->vertices[0]; - float *optr = ptr; - - std::list vertexBoneAssignments; - - Nif::NiTriShapeCopy copy = shape->clone(); - - if(!shape->controller.empty()) - { - Nif::Controller* cont = shape->controller.getPtr(); - if(cont->recType == RC_NiGeomMorpherController) - { - Nif::NiGeomMorpherController* morph = dynamic_cast (cont); - copy.morph = morph->data.get(); - copy.morph.setStartTime(morph->timeStart); - copy.morph.setStopTime(morph->timeStop); - saveTheShape = true; - } - - } - //use niskindata for the position of vertices. - if (!shape->skin.empty()) - { - - - - // vector that stores if the position of a vertex is absolute - std::vector vertexPosAbsolut(numVerts,false); - std::vector vertexPosOriginal(numVerts, Ogre::Vector3::ZERO); - std::vector vertexNormalOriginal(numVerts, Ogre::Vector3::ZERO); - - float *ptrNormals = (float*)&data->normals[0]; - //the bone from skin->bones[boneIndex] is linked to skin->data->bones[boneIndex] - //the first one contains a link to the bone, the second vertex transformation - //relative to the bone - int boneIndex = 0; - Ogre::Bone *bonePtr; - Ogre::Vector3 vecPos; - Ogre::Quaternion vecRot; - - std::vector boneList = shape->skin->data->bones; - - /* - Iterate through the boneList which contains what vertices are linked to - the bone (it->weights array) and at what position (it->trafo) - That position is added to every vertex. - */ - for (std::vector::iterator it = boneList.begin(); - it != boneList.end(); it++) + if(node->recType == Nif::RC_NiTriShape) { - if(mSkel.isNull()) + const NiTriShape *shape = dynamic_cast(node); + + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + std::string fullname = mName+"@shape="+shape->name; + if(mSkelName.length() > 0 && mName != mSkelName) + fullname += "@skel="+mSkelName; + + std::transform(fullname.begin(), fullname.end(), fullname.begin(), ::tolower); + Ogre::MeshPtr mesh = meshMgr.getByName(fullname); + if(mesh.isNull()) { - std::cout << "No skeleton for :" << shape->skin->bones[boneIndex].name << std::endl; - break; - } - //get the bone from bones array of skindata - if(!mSkel->hasBone(shape->skin->bones[boneIndex].name)) - std::cout << "We don't have this bone"; - bonePtr = mSkel->getBone(shape->skin->bones[boneIndex].name); - - // final_vector = old_vector + old_rotation*new_vector*old_scale - - - Nif::NiSkinData::BoneInfoCopy boneinfocopy; - boneinfocopy.trafo.rotation = it->trafo.rotation; - boneinfocopy.trafo.trans = it->trafo.trans; - boneinfocopy.bonename = shape->skin->bones[boneIndex].name; - boneinfocopy.bonehandle = bonePtr->getHandle(); - copy.boneinfo.push_back(boneinfocopy); - for (unsigned int i=0; iweights.size(); i++) - { - vecPos = bonePtr->_getDerivedPosition() + - bonePtr->_getDerivedOrientation() * it->trafo.trans; - - vecRot = bonePtr->_getDerivedOrientation() * it->trafo.rotation; - unsigned int verIndex = it->weights[i].vertex; - //boneinfo.weights.push_back(*(it->weights.ptr + i)); - Nif::NiSkinData::IndividualWeight ind; - ind.weight = it->weights[i].weight; - ind.boneinfocopyindex = copy.boneinfo.size() - 1; - if(copy.vertsToWeights.find(verIndex) == copy.vertsToWeights.end()) + NIFMeshLoader *loader = &sLoaders[fullname]; + *loader = *this; + if(!(flags&0x01)) // Not hidden { - std::vector blank; - blank.push_back(ind); - copy.vertsToWeights[verIndex] = blank; - } - else - { - copy.vertsToWeights[verIndex].push_back(ind); + loader->mShapeName = shape->name; + loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup); } - //Check if the vertex is relativ, FIXME: Is there a better solution? - if (vertexPosAbsolut[verIndex] == false) - { - //apply transformation to the vertices - Ogre::Vector3 absVertPos = vecPos + vecRot * Ogre::Vector3(ptr + verIndex *3); - absVertPos = absVertPos * it->weights[i].weight; - vertexPosOriginal[verIndex] = Ogre::Vector3(ptr + verIndex *3); - - mBoundingBox.merge(absVertPos); - //convert it back to float * - for (int j=0; j<3; j++) - (ptr + verIndex*3)[j] = absVertPos[j]; - - //apply rotation to the normals (not every vertex has a normal) - //FIXME: I guessed that vertex[i] = normal[i], is that true? - if (verIndex < data->normals.size()) - { - Ogre::Vector3 absNormalsPos = vecRot * Ogre::Vector3(ptrNormals + verIndex *3); - absNormalsPos = absNormalsPos * it->weights[i].weight; - vertexNormalOriginal[verIndex] = Ogre::Vector3(ptrNormals + verIndex *3); - - for (int j=0; j<3; j++) - (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; - } - - vertexPosAbsolut[verIndex] = true; - } - else - { - Ogre::Vector3 absVertPos = vecPos + vecRot * vertexPosOriginal[verIndex]; - absVertPos = absVertPos * it->weights[i].weight; - Ogre::Vector3 old = Ogre::Vector3(ptr + verIndex *3); - absVertPos = absVertPos + old; - - mBoundingBox.merge(absVertPos); - //convert it back to float * - for (int j=0; j<3; j++) - (ptr + verIndex*3)[j] = absVertPos[j]; - - //apply rotation to the normals (not every vertex has a normal) - //FIXME: I guessed that vertex[i] = normal[i], is that true? - if (verIndex < data->normals.size()) - { - Ogre::Vector3 absNormalsPos = vecRot * vertexNormalOriginal[verIndex]; - absNormalsPos = absNormalsPos * it->weights[i].weight; - Ogre::Vector3 oldNormal = Ogre::Vector3(ptrNormals + verIndex *3); - absNormalsPos = absNormalsPos + oldNormal; - - for (int j=0; j<3; j++) - (ptrNormals + verIndex*3)[j] = absNormalsPos[j]; - } - } - - - Ogre::VertexBoneAssignment vba; - vba.boneIndex = bonePtr->getHandle(); - vba.vertexIndex = verIndex; - vba.weight = it->weights[i].weight; - - - vertexBoneAssignments.push_back(vba); + mesh = meshMgr.createManual(fullname, mGroup, loader); } - - boneIndex++; + meshes.push_back(std::make_pair(mesh, shape->name)); } + else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && + node->recType != Nif::RC_NiRotatingParticles) + warn("Unhandled mesh node type: "+node->recName); - - } - else - { - - copy.boneSequence = boneSequence; - // Rotate, scale and translate all the vertices, - const Ogre::Matrix3 &rot = shape->trafo.rotation; - const Ogre::Vector3 &pos = shape->trafo.pos; - float scale = shape->trafo.scale; - - copy.trafo.trans = original.pos; - copy.trafo.rotation = original.rotation; - copy.trafo.scale = original.scale; - //We don't use velocity for anything yet, so it does not need to be saved - - // Computes C = B + AxC*scale - for (int i=0; i(node); + if(ninode) { - vectorMulAdd(rot, pos, ptr, scale); - Ogre::Vector3 absVertPos = Ogre::Vector3(ptr); - mBoundingBox.merge(absVertPos); - ptr += 3; - } - - // Remember to rotate all the vertex normals as well - if (data->normals.size()) - { - ptr = (float*)&data->normals[0]; - for (int i=0; ichildren; + for(size_t i = 0;i < children.length();i++) { - vectorMul(rot, ptr); - ptr += 3; + if(!children[i].empty()) + createMeshes(children[i].getPtr(), meshes, flags); } } - if(!mSkel.isNull() ){ - int boneIndex; - - boneIndex = mSkel->getNumBones() - 1; - for(int i = 0; i < numVerts; i++){ - Ogre::VertexBoneAssignment vba; - vba.boneIndex = boneIndex; - vba.vertexIndex = i; - vba.weight = 1; - vertexBoneAssignments.push_back(vba); - } - } } +}; +NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; - if (!hidden) - { - // Add this vertex set to the bounding box - bounds.add(optr, numVerts); - if(saveTheShape) - shapes.push_back(copy); - // Create the submesh - createOgreSubMesh(shape, material, vertexBoneAssignments); - } -} - -void NIFLoader::calculateTransform() +MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group) { - // Calculate transform - Ogre::Matrix4 transform = Ogre::Matrix4::IDENTITY; - transform = Ogre::Matrix4::getScale(vector) * transform; + MeshPairList meshes; - // Check whether we have to flip vertex winding. - // We do have to, if we changed our right hand base. - // We can test it by using the cross product from X and Y and see, if it is a non-negative - // projection on Z. Actually it should be exactly Z, as we don't do non-uniform scaling yet, - // but the test is cheap either way. - Ogre::Matrix3 m3; - transform.extract3x3Matrix(m3); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower); - if (m3.GetColumn(0).crossProduct(m3.GetColumn(1)).dotProduct(m3.GetColumn(2)) < 0) - { - mFlipVertexWinding = true; - } - - mTransform = transform; -} -void NIFLoader::handleNode(Nif::Node *node, int flags, - const Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone, std::vector boneSequence) -{ - // Accumulate the flags from all the child nodes. This works for all - // the flags we currently use, at least. - flags |= node->flags; - - // Check for extra data - Extra *e = node; - while (!e->extra.empty()) - { - // Get the next extra data in the list - e = e->extra.getPtr(); - assert(e != NULL); - - if (e->recType == RC_NiStringExtraData) - { - // String markers may contain important information - // affecting the entire subtree of this node - NiStringExtraData *sd = (NiStringExtraData*)e; - - if (sd->string == "NCO") - // No collision. Use an internal flag setting to mark this. - flags |= 0x800; - else if (sd->string == "MRK") - // Marker objects. These are only visible in the - // editor. Until and unless we add an editor component to - // the engine, just skip this entire node. - return; - } - - if (e->recType == RC_NiTextKeyExtraData){ - Nif::NiTextKeyExtraData* extra = dynamic_cast (e); - - std::ofstream file; - - if(mOutputAnimFiles){ - std::string cut = ""; - for(unsigned int i = 0; i < name.length(); i++) - { - if(!(name.at(i) == '\\' || name.at(i) == '/' || name.at(i) == '>' || name.at(i) == '<' || name.at(i) == '?' || name.at(i) == '*' || name.at(i) == '|' || name.at(i) == ':' || name.at(i) == '"')) - { - cut += name.at(i); - } - } - - std::cout << "Outputting " << cut << "\n"; - - file.open((verbosePath + "/Indices" + cut + ".txt").c_str()); - } - - for(std::vector::iterator textiter = extra->list.begin(); textiter != extra->list.end(); textiter++) - { - std::string text = textiter->text; - - replace(text.begin(), text.end(), '\n', '/'); - - text.erase(std::remove(text.begin(), text.end(), '\r'), text.end()); - std::size_t i = 0; - while(i < text.length()){ - while(i < text.length() && text.at(i) == '/' ){ - i++; - } - std::size_t first = i; - int length = 0; - while(i < text.length() && text.at(i) != '/' ){ - i++; - length++; - } - if(first < text.length()){ - //length = text.length() - first; - std::string sub = text.substr(first, length); - - if(mOutputAnimFiles) - file << "Time: " << textiter->time << "|" << sub << "\n"; - - textmappings[sub] = textiter->time; - } - } - } - file.close(); - } - } - - Ogre::Bone *bone = 0; - - // create skeleton or add bones - if (node->recType == RC_NiNode) - { - //FIXME: "Bip01" isn't every time the root bone - if (node->name == "Bip01" || node->name == "Root Bone") //root node, create a skeleton - { - inTheSkeletonTree = true; - - mSkel = Ogre::SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true); - } - else if (!mSkel.isNull() && !parentBone) - inTheSkeletonTree = false; - - if (!mSkel.isNull()) //if there is a skeleton - { - std::string name = node->name; - - // Quick-n-dirty workaround for the fact that several - // bones may have the same name. - if(!mSkel->hasBone(name)) - { - boneSequence.push_back(name); - bone = mSkel->createBone(name); - - if (parentBone) - parentBone->addChild(bone); - - bone->setInheritOrientation(true); - bone->setPosition(node->trafo.pos); - bone->setOrientation(node->trafo.rotation); - } - } - } - Transformation original = node->trafo; - // Apply the parent transformation to this node. We overwrite the - // existing data with the final transformation. - if (trafo) - { - // Get a non-const reference to the node's data, since we're - // overwriting it. TODO: Is this necessary? - Transformation &final = node->trafo; - - // For both position and rotation we have that: - // final_vector = old_vector + old_rotation*new_vector*old_scale - final.pos = trafo->pos + trafo->rotation*final.pos*trafo->scale; - final.velocity = trafo->velocity + trafo->rotation*final.velocity*trafo->scale; - - // Merge the rotations together - final.rotation = trafo->rotation * final.rotation; - - // Scale - final.scale *= trafo->scale; - } - - // For NiNodes, loop through children - if (node->recType == RC_NiNode) - { - NodeList &list = ((NiNode*)node)->children; - int n = list.length(); - for (int i = 0; itrafo, bounds, bone, boneSequence); - } - } - else if (node->recType == RC_NiTriShape && bNiTri) - { - std::string nodename = node->name; - - if (triname == "") - { - handleNiTriShape(dynamic_cast(node), flags, bounds, original, boneSequence); - } - else if(nodename.length() >= triname.length()) - { - std::transform(nodename.begin(), nodename.end(), nodename.begin(), ::tolower); - if(triname == nodename.substr(0, triname.length())) - handleNiTriShape(dynamic_cast(node), flags, bounds, original, boneSequence); - } - } -} - -void NIFLoader::loadResource(Ogre::Resource *resource) -{ - inTheSkeletonTree = false; - allanim.clear(); - shapes.clear(); - needBoneAssignments.clear(); - // needBoneAssignments.clear(); - mBoundingBox.setNull(); - mesh = 0; - mSkel.setNull(); - flip = false; - name = resource->getName(); - char suffix = name.at(name.length() - 2); - bool addAnim = true; - bool hasAnim = false; - bool linkSkeleton = true; - //bool baddin = false; - bNiTri = true; - if(name == "meshes\\base_anim.nif" || name == "meshes\\base_animkna.nif") - { - bNiTri = false; - } - - if(suffix == '*') - { - vector = Ogre::Vector3(-1,1,1); - flip = true; - } - else if(suffix == '?'){ - vector = Ogre::Vector3(1,-1,1); - flip = true; - } - else if(suffix == '<'){ - vector = Ogre::Vector3(1,1,-1); - flip = true; - } - else if(suffix == '>') - { - //baddin = true; - bNiTri = true; - std::string sub = name.substr(name.length() - 6, 4); - - if(sub.compare("0000") != 0) - addAnim = false; - - } - else if(suffix == ':') - { - //baddin = true; - linkSkeleton = false; - bNiTri = true; - std::string sub = name.substr(name.length() - 6, 4); - - if(sub.compare("0000") != 0) - addAnim = false; - - } - - switch(name.at(name.length() - 1)) - { - case '"': - triname = "tri chest"; - break; - case '*': - triname = "tri tail"; - break; - case ':': - triname = "tri left foot"; - break; - case '<': - triname = "tri right foot"; - break; - case '>': - triname = "tri left hand"; - break; - case '?': - triname = "tri right hand"; - break; - default: - triname = ""; - break; - } - if(flip) - { - calculateTransform(); - } - // Get the mesh - mesh = dynamic_cast(resource); - assert(mesh); - - // Look it up - resourceName = mesh->getName(); - - - // Helper that computes bounding boxes for us. - BoundsFinder bounds; - - // Load the NIF. TODO: Wrap this in a try-catch block once we're out - // of the early stages of development. Right now we WANT to catch - // every error as early and intrusively as possible, as it's most - // likely a sign of incomplete code rather than faulty input. - NIFFile nif(resourceName); + Nif::NIFFile nif(name); if (nif.numRecords() < 1) { - warn("Found no records in NIF."); - return; + nif.warn("Found no records in NIF."); + return meshes; } // The first record is assumed to be the root node - Record *r = nif.getRecord(0); + Nif::Record *r = nif.getRecord(0); assert(r != NULL); Nif::Node *node = dynamic_cast(r); - - if (node == NULL) + if(node == NULL) { - warn("First record in file was not a node, but a " + - r->recName + ". Skipping file."); - return; + nif.warn("First record in file was not a node, but a "+ + r->recName+". Skipping file."); + return meshes; } - // Handle the node - std::vector boneSequence; + NIFSkeletonLoader skelldr; + bool hasSkel = skelldr.createSkeleton(skelName, group, textkeys, node); + NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); + meshldr.createMeshes(node, meshes); + return meshes; +} - handleNode(node, 0, NULL, bounds, 0, boneSequence); - if(addAnim) +EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textkeys, const std::string &name, const std::string &group) +{ + EntityList entitylist; + + MeshPairList meshes = load(name, name, textkeys, group); + if(meshes.size() == 0) + return entitylist; + + Ogre::SceneManager *sceneMgr = parent->getCreator(); + for(size_t i = 0;i < meshes.size();i++) { - for(int i = 0; i < nif.numRecords(); i++) + entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); + Ogre::Entity *entity = entitylist.mEntities.back(); + if(!entitylist.mSkelBase && entity->hasSkeleton()) + entitylist.mSkelBase = entity; + } + + if(entitylist.mSkelBase) + { + parent->attachObject(entitylist.mSkelBase); + for(size_t i = 0;i < entitylist.mEntities.size();i++) { - Nif::NiKeyframeController *f = dynamic_cast(nif.getRecord(i)); - - if(f != NULL) + Ogre::Entity *entity = entitylist.mEntities[i]; + if(entity != entitylist.mSkelBase && entity->hasSkeleton()) { - hasAnim = true; - Nif::Node *o = dynamic_cast(f->target.getPtr()); - Nif::NiKeyframeDataPtr data = f->data; + entity->shareSkeletonInstanceWith(entitylist.mSkelBase); + parent->attachObject(entity); + } + else if(entity != entitylist.mSkelBase) + entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity); + } + } + else + { + for(size_t i = 0;i < entitylist.mEntities.size();i++) + parent->attachObject(entitylist.mEntities[i]); + } - if (f->timeStart >= 10000000000000000.0f) - continue; - data->setBonename(o->name); - data->setStartTime(f->timeStart); - data->setStopTime(f->timeStop); + return entitylist; +} - allanim.push_back(data.get()); +struct checklow { + bool operator()(const char &a, const char &b) const + { + return ::tolower(a) == ::tolower(b); + } +}; + +EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + const std::string &name, + const std::string &group) +{ + EntityList entitylist; + + MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), NULL, group); + if(meshes.size() == 0) + return entitylist; + + Ogre::SceneManager *sceneMgr = parentNode->getCreator(); + std::string filter = "Tri "+bonename; + for(size_t i = 0;i < meshes.size();i++) + { + Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); + if(ent->hasSkeleton()) + { + if(meshes[i].second.length() < filter.length() || + std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow()).first != filter.end()) + { + sceneMgr->destroyEntity(ent); + meshes.erase(meshes.begin()+i); + i--; + continue; + } + if(!entitylist.mSkelBase) + entitylist.mSkelBase = ent; + } + entitylist.mEntities.push_back(ent); + } + + Ogre::Vector3 scale(1.0f); + if(bonename.find("Left") != std::string::npos) + scale.x *= -1.0f; + + if(entitylist.mSkelBase) + { + entitylist.mSkelBase->shareSkeletonInstanceWith(parent); + parentNode->attachObject(entitylist.mSkelBase); + for(size_t i = 0;i < entitylist.mEntities.size();i++) + { + Ogre::Entity *entity = entitylist.mEntities[i]; + if(entity != entitylist.mSkelBase && entity->hasSkeleton()) + { + entity->shareSkeletonInstanceWith(parent); + parentNode->attachObject(entity); + } + else if(entity != entitylist.mSkelBase) + { + Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity); + tag->setScale(scale); } } } - // set the bounding value. - if (bounds.isValid()) + else { - mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ())); - mesh->_setBoundingSphereRadius(bounds.getRadius()); - } - if(hasAnim && addAnim){ - allanimmap[name] = allanim; - alltextmappings[name] = textmappings; - } - if(!mSkel.isNull() && shapes.size() > 0 && addAnim) - { - allshapesmap[name] = shapes; - - } - - if(flip){ - mesh->_setBounds(mBoundingBox, false); - } - - if (!mSkel.isNull() ) - { - for(std::vector::iterator iter = needBoneAssignments.begin(); iter != needBoneAssignments.end(); iter++) + for(size_t i = 0;i < entitylist.mEntities.size();i++) { - int boneIndex = mSkel->getNumBones() - 1; - Ogre::VertexBoneAssignment vba; - vba.boneIndex = boneIndex; - vba.vertexIndex = 0; - vba.weight = 1; - - - (*iter)->addBoneAssignment(vba); + Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entitylist.mEntities[i]); + tag->setScale(scale); } - //Don't link on npc parts to eliminate redundant skeletons - //Will have to be changed later slightly for robes/skirts - if(linkSkeleton) - mesh->_notifySkeleton(mSkel); } + + return entitylist; } - - - -Ogre::MeshPtr NIFLoader::load(const std::string &name, const std::string &group) -{ - - Ogre::MeshManager *m = Ogre::MeshManager::getSingletonPtr(); - // Check if the resource already exists - Ogre::ResourcePtr ptr = m->getByName(name, group); - Ogre::MeshPtr themesh; - if (!ptr.isNull()){ - themesh = Ogre::MeshPtr(ptr); - } - else // Nope, create a new one. - { - themesh = Ogre::MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr()); - } - return themesh; -} - -/* -This function shares much of the same code handleShapes() in MWRender::Animation -This function also creates new position and normal buffers for submeshes. -This function points to existing texture and IndexData buffers -*/ - -std::vector* NIFLoader::getAnim(std::string lowername){ - - std::map,ciLessBoost>::iterator iter = allanimmap.find(lowername); - std::vector* pass = 0; - if(iter != allanimmap.end()) - pass = &(iter->second); - return pass; - -} -std::vector* NIFLoader::getShapes(std::string lowername){ - - std::map,ciLessBoost>::iterator iter = allshapesmap.find(lowername); - std::vector* pass = 0; - if(iter != allshapesmap.end()) - pass = &(iter->second); - return pass; -} - -std::map* NIFLoader::getTextIndices(std::string lowername){ - std::map, ciLessBoost>::iterator iter = alltextmappings.find(lowername); - std::map* pass = 0; - if(iter != alltextmappings.end()) - pass = &(iter->second); - return pass; -} - - - - /* More code currently not in use, from the old D source. This was used in the first attempt at loading NIF meshes, where each submesh in the file was given a separate bone in a skeleton. Unfortunately diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 8e317ff4d..b6610d8a7 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -26,7 +26,10 @@ #include #include +#include +#include +#include #include #include @@ -55,112 +58,47 @@ namespace Nif namespace NifOgre { +// FIXME: These should not be in NifOgre, it works agnostic of what model format is used +typedef std::map TextKeyMap; +struct EntityList { + std::vector mEntities; + Ogre::Entity *mSkelBase; + + EntityList() : mSkelBase(0) + { } +}; + + +/** This holds a list of meshes along with the names of their parent nodes + */ +typedef std::vector< std::pair > MeshPairList; /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into - something Ogre can use. Later it will also handle the insertion of - collision meshes into Bullet / OgreBullet. + something Ogre can use. You have to insert meshes manually into Ogre like this: NIFLoader::load("somemesh.nif"); - Afterwards, you can use the mesh name "somemesh.nif" normally to - create entities and so on. The mesh isn't loaded from disk until - OGRE needs it for rendering. Thus the above load() command is not - very resource intensive, and can safely be done for a large number - of meshes at load time. + This returns a list of meshes used by the model, as well as the names of + their parent nodes (as they pertain to the skeleton, which is optionally + returned in the second argument if it exists). */ -class NIFLoader : Ogre::ManualResourceLoader +class NIFLoader { - public: - static int numberOfMeshes; - static NIFLoader& getSingleton(); - static NIFLoader* getSingletonPtr(); - - virtual void loadResource(Ogre::Resource *resource); - - static Ogre::MeshPtr load(const std::string &name, - const std::string &group="General"); - //void insertMeshInsideBase(Ogre::Mesh* mesh); - std::vector* getAnim(std::string name); - std::vector* getShapes(std::string name); - std::map* getTextIndices(std::string name); - - - void setOutputAnimFiles(bool output); - void setVerbosePath(std::string path); - - private: - - NIFLoader() : resourceName(""), resourceGroup("General"), flip(false), mNormaliseNormals(false), - mFlipVertexWinding(false), mOutputAnimFiles(false), inTheSkeletonTree(false) {} - NIFLoader(NIFLoader& n) {} - - void calculateTransform(); - - - void warn(std::string msg); - void fail(std::string msg); - - void handleNode( Nif::Node *node, int flags, - const Nif::Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone, std::vector boneSequence); - - void handleNiTriShape(Nif::NiTriShape *shape, int flags, BoundsFinder &bounds, Nif::Transformation original, std::vector boneSequence); - - void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material, std::list &vertexBoneAssignments); - - void createMaterial(const Ogre::String &name, - const Ogre::Vector3 &ambient, - const Ogre::Vector3 &diffuse, - const Ogre::Vector3 &specular, - const Ogre::Vector3 &emissive, - float glossiness, float alpha, - int alphaFlags, float alphaTest, - const Ogre::String &texName, - bool vertexColor); - - void findRealTexture(Ogre::String &texName); - - Ogre::String getUniqueName(const Ogre::String &input); - - //returns the skeleton name of this mesh - std::string getSkeletonName() - { - return resourceName + ".skel"; - } - - std::string verbosePath; - std::string resourceName; - std::string resourceGroup; - Ogre::Matrix4 mTransform; - Ogre::AxisAlignedBox mBoundingBox; - bool flip; - bool mNormaliseNormals; - bool mFlipVertexWinding; - bool bNiTri; - bool mOutputAnimFiles; - std::multimap MaterialMap; - - // pointer to the ogre mesh which is currently build - Ogre::Mesh *mesh; - Ogre::SkeletonPtr mSkel; - Ogre::Vector3 vector; - std::vector shapes; - std::string name; - std::string triname; - std::vector allanim; - - std::map textmappings; - std::map,ciLessBoost> alltextmappings; - std::map,ciLessBoost> allanimmap; - std::map,ciLessBoost> allshapesmap; - std::vector mAnim; - std::vector mS; - std::vector needBoneAssignments; - bool inTheSkeletonTree; + static MeshPairList load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group); +public: + static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + const std::string &name, + const std::string &group="General"); + static EntityList createEntities(Ogre::SceneNode *parent, + TextKeyMap *textkeys, + const std::string &name, + const std::string &group="General"); }; } diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 238ccdcf6..dba239c14 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -192,7 +192,7 @@ #if SHADOWS || SHADOWS_PSSM float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; float fade = 1-((depthPassthrough - shadowFar_fadeStart.y) / fadeRange); - shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); + shadow = (depthPassthrough > shadowFar_fadeStart.x) ? 1.0 : ((depthPassthrough > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); #endif #if !SHADOWS && !SHADOWS_PSSM @@ -205,13 +205,12 @@ #if UNDERWATER float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePositionPassthrough,1)).xyz; float3 waterEyePos = float3(1,1,1); - if (worldPos.y < waterLevel && waterEnabled == 1) - { - // NOTE: this calculation would be wrong for non-uniform scaling - float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); - caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - } + // NOTE: this calculation would be wrong for non-uniform scaling + float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); + caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + if (worldPos.y >= waterLevel || waterEnabled != 1) + caustics = float3(1,1,1); #endif diff --git a/files/materials/shadows.h b/files/materials/shadows.h index 769a4fea7..b1dc92d21 100644 --- a/files/materials/shadows.h +++ b/files/materials/shadows.h @@ -36,12 +36,16 @@ float pssmDepthShadow ( { float shadow; + float pcf1 = depthShadowPCF(shadowMap0, lightSpacePos0, invShadowmapSize0); + float pcf2 = depthShadowPCF(shadowMap1, lightSpacePos1, invShadowmapSize1); + float pcf3 = depthShadowPCF(shadowMap2, lightSpacePos2, invShadowmapSize2); + if (depth < pssmSplitPoints.x) - shadow = depthShadowPCF(shadowMap0, lightSpacePos0, invShadowmapSize0); + shadow = pcf1; else if (depth < pssmSplitPoints.y) - shadow = depthShadowPCF(shadowMap1, lightSpacePos1, invShadowmapSize1); + shadow = pcf2; else - shadow = depthShadowPCF(shadowMap2, lightSpacePos2, invShadowmapSize2); + shadow = pcf3; return shadow; } diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index a562d1ec3..7ef26d035 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -55,7 +55,7 @@ shUniform(float2, lodMorph) @shAutoConstant(lodMorph, custom, 1001) shVertexInput(float2, uv0) - shVertexInput(float2, delta) // lodDelta, lodThreshold + shVertexInput(float2, uv1) // lodDelta, lodThreshold #if SHADOWS shUniform(float4x4, texViewProjMatrix0) @shAutoConstant(texViewProjMatrix0, texture_viewproj_matrix) @@ -85,11 +85,11 @@ // result is negative (it will only be -1 in fact, since after that // the vertex will never be indexed), we will achieve our aim. // sign(vertexLOD - targetLOD) == -1 is to morph - float toMorph = -min(0, sign(delta.y - lodMorph.y)); + float toMorph = -min(0, sign(uv1.y - lodMorph.y)); // morph // this assumes XZ terrain alignment - worldPos.y += delta.x * toMorph * lodMorph.x; + worldPos.y += uv1.x * toMorph * lodMorph.x; shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); @@ -161,7 +161,7 @@ #if LIGHTING shUniform(float4, lightAmbient) @shAutoConstant(lightAmbient, ambient_light_colour) - @shForeach(@shGlobalSettingString(num_lights)) + @shForeach(@shGlobalSettingString(terrain_num_lights)) shUniform(float4, lightPosObjSpace@shIterator) @shAutoConstant(lightPosObjSpace@shIterator, light_position_object_space, @shIterator) shUniform(float4, lightAttenuation@shIterator) @shAutoConstant(lightAttenuation@shIterator, light_attenuation, @shIterator) shUniform(float4, lightDiffuse@shIterator) @shAutoConstant(lightDiffuse@shIterator, light_diffuse_colour, @shIterator) @@ -224,13 +224,14 @@ float3 worldPos = shMatrixMult(worldMatrix, float4(objSpacePosition,1)).xyz; float3 waterEyePos = float3(1,1,1); - if (worldPos.y < waterLevel) - { - // NOTE: this calculation would be wrong for non-uniform scaling - float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); - waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); - caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - } + // NOTE: this calculation would be wrong for non-uniform scaling + float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); + caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); + if (worldPos.y >= waterLevel) + caustics = float3(1,1,1); + + #endif @@ -285,7 +286,7 @@ #if SHADOWS || SHADOWS_PSSM float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; float fade = 1-((depth - shadowFar_fadeStart.y) / fadeRange); - shadow = (depth > shadowFar_fadeStart.x) ? 1 : ((depth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); + shadow = (depth > shadowFar_fadeStart.x) ? 1.0 : ((depth > shadowFar_fadeStart.y) ? 1.0-((1.0-shadow)*fade) : shadow); #endif #if !SHADOWS && !SHADOWS_PSSM @@ -298,7 +299,7 @@ float3 diffuse = float3(0,0,0); float d; - @shForeach(@shGlobalSettingString(num_lights)) + @shForeach(@shGlobalSettingString(terrain_num_lights)) lightDir = lightPosObjSpace@shIterator.xyz - (objSpacePosition.xyz * lightPosObjSpace@shIterator.w); d = length(lightDir); diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 11c18010e..a778aef3a 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -11,6 +11,7 @@ #include "BtOgreExtras.h" #include +#include #define BIT(x) (1<<(x)) @@ -333,10 +334,8 @@ namespace Physic RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { - char uniqueID[8]; - sprintf( uniqueID, "%07.3f", scale ); - std::string sid = uniqueID; - std::string outputstring = mesh + uniqueID + "\"|"; + std::string sid = (boost::format("%07.3f") % scale).str(); + std::string outputstring = mesh + sid; //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif @@ -568,4 +567,20 @@ namespace Physic return results2; } + + void PhysicEngine::getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max) + { + std::string sid = (boost::format("%07.3f") % scale).str(); + std::string outputstring = mesh + sid; + + mShapeLoader->load(outputstring, "General"); + BulletShapeManager::getSingletonPtr()->load(outputstring, "General"); + BulletShapePtr shape = + BulletShapeManager::getSingleton().getByName(outputstring, "General"); + + btTransform trans; + trans.setIdentity(); + + shape->Shape->getAabb(trans, min, max); + } }}; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 3988c75a4..9ae8e7607 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -221,6 +221,8 @@ namespace Physic bool toggleDebugRendering(); + void getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max); + /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). */ diff --git a/readme.txt b/readme.txt index ded9bcd7b..ce3d115e3 100644 --- a/readme.txt +++ b/readme.txt @@ -66,9 +66,16 @@ Allowed options: --debug [=arg(=1)] (=0) debug mode --nosound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output - --new-game [=arg(=1)] (=0) activate char gen/new game mechanics --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scri pts) at startup + --script-console [=arg(=1)] (=0) enable console-only script functionality + --script-run arg select a file that is executed in the consol + e on startup + + Note: The file contains a list of script + lines, but not a complete scripts. That mean + s no begin/end and no variable declarations. + --new-game [=arg(=1)] (=0) activate char gen/new game mechanics --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding ) --encoding arg (=win1252) Character encoding used in OpenMW game messa