diff --git a/CMakeLists.txt b/CMakeLists.txt index 42c0bcaf3..6601d64c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,24 +30,24 @@ cmake_minimum_required(VERSION 2.6) IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1") set(PREBUILT_DIR "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd") message (STATUS "OpenMW pre-built binaries found at ${PREBUILT_DIR}.") - + SET(ENV{OGRE_HOME} "${PREBUILT_DIR}/ogre_1_7_1") - + SET(ENV{BOOST_ROOT} "${PREBUILT_DIR}/boost_1_42_0") set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) set(ENV{BOOST_INCLUDEDIR} "${BOOST_ROOT}/include") set(ENV{BOOST_LIBRARYDIR} "${BOOST_ROOT}/lib") - + set(ENV{FREETYPE_DIR} "${PREBUILT_DIR}/freetype-2.3.5-1") - + set(USE_MPG123 OFF) - set(USE_AUDIERE ON) + set(USE_AUDIERE ON) set(AUDIERE_INCLUDE_DIR "${PREBUILT_DIR}/audiere-1.9.4/include") set(AUDIERE_LIBRARY "${PREBUILT_DIR}/audiere-1.9.4/lib/audiere.lib") - + set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK") - + ELSE() message (STATUS "OpenMW pre-built binaries not found. Using standard locations.") ENDIF() @@ -88,6 +88,12 @@ set(NIFOGRE_HEADER ${COMP_DIR}/nifogre/ogre_nif_loader.hpp) source_group(components\\nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER}) +set(NIFBULLET + ${COMP_DIR}/nifbullet/bullet_nif_loader.cpp) +set(NIFBULLET_HEADER + ${COMP_DIR}/nifbullet/bullet_nif_loader.hpp) +source_group(components\\nifbullet FILES ${NIFBULLET} ${NIFBULLET_HEADER}) + set(TO_UTF8 ${COMP_DIR}/to_utf8/to_utf8.cpp) set(TO_UTF8_HEADER @@ -137,10 +143,10 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp) source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER}) set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC} ${TO_UTF8} - ${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER}) + ${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER} ${NIFBULLET}) set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER} ${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} ${TO_UTF8_HEADER} - ${INTERPRETER_HEADER} ${FILE_FINDER_HEADER}) + ${INTERPRETER_HEADER} ${FILE_FINDER_HEADER} ${NIFBULLET_HEADER}) # source directory: libs @@ -160,6 +166,19 @@ set(OENGINE_GUI ${LIBDIR}/openengine/gui/manager.cpp ) +set(OENGINE_BULLET + ${LIBDIR}/openengine/bullet/btKinematicCharacterController.cpp + ${LIBDIR}/openengine/bullet/btKinematicCharacterController.h + ${LIBDIR}/openengine/bullet/BtOgre.cpp + ${LIBDIR}/openengine/bullet/BtOgreExtras.h + ${LIBDIR}/openengine/bullet/BtOgreGP.h + ${LIBDIR}/openengine/bullet/BtOgrePG.h + ${LIBDIR}/openengine/bullet/CMotionState.cpp + ${LIBDIR}/openengine/bullet/CMotionState.h + ${LIBDIR}/openengine/bullet/physic.cpp + ${LIBDIR}/openengine/bullet/physic.hpp +) + # Sound setup if (USE_AUDIERE) set(MANGLE_SOUND_OUTPUT @@ -200,7 +219,7 @@ set(OENGINE_SOUND ${LIBDIR}/mangle/sound/outputs/openal_out.cpp ${MANGLE_SOUND_OUTPUT} ) -set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND}) +set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND} ${OENGINE_BULLET}) source_group(libs\\openengine FILES ${OENGINE_ALL}) set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL}) @@ -226,6 +245,7 @@ find_package(OGRE REQUIRED) find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread) find_package(OIS REQUIRED) find_package(OpenAL REQUIRED) +find_package(Bullet REQUIRED) include_directories("." ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} @@ -380,7 +400,7 @@ if (WIN32) set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS") set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS") endif() - + # Release builds use the debug console set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") @@ -402,7 +422,7 @@ if (WIN32) set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "-Wl,-subsystem,console") set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE") endif(MINGW) - + # TODO: At some point release builds should not use the console but rather write to a log file #set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") #set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS") diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5d10b4fed..0f2ac10e5 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -125,6 +125,7 @@ set(GAMEWORLD mwworld/actiontake.cpp mwworld/containerutil.cpp mwworld/player.cpp + mwworld/doingphysics.cpp ) set(GAMEWORLD_HEADER mwworld/refdata.hpp @@ -143,6 +144,8 @@ set(GAMEWORLD_HEADER mwworld/manualref.hpp mwworld/containerutil.hpp mwworld/player.hpp + mwworld/doingphysics.hpp + mwworld/cellfunctors.hpp ) source_group(apps\\openmw\\mwworld FILES ${GAMEWORLD} ${GAMEWORLD_HEADER}) @@ -204,6 +207,7 @@ set(GAMEMECHANICS_HEADER mwmechanics/stat.hpp mwmechanics/creaturestats.hpp mwmechanics/magiceffects.hpp + mwmechanics/movement.hpp ) source_group(apps\\openmw\\mwmechanics FILES ${GAMEMECHANICS} ${GAMEMECHANICS_HEADER}) @@ -225,7 +229,7 @@ add_executable(openmw # Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING # when we change the backend. -include_directories(${SOUND_INPUT_INCLUDES}) +include_directories(${SOUND_INPUT_INCLUDES} ${BULLET_INCLUDE_DIRS}) add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw @@ -234,6 +238,7 @@ target_link_libraries(openmw ${Boost_LIBRARIES} ${OPENAL_LIBRARY} ${SOUND_INPUT_LIBRARY} + ${BULLET_LIBRARIES} caelum MyGUIEngine MyGUIOgrePlatform diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3eaff2fe1..4aae0f91d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "components/esm/records.hpp" #include #include @@ -158,7 +160,8 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) mEnvironment.mWorld->markCellAsUnchanged(); // update actors - mEnvironment.mMechanicsManager->update(); + std::vector > movement; + mEnvironment.mMechanicsManager->update (movement); if (focusFrameCounter++ == focusUpdateFrame) { @@ -179,6 +182,9 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) focusFrameCounter = 0; } + + if (mEnvironment.mWindowManager->getMode()==MWGui::GM_Game) + mEnvironment.mWorld->doPhysics (movement, mEnvironment.mFrameDuration); } catch (const std::exception& e) { @@ -198,6 +204,7 @@ OMW::Engine::Engine() , mScriptManager (0) , mScriptContext (0) , mGuiManager (0) + , mPhysicEngine (0) { MWClass::registerClasses(); } @@ -212,6 +219,7 @@ OMW::Engine::~Engine() delete mEnvironment.mDialogueManager; delete mScriptManager; delete mScriptContext; + delete mPhysicEngine; } // Load all BSA files in data directory. @@ -333,8 +341,12 @@ void OMW::Engine::go() loadBSA(); + // Create physics + mPhysicEngine = new OEngine::Physic::PhysicEngine(); + // Create the world - mEnvironment.mWorld = new MWWorld::World (mOgre, mDataDir, mMaster, mResDir, mNewGame, mEnvironment); + mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mDataDir, mMaster, mResDir, mNewGame, mEnvironment); + // Set up the GUI system mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir); @@ -388,7 +400,7 @@ void OMW::Engine::go() else { pos.pos[0] = pos.pos[1] = 0; - mEnvironment.mWorld->changeCell (mCellName, pos); + mEnvironment.mWorld->changeToInteriorCell (mCellName, pos); } // Sets up the input system diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 5b636398c..8dff580bd 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include "mwworld/environment.hpp" @@ -59,6 +60,7 @@ namespace OMW boost::filesystem::path mDataDir; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer mOgre; + OEngine::Physic::PhysicEngine* mPhysicEngine; std::string mCellName; std::string mMaster; bool mDebug; diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 6c96811d8..7a72ddb8c 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -23,6 +23,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 1d4afc096..2dbeae360 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 1217d56a6..d63795641 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 3679eb7ee..964ccbdfe 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 07ded0264..a264f2a1c 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index ea61508b8..1da55e6f6 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -23,6 +23,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0d358441a..8cde67671 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -35,6 +35,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertActorPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 4f294ce87..8a34d3abe 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -30,6 +30,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index e4ea509f9..c88e1794b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index de5c06a73..159196eb2 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -31,6 +31,7 @@ namespace MWClass MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); // Extract the color and convert to floating point const int color = ref->base->data.color; diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 3b08ff199..9a4b98b43 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index d4ffdce38..f9995b77f 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ed247cc80..e037d691e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -16,7 +16,7 @@ #include "../mwmechanics/mechanicsmanager.hpp" #include -namespace +namespace { const Ogre::Radian kOgrePi (Ogre::Math::PI); const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0)); @@ -38,7 +38,7 @@ namespace MWClass //Ogre::SceneNode *chest; ESMS::LiveCellRef *ref = ptr.get(); - + //Store scenenodes by npc's name + bodypart [0] , npc's name + bodypart [1] //Ex. Fargothchest , Fargothneck @@ -58,7 +58,7 @@ namespace MWClass environment.mWorld->getStore().bodyParts.find(hairID)->model; MWRender::Rendering rendering (cellRender, ref->ref); - + //TODO: define consts for each bodypart e.g. chest, foot, wrist... and put the parts in the // right place @@ -69,7 +69,7 @@ namespace MWClass Ogre::Vector3 pos = Ogre::Vector3( 20, 20, 20); Ogre::Vector3 axis = Ogre::Vector3( 0, 0, 1); Ogre::Radian angle = Ogre::Radian(0); - + std::string addresses[6] = {"", "", "", "","", ""}; std::string addresses2[6] = {"", "", "", "", "", ""}; std::string upperleft[5] = {"", "", "", "", ""}; @@ -79,9 +79,9 @@ namespace MWClass int numbers = 0; int uppernumbers = 0; int neckNumbers = 0; - + if (bodyPart){ - + cellRender.insertMesh("meshes\\" + bodyPart->model, pos, axis, angle, npcName + "chest", addresses, numbers, true); //2 0 addresses2[numbers] = npcName + "chest"; addresses[numbers++] = npcName + "chest"; @@ -111,7 +111,7 @@ namespace MWClass Ogre::Vector3 pos2 = Ogre::Vector3( 0, .5, 75); std::string upperarmpath[2] = {npcName + "chest", npcName + "upper arm"}; - + if (groin){ cellRender.insertMesh("meshes\\" + groin->model, pos2, axis, kOgrePi, npcName + "groin", addresses, numbers); addresses2[numbers] = npcName + "groin"; @@ -121,7 +121,7 @@ namespace MWClass cellRender.insertMesh("tail\\" + tail->model, Ogre::Vector3(0 , 0, -76), axis, kOgrePi, npcName + "tail", addresses, numbers, "tail"); //std::cout << "TAIL\n"; } - + //addresses[1] = npcName + "groin"; if(upperleg){ cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( 6, 0, -16), axis, kOgrePi, npcName + "upper leg", addresses, numbers); //-18 @@ -135,15 +135,15 @@ namespace MWClass cellRender.insertMesh ("meshes\\" + knee->model, Ogre::Vector3( 0, -1, -23), axis, Ogre::Radian(0), npcName + "knee", addresses, numbers); //cellRender.rotateMesh(Ogre::Vector3(0, 1, 0), Ogre::Radian (1), npcName + "upper arm"); cellRender.insertMesh ("meshes\\" + knee->model, Ogre::Vector3( 0, -1, -23), axis, Ogre::Radian(0), npcName + "knee2", addresses2, numbers); - + addresses2[numbers] = npcName + "knee2"; addresses[numbers++] = npcName + "knee"; } if(ankle){ - + cellRender.insertMesh ("meshes\\" + ankle->model, Ogre::Vector3( 0, 0, -20), axis, Ogre::Radian(0), npcName + "ankle", addresses, numbers); //-1 cellRender.insertMesh ("meshes\\" + ankle->model, Ogre::Vector3( 0,0, -20), axis, Ogre::Radian(0), npcName + "ankle2", addresses2, numbers); //-1 - + addresses2[numbers] = npcName + "ankle2"; addresses[numbers++] = npcName + "ankle"; } @@ -163,7 +163,7 @@ namespace MWClass //cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), addresses, numbers); } if(feet){ - + cellRender.insertMesh ("foot\\" + feet->model, Ogre::Vector3( 7, 4, -16), axis, kOgrePi, npcName + "foot", addresses, numbers); //9, 0, -14 cellRender.insertMesh ("foot\\" + feet->model, Ogre::Vector3( 7, 4, -16), axis, kOgrePi, npcName + "foot2", addresses2, numbers); @@ -171,8 +171,8 @@ namespace MWClass addresses[numbers++] = npcName + "foot"; //cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), addresses, numbers); } - - + + if (arm){ //010 cellRender.insertMesh("meshes\\" + arm->model, Ogre::Vector3(-12.5, 0, 104), Ogre::Vector3(0, 1, 0), -kOgrePiOverTwo, npcName + "upper arm", upperleft, uppernumbers); //1, 0,.75 @@ -209,7 +209,7 @@ namespace MWClass upperleft[uppernumbers] = npcName + "wrist"; upperright[uppernumbers++] = npcName + "wrist2"; } - + if(hand) { @@ -218,7 +218,7 @@ namespace MWClass //std::cout << "WE FOUND HANDS\n"; std::string pass; if(hand->model.compare("b\\B_N_Dark Elf_F_Hands.1st.NIF")==0 && bodyRaceID.compare("b_n_dark elf_m_") == 0) - pass = "b\\B_N_Dark Elf_M_Hands.1st.NIF"; + pass = "b\\B_N_Dark Elf_M_Hands.1st.NIF"; else pass = hand->model; cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers,false); //0, 100, -100 0,0,120 @@ -256,8 +256,9 @@ namespace MWClass cellRender.insertMesh (headModel, Ogre::Vector3( 0, 0, 5), axis, Ogre::Radian(0), npcName + "head", neckandup, neckNumbers); neckandup[neckNumbers++] = npcName + "head"; cellRender.insertMesh (hairModel, Ogre::Vector3( 0, -1, 0), axis, Ogre::Radian(0), npcName + "hair", neckandup, neckNumbers); + + cellRender.insertActorPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); - } void Npc::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const @@ -311,7 +312,6 @@ namespace MWClass { if (!ptr.getRefData().getNpcStats().get()) { - // xxx boost::shared_ptr stats ( new MWMechanics::NpcStats); @@ -363,6 +363,113 @@ namespace MWClass return ref->base->script; } + void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const + { + MWMechanics::NpcStats& stats = getNpcStats (ptr); + + switch (stance) + { + case Run: + + stats.mForceRun = force; + break; + + case Sneak: + + stats.mForceSneak = force; + break; + + case Combat: + + throw std::runtime_error ("combat stance not enforcable for NPCs"); + } + } + + void Npc::setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const + { + MWMechanics::NpcStats& stats = getNpcStats (ptr); + + switch (stance) + { + case Run: + + stats.mRun = set; + break; + + case Sneak: + + stats.mSneak = set; + break; + + case Combat: + + stats.mCombat = set; + break; + } + } + + bool Npc::getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce) const + { + MWMechanics::NpcStats& stats = getNpcStats (ptr); + + switch (stance) + { + case Run: + + if (!ignoreForce && stats.mForceRun) + return true; + + return stats.mRun; + + case Sneak: + + if (!ignoreForce && stats.mForceSneak) + return true; + + return stats.mSneak; + + case Combat: + + return stats.mCombat; + } + + return false; + } + + float Npc::getSpeed (const MWWorld::Ptr& ptr) const + { + return getStance (ptr, Run) ? 600 : 300; // TODO calculate these values from stats + } + + MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getMovement().get()) + { + boost::shared_ptr movement ( + new MWMechanics::Movement); + + ptr.getRefData().getMovement() = movement; + } + + return *ptr.getRefData().getMovement(); + } + + Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const + { + Ogre::Vector3 vector (0, 0, 0); + + if (ptr.getRefData().getMovement().get()) + { + vector.x = - ptr.getRefData().getMovement()->mLeftRight * 200; + vector.y = ptr.getRefData().getMovement()->mForwardBackward * 200; + + if (getStance (ptr, Run, false)) + vector *= 2; + } + + return vector; + } + void Npc::registerSelf() { boost::shared_ptr instance (new Npc); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index e54c35ad8..9f29878da 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -8,7 +8,7 @@ namespace MWClass class Npc : public MWWorld::Class { public: - + virtual std::string getId (const MWWorld::Ptr& ptr) const; ///< Return ID of \a ptr @@ -43,6 +43,26 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual void setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const; + ///< Force or unforce a stance. + + virtual void setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const; + ///< Set or unset a stance. + + virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false) + const; + ////< Check if a stance is active or not. + + virtual float getSpeed (const MWWorld::Ptr& ptr) const; + ///< Return movement speed. + + virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; + ///< Return desired movement. + + virtual Ogre::Vector3 getMovementVector (const MWWorld::Ptr& ptr) const; + ///< Return desired movement vector (determined based on movement settings, + /// stance and stats). + static void registerSelf(); }; } diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index e163473f7..6c9889a7b 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 9188ae9ea..028d8b5e3 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 7b42bc95c..b9b5036fb 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 33e3e0ed4..851b4d7bf 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -21,6 +21,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 4c14e58ea..a985a7e42 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -26,6 +26,7 @@ namespace MWClass { MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); + cellRender.insertObjectPhysics(); ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index feabddd05..d4ab74a98 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -10,6 +10,9 @@ #include #include +#undef min +#undef max + using namespace MWGui; /* GenerateClassResultDialog */ diff --git a/apps/openmw/mwgui/layouts.cpp b/apps/openmw/mwgui/layouts.cpp index 06727b55c..73b8d77e8 100644 --- a/apps/openmw/mwgui/layouts.cpp +++ b/apps/openmw/mwgui/layouts.cpp @@ -9,6 +9,9 @@ #include #include +#undef min +#undef max + using namespace MWGui; const int StatsWindow::lineHeight = 18; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 74632134a..c165d942e 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -10,6 +10,9 @@ #include +#undef min +#undef max + using namespace MWGui; using namespace Widgets; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 7f23ff34e..a66509758 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -6,6 +6,9 @@ #include +#undef min +#undef max + using namespace MWGui; using namespace MWGui::Widgets; diff --git a/apps/openmw/mwinput/inputmanager.cpp b/apps/openmw/mwinput/inputmanager.cpp index 31623fb58..dc21680af 100644 --- a/apps/openmw/mwinput/inputmanager.cpp +++ b/apps/openmw/mwinput/inputmanager.cpp @@ -41,13 +41,31 @@ namespace MWInput A_MoveLeft, // Move player left / right A_MoveRight, - A_MoveUp, // Move up / down - A_MoveDown, A_MoveForward, // Forward / Backward A_MoveBackward, A_Activate, + A_Use, //Use weapon, spell, etc. + A_Jump, + A_AutoMove, //Toggle Auto-move forward + A_Rest, //Rest + A_Journal, //Journal + A_Weapon, //Draw/Sheath weapon + A_Spell, //Ready/Unready Casting + A_AlwaysRun, //Toggle Always Run + A_CycleSpellLeft, //cycling through spells + A_CycleSpellRight, + A_CycleWeaponLeft,//Cycling through weapons + A_CycleWeaponRight, + A_ToggleSneak, //Toggles Sneak, add Push-Sneak later + A_ToggleWalk, //Toggle Walking/Running + + A_QuickSave, + A_QuickLoad, + A_QuickMenu, + A_GameMenu, + A_LAST // Marker for the last item }; @@ -68,11 +86,14 @@ namespace MWInput // Count screenshots. int shotCount; + + /* InputImpl Methods */ + // Write screenshot to file. void screenshot() { - // Find the first unused filename. - // + + // Find the first unused filename with a do-while char buf[50]; do { @@ -82,8 +103,8 @@ namespace MWInput ogre.screenshot(buf); } - // Called when the user presses the button to toggle the inventory - // screen. + + /* toggleInventory() is called when the user presses the button to toggle the inventory screen. */ void toggleInventory() { using namespace MWGui; @@ -118,11 +139,21 @@ namespace MWInput mEngine.activate(); } + void toggleAutoMove() + { + player.setAutoMove (!player.getAutoMove()); + } + + void toggleWalking() + { + player.toggleRunning(); + } + // Exit program now button (which is disabled in GUI mode) void exitNow() { - if(!windows.isGuiMode()) - exit.exitNow(); + if(!windows.isGuiMode()) + exit.exitNow(); } public: @@ -159,7 +190,10 @@ namespace MWInput "Toggle console"); disp->funcs.bind(A_Activate, boost::bind(&InputImpl::activate, this), "Activate"); - + disp->funcs.bind(A_AutoMove, boost::bind(&InputImpl::toggleAutoMove, this), + "Auto Move"); + disp->funcs.bind(A_ToggleWalk, boost::bind(&InputImpl::toggleWalking, this), + "Toggle Walk/Run"); // Add the exit listener ogre.getRoot()->addFrameListener(&exit); @@ -195,6 +229,7 @@ namespace MWInput **********************************/ // Key bindings for keypress events + // NOTE: These keys do not require constant polling - use in conjuction with variables in loops. disp->bind(A_Quit, KC_Q); disp->bind(A_Quit, KC_ESCAPE); @@ -202,8 +237,12 @@ namespace MWInput disp->bind(A_Inventory, KC_I); disp->bind(A_Console, KC_F1); disp->bind(A_Activate, KC_SPACE); + disp->bind(A_AutoMove, KC_Z); + disp->bind(A_ToggleSneak, KC_X); + disp->bind(A_ToggleWalk, KC_C); // Key bindings for polled keys + // NOTE: These keys are constantly being polled. Only add keys that must be checked each frame. // Arrow keys poller.bind(A_MoveLeft, KC_LEFT); @@ -216,47 +255,54 @@ namespace MWInput poller.bind(A_MoveRight, KC_D); poller.bind(A_MoveForward, KC_W); poller.bind(A_MoveBackward, KC_S); - - // Use shift and ctrl for up and down - poller.bind(A_MoveUp, KC_LSHIFT); - poller.bind(A_MoveDown, KC_LCONTROL); } - // Used to check for movement keys + //NOTE: Used to check for movement keys bool frameStarted(const Ogre::FrameEvent &evt) { - // Tell OIS to handle all input events - input.capture(); + // Tell OIS to handle all input events + input.capture(); - // Update windows/gui as a result of input events - // For instance this could mean opening a new window/dialog, - // by doing this after the input events are handled we - // ensure that window/gui changes appear quickly while - // avoiding that window/gui changes does not happen in - // event callbacks (which may crash) - windows.update(); + // Update windows/gui as a result of input events + // For instance this could mean opening a new window/dialog, + // by doing this after the input events are handled we + // ensure that window/gui changes appear quickly while + // avoiding that window/gui changes does not happen in + // event callbacks (which may crash) + windows.update(); - // Disable movement in Gui mode - if(windows.isGuiMode()) return true; + // Disable movement in Gui mode + if (windows.isGuiMode()) return true; - float speed = 300 * evt.timeSinceLastFrame; - float moveX = 0, moveY = 0, moveZ = 0; + // Configure player movement according to keyboard input. Actual movement will + // be done in the physics system. + if (poller.isDown(A_MoveLeft)) + { + player.setAutoMove (false); + player.setLeftRight (1); + } + else if (poller.isDown(A_MoveRight)) + { + player.setAutoMove (false); + player.setLeftRight (-1); + } + else + player.setLeftRight (0); - if(poller.isDown(A_MoveLeft)) moveX -= speed; - if(poller.isDown(A_MoveRight)) moveX += speed; - if(poller.isDown(A_MoveForward)) moveZ -= speed; - if(poller.isDown(A_MoveBackward)) moveZ += speed; + if (poller.isDown(A_MoveForward)) + { + player.setAutoMove (false); + player.setForwardBackward (1); + } + else if (poller.isDown(A_MoveBackward)) + { + player.setAutoMove (false); + player.setForwardBackward (-1); + } + else + player.setForwardBackward (0); - // TODO: These should be enabled for floating modes (like - // swimming and levitation) and disabled for everything else. - if(poller.isDown(A_MoveUp)) moveY += speed; - if(poller.isDown(A_MoveDown)) moveY -= speed; - - if(moveX != 0 || moveY != 0 || moveZ != 0) - player.moveRel(moveX, moveY, moveZ); - - - return true; + return true; } // Switch between gui modes. Besides controlling the Gui windows @@ -288,6 +334,7 @@ namespace MWInput } }; + /***CONSTRUCTOR***/ MWInputManager::MWInputManager(OEngine::Render::OgreRenderer &ogre, MWWorld::Player &player, MWGui::WindowManager &windows, @@ -297,6 +344,7 @@ namespace MWInput impl = new InputImpl(ogre,player,windows,debug, engine); } + /***DESTRUCTOR***/ MWInputManager::~MWInputManager() { delete impl; diff --git a/apps/openmw/mwmechanics/mechanicsmanager.cpp b/apps/openmw/mwmechanics/mechanicsmanager.cpp index 05219d6aa..df0b5616c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.cpp @@ -234,11 +234,17 @@ namespace MWMechanics void MechanicsManager::removeActor (const MWWorld::Ptr& ptr) { + if (ptr==mWatched) + mWatched = MWWorld::Ptr(); + mActors.erase (ptr); } void MechanicsManager::dropActors (const MWWorld::Ptr::CellStore *cellStore) { + if (!mWatched.isEmpty() && mWatched.getCell()==cellStore) + mWatched = MWWorld::Ptr(); + std::set::iterator iter = mActors.begin(); while (iter!=mActors.end()) @@ -255,7 +261,7 @@ namespace MWMechanics mWatched = ptr; } - void MechanicsManager::update() + void MechanicsManager::update (std::vector >& movement) { if (!mWatched.isEmpty()) { @@ -350,6 +356,15 @@ namespace MWMechanics mEnvironment.mWindowManager->configureSkills (majorSkills, minorSkills); } + + for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); + ++iter) + { + Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter); + + if (vector!=Ogre::Vector3::ZERO) + movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector)); + } } void MechanicsManager::setPlayerName (const std::string& name) diff --git a/apps/openmw/mwmechanics/mechanicsmanager.hpp b/apps/openmw/mwmechanics/mechanicsmanager.hpp index 26d3621ab..2e2192638 100644 --- a/apps/openmw/mwmechanics/mechanicsmanager.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanager.hpp @@ -2,12 +2,19 @@ #define GAME_MWMECHANICS_MECHANICSMANAGER_H #include +#include +#include #include "../mwworld/ptr.hpp" #include "creaturestats.hpp" #include "npcstats.hpp" +namespace Ogre +{ + class Vector3; +} + namespace MWWorld { class Environment; @@ -53,8 +60,8 @@ namespace MWMechanics ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - void update(); - ///< Update actor stats + void update (std::vector >& movement); + ///< Update actor stats and store desired velocity vectors in \a movement void setPlayerName (const std::string& name); ///< Set player name. diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp new file mode 100644 index 000000000..a555ac010 --- /dev/null +++ b/apps/openmw/mwmechanics/movement.hpp @@ -0,0 +1,16 @@ +#ifndef GAME_MWMECHANICS_MOVEMENT_H +#define GAME_MWMECHANICS_MOVEMENT_H + +namespace MWMechanics +{ + /// Desired movement for an actor + struct Movement + { + signed char mLeftRight; // 1: wants to move left, -1: wants to move right + signed char mForwardBackward; // 1:wants to move forward, -1: wants to move backward + + Movement() : mLeftRight (0), mForwardBackward (0) {} + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index b5ca93749..aeb5f56d5 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -18,6 +18,15 @@ namespace MWMechanics std::map mFactionRank; Stat mSkill[27]; + + bool mForceRun; + bool mForceSneak; + bool mRun; + bool mSneak; + bool mCombat; + + NpcStats() : mForceRun (false), mForceSneak (false), mRun (false), mSneak (false), + mCombat (false) {} }; } diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 075417850..2449075a8 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -1,6 +1,9 @@ #ifndef GAME_MWMECHANICS_STAT_H #define GAME_MWMECHANICS_STAT_H +#undef min +#undef max + #include namespace MWMechanics diff --git a/apps/openmw/mwrender/cellimp.hpp b/apps/openmw/mwrender/cellimp.hpp index 91676e11c..272bfeeab 100644 --- a/apps/openmw/mwrender/cellimp.hpp +++ b/apps/openmw/mwrender/cellimp.hpp @@ -45,6 +45,9 @@ namespace MWRender virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements) = 0; + virtual void insertObjectPhysics() = 0; + + virtual void insertActorPhysics() = 0; /// insert a light related to the most recent insertBegin call. virtual void insertLight(float r, float g, float b, float radius) = 0; diff --git a/apps/openmw/mwrender/exterior.cpp b/apps/openmw/mwrender/exterior.cpp index cd2a7680d..c1d7641b8 100644 --- a/apps/openmw/mwrender/exterior.cpp +++ b/apps/openmw/mwrender/exterior.cpp @@ -58,6 +58,8 @@ void ExteriorCellRender::insertBegin (ESM::CellRef &ref) // Rotates first around z, then y, then x mInsert->setOrientation(xr*yr*zr); + + mInsertMesh.clear(); } @@ -81,18 +83,18 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, if(sceneParent[i] != "" && parent->getChild(sceneParent[i])) parent = dynamic_cast (parent->getChild(sceneParent[i])); } - + mNpcPart = parent->createChildSceneNode(sceneNodeName); NIFLoader::load(mesh); MovableObject *ent = mScene.getMgr()->createEntity(mesh); - + mNpcPart->translate(vec); mNpcPart->rotate(axis, angle); // mNpcPart->translate(vec); //mNpcPart->rotate(axis, angle); mNpcPart->attachObject(ent); //mNpcPart-> - + } */ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements) @@ -108,7 +110,7 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, if(sceneParent[i] != "" && parent->getChild(sceneParent[i])) parent = dynamic_cast (parent->getChild(sceneParent[i])); } - + mNpcPart = parent->createChildSceneNode(sceneNodeName); MeshPtr good2 = NIFLoader::load(mesh); @@ -120,7 +122,7 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, mNpcPart->rotate(axis, angle); } else{ - + mNpcPart->rotate(axis, angle); mNpcPart->translate(vec); } @@ -202,6 +204,22 @@ void ExteriorCellRender::insertMesh(const std::string &mesh) NIFLoader::load(mesh); MovableObject *ent = mScene.getMgr()->createEntity(mesh); mInsert->attachObject(ent); + + if (mInsertMesh.empty()) + mInsertMesh = mesh; +} + +void ExteriorCellRender::insertObjectPhysics() +{ + if (!mInsertMesh.empty()) + mScene.addObject (mInsert->getName(), mInsertMesh, mInsert->getOrientation(), + mInsert->getScale().x, mInsert->getPosition()); +} + +void ExteriorCellRender::insertActorPhysics() +{ + if (!mInsertMesh.empty()) + mScene.addActor (mInsert->getName(), mInsertMesh, mInsert->getPosition()); } // insert a light related to the most recent insertBegin call. diff --git a/apps/openmw/mwrender/exterior.hpp b/apps/openmw/mwrender/exterior.hpp index 67d41bcac..93491f263 100644 --- a/apps/openmw/mwrender/exterior.hpp +++ b/apps/openmw/mwrender/exterior.hpp @@ -54,6 +54,7 @@ namespace MWRender Ogre::SceneNode *mBase; Ogre::SceneNode *mInsert; + std::string mInsertMesh; Ogre::SceneNode *mNpcPart; // 0 normal, 1 more bright, 2 max @@ -67,11 +68,15 @@ namespace MWRender /// insert a mesh related to the most recent insertBegin call. virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements); virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst); - + virtual void insertMesh(const std::string &mesh); virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements); virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements); + virtual void insertObjectPhysics(); + + virtual void insertActorPhysics(); + /// insert a light related to the most recent insertBegin call. virtual void insertLight(float r, float g, float b, float radius); @@ -85,7 +90,7 @@ namespace MWRender void configureFog(); void setAmbientMode(); - + public: diff --git a/apps/openmw/mwrender/interior.cpp b/apps/openmw/mwrender/interior.cpp index 6e1f0d297..e096b7757 100644 --- a/apps/openmw/mwrender/interior.cpp +++ b/apps/openmw/mwrender/interior.cpp @@ -60,6 +60,8 @@ void InteriorCellRender::insertBegin (ESM::CellRef &ref) // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); + + mInsertMesh.clear(); } // insert a mesh related to the most recent insertBegin call. @@ -100,7 +102,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, if(sceneParent[i] != "" && parent->getChild(sceneParent[i])) parent = dynamic_cast (parent->getChild(sceneParent[i])); } - + npcPart = parent->createChildSceneNode(sceneNodeName); //npcPart->showBoundingBox(true); @@ -108,7 +110,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, MovableObject *ent = scene.getMgr()->createEntity(mesh); //ent->extr - + // MovableObject *ent2 = scene.getMgr()->createEntity(bounds // ); //ent-> @@ -119,7 +121,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec, npcPart->rotate(axis, angle); } else{ - + npcPart->rotate(axis, angle); npcPart->translate(vec); } @@ -185,6 +187,22 @@ void InteriorCellRender::insertMesh(const std::string &mesh) NIFLoader::load(mesh); MovableObject *ent = scene.getMgr()->createEntity(mesh); insert->attachObject(ent); + + if (mInsertMesh.empty()) + mInsertMesh = mesh; +} + +void InteriorCellRender::insertObjectPhysics() +{ + if (!mInsertMesh.empty()) + scene.addObject (insert->getName(), mInsertMesh, insert->getOrientation(), + insert->getScale().x, insert->getPosition()); +} + +void InteriorCellRender::insertActorPhysics() +{ + if (!mInsertMesh.empty()) + scene.addActor (insert->getName(), mInsertMesh, insert->getPosition()); } // insert a light related to the most recent insertBegin call. diff --git a/apps/openmw/mwrender/interior.hpp b/apps/openmw/mwrender/interior.hpp index a38943521..7c4b2aaf1 100644 --- a/apps/openmw/mwrender/interior.hpp +++ b/apps/openmw/mwrender/interior.hpp @@ -54,6 +54,7 @@ namespace MWRender Ogre::SceneNode *base; Ogre::SceneNode *insert; + std::string mInsertMesh; Ogre::SceneNode *npcPart; // 0 normal, 1 more bright, 2 max @@ -69,6 +70,11 @@ namespace MWRender virtual void insertMesh(const std::string &mesh); virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements); virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst); + + virtual void insertObjectPhysics(); + + virtual void insertActorPhysics(); + /// insert a light related to the most recent insertBegin call. virtual void insertLight(float r, float g, float b, float radius); diff --git a/apps/openmw/mwrender/mwscene.cpp b/apps/openmw/mwrender/mwscene.cpp index 5fff2c295..09ee0e3e3 100644 --- a/apps/openmw/mwrender/mwscene.cpp +++ b/apps/openmw/mwrender/mwscene.cpp @@ -11,6 +11,7 @@ #include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwworld/ptr.hpp" +#include "../mwworld/doingphysics.hpp" #include #include "player.hpp" @@ -18,30 +19,38 @@ using namespace MWRender; using namespace Ogre; -MWScene::MWScene(OEngine::Render::OgreRenderer &_rend) - : rend(_rend) +MWScene::MWScene(OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng) + : rend(_rend) { - rend.createScene("PlayerCam", 55, 5); + eng = physEng; + rend.createScene("PlayerCam", 55, 5); - // Set default mipmap level (NB some APIs ignore this) - TextureManager::getSingleton().setDefaultNumMipmaps(5); + // Set default mipmap level (NB some APIs ignore this) + TextureManager::getSingleton().setDefaultNumMipmaps(5); - // Load resources - ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); + // Load resources + ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); - // Turn the entire scene (represented by the 'root' node) -90 - // degrees around the x axis. This makes Z go upwards, and Y go into - // the screen (when x is to the right.) This is the orientation that - // Morrowind uses, and it automagically makes everything work as it - // should. - SceneNode *rt = rend.getScene()->getRootSceneNode(); - mwRoot = rt->createChildSceneNode(); - mwRoot->pitch(Degree(-90)); + // Turn the entire scene (represented by the 'root' node) -90 + // degrees around the x axis. This makes Z go upwards, and Y go into + // the screen (when x is to the right.) This is the orientation that + // Morrowind uses, and it automagically makes everything work as it + // should. + SceneNode *rt = rend.getScene()->getRootSceneNode(); + mwRoot = rt->createChildSceneNode(); + mwRoot->pitch(Degree(-90)); - //used to obtain ingame information of ogre objects (which are faced or selected) - mRaySceneQuery = rend.getScene()->createRayQuery(Ray()); + //used to obtain ingame information of ogre objects (which are faced or selected) + mRaySceneQuery = rend.getScene()->createRayQuery(Ray()); - mPlayer = new MWRender::Player (getCamera()); + Ogre::SceneNode *playerNode = mwRoot->createChildSceneNode(); + playerNode->pitch(Degree(90)); + Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode(); + Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode(); + cameraPitchNode->attachObject(getCamera()); + + + mPlayer = new MWRender::Player (getCamera(), playerNode->getName()); } MWScene::~MWScene() @@ -58,9 +67,11 @@ std::pair MWScene::getFacedHandle (MWWorld::World& world) Ray centerRay = getCamera()->getCameraToViewportRay( getViewport()->getWidth()/2, getViewport()->getHeight()/2); + btVector3 from(centerRay.getOrigin().x,centerRay.getOrigin().y,centerRay.getOrigin().z); + btVector3 to(centerRay.getPoint(1000).x,centerRay.getPoint(1000).x,centerRay.getPoint(1000).x); // get all objects touched by the ray - getRaySceneQuery()->setRay (centerRay ); + /*getRaySceneQuery()->setRay (centerRay ); RaySceneQueryResult &result = getRaySceneQuery()->execute(); RaySceneQueryResult::iterator nearest = result.end(); @@ -93,7 +104,103 @@ std::pair MWScene::getFacedHandle (MWWorld::World& world) { handle = nearest->movable->getParentSceneNode()->getName(); distance = nearest->distance; + }*/ + + return eng->rayTest(from,to); +} + +void MWScene::doPhysics (float duration, MWWorld::World& world, + const std::vector >& actors) +{ + // stop changes to world from being reported back to the physics system + MWWorld::DoingPhysics scopeGuard; + + //set the DebugRenderingMode. To disable it,set it to 0 + eng->setDebugRenderingMode(1); + + //set the walkdirection to 0 (no movement) for every actor) + for(std::map::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++) + { + OEngine::Physic::PhysicActor* act = it->second; + act->setWalkDirection(btVector3(0,0,0)); } - return std::pair(handle, distance); + for (std::vector >::const_iterator iter (actors.begin()); + iter!=actors.end(); ++iter) + { + OEngine::Physic::PhysicActor* act = eng->getCharacter(iter->first); + + //dirty stuff to get the camera orientation. Must be changed! + Ogre::SceneNode *sceneNode = rend.getScene()->getSceneNode (iter->first); + Ogre::Quaternion quat = sceneNode->getChildIterator().getNext()->getOrientation(); + + Ogre::Vector3 dir1(iter->second.x,iter->second.z,-iter->second.y); + Ogre::Vector3 dir = 0.01*(quat*dir1); + + //set the walk direction + act->setWalkDirection(btVector3(dir.x,-dir.z,dir.y)); + } + eng->stepSimulation(duration); + + for(std::map::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++) + { + OEngine::Physic::PhysicActor* act = it->second; + btVector3 newPos = act->getPosition(); + MWWorld::Ptr ptr = world.getPtrViaHandle (it->first); + world.moveObject (ptr, newPos.x(), newPos.y(), newPos.z()); + } +} + +void MWScene::addObject (const std::string& handle, const std::string& mesh, + const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position) +{ + OEngine::Physic::RigidBody* body = eng->createRigidBody(mesh,handle); + eng->addRigidBody(body); + btTransform tr; + tr.setOrigin(btVector3(position.x,position.y,position.z)); + tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); + body->setWorldTransform(tr); +} + +void MWScene::addActor (const std::string& handle, const std::string& mesh, + const Ogre::Vector3& position) +{ + //TODO:optimize this. Searching the std::map isn't very efficient i think. + eng->addCharacter(handle); + OEngine::Physic::PhysicActor* act = eng->getCharacter(handle); + act->setPosition(btVector3(position.x,position.y,position.z)); +} + +void MWScene::removeObject (const std::string& handle) +{ + //TODO:check if actor??? + eng->removeRigidBody(handle); + eng->deleteRigidBody(handle); +} + +void MWScene::moveObject (const std::string& handle, const Ogre::Vector3& position, bool updatePhysics) +{ + rend.getScene()->getSceneNode(handle)->setPosition(position); + + if(updatePhysics)//TODO: is it an actor? + { + OEngine::Physic::RigidBody* body = eng->getRigidBody(handle); + btTransform tr = body->getWorldTransform(); + tr.setOrigin(btVector3(position.x,position.y,position.z)); + body->setWorldTransform(tr); + } +} + +void MWScene::rotateObject (const std::string& handle, const Ogre::Quaternion& rotation) +{ +} + +void MWScene::scaleObject (const std::string& handle, float scale) +{ + +} + +void MWScene::toggleCollisionMode() +{ + } diff --git a/apps/openmw/mwrender/mwscene.hpp b/apps/openmw/mwrender/mwscene.hpp index b4c8357cf..fd3ef783f 100644 --- a/apps/openmw/mwrender/mwscene.hpp +++ b/apps/openmw/mwrender/mwscene.hpp @@ -3,6 +3,10 @@ #include #include +#include + +#include +#include namespace Ogre { @@ -11,6 +15,8 @@ namespace Ogre class SceneManager; class SceneNode; class RaySceneQuery; + class Quaternion; + class Vector3; } namespace MWWorld @@ -34,11 +40,13 @@ namespace MWRender Ogre::SceneNode *mwRoot; Ogre::RaySceneQuery *mRaySceneQuery; + OEngine::Physic::PhysicEngine* eng; + MWRender::Player *mPlayer; public: - MWScene (OEngine::Render::OgreRenderer &_rend); + MWScene (OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng); ~MWScene(); @@ -54,6 +62,34 @@ namespace MWRender /// name is empty and distance = -1 if there is no object which /// can be faced std::pair getFacedHandle (MWWorld::World& world); + + /// Run physics simulation and modify \a world accordingly. + void doPhysics (float duration, MWWorld::World& world, + const std::vector >& actors); + + /// Add object to physics system. + void addObject (const std::string& handle, const std::string& mesh, + const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position); + + /// Add actor to physics system. + void addActor (const std::string& handle, const std::string& mesh, + const Ogre::Vector3& position); + + /// Remove object from physic systems. + void removeObject (const std::string& handle); + + /// Move object. + void moveObject (const std::string& handle, const Ogre::Vector3& position, bool updatePhysics); + + /// Change object's orientation. + void rotateObject (const std::string& handle, const Ogre::Quaternion& rotation); + + /// Change object's scale. + void scaleObject (const std::string& handle, float scale); + + /// Toggle collision mode for player. If disabled player object should ignore + /// collisions and gravity. + void toggleCollisionMode(); }; } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index badc77bda..7dcaeee09 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -3,6 +3,7 @@ namespace MWRender { - Player::Player (Ogre::Camera *camera) : mCamera (camera) + Player::Player (Ogre::Camera *camera, const std::string& handle) + : mCamera (camera), mHandle (handle) {} } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index a8f8d24fe..f2d819116 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWRENDER_PLAYER_H #define GAME_MWRENDER_PLAYER_H +#include + namespace Ogre { class Camera; @@ -12,12 +14,15 @@ namespace MWRender class Player { Ogre::Camera *mCamera; + std::string mHandle; public: - Player (Ogre::Camera *camera); + Player (Ogre::Camera *camera, const std::string& handle); Ogre::Camera *getCamera() { return mCamera; } + + std::string getHandle() const { return mHandle; } }; } diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 2dd46022b..9c756e11f 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -54,7 +54,7 @@ namespace MWScript else { pos.pos[0] = pos.pos[1] = 0; - context.getWorld().changeCell (cell, pos); + context.getWorld().changeToInteriorCell (cell, pos); } } }; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 5020675d1..f5a5c08a4 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -46,7 +46,7 @@ namespace MWScript InterpreterContext& context = static_cast (runtime.getContext()); - context.getWorld().getPlayer().toggleCollisionMode(); + context.getWorld().toggleCollisionMode(); } }; diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 3db294818..207e2c50e 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -16,6 +16,6 @@ namespace MWWorld if (mCellName.empty()) environment.mWorld->changeToExteriorCell (mPosition); else - environment.mWorld->changeCell (mCellName, mPosition); + environment.mWorld->changeToInteriorCell (mCellName, mPosition); } } diff --git a/apps/openmw/mwworld/cellfunctors.hpp b/apps/openmw/mwworld/cellfunctors.hpp new file mode 100644 index 000000000..5ff801f01 --- /dev/null +++ b/apps/openmw/mwworld/cellfunctors.hpp @@ -0,0 +1,31 @@ +#ifndef GAME_MWWORLD_CELLFUNCTORS_H +#define GAME_MWWORLD_CELLFUNCTORS_H + +#include +#include + +#include "refdata.hpp" + +namespace ESM +{ + class CellRef; +} + +namespace MWWorld +{ + /// List all (Ogre-)handles. + struct ListHandles + { + std::vector mHandles; + + bool operator() (ESM::CellRef& ref, RefData& data) + { + std::string handle = data.getHandle(); + if (!handle.empty()) + mHandles.push_back (handle); + return true; + } + }; +} + +#endif diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 1e9f25bbd..670cf90b2 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -3,6 +3,8 @@ #include +#include + #include "ptr.hpp" #include "nullaction.hpp" @@ -92,6 +94,36 @@ namespace MWWorld return ""; } + void Class::setForceStance (const Ptr& ptr, Stance stance, bool force) const + { + throw std::runtime_error ("stance not supported by class"); + } + + void Class::setStance (const Ptr& ptr, Stance stance, bool set) const + { + throw std::runtime_error ("stance not supported by class"); + } + + bool Class::getStance (const Ptr& ptr, Stance stance, bool ignoreForce) const + { + return false; + } + + float Class::getSpeed (const Ptr& ptr) const + { + return 0; + } + + MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const + { + throw std::runtime_error ("movement settings not supported by class"); + } + + Ogre::Vector3 Class::getMovementVector (const Ptr& ptr) const + { + return Ogre::Vector3 (0, 0, 0); + } + const Class& Class::get (const std::string& key) { std::map >::const_iterator iter = sClasses.find (key); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 5153d7468..8ad9ba58f 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -10,6 +10,11 @@ #include "containerstore.hpp" #include "refdata.hpp" +namespace Ogre +{ + class Vector3; +} + namespace MWRender { class CellRenderImp; @@ -19,6 +24,7 @@ namespace MWMechanics { struct CreatureStats; struct NpcStats; + struct Movement; } namespace MWWorld @@ -41,6 +47,12 @@ namespace MWWorld public: + /// NPC-stances. + enum Stance + { + Run, Sneak, Combat + }; + virtual ~Class(); virtual std::string getId (const Ptr& ptr) const; @@ -108,6 +120,25 @@ namespace MWWorld ///< Return name of the script attached to ptr (default implementation: return an empty /// string). + virtual void setForceStance (const Ptr& ptr, Stance stance, bool force) const; + ///< Force or unforce a stance. + + virtual void setStance (const Ptr& ptr, Stance stance, bool set) const; + ///< Set or unset a stance. + + virtual bool getStance (const Ptr& ptr, Stance stance, bool ignoreForce = false) const; + ////< Check if a stance is active or not. + + virtual float getSpeed (const Ptr& ptr) const; + ///< Return movement speed. + + virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; + ///< Return desired movement. + + virtual Ogre::Vector3 getMovementVector (const Ptr& ptr) const; + ///< Return desired movement vector (determined based on movement settings, + /// stance and stats). + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. diff --git a/apps/openmw/mwworld/doingphysics.cpp b/apps/openmw/mwworld/doingphysics.cpp new file mode 100644 index 000000000..27fb27c57 --- /dev/null +++ b/apps/openmw/mwworld/doingphysics.cpp @@ -0,0 +1,33 @@ + +#include "doingphysics.hpp" + +namespace MWWorld +{ + int DoingPhysics::sCounter = 0; + int DoingPhysics::sSuppress = 0; + + DoingPhysics::DoingPhysics() + { + ++sCounter; + } + + DoingPhysics::~DoingPhysics() + { + --sCounter; + } + + bool DoingPhysics::isDoingPhysics() + { + return sCounter>0 || sSuppress>0; + } + + SuppressDoingPhysics::SuppressDoingPhysics() + { + ++DoingPhysics::sSuppress; + } + + SuppressDoingPhysics::~SuppressDoingPhysics() + { + --DoingPhysics::sSuppress; + } +} diff --git a/apps/openmw/mwworld/doingphysics.hpp b/apps/openmw/mwworld/doingphysics.hpp new file mode 100644 index 000000000..d38e4bb15 --- /dev/null +++ b/apps/openmw/mwworld/doingphysics.hpp @@ -0,0 +1,46 @@ +#ifndef GAME_MWWORLD_DOINGPHYSICS_H +#define GAME_MWWORLD_DOINGPHYSICS_H + +namespace MWWorld +{ + class SuppressDoingPhysics; + + /// Scope guard for blocking physics updates during physics simulation. + class DoingPhysics + { + static int sCounter; + static int sSuppress; + + private: + + DoingPhysics (const DoingPhysics&); + DoingPhysics& operator= (const DoingPhysics&); + + public: + + DoingPhysics(); + + ~DoingPhysics(); + + static bool isDoingPhysics(); + + friend class SuppressDoingPhysics; + }; + + /// Scope guard for temporarily lifting the block issues by DoingPhysics + class SuppressDoingPhysics + { + private: + + SuppressDoingPhysics (const SuppressDoingPhysics&); + SuppressDoingPhysics& operator= (const SuppressDoingPhysics&); + + public: + + SuppressDoingPhysics(); + + ~SuppressDoingPhysics(); + }; +} + +#endif diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index cc7abccac..259348938 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -4,17 +4,20 @@ #include "../mwrender/player.hpp" #include "world.hpp" +#include "class.hpp" namespace MWWorld { Player::Player (MWRender::Player *renderer, const ESM::NPC *player, MWWorld::World& world) : - mCellStore (0), mRenderer (renderer), mWorld (world), mClass (0), mCollisionMode (true) + mCellStore (0), mRenderer (renderer), mWorld (world), mClass (0), + mAutoMove (false), mForwardBackward (0) { mPlayer.base = player; mName = player->name; mMale = !(player->flags & ESM::NPC::Female); mRace = player->race; mPlayer.ref.pos.pos[0] = mPlayer.ref.pos.pos[1] = mPlayer.ref.pos.pos[2] = 0; + mPlayer.mData.setHandle (renderer->getHandle()); mClass = new ESM::Class (*world.getStore().classes.find (player->cls)); } @@ -34,31 +37,52 @@ namespace MWWorld -mPlayer.ref.pos.pos[1])); } - void Player::moveRel (float &relX, float &relY, float &relZ) - { - // Move camera relative to its own direction - mRenderer->getCamera()->moveRelative (Ogre::Vector3(relX,0,relZ)); - - // Up/down movement is always done relative the world axis. - mRenderer->getCamera()->move (Ogre::Vector3(0,relY,0)); - - // Get new camera position, converting back to MW coords. - Ogre::Vector3 pos = mRenderer->getCamera()->getPosition(); - relX = pos[0]; - relY = -pos[2]; - relZ = pos[1]; - - // TODO: Collision detection must be used to find the REAL new - // position, if mCollisionMode==true - - // Set the position - setPos(relX, relY, relZ); - } - void Player::setClass (const ESM::Class& class_) { ESM::Class *new_class = new ESM::Class (class_); delete mClass; mClass = new_class; } + + void Player::setAutoMove (bool enable) + { + MWWorld::Ptr ptr = getPlayer(); + + mAutoMove = enable; + + int value = mForwardBackward; + + if (mAutoMove) + value = 1; + + MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; + } + + void Player::setLeftRight (int value) + { + MWWorld::Ptr ptr = getPlayer(); + + MWWorld::Class::get (ptr).getMovementSettings (ptr).mLeftRight = value; + } + + void Player::setForwardBackward (int value) + { + MWWorld::Ptr ptr = getPlayer(); + + mForwardBackward = value; + + if (mAutoMove) + value = 1; + + MWWorld::Class::get (ptr).getMovementSettings (ptr).mForwardBackward = value; + } + + void Player::toggleRunning() + { + MWWorld::Ptr ptr = getPlayer(); + + bool running = MWWorld::Class::get (ptr).getStance (ptr, MWWorld::Class::Run, true); + + MWWorld::Class::get (ptr).setStance (ptr, MWWorld::Class::Run, !running); + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 997282ca6..fc29286a9 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -29,7 +29,8 @@ namespace MWWorld std::string mRace; std::string mBirthsign; ESM::Class *mClass; - bool mCollisionMode; + bool mAutoMove; + int mForwardBackward; public: @@ -45,10 +46,6 @@ namespace MWWorld mCellStore = cellStore; } - /// Move the player relative to her own position and - /// orientation. After the call, the new position is returned. - void moveRel (float &relX, float &relY, float &relZ); - MWWorld::Ptr getPlayer() { MWWorld::Ptr ptr (&mPlayer, mCellStore); @@ -104,10 +101,18 @@ namespace MWWorld return *mClass; } - void toggleCollisionMode() + bool getAutoMove() { - mCollisionMode = !mCollisionMode; + return mAutoMove; } - }; + + void setAutoMove (bool enable); + + void setLeftRight (int value); + + void setForwardBackward (int value); + + void toggleRunning(); + }; } #endif diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index a8cf3aed8..a9613248e 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -9,6 +9,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/movement.hpp" #include "containerstore.hpp" @@ -36,6 +37,7 @@ namespace MWWorld // are never copied outside of container operations. boost::shared_ptr mCreatureStats; boost::shared_ptr mNpcStats; + boost::shared_ptr mMovement; boost::shared_ptr > mContainerStore; @@ -102,6 +104,11 @@ namespace MWWorld return mNpcStats; } + boost::shared_ptr& getMovement() + { + return mMovement; + } + boost::shared_ptr >& getContainerStore() { return mContainerStore; diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 44bfa9112..66f866b07 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -21,6 +21,8 @@ #include "refdata.hpp" #include "globals.hpp" +#include "doingphysics.hpp" +#include "cellfunctors.hpp" namespace { @@ -270,6 +272,15 @@ namespace MWWorld void World::unloadCell (CellRenderCollection::iterator iter) { + ListHandles functor; + iter->first->forEach(functor); + + { // silence annoying g++ warning + for (std::vector::const_iterator iter (functor.mHandles.begin()); + iter!=functor.mHandles.end(); ++iter) + mScene.removeObject (*iter); + } + removeScripts (iter->first); mEnvironment.mMechanicsManager->dropActors (iter->first); iter->second->destroy(); @@ -294,9 +305,12 @@ namespace MWWorld } } - void World::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position) + void World::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position, + bool adjustPlayerPos) { - mPlayer->setPos (position.pos[0], position.pos[1], position.pos[2], true); + if (adjustPlayerPos) + mPlayer->setPos (position.pos[0], position.pos[1], position.pos[2], true); + mPlayer->setCell (cell); // TODO orientation @@ -315,11 +329,91 @@ namespace MWWorld } } - World::World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir, - const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment) - : mSkyManager (0), mScene (renderer), mPlayer (0), mCurrentCell (0), mGlobalVariables (0), + void World::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) + { + SuppressDoingPhysics scopeGuard; + + // remove active + mEnvironment.mMechanicsManager->removeActor (mPlayer->getPlayer()); + + CellRenderCollection::iterator active = mActiveCells.begin(); + + while (active!=mActiveCells.end()) + { + if (!(active->first->cell->data.flags & ESM::Cell::Interior)) + { + if (std::abs (X-active->first->cell->data.gridX)<=1 && + std::abs (Y-active->first->cell->data.gridY)<=1) + { + // keep cells within the new 3x3 grid + ++active; + continue; + } + } + + unloadCell (active++); + } + + // Load cells + for (int x=X-1; x<=X+1; ++x) + for (int y=Y-1; y<=Y+1; ++y) + { + CellRenderCollection::iterator iter = mActiveCells.begin(); + + while (iter!=mActiveCells.end()) + { + assert (!(iter->first->cell->data.flags & ESM::Cell::Interior)); + + if (x==iter->first->cell->data.gridX && + y==iter->first->cell->data.gridY) + break; + + ++iter; + } + + if (iter==mActiveCells.end()) + { + mExteriors[std::make_pair (x, y)].loadExt (x, y, mStore, mEsm); + Ptr::CellStore *cell = &mExteriors[std::make_pair (x, y)]; + + loadCell (cell, new MWRender::ExteriorCellRender (*cell, mEnvironment, mScene)); + } + } + + // find current cell + CellRenderCollection::iterator iter = mActiveCells.begin(); + + while (iter!=mActiveCells.end()) + { + assert (!(iter->first->cell->data.flags & ESM::Cell::Interior)); + + if (X==iter->first->cell->data.gridX && + Y==iter->first->cell->data.gridY) + break; + + ++iter; + } + + assert (iter!=mActiveCells.end()); + + mCurrentCell = iter->first; + + // adjust player + playerCellChange (&mExteriors[std::make_pair (X, Y)], position, adjustPlayerPos); + + // Sky system + adjustSky(); + + mCellChanged = true; + } + + World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir, + const std::string& master, const boost::filesystem::path& resDir, + bool newGame, Environment& environment) + : mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0), mSky (false), mCellChanged (false), mEnvironment (environment) { + mPhysEngine = physEng; boost::filesystem::path masterPath (dataDir); masterPath /= master; @@ -330,6 +424,7 @@ namespace MWWorld mStore.load (mEsm); mPlayer = new MWWorld::Player (mScene.getPlayer(), mStore.npcs.find ("player"), *this); + mScene.addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); // global variables mGlobalVariables = new Globals (mStore); @@ -342,6 +437,8 @@ namespace MWWorld mSkyManager = MWRender::SkyManager::create(renderer.getWindow(), mScene.getCamera(), resDir); + + mPhysEngine = new OEngine::Physic::PhysicEngine(); } World::~World() @@ -357,6 +454,8 @@ namespace MWWorld delete mPlayer; delete mSkyManager; delete mGlobalVariables; + + delete mPhysEngine; } MWWorld::Player& World::getPlayer() @@ -417,7 +516,8 @@ namespace MWWorld Ptr World::getPtrViaHandle (const std::string& handle) { - // TODO player + if (mPlayer->getPlayer().getRefData().getHandle()==handle) + return mPlayer->getPlayer(); for (CellRenderCollection::iterator iter (mActiveCells.begin()); iter!=mActiveCells.end(); ++iter) @@ -586,8 +686,10 @@ namespace MWWorld return mGlobalVariables->getInt ("timescale"); } - void World::changeCell (const std::string& cellName, const ESM::Position& position) + void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + SuppressDoingPhysics scopeGuard; + // remove active CellRenderCollection::iterator active = mActiveCells.begin(); @@ -613,89 +715,14 @@ namespace MWWorld //currentRegion->name = ""; } - void World::changeCell (int X, int Y, const ESM::Position& position) - { - // remove active - CellRenderCollection::iterator active = mActiveCells.begin(); - - while (active!=mActiveCells.end()) - { - if (!(active->first->cell->data.flags & ESM::Cell::Interior)) - { - if (std::abs (X-active->first->cell->data.gridX)<=1 && - std::abs (Y-active->first->cell->data.gridY)<=1) - { - // keep cells within the new 3x3 grid - ++active; - continue; - } - } - - unloadCell (active++); - } - - // Load cells - for (int x=X-1; x<=X+1; ++x) - for (int y=Y-1; y<=Y+1; ++y) - { - CellRenderCollection::iterator iter = mActiveCells.begin(); - - while (iter!=mActiveCells.end()) - { - assert (!(iter->first->cell->data.flags & ESM::Cell::Interior)); - - if (x==iter->first->cell->data.gridX && - y==iter->first->cell->data.gridY) - break; - - ++iter; - } - - if (iter==mActiveCells.end()) - { - mExteriors[std::make_pair (x, y)].loadExt (x, y, mStore, mEsm); - Ptr::CellStore *cell = &mExteriors[std::make_pair (x, y)]; - - loadCell (cell, new MWRender::ExteriorCellRender (*cell, mEnvironment, mScene)); - } - } - - // find current cell - CellRenderCollection::iterator iter = mActiveCells.begin(); - - while (iter!=mActiveCells.end()) - { - assert (!(iter->first->cell->data.flags & ESM::Cell::Interior)); - - if (X==iter->first->cell->data.gridX && - Y==iter->first->cell->data.gridY) - break; - - ++iter; - } - - assert (iter!=mActiveCells.end()); - - mCurrentCell = iter->first; - - // adjust player - playerCellChange (&mExteriors[std::make_pair (X, Y)], position); - - // Sky system - adjustSky(); - - mCellChanged = true; - } - - - void World::changeToExteriorCell (const ESM::Position& position) + void World::changeToExteriorCell (const ESM::Position& position) { int x = 0; int y = 0; positionToIndex (position.pos[0], position.pos[1], x, y); - changeCell (x, y, position); + changeCell (x, y, position, true); } const ESM::Cell *World::getExterior (const std::string& cellName) const @@ -721,6 +748,7 @@ namespace MWWorld return 0; } + void World::markCellAsUnchanged() { mCellChanged = false; @@ -745,14 +773,17 @@ namespace MWWorld if (MWRender::CellRender *render = searchRender (ptr.getCell())) { - render->deleteObject (ptr.getRefData().getHandle()); - ptr.getRefData().setHandle (""); - if (mActiveCells.find (ptr.getCell())!=mActiveCells.end()) { Class::get (ptr).disable (ptr, mEnvironment); mEnvironment.mSoundManager->stopSound3D (ptr); + + if (!DoingPhysics::isDoingPhysics()) + mScene.removeObject (ptr.getRefData().getHandle()); } + + render->deleteObject (ptr.getRefData().getHandle()); + ptr.getRefData().setHandle (""); } } } @@ -777,12 +808,16 @@ namespace MWWorld if (mCurrentCell->cell->data.gridX!=cellX || mCurrentCell->cell->data.gridY!=cellY) { - changeCell (cellX, cellY, mPlayer->getPlayer().getCellRef().pos); + changeCell (cellX, cellY, mPlayer->getPlayer().getCellRef().pos, false); } + } } } + mScene.moveObject (ptr.getRefData().getHandle(), Ogre::Vector3 (x, y, z), + !DoingPhysics::isDoingPhysics()); + // TODO cell change for non-player ref } @@ -814,4 +849,15 @@ namespace MWWorld if (y<0) --cellY; } + + void World::doPhysics (const std::vector >& actors, + float duration) + { + mScene.doPhysics (duration, *this, actors); + } + + void World::toggleCollisionMode() + { + mScene.toggleCollisionMode(); + } } diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index ce66a63d7..5cd99d9cd 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -14,6 +14,13 @@ #include "ptr.hpp" #include "globals.hpp" +#include + +namespace Ogre +{ + class Vector3; +} + namespace ESM { struct Position; @@ -63,6 +70,8 @@ namespace MWWorld bool mCellChanged; Environment& mEnvironment; + OEngine::Physic::PhysicEngine* mPhysEngine; + // not implemented World (const World&); World& operator= (const World&); @@ -83,14 +92,19 @@ namespace MWWorld void loadCell (Ptr::CellStore *cell, MWRender::CellRender *render); - void playerCellChange (Ptr::CellStore *cell, const ESM::Position& position); + void playerCellChange (Ptr::CellStore *cell, const ESM::Position& position, + bool adjustPlayerPos = true); void adjustSky(); + void changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos); + ///< Move from exterior to interior or from interior cell to a different + /// interior cell. public: - World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir, - const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment); + World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir, + const std::string& master, const boost::filesystem::path& resDir, bool newGame, + Environment& environment); ~World(); @@ -138,12 +152,11 @@ namespace MWWorld float getTimeScaleFactor() const; - void changeCell (const std::string& cellName, const ESM::Position& position); - ///< works only for interior cells currently. - - void changeCell (int X, int Y, const ESM::Position& position); + void changeToInteriorCell (const std::string& cellName, const ESM::Position& position); + ///< Move to interior cell. void changeToExteriorCell (const ESM::Position& position); + ///< Move to exterior cell. const ESM::Cell *getExterior (const std::string& cellName) const; ///< Return a cell matching the given name or a 0-pointer, if there is no such cell. @@ -162,6 +175,14 @@ namespace MWWorld void positionToIndex (float x, float y, int &cellX, int &cellY) const; ///< Convert position to cell numbers + + void doPhysics (const std::vector >& actors, + float duration); + ///< Run physics simulation and modify \a world accordingly. + + void toggleCollisionMode(); + ///< Toggle collision mode for player. If disabled player object should ignore + /// collisions and gravity. }; } diff --git a/cmake/FindBullet.cmake b/cmake/FindBullet.cmake new file mode 100644 index 000000000..7bdf75a0a --- /dev/null +++ b/cmake/FindBullet.cmake @@ -0,0 +1,76 @@ +# - Try to find the Bullet physics engine +# +# This module defines the following variables +# +# BULLET_FOUND - Was bullet found +# BULLET_INCLUDE_DIRS - the Bullet include directories +# BULLET_LIBRARIES - Link to this, by default it includes +# all bullet components (Dynamics, +# Collision, LinearMath, & SoftBody) +# +# This module accepts the following variables +# +# BULLET_ROOT - Can be set to bullet install path or Windows build path +# + +# Copyright (c) 2009, Philip Lowman +# +# Redistribution AND use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +macro(_FIND_BULLET_LIBRARY _var) + find_library(${_var} + NAMES + ${ARGN} + PATHS + ${BULLET_ROOT} + ${BULLET_ROOT}/out/release8/libs + ${BULLET_ROOT}/out/debug8/libs + PATH_SUFFIXES lib + ) + mark_as_advanced(${_var}) +endmacro() + +macro(_BULLET_APPEND_LIBRARIES _list _release) + set(_debug ${_release}_DEBUG) + if(${_debug}) + set(${_list} ${${_list}} optimized ${${_release}} debug ${${_debug}}) + else() + set(${_list} ${${_list}} ${${_release}}) + endif() +endmacro() + +find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h + PATHS + ${BULLET_ROOT}/include + ${BULLET_ROOT}/src + PATH_SUFFIXES bullet +) + +# Find the libraries + +_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics) +_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_d) +_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision) +_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_d) +_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY LinearMath BulletMath) +_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG LinearMath_d BulletMath_d) +_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody) +_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_d) + + +# handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Bullet DEFAULT_MSG + BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY + BULLET_SOFTBODY_LIBRARY BULLET_INCLUDE_DIR) + +set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR}) +if(BULLET_FOUND) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_SOFTBODY_LIBRARY) +endif() diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index 6acd81dce..43860dff3 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -133,7 +133,48 @@ namespace ESMS loadRefs(store, esm); } + /// Call functor (ref) for each reference. functor must return a bool. Returning + /// false will abort the iteration. + /// \return Iteration completed? + template + bool forEach (Functor& functor) + { + return + forEachImp (functor, activators) && + forEachImp (functor, potions) && + forEachImp (functor, appas) && + forEachImp (functor, armors) && + forEachImp (functor, books) && + forEachImp (functor, clothes) && + forEachImp (functor, containers) && + forEachImp (functor, creatures) && + forEachImp (functor, doors) && + forEachImp (functor, ingreds) && + forEachImp (functor, creatureLists) && + forEachImp (functor, itemLists) && + forEachImp (functor, lights) && + forEachImp (functor, lockpicks) && + forEachImp (functor, miscItems) && + forEachImp (functor, npcs) && + forEachImp (functor, probes) && + forEachImp (functor, repairs) && + forEachImp (functor, statics) && + forEachImp (functor, weapons); + } + private: + + template + bool forEachImp (Functor& functor, List& list) + { + for (typename List::List::iterator iter (list.list.begin()); iter!=list.list.end(); + ++iter) + if (!functor (iter->ref, iter->mData)) + return false; + + return true; + } + void loadRefs(const ESMStore &store, ESMReader &esm) { assert (cell); diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index fb1b988a7..6a4eb337c 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -83,7 +83,6 @@ void NIFFile::parse() // NiNodes if(rec == "NiNode" || rec == "AvoidNode" || - rec == "RootCollisionNode" || rec == "NiBSParticleNode" || rec == "NiBSAnimationNode" || rec == "NiBillboardNode") { r = new NiNode; r->recType = RC_NiNode; } @@ -93,6 +92,8 @@ void NIFFile::parse() else if(rec == "NiRotatingParticles") { r = new NiRotatingParticles; r->recType = RC_NiRotatingParticles; } else if(rec == "NiAutoNormalParticles") { r = new NiAutoNormalParticles; r->recType = RC_NiAutoNormalParticles; } else if(rec == "NiCamera") { r = new NiCamera; r->recType = RC_NiCamera; } + else if(rec == "RootCollisionNode"){ r = new NiNode; r->recType = RC_RootCollisionNode; }// a root collision node is exactly like a node + //that's why there is no need to create a new type // Properties else if(rec == "NiTexturingProperty") { r = new NiTexturingProperty; r->recType = RC_NiTexturingProperty; } diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 9be3faf8d..8b8910118 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -79,7 +79,8 @@ enum RecordType RC_NiAutoNormalParticlesData, RC_NiSequenceStreamHelper, RC_NiSourceTexture, - RC_NiSkinInstance + RC_NiSkinInstance, + RC_RootCollisionNode }; /// Base class for all records diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp new file mode 100644 index 000000000..6a958e0e7 --- /dev/null +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -0,0 +1,400 @@ +/* +OpenMW - The completely unofficial reimplementation of Morrowind +Copyright (C) 2008-2010 Nicolay Korslund +Email: < korslund@gmail.com > +WWW: http://openmw.sourceforge.net/ + +This file (ogre_nif_loader.cpp) is part of the OpenMW package. + +OpenMW is distributed as free software: you can redistribute it +and/or modify it under the terms of the GNU General Public License +version 3, as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +version 3 along with this program. If not, see +http://www.gnu.org/licenses/ . + +*/ + +#include "bullet_nif_loader.hpp" +#include +#include + +#include +#include "../nif/nif_file.hpp" +#include "../nif/node.hpp" +#include "../nif/data.hpp" +#include "../nif/property.hpp" +#include "../nif/controller.hpp" +#include "../nif/extra.hpp" +#include + +#include +#include +// For warning messages +#include + +// float infinity +#include + +typedef unsigned char ubyte; + +using namespace std; +using namespace Ogre; +using namespace Nif; +using namespace Mangle::VFS; + + +BulletShape::BulletShape(Ogre::ResourceManager* creator, const Ogre::String &name, + Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual, + Ogre::ManualResourceLoader *loader) : +Ogre::Resource(creator, name, handle, group, isManual, loader) +{ + /* If you were storing a pointer to an object, then you would set that pointer to NULL here. + */ + + /* For consistency with StringInterface, but we don't add any parameters here + That's because the Resource implementation of StringInterface is to + list all the options that need to be set before loading, of which + we have none as such. Full details can be set through scripts. + */ + Shape = NULL; + collide = true; + createParamDictionary("BulletShape"); +} + +BulletShape::~BulletShape() +{ +} + +// farm out to BulletShapeLoader +void BulletShape::loadImpl() +{ + mLoader->loadResource(this); +} + +void BulletShape::deleteShape(btCollisionShape* mShape) +{ + if(mShape!=NULL) + { + if(mShape->isCompound()) + { + btCompoundShape* ms = static_cast(Shape); + int a = ms->getNumChildShapes(); + for(int i=0; i getChildShape(i)); + } + } + delete mShape; + } + mShape = NULL; +} + +void BulletShape::unloadImpl() +{ + deleteShape(Shape); +} + +//TODO:change this? +size_t BulletShape::calculateSize() const +{ + return 1; +} + + +//============================================================================================================= +template<> BulletShapeManager *Ogre::Singleton::ms_Singleton = 0; + +BulletShapeManager *BulletShapeManager::getSingletonPtr() +{ + return ms_Singleton; +} + +BulletShapeManager &BulletShapeManager::getSingleton() +{ + assert(ms_Singleton); + return(*ms_Singleton); +} + +BulletShapeManager::BulletShapeManager() +{ + mResourceType = "BulletShape"; + + // low, because it will likely reference other resources + mLoadOrder = 30.0f; + + // this is how we register the ResourceManager with OGRE + Ogre::ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this); +} + +BulletShapeManager::~BulletShapeManager() +{ + // and this is how we unregister it + Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType); +} + +BulletShapePtr BulletShapeManager::load(const Ogre::String &name, const Ogre::String &group) +{ + BulletShapePtr textf = getByName(name); + + if (textf.isNull()) + textf = create(name, group); + + textf->load(); + return textf; +} + +Ogre::Resource *BulletShapeManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle, + const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, + const Ogre::NameValuePairList *createParams) +{ + BulletShape* res = new BulletShape(this, name, handle, group, isManual, loader); + //if(isManual) + //{ + //loader->loadResource(res); + //} + return res; +} + +//==================================================================================================== +Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(Nif::Transformation* tr) +{ + Ogre::Matrix3 rot(tr->rotation.v[0].array[0],tr->rotation.v[0].array[1],tr->rotation.v[0].array[2], + tr->rotation.v[1].array[0],tr->rotation.v[1].array[1],tr->rotation.v[1].array[2], + tr->rotation.v[2].array[0],tr->rotation.v[2].array[1],tr->rotation.v[2].array[2]); + return rot; +} +Ogre::Vector3 ManualBulletShapeLoader::getVector(Nif::Transformation* tr) +{ + Ogre::Vector3 vect3(tr->pos.array[0],tr->pos.array[1],tr->pos.array[2]); + return vect3; +} + +btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 m) +{ + Ogre::Quaternion oquat(m); + btQuaternion quat; + quat.setW(oquat.w); + quat.setX(oquat.x); + quat.setY(oquat.y); + quat.setZ(oquat.z); + return quat; +} + +btVector3 ManualBulletShapeLoader::getbtVector(Nif::Vector v) +{ + btVector3 a(v.array[0],v.array[1],v.array[2]); + return a; +} + +void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) +{ + cShape = static_cast(resource); + resourceName = cShape->getName(); + cShape->collide = false; + + currentShape = new btCompoundShape(); + cShape->Shape = currentShape; + + if (!vfs) vfs = new OgreVFS(resourceGroup); + + if (!vfs->isFile(resourceName)) + { + warn("File not found."); + return; + } + + // 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. + Nif::NIFFile nif(vfs->open(resourceName), resourceName); + + if (nif.numRecords() < 1) + { + warn("Found no records in NIF."); + return; + } + + + // The first record is assumed to be the root node + Nif::Record *r = nif.getRecord(0); + assert(r != NULL); + + Nif::Node *node = dynamic_cast(r); + + if (node == NULL) + { + warn("First record in file was not a node, but a " + + r->recName.toString() + ". Skipping file."); + return; + } + + //do a first pass + handleNode(node,0,Ogre::Matrix3::IDENTITY,Ogre::Vector3::ZERO,1,false,false); + + //if collide = false, then it does a second pass which create a shape for raycasting. + if(cShape->collide == false) + { + handleNode(node,0,Ogre::Matrix3::IDENTITY,Ogre::Vector3::ZERO,1,false,true); + } +} + +void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, + Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale,bool isCollisionNode,bool raycastingOnly) +{ + // 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 + Nif::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 == Nif::RC_NiStringExtraData) + { + // String markers may contain important information + // affecting the entire subtree of this node + Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; + + if (sd->string == "NCO" && !raycastingOnly) + { + // No collision. Use an internal flag setting to mark this. + // We ignor this node! + flags |= 0x800; + return; + } + else if (sd->string == "MRK" && !raycastingOnly) + // 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; + } + } + + //transfo of parents node + curent node + Ogre::Matrix3 finalRot; + Ogre::Vector3 finalPos; + float finalScale; + + Nif::Transformation &final = *((Nif::Transformation*)node->trafo); + Ogre::Vector3 nodePos = getVector(&final); + Ogre::Matrix3 nodeRot = getMatrix(&final); + + finalPos = nodePos + parentPos; + finalRot = parentRot*nodeRot; + finalScale = final.scale*parentScale; + + + // For NiNodes, loop through children + if (node->recType == Nif::RC_NiNode) + { + Nif::NodeList &list = ((Nif::NiNode*)node)->children; + int n = list.length(); + for (int i=0; irecType == Nif::RC_NiTriShape && isCollisionNode) + { + cShape->collide = true; + handleNiTriShape(dynamic_cast(node), flags,finalRot,finalPos,finalScale,raycastingOnly); + } + else if(node->recType == Nif::RC_RootCollisionNode) + { + Nif::NodeList &list = ((Nif::NiNode*)node)->children; + int n = list.length(); + for (int i=0; i