diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ef85e34d..e583f23d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,7 +74,6 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp - ${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ) set(OENGINE_GUI @@ -94,8 +93,6 @@ set(OENGINE_BULLET ${LIBDIR}/openengine/bullet/physic.hpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.h - ${LIBDIR}/openengine/bullet/pmove.cpp - ${LIBDIR}/openengine/bullet/pmove.h ${LIBDIR}/openengine/bullet/trace.cpp ${LIBDIR}/openengine/bullet/trace.h @@ -299,7 +296,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg "${OpenMW_BINARY_DIR}/openmw.cfg.install") -if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") +if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop "${OpenMW_BINARY_DIR}/openmw.desktop") endif() @@ -380,7 +377,7 @@ if(WIN32) "${OpenMW_SOURCE_DIR}/readme.txt" "${OpenMW_SOURCE_DIR}/GPL3.txt" "${OpenMW_SOURCE_DIR}/OFL.txt" - "${OpenMW_SOURCE_DIR}/Bitstream Vera License.txt" + "${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" "${OpenMW_SOURCE_DIR}/Daedric Font License.txt" "${OpenMW_BINARY_DIR}/launcher.qss" "${OpenMW_BINARY_DIR}/settings-default.cfg" @@ -391,7 +388,7 @@ if(WIN32) INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") SET(CPACK_GENERATOR "NSIS") - SET(CPACK_PACKAGE_NAME "OpenMW ${OPENMW_VERSION}") + SET(CPACK_PACKAGE_NAME "OpenMW") SET(CPACK_PACKAGE_VENDOR "OpenMW.org") SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) @@ -406,7 +403,7 @@ if(WIN32) SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt") SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") - SET(CPACK_NSIS_DISPLAY_NAME "OpenMW") + SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe") @@ -529,6 +526,8 @@ if (WIN32) set(WARNINGS "${WARNINGS} /wd${d}") endforeach(d) + set_target_properties(shiny PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS}) if (BUILD_LAUNCHER) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) @@ -657,10 +656,18 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) # Install binaries INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) - INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) + IF(BUILD_LAUNCHER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_LAUNCHER) + IF(BUILD_ESMTOOL) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_ESMTOOL) + IF(BUILD_MWINIIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_OPENCS) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) + ENDIF(BUILD_OPENCS) # Install icon and .desktop INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}") @@ -674,5 +681,7 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) # Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" ) - INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) + IF(BUILD_LAUNCHER) + INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" ) + ENDIF(BUILD_LAUNCHER) endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index d63aaeccb..b361577be 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -20,6 +20,7 @@ void CSMDoc::Document::load (const std::vector::const_i getData().loadFile (*end2, false); addOptionalGmsts(); + addOptionalGlobals(); } void CSMDoc::Document::addOptionalGmsts() @@ -139,6 +140,26 @@ void CSMDoc::Document::addOptionalGmsts() } } +void CSMDoc::Document::addOptionalGlobals() +{ + static const char *sGlobals[] = + { + "dayspassed", + "pcwerewolf", + "pcyear", + 0 + }; + + for (int i=0; sGlobals[i]; ++i) + { + ESM::Global global; + global.mId = sGlobals[i]; + global.mType = ESM::VT_Int; + global.mValue = 0; + addOptionalGlobal (global); + } +} + void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst) { if (getData().getGmsts().searchId (gmst.mId)==-1) @@ -150,6 +171,17 @@ void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst) } } +void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global) +{ + if (getData().getGlobals().searchId (global.mId)==-1) + { + CSMWorld::Record record; + record.mBase = global; + record.mState = CSMWorld::RecordBase::State_BaseOnly; + getData().getGlobals().appendRecord (record); + } +} + void CSMDoc::Document::createBase() { static const char *sGlobals[] = diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 413e840d3..a7b198689 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -20,6 +20,7 @@ class QAbstractItemModel; namespace ESM { struct GameSetting; + struct Global; } namespace CSMDoc @@ -53,8 +54,12 @@ namespace CSMDoc void addOptionalGmsts(); + void addOptionalGlobals(); + void addOptionalGmst (const ESM::GameSetting& gmst); + void addOptionalGlobal (const ESM::Global& global); + public: Document (const std::vector& files, bool new_); diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 5a1d21ae4..9b69dfb88 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -171,7 +171,7 @@ namespace CSMWorld record2.mModified = record; mRecords.push_back (record2); - mIndex.insert (std::make_pair (id, mRecords.size()-1)); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1)); } else { @@ -306,7 +306,7 @@ namespace CSMWorld void IdCollection::appendRecord (const RecordBase& record) { mRecords.push_back (dynamic_cast&> (record)); - mIndex.insert (std::make_pair (getId (record), mRecords.size()-1)); + mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (getId (record)), mRecords.size()-1)); } template diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp index 58cef1343..7ee4b8726 100644 --- a/apps/opencs/view/doc/operations.cpp +++ b/apps/opencs/view/doc/operations.cpp @@ -16,7 +16,7 @@ CSVDoc::Operations::Operations() widgetContainer->setLayout (mLayout); setWidget (widgetContainer); - + setVisible (false); setFixedHeight (widgetContainer->height()); setTitleBarWidget (new QWidget (this)); } @@ -42,6 +42,8 @@ void CSVDoc::Operations::setProgress (int current, int max, int type, int thread if ( oldCount > 0) setFixedHeight (height()/oldCount * newCount); + + setVisible (true); } void CSVDoc::Operations::quitOperation (int type) @@ -59,6 +61,8 @@ void CSVDoc::Operations::quitOperation (int type) if (oldCount > 1) setFixedHeight (height() / oldCount * newCount); + else + setVisible (false); break; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index b4d137be2..fea121aa0 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -242,7 +242,6 @@ void CSVDoc::View::addGmstsSubView() void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); - mOperations->quitOperation (type); updateActions(); } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 3dd5ed45d..7cfeb84c5 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -14,8 +14,8 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player animation npcanimation creatureanimation actors objects - renderinginterface localmap occlusionquery terrain terrainmaterial water shadows + renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation + actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows compositors characterpreview externalrendering globalmap videoplayer ) @@ -26,11 +26,11 @@ add_openmw_dir (mwinput add_openmw_dir (mwgui text_input widgets race class birth review windowmanagerimp console dialogue dialogue_history window_base stats_window messagebox journalwindow charactercreation - map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list + map_window window_pinnable_base tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog - enchantingdialog trainingwindow travelwindow imagebutton exposedwindow + enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor ) add_openmw_dir (mwdialogue @@ -62,8 +62,9 @@ add_openmw_dir (mwclass ) add_openmw_dir (mwmechanics - mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells - activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate + mechanicsmanagerimp stat character creaturestats magiceffects movement actors activators + drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow + aiescort aiactivate ) add_openmw_dir (mwbase diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 48206ba23..d14696cdd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -17,7 +18,6 @@ #include "mwinput/inputmanagerimp.hpp" #include "mwgui/windowmanagerimp.hpp" -#include "mwgui/cursorreplace.hpp" #include "mwscript/scriptmanagerimp.hpp" #include "mwscript/extensions.hpp" @@ -66,14 +66,15 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) { try { - mEnvironment.setFrameDuration (evt.timeSinceLastFrame); + float frametime = std::min(evt.timeSinceLastFrame, 0.2f); + mEnvironment.setFrameDuration(frametime); // update input - MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame, false); + MWBase::Environment::get().getInputManager()->update(frametime, false); // sound if (mUseSound) - MWBase::Environment::get().getSoundManager()->update (evt.timeSinceLastFrame); + MWBase::Environment::get().getSoundManager()->update(frametime); // global scripts MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); @@ -87,23 +88,19 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // passing of time if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->advanceTime ( - mEnvironment.getFrameDuration()*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); + MWBase::Environment::get().getWorld()->advanceTime( + frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); if (changed) // keep change flag for another frame, if cell changed happend in local script MWBase::Environment::get().getWorld()->markCellAsUnchanged(); // update actors - std::vector > movement; - MWBase::Environment::get().getMechanicsManager()->update (movement, mEnvironment.getFrameDuration(), + MWBase::Environment::get().getMechanicsManager()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); - if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) - MWBase::Environment::get().getWorld()->doPhysics (movement, mEnvironment.getFrameDuration()); - // update world - MWBase::Environment::get().getWorld()->update (evt.timeSinceLastFrame, MWBase::Environment::get().getWindowManager()->isGuiMode()); + MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); // update GUI Ogre::RenderWindow* window = mOgre->getWindow(); @@ -111,7 +108,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch); MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); - MWBase::Environment::get().getWindowManager()->onFrame(evt.timeSinceLastFrame); + MWBase::Environment::get().getWindowManager()->onFrame(frametime); } catch (const std::exception& e) { @@ -335,9 +332,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) loadBSA(); - // cursor replacer (converts the cursor from the bsa so they can be used by mygui) - MWGui::CursorReplace replacer; - // Create the world mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index a1023c986..b8733259f 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -37,24 +37,24 @@ namespace MWBase virtual ~MechanicsManager() {} - virtual void addActor (const MWWorld::Ptr& ptr) = 0; - ///< Register an actor for stats management - /// - /// \note Dead actors are ignored. + virtual void add (const MWWorld::Ptr& ptr) = 0; + ///< Register an object for management - virtual void removeActor (const MWWorld::Ptr& ptr) = 0; - ///< Deregister an actor for stats management + virtual void remove (const MWWorld::Ptr& ptr) = 0; + ///< Deregister an object for management - virtual void dropActors (const MWWorld::CellStore *cellStore) = 0; - ///< Deregister all actors in the given cell. + virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) = 0; + ///< Moves an object to a new cell + + virtual void drop (const MWWorld::CellStore *cellStore) = 0; + ///< Deregister all objects in the given cell. virtual void watchActor (const MWWorld::Ptr& ptr) = 0; ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - virtual void update (std::vector >& movement, - float duration, bool paused) = 0; - ///< Update actor stats and store desired velocity vectors in \a movement + virtual void update (float duration, bool paused) = 0; + ///< Update objects /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). @@ -98,6 +98,17 @@ namespace MWBase virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; ///< Perform a persuasion action on NPC + + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; + ///< Run animation for a MW-reference. Calls to this function for references that are currently not + /// in the scene should be ignored. + /// + /// \param mode 0 normal, 1 immediate start, 2 immediate loop + /// \param count How many times the animation should be run + + virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; + ///< Skip the animation for the given MW-reference for one frame. Calls to this function for + /// references that are currently not in the scene should be ignored. }; } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4f7435fad..81d7cb9fe 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -198,7 +198,7 @@ namespace MWBase virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons) = 0; + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector()) = 0; virtual void enterPressed () = 0; virtual int readPressedButton() = 0; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index a1b884411..eef844c76 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -6,6 +6,7 @@ #include #include "../mwworld/globals.hpp" +#include "../mwworld/ptr.hpp" namespace Ogre { @@ -19,6 +20,11 @@ namespace OEngine { class Fader; } + + namespace Physic + { + class PhysicEngine; + } } namespace ESM @@ -35,6 +41,7 @@ namespace ESM namespace MWRender { class ExternalRendering; + class Animation; } namespace MWWorld @@ -42,10 +49,11 @@ namespace MWWorld class CellStore; class Player; class LocalScripts; - class Ptr; class TimeStamp; class ESMStore; class RefData; + + typedef std::vector > PtrMovementList; } namespace MWBase @@ -227,8 +235,7 @@ namespace MWBase virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0; ///< Convert position to cell numbers - virtual void doPhysics (const std::vector >& actors, - float duration) = 0; + virtual void doPhysics (const MWWorld::PtrMovementList &actors, float duration) = 0; ///< Run physics simulation and modify \a world accordingly. virtual bool toggleCollisionMode() = 0; @@ -263,18 +270,6 @@ namespace MWBase ///< Create a new recrod (of type npc) in the ESM store. /// \return pointer to created record - virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, - int mode, int number = 1) = 0; - ///< Run animation for a MW-reference. Calls to this function for references that are - /// currently not in the rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run - - virtual void skipAnimation (const MWWorld::Ptr& ptr) = 0; - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the rendered scene should be ignored. - virtual void update (float duration, bool paused) = 0; virtual bool placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY) = 0; @@ -291,8 +286,10 @@ namespace MWBase virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; - virtual bool isSwimming(const MWWorld::Ptr &object) = 0; - virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; + virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; + virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; + virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; + virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual void togglePOV() = 0; virtual void togglePreviewMode(bool enable) = 0; @@ -311,6 +308,8 @@ namespace MWBase /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + /// \todo Probably shouldn't be here + virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; /// \todo this does not belong here virtual void playVideo(const std::string& name, bool allowSkipping) = 0; diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 26d286aa1..3a60d9c39 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -5,12 +5,13 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/physicssystem.hpp" -#include "../mwrender/objects.hpp" +#include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" @@ -21,9 +22,8 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - MWRender::Objects& objects = renderingInterface.getObjects(); - objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); - objects.insertMesh(ptr, model); + MWRender::Actors& actors = renderingInterface.getActors(); + actors.insertActivator(ptr); } } @@ -32,6 +32,7 @@ namespace MWClass const std::string model = getModel(ptr); if(!model.empty()) physics.addObject(ptr); + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Activator::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index edf5dd25d..90dc70715 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -18,6 +18,7 @@ #include "../mwworld/physicssystem.hpp" #include "../mwrender/renderinginterface.hpp" +#include "../mwrender/actors.hpp" #include "../mwgui/tooltips.hpp" @@ -96,7 +97,7 @@ namespace MWClass const std::string model = getModel(ptr); if(!model.empty()) physics.addActor(ptr); - MWBase::Environment::get().getMechanicsManager()->addActor (ptr); + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Creature::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index a158fa743..a96c18a8c 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -1,9 +1,7 @@ #ifndef GAME_MWCLASS_CREATURE_H #define GAME_MWCLASS_CREATURE_H -#include "../mwrender/renderinginterface.hpp" -#include "../mwrender/actors.hpp" - +#include "../mwworld/class.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 235e57d37..200f6e2d4 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -36,14 +36,9 @@ namespace MWClass objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); if (!model.empty()) - objects.insertMesh(ptr, "meshes\\" + model); - - const int color = ref->mBase->mData.mColor; - const float r = ((color >> 0) & 0xFF) / 255.0f; - const float g = ((color >> 8) & 0xFF) / 255.0f; - const float b = ((color >> 16) & 0xFF) / 255.0f; - const float radius = float (ref->mBase->mData.mRadius); - objects.insertLight (ptr, r, g, b, radius); + objects.insertMesh(ptr, "meshes\\" + model, true); + else + objects.insertLight(ptr); } void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index cfbc64b87..c8c61e118 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -55,9 +55,35 @@ namespace MWClass { void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const { + static bool inited = false; + if(!inited) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + + fMinWalkSpeed = gmst.find("fMinWalkSpeed"); + fMaxWalkSpeed = gmst.find("fMaxWalkSpeed"); + fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect"); + fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier"); + fAthleticsRunBonus = gmst.find("fAthleticsRunBonus"); + fBaseRunMultiplier = gmst.find("fBaseRunMultiplier"); + fMinFlySpeed = gmst.find("fMinFlySpeed"); + fMaxFlySpeed = gmst.find("fMaxFlySpeed"); + fSwimRunBase = gmst.find("fSwimRunBase"); + fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); + fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase"); + fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier"); + fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase"); + fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); + fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); + // Added in Tribunal/Bloodmoon, may not exist + fWereWolfRunMult = gmst.search("fWereWolfRunMult"); + + inited = true; + } if (!ptr.getRefData().getCustomData()) { - std::auto_ptr data (new CustomData); + std::auto_ptr data(new CustomData); MWWorld::LiveCellRef *ref = ptr.get(); @@ -142,7 +168,7 @@ namespace MWClass void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const { physics.addActor(ptr); - MWBase::Environment::get().getMechanicsManager()->addActor(ptr); + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Npc::getModel(const MWWorld::Ptr &ptr) const @@ -297,9 +323,88 @@ namespace MWClass return false; } - float Npc::getSpeed (const MWWorld::Ptr& ptr) const + float Npc::getSpeed(const MWWorld::Ptr& ptr) const { - return getStance (ptr, Run) ? 600 : 300; // TODO calculate these values from stats + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects(); + + const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); + + float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified()* + (fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat()); + walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; + walkSpeed = std::max(0.0f, walkSpeed); + if(Npc::getStance(ptr, Sneak, false)) + walkSpeed *= fSneakSpeedMultiplier->getFloat(); + + float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * + fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); + if(npcdata->mNpcStats.isWerewolf()) + runSpeed *= fWereWolfRunMult->getFloat(); + + float moveSpeed; + if(normalizedEncumbrance >= 1.0f) + moveSpeed = 0.0f; + else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0) + { + float flySpeed = 0.01f*(npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified() + + mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude); + flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); + flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; + flySpeed = std::max(0.0f, flySpeed); + moveSpeed = flySpeed; + } + else if(world->isSwimming(ptr)) + { + float swimSpeed = walkSpeed; + if(Npc::getStance(ptr, Run, false)) + swimSpeed = runSpeed; + swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).mMagnitude; + swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* + fSwimRunAthleticsMult->getFloat(); + moveSpeed = swimSpeed; + } + else if(Npc::getStance(ptr, Run, false)) + moveSpeed = runSpeed; + else + moveSpeed = walkSpeed; + + if(getMovementSettings(ptr).mLeftRight != 0 && getMovementSettings(ptr).mForwardBackward == 0) + moveSpeed *= 0.75f; + + return moveSpeed; + } + + float Npc::getJump(const MWWorld::Ptr &ptr) const + { + const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects(); + const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + + fJumpEncumbranceMultiplier->getFloat() * + (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr)); + + float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified(); + float b = 0.0f; + if(a > 50.0f) + { + b = a - 50.0f; + a = 50.0f; + } + + float x = fJumpAcrobaticsBase->getFloat() + + std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); + x += 3 * b * fJumpAcroMultiplier->getFloat(); + x += mageffects.get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude * 64; + x *= encumbranceTerm; + + if(Npc::getStance(ptr, Run, false)) + x *= fJumpRunMultiplier->getFloat(); + x *= 1.25f;//fatigueTerm; + x -= -627.2/*gravity constant*/; + x /= 3; + + return x; } MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const @@ -311,14 +416,10 @@ namespace MWClass Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const { - Ogre::Vector3 vector (0, 0, 0); - - vector.x = getMovementSettings (ptr).mLeftRight * 127; - vector.y = getMovementSettings (ptr).mForwardBackward * 127; - vector.z = getMovementSettings(ptr).mUpDown * 127; - - //if (getStance (ptr, Run, false)) - // vector *= 2; + Ogre::Vector3 vector; + vector.x = getMovementSettings(ptr).mLeftRight; + vector.y = getMovementSettings(ptr).mForwardBackward; + vector.z = getMovementSettings(ptr).mUpDown; return vector; } @@ -420,4 +521,21 @@ namespace MWClass return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell); } + + const ESM::GameSetting *Npc::fMinWalkSpeed; + const ESM::GameSetting *Npc::fMaxWalkSpeed; + const ESM::GameSetting *Npc::fEncumberedMoveEffect; + const ESM::GameSetting *Npc::fSneakSpeedMultiplier; + const ESM::GameSetting *Npc::fAthleticsRunBonus; + const ESM::GameSetting *Npc::fBaseRunMultiplier; + const ESM::GameSetting *Npc::fMinFlySpeed; + const ESM::GameSetting *Npc::fMaxFlySpeed; + const ESM::GameSetting *Npc::fSwimRunBase; + const ESM::GameSetting *Npc::fSwimRunAthleticsMult; + const ESM::GameSetting *Npc::fJumpEncumbranceBase; + const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier; + const ESM::GameSetting *Npc::fJumpAcrobaticsBase; + const ESM::GameSetting *Npc::fJumpAcroMultiplier; + const ESM::GameSetting *Npc::fJumpRunMultiplier; + const ESM::GameSetting *Npc::fWereWolfRunMult; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 20c2da4b1..f41edb0df 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -3,6 +3,11 @@ #include "../mwworld/class.hpp" +namespace ESM +{ + class GameSetting; +} + namespace MWClass { class Npc : public MWWorld::Class @@ -12,6 +17,23 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + static const ESM::GameSetting *fMinWalkSpeed; + static const ESM::GameSetting *fMaxWalkSpeed; + static const ESM::GameSetting *fEncumberedMoveEffect; + static const ESM::GameSetting *fSneakSpeedMultiplier; + static const ESM::GameSetting *fAthleticsRunBonus; + static const ESM::GameSetting *fBaseRunMultiplier; + static const ESM::GameSetting *fMinFlySpeed; + static const ESM::GameSetting *fMaxFlySpeed; + static const ESM::GameSetting *fSwimRunBase; + static const ESM::GameSetting *fSwimRunAthleticsMult; + static const ESM::GameSetting *fJumpEncumbranceBase; + static const ESM::GameSetting *fJumpEncumbranceMultiplier; + static const ESM::GameSetting *fJumpAcrobaticsBase; + static const ESM::GameSetting *fJumpAcroMultiplier; + static const ESM::GameSetting *fJumpRunMultiplier; + static const ESM::GameSetting *fWereWolfRunMult; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; @@ -64,6 +86,9 @@ namespace MWClass virtual float getSpeed (const MWWorld::Ptr& ptr) const; ///< Return movement speed. + virtual float getJump(const MWWorld::Ptr &ptr) const; + ///< Return jump velocity (not accounting for movement) + virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index f548c46f7..35f0c9493 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -2,6 +2,7 @@ #include "dialoguemanagerimp.hpp" #include +#include #include #include @@ -251,8 +252,12 @@ namespace MWDialogue MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - if (const ESM::DialInfo *info = filter.search (dialogue, true)) + std::vector infos = filter.list (dialogue, true, true); + + if (!infos.empty()) { + const ESM::DialInfo* info = infos[std::rand() % infos.size()]; + parseText (info->mResponse); if (dialogue.mType==ESM::Dialogue::Persuasion) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 09bb0ddc4..10740794e 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -289,7 +289,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con case SelectWrapper::Function_PcGender: - return player.get()->mBase->mFlags & ESM::NPC::Female ? 0 : 1; + return player.get()->mBase->isMale() ? 0 : 1; case SelectWrapper::Function_PcClothingModifier: { @@ -559,8 +559,21 @@ MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedTo : mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer) {} -const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const +const ESM::DialInfo* MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const { + std::vector suitableInfos = list (dialogue, fallbackToInfoRefusal, false); + + if (suitableInfos.empty()) + return NULL; + else + return suitableInfos[0]; +} + +std::vector MWDialogue::Filter::list (const ESM::Dialogue& dialogue, + bool fallbackToInfoRefusal, bool searchAll) const +{ + std::vector infos; + bool infoRefusal = false; // Iterate over topic responses to find a matching one @@ -569,14 +582,17 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue, { if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) { - if (testDisposition (*iter)) - return &*iter; + if (testDisposition (*iter)) { + infos.push_back(&*iter); + if (!searchAll) + break; + } else infoRefusal = true; } } - if (infoRefusal && fallbackToInfoRefusal) + if (infos.empty() && infoRefusal && fallbackToInfoRefusal) { // No response is valid because of low NPC disposition, // search a response in the topic "Info Refusal" @@ -588,11 +604,14 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue, for (std::vector::const_iterator iter = infoRefusalDialogue.mInfo.begin(); iter!=infoRefusalDialogue.mInfo.end(); ++iter) - if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter)) - return &*iter; + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter)) { + infos.push_back(&*iter); + if (!searchAll) + break; + } } - return 0; + return infos; } bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 707c0154b..069bf6353 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWDIALOGUE_FILTER_H #define GAME_MWDIALOGUE_FILTER_H +#include + #include "../mwworld/ptr.hpp" namespace ESM @@ -51,7 +53,10 @@ namespace MWDialogue Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); - const ESM::DialInfo *search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; + std::vector list (const ESM::Dialogue& dialogue, + bool fallbackToInfoRefusal, bool searchAll) const; + + const ESM::DialInfo* search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; ///< Get a matching response for the requested dialogue. /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. diff --git a/apps/openmw/mwgui/birth.cpp b/apps/openmw/mwgui/birth.cpp index c53a68cf4..53e5c022d 100644 --- a/apps/openmw/mwgui/birth.cpp +++ b/apps/openmw/mwgui/birth.cpp @@ -25,7 +25,7 @@ bool sortBirthSigns(const std::pair& left, c } BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_birth.layout", parWindowManager) + : WindowModal("openmw_chargen_birth.layout", parWindowManager) { // Centre dialog center(); @@ -66,7 +66,7 @@ void BirthDialog::setNextButtonShow(bool shown) void BirthDialog::open() { - WindowBase::open(); + WindowModal::open(); updateBirths(); updateSpells(); } diff --git a/apps/openmw/mwgui/birth.hpp b/apps/openmw/mwgui/birth.hpp index f16f92325..ad1c0b40f 100644 --- a/apps/openmw/mwgui/birth.hpp +++ b/apps/openmw/mwgui/birth.hpp @@ -10,7 +10,7 @@ namespace MWGui { - class BirthDialog : public WindowBase + class BirthDialog : public WindowModal { public: BirthDialog(MWBase::WindowManager& parWindowManager); diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 2eed21a52..f3bac898b 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -21,7 +21,7 @@ using namespace MWGui; /* GenerateClassResultDialog */ GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager) + : WindowModal("openmw_chargen_generate_class_result.layout", parWindowManager) { // Centre dialog center(); @@ -68,7 +68,7 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender) /* PickClassDialog */ PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_class.layout", parWindowManager) + : WindowModal("openmw_chargen_class.layout", parWindowManager) { // Centre dialog center(); @@ -122,6 +122,7 @@ void PickClassDialog::setNextButtonShow(bool shown) void PickClassDialog::open() { + WindowModal::open (); updateClasses(); updateStats(); } @@ -276,7 +277,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin) } InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_infobox.layout", parWindowManager) + : WindowModal("openmw_infobox.layout", parWindowManager) , mCurrentButton(-1) { getWidget(mTextBox, "TextBox"); @@ -327,6 +328,7 @@ void InfoBoxDialog::setButtons(ButtonList &buttons) void InfoBoxDialog::open() { + WindowModal::open(); // Fix layout layoutVertically(mTextBox, 4); layoutVertically(mButtonBar, 6); @@ -373,7 +375,7 @@ ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager) /* CreateClassDialog */ CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_create_class.layout", parWindowManager) + : WindowModal("openmw_chargen_create_class.layout", parWindowManager) , mSpecDialog(nullptr) , mAttribDialog(nullptr) , mSkillDialog(nullptr) diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index c7699b308..2662d94cc 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -12,7 +12,7 @@ namespace MWGui { - class InfoBoxDialog : public WindowBase + class InfoBoxDialog : public WindowModal { public: InfoBoxDialog(MWBase::WindowManager& parWindowManager); @@ -63,7 +63,7 @@ namespace MWGui ClassChoiceDialog(MWBase::WindowManager& parWindowManager); }; - class GenerateClassResultDialog : public WindowBase + class GenerateClassResultDialog : public WindowModal { public: GenerateClassResultDialog(MWBase::WindowManager& parWindowManager); @@ -90,7 +90,7 @@ namespace MWGui std::string mCurrentClassId; }; - class PickClassDialog : public WindowBase + class PickClassDialog : public WindowModal { public: PickClassDialog(MWBase::WindowManager& parWindowManager); @@ -238,7 +238,7 @@ namespace MWGui MyGUI::EditPtr mTextEdit; }; - class CreateClassDialog : public WindowBase + class CreateClassDialog : public WindowModal { public: CreateClassDialog(MWBase::WindowManager& parWindowManager); diff --git a/apps/openmw/mwgui/cursor.cpp b/apps/openmw/mwgui/cursor.cpp new file mode 100644 index 000000000..15f61d5b6 --- /dev/null +++ b/apps/openmw/mwgui/cursor.cpp @@ -0,0 +1,130 @@ +#include "cursor.hpp" + +#include +#include +#include +#include +#include + +#include + + +namespace MWGui +{ + + + ResourceImageSetPointerFix::ResourceImageSetPointerFix() : + mImageSet(nullptr) + { + } + + ResourceImageSetPointerFix::~ResourceImageSetPointerFix() + { + } + + void ResourceImageSetPointerFix::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version) + { + Base::deserialization(_node, _version); + + MyGUI::xml::ElementEnumerator info = _node->getElementEnumerator(); + while (info.next("Property")) + { + const std::string& key = info->findAttribute("key"); + const std::string& value = info->findAttribute("value"); + + if (key == "Point") + mPoint = MyGUI::IntPoint::parse(value); + else if (key == "Size") + mSize = MyGUI::IntSize::parse(value); + else if (key == "Rotation") + mRotation = MyGUI::utility::parseInt(value); + else if (key == "Resource") + mImageSet = MyGUI::ResourceManager::getInstance().getByName(value)->castType(); + } + } + + int ResourceImageSetPointerFix::getRotation() + { + return mRotation; + } + + void ResourceImageSetPointerFix::setImage(MyGUI::ImageBox* _image) + { + if (mImageSet != nullptr) + _image->setItemResourceInfo(mImageSet->getIndexInfo(0, 0)); + } + + void ResourceImageSetPointerFix::setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point) + { + _image->setCoord(_point.left - mPoint.left, _point.top - mPoint.top, mSize.width, mSize.height); + } + + MyGUI::ResourceImageSetPtr ResourceImageSetPointerFix:: getImageSet() + { + return mImageSet; + } + + MyGUI::IntPoint ResourceImageSetPointerFix::getHotSpot() + { + return mPoint; + } + + MyGUI::IntSize ResourceImageSetPointerFix::getSize() + { + return mSize; + } + + // ---------------------------------------------------------------------------------------- + + Cursor::Cursor() + { + // hide mygui's pointer since we're rendering it ourselves (because mygui's pointer doesn't support rotation) + MyGUI::PointerManager::getInstance().setVisible(false); + + MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &Cursor::onCursorChange); + + mWidget = MyGUI::Gui::getInstance().createWidget("RotatingSkin",0,0,0,0,MyGUI::Align::Default,"Pointer",""); + + onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); + } + + Cursor::~Cursor() + { + } + + void Cursor::onCursorChange(const std::string &name) + { + ResourceImageSetPointerFix* imgSetPtr = dynamic_cast( + MyGUI::PointerManager::getInstance().getByName(name)); + assert(imgSetPtr != NULL); + + MyGUI::ResourceImageSet* imgSet = imgSetPtr->getImageSet(); + + std::string texture = imgSet->getIndexInfo(0,0).texture; + + mSize = imgSetPtr->getSize(); + mHotSpot = imgSetPtr->getHotSpot(); + + int rotation = imgSetPtr->getRotation(); + + mWidget->setImageTexture(texture); + MyGUI::ISubWidget* main = mWidget->getSubWidgetMain(); + MyGUI::RotatingSkin* rotatingSubskin = main->castType(); + rotatingSubskin->setCenter(MyGUI::IntPoint(mSize.width/2,mSize.height/2)); + rotatingSubskin->setAngle(Ogre::Degree(rotation).valueRadians()); + } + + void Cursor::update() + { + MyGUI::IntPoint position = MyGUI::InputManager::getInstance().getMousePosition(); + + mWidget->setPosition(position - mHotSpot); + mWidget->setSize(mSize); + } + + void Cursor::setVisible(bool visible) + { + mWidget->setVisible(visible); + } + +} diff --git a/apps/openmw/mwgui/cursor.hpp b/apps/openmw/mwgui/cursor.hpp new file mode 100644 index 000000000..3a4a05f4c --- /dev/null +++ b/apps/openmw/mwgui/cursor.hpp @@ -0,0 +1,62 @@ +#ifndef MWGUI_CURSOR_H +#define MWGUI_CURSOR_H + +#include +#include +#include + +namespace MWGui +{ + + /// \brief Allows us to get the members of + /// ResourceImageSetPointer that we need. + /// \example MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + /// MyGUI::ResourceManager::getInstance().load("core.xml"); + class ResourceImageSetPointerFix : + public MyGUI::IPointer + { + MYGUI_RTTI_DERIVED( ResourceImageSetPointerFix ) + + public: + ResourceImageSetPointerFix(); + virtual ~ResourceImageSetPointerFix(); + + virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version); + + virtual void setImage(MyGUI::ImageBox* _image); + virtual void setPosition(MyGUI::ImageBox* _image, const MyGUI::IntPoint& _point); + + //and now for the whole point of this class, allow us to get + //the hot spot, the image and the size of the cursor. + virtual MyGUI::ResourceImageSetPtr getImageSet(); + virtual MyGUI::IntPoint getHotSpot(); + virtual MyGUI::IntSize getSize(); + virtual int getRotation(); + + private: + MyGUI::IntPoint mPoint; + MyGUI::IntSize mSize; + MyGUI::ResourceImageSetPtr mImageSet; + int mRotation; // rotation in degrees + }; + + class Cursor + { + public: + Cursor(); + ~Cursor(); + void update (); + + void setVisible (bool visible); + + void onCursorChange (const std::string& name); + + private: + MyGUI::ImageBox* mWidget; + + MyGUI::IntSize mSize; + MyGUI::IntPoint mHotSpot; + }; +} + +#endif diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp deleted file mode 100644 index 2079538fc..000000000 --- a/apps/openmw/mwgui/cursorreplace.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "cursorreplace.hpp" - -#include -#include - -#include -#include - -using namespace MWGui; - -CursorReplace::CursorReplace() -{ - OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90); - OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45); - OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); -} diff --git a/apps/openmw/mwgui/cursorreplace.hpp b/apps/openmw/mwgui/cursorreplace.hpp deleted file mode 100644 index 06fe28e39..000000000 --- a/apps/openmw/mwgui/cursorreplace.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef GAME_CURSORREPLACE_H -#define GAME_CURSORREPLACE_H - -#include - -namespace MWGui -{ - /// \brief MyGUI does not support rotating cursors, so we have to do it manually - class CursorReplace - { - public: - CursorReplace(); - }; -} - -#endif diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index dd5289edb..e7c7acb53 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -106,7 +106,7 @@ namespace MWGui float progress = (float(mCurrentCellLoading)+refProgress) / float(mTotalCellsLoading); assert(progress <= 1 && progress >= 0); - mLoadingText->setCaption(stage + "... "); + mLoadingText->setCaption(stage); mProgressBar->setProgressPosition (static_cast(progress * 1000)); static float loadingScreenFps = 30.f; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 14309abc5..5402d3542 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -5,6 +5,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" namespace MWGui { @@ -65,6 +66,7 @@ namespace MWGui void MainMenu::onButtonClicked(MyGUI::Widget *sender) { + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); if (sender == mButtons["return"]) MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); else if (sender == mButtons["options"]) diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 4e2ee517e..52b108f85 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -13,6 +15,8 @@ #include "../mwrender/globalmap.hpp" +#include "widgets.hpp" + using namespace MWGui; LocalMapBase::LocalMapBase() @@ -88,7 +92,7 @@ void LocalMapBase::applyFogOfWar() + boost::lexical_cast(my); std::string image = mPrefix+"_"+ boost::lexical_cast(mCurX + (mx-1)) + "_" - + boost::lexical_cast(mCurY + (mInterior ? (my-1) : -1*(my-1))); + + boost::lexical_cast(mCurY + (-1*(my-1))); MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx]; fog->setImageTexture(mFogOfWar ? ((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog" @@ -96,6 +100,7 @@ void LocalMapBase::applyFogOfWar() : ""); } } + notifyMapChanged (); } void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2) @@ -127,7 +132,7 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) { // map std::string image = mPrefix+"_"+ boost::lexical_cast(x + (mx-1)) + "_" - + boost::lexical_cast(y + (interior ? (my-1) : -1*(my-1))); + + boost::lexical_cast(y + (-1*(my-1))); std::string name = "Map_" + boost::lexical_cast(mx) + "_" + boost::lexical_cast(my); @@ -173,7 +178,7 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior) } else { - Ogre::Vector2 position (marker.x, -marker.y); + Ogre::Vector2 position (marker.x, marker.y); MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy); widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8); @@ -394,10 +399,10 @@ void MapWindow::globalMapUpdatePlayer () { Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition (); Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation (); - Ogre::Vector2 dir (orient.yAxis ().x, -orient.yAxis().z); + Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y); float worldX, worldY; - mGlobalMapRender->worldPosToImageSpace (pos.x, pos.z, worldX, worldY); + mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY); worldX *= mGlobalMapRender->getWidth(); worldY *= mGlobalMapRender->getHeight(); @@ -425,3 +430,17 @@ void MapWindow::notifyPlayerUpdate () { globalMapUpdatePlayer (); } + +void MapWindow::notifyMapChanged () +{ + // workaround to prevent the map from drawing on top of the button + MyGUI::IntCoord oldCoord = mButton->getCoord (); + MyGUI::Gui::getInstance().destroyWidget (mButton); + mButton = mMainWidget->createWidget("MW_Button", + oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right); + mButton->setProperty ("ExpandDirection", "Left"); + + mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked); + mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" : + "#{sWorld}"); +} diff --git a/apps/openmw/mwgui/map_window.hpp b/apps/openmw/mwgui/map_window.hpp index 4e2dd6756..39770a7a2 100644 --- a/apps/openmw/mwgui/map_window.hpp +++ b/apps/openmw/mwgui/map_window.hpp @@ -50,6 +50,7 @@ namespace MWGui void onMarkerUnfocused(MyGUI::Widget* w1, MyGUI::Widget* w2); virtual void notifyPlayerUpdate() {} + virtual void notifyMapChanged() {} OEngine::GUI::Layout* mLayout; @@ -99,6 +100,8 @@ namespace MWGui virtual void onPinToggled(); virtual void notifyPlayerUpdate(); + virtual void notifyMapChanged(); + }; } #endif diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 896ab3bb5..0ee042e32 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -1,6 +1,8 @@ #include #include "messagebox.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" using namespace MWGui; @@ -375,6 +377,7 @@ void InteractiveMessageBox::enterPressed() if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok) { buttonActivated(*button); + MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); break; } } diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 054cce7b8..71a4d1b3e 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -20,7 +20,7 @@ using namespace MWGui; using namespace Widgets; RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_race.layout", parWindowManager) + : WindowModal("openmw_chargen_race.layout", parWindowManager) , mGenderIndex(0) , mFaceIndex(0) , mHairIndex(0) @@ -61,7 +61,7 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair); nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair); - setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu4", "Race")); + setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu5", "Race")); getWidget(mRaceList, "RaceList"); mRaceList->setScrollVisible(true); mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); @@ -100,6 +100,8 @@ void RaceDialog::setNextButtonShow(bool shown) void RaceDialog::open() { + WindowModal::open(); + updateRaces(); updateSkills(); updateSpellPowers(); diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index e0dc3306a..619556906 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -23,7 +23,7 @@ namespace MWGui namespace MWGui { - class RaceDialog : public WindowBase + class RaceDialog : public WindowModal { public: RaceDialog(MWBase::WindowManager& parWindowManager); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 45adb5383..50dc26e42 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -23,7 +23,7 @@ using namespace Widgets; const int ReviewDialog::sLineHeight = 18; ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_chargen_review.layout", parWindowManager) + : WindowModal("openmw_chargen_review.layout", parWindowManager) , mLastPos(0) { // Centre dialog @@ -97,6 +97,7 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager) void ReviewDialog::open() { + WindowModal::open(); updateSkillArea(); } diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 2b0740234..aac609a64 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -17,7 +17,7 @@ Layout is defined by resources/mygui/openmw_chargen_review.layout. namespace MWGui { - class ReviewDialog : public WindowBase + class ReviewDialog : public WindowModal { public: enum Dialogs { diff --git a/apps/openmw/mwgui/text_input.cpp b/apps/openmw/mwgui/text_input.cpp index 3dbe75165..c19394833 100644 --- a/apps/openmw/mwgui/text_input.cpp +++ b/apps/openmw/mwgui/text_input.cpp @@ -5,7 +5,7 @@ using namespace MWGui; TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager) - : WindowBase("openmw_text_input.layout", parWindowManager) + : WindowModal("openmw_text_input.layout", parWindowManager) { // Centre dialog center(); @@ -39,6 +39,7 @@ void TextInputDialog::setTextLabel(const std::string &label) void TextInputDialog::open() { + WindowModal::open(); // Make sure the edit box has focus MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit); } diff --git a/apps/openmw/mwgui/text_input.hpp b/apps/openmw/mwgui/text_input.hpp index 848310369..649990281 100644 --- a/apps/openmw/mwgui/text_input.hpp +++ b/apps/openmw/mwgui/text_input.hpp @@ -13,7 +13,7 @@ namespace MWGui namespace MWGui { - class TextInputDialog : public WindowBase + class TextInputDialog : public WindowModal { public: TextInputDialog(MWBase::WindowManager& parWindowManager); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 2cc8ff444..290310760 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -19,11 +19,11 @@ #include "inventorywindow.hpp" -static const float BALANCE_CHANGE_INITIAL_PAUSE = 0.5; // in seconds -static const float BALANCE_CHANGE_INTERVAL = 0.1; // in seconds - namespace MWGui { + const float TradeWindow::sBalanceChangeInitialPause = 0.5; + const float TradeWindow::sBalanceChangeInterval = 0.1; + TradeWindow::TradeWindow(MWBase::WindowManager& parWindowManager) : WindowBase("openmw_trade_window.layout", parWindowManager) , ContainerBase(NULL) // no drag&drop @@ -157,7 +157,7 @@ namespace MWGui mBalanceChangePause -= frameDuration; if (mBalanceChangePause < 0.0) { - mBalanceChangePause += BALANCE_CHANGE_INTERVAL; + mBalanceChangePause += sBalanceChangeInterval; if (mBalanceButtonsState == BBS_Increase) onIncreaseButtonTriggered(); else if (mBalanceButtonsState == BBS_Decrease) @@ -296,14 +296,14 @@ namespace MWGui void TradeWindow::onIncreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { mBalanceButtonsState = BBS_Increase; - mBalanceChangePause = BALANCE_CHANGE_INITIAL_PAUSE; + mBalanceChangePause = sBalanceChangeInitialPause; onIncreaseButtonTriggered(); } void TradeWindow::onDecreaseButtonPressed(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) { mBalanceButtonsState = BBS_Decrease; - mBalanceChangePause = BALANCE_CHANGE_INITIAL_PAUSE; + mBalanceChangePause = sBalanceChangeInitialPause; onDecreaseButtonTriggered(); } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 20d9b6069..c1d31917b 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -37,6 +37,9 @@ namespace MWGui void onFrame(float frameDuration); protected: + static const float sBalanceChangeInitialPause; // in seconds + static const float sBalanceChangeInterval; // in seconds + MyGUI::Button* mFilterAll; MyGUI::Button* mFilterWeapon; MyGUI::Button* mFilterApparel; diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 2c74dd2fd..794a9ab55 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -95,31 +95,46 @@ namespace MWGui // http://www.uesp.net/wiki/Lore:Calendar std::string month; int m = MWBase::Environment::get().getWorld ()->getMonth (); - if (m == 0) - month = "#{sMonthMorningstar}"; - else if (m == 1) - month = "#{sMonthSunsdawn}"; - else if (m == 2) - month = "#{sMonthFirstseed}"; - else if (m == 3) - month = "#{sMonthRainshand}"; - else if (m == 4) - month = "#{sMonthSecondseed}"; - else if (m == 5) - month = "#{sMonthMidyear}"; - else if (m == 6) - month = "#{sMonthSunsheight}"; - else if (m == 7) - month = "#{sMonthLastseed}"; - else if (m == 8) - month = "#{sMonthHeartfire}"; - else if (m == 9) - month = "#{sMonthFrostfall}"; - else if (m == 10) - month = "#{sMonthSunsdusk}"; - else if (m == 11) - month = "#{sMonthEveningstar}"; - + switch (m) { + case 0: + month = "#{sMonthMorningstar}"; + break; + case 1: + month = "#{sMonthSunsdawn}"; + break; + case 2: + month = "#{sMonthFirstseed}"; + break; + case 3: + month = "#{sMonthRainshand}"; + break; + case 4: + month = "#{sMonthSecondseed}"; + break; + case 5: + month = "#{sMonthMidyear}"; + break; + case 6: + month = "#{sMonthSunsheight}"; + break; + case 7: + month = "#{sMonthLastseed}"; + break; + case 8: + month = "#{sMonthHeartfire}"; + break; + case 9: + month = "#{sMonthFrostfall}"; + break; + case 10: + month = "#{sMonthSunsdusk}"; + break; + case 11: + month = "#{sMonthEveningstar}"; + break; + default: + break; + } int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour (); bool pm = hour >= 12; if (hour >= 13) hour -= 12; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 1dc11f2c4..1138f62fa 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -53,6 +53,7 @@ #include "trainingwindow.hpp" #include "imagebutton.hpp" #include "exposedwindow.hpp" +#include "cursor.hpp" using namespace MWGui; @@ -130,6 +131,9 @@ WindowManager::WindowManager( MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Resource", "ResourceImageSetPointer"); + MyGUI::ResourceManager::getInstance().load("core.xml"); + MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Get size info from the Gui object @@ -178,6 +182,8 @@ WindowManager::WindowManager( mInputBlocker = mGui->createWidget("",0,0,w,h,MyGUI::Align::Default,"Windows",""); + mCursor = new Cursor(); + // The HUD is always on mHud->setVisible(true); @@ -236,6 +242,7 @@ WindowManager::~WindowManager() delete mTrainingWindow; delete mCountDialog; delete mQuickKeysMenu; + delete mCursor; cleanupGarbage(); @@ -262,6 +269,8 @@ void WindowManager::update() mHud->setFPS(mFPS); mHud->setTriangleCount(mTriangleCount); mHud->setBatchCount(mBatchCount); + + mCursor->update(); } void WindowManager::updateVisible() @@ -293,7 +302,7 @@ void WindowManager::updateVisible() mHud->setVisible(true); // Mouse is visible whenever we're not in game mode - MyGUI::PointerManager::getInstance().setVisible(isGuiMode()); + mCursor->setVisible(isGuiMode()); bool gameMode = !isGuiMode(); @@ -421,13 +430,19 @@ void WindowManager::updateVisible() break; case GM_LoadingWallpaper: mHud->setVisible(false); - MyGUI::PointerManager::getInstance().setVisible(false); + mCursor->setVisible(false); break; case GM_Loading: - MyGUI::PointerManager::getInstance().setVisible(false); + // Show the pinned windows + mMap->setVisible(mMap->pinned()); + mStatsWindow->setVisible(mStatsWindow->pinned()); + mInventoryWindow->setVisible(mInventoryWindow->pinned()); + mSpellWindow->setVisible(mSpellWindow->pinned()); + + mCursor->setVisible(false); break; case GM_Video: - MyGUI::PointerManager::getInstance().setVisible(false); + mCursor->setVisible(false); mHud->setVisible(false); break; default: @@ -749,7 +764,7 @@ void WindowManager::setSpellVisibility(bool visible) void WindowManager::setMouseVisible(bool visible) { - MyGUI::PointerManager::getInstance().setVisible(visible); + mCursor->setVisible(visible); } void WindowManager::setDragDrop(bool dragDrop) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index fff627366..e2d64a855 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -72,6 +72,7 @@ namespace MWGui class SpellCreationDialog; class EnchantingDialog; class TrainingWindow; + class Cursor; class WindowManager : public MWBase::WindowManager { @@ -188,7 +189,7 @@ namespace MWGui virtual void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - virtual void messageBox (const std::string& message, const std::vector& buttons); + virtual void messageBox (const std::string& message, const std::vector& buttons = std::vector()); virtual void enterPressed (); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) @@ -260,6 +261,7 @@ namespace MWGui EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; Translation::Storage& mTranslationDataStorage; + Cursor* mCursor; CharacterCreation* mCharGen; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f1f88b9ae..850ffa4ca 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -21,6 +21,7 @@ #include "../engine.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -51,6 +52,7 @@ namespace MWInput , mUIYMultiplier (Settings::Manager::getFloat("ui y multiplier", "Input")) , mPreviewPOVDelay(0.f) , mTimeIdle(0.f) + , mOverencumberedMessageDelay(0.f) { Ogre::RenderWindow* window = ogre.getWindow (); size_t windowHnd; @@ -85,10 +87,12 @@ namespace MWInput std::string("false"))); pl.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false"))); - pl.insert(std::make_pair(std::string("XAutoRepeatOn"), - std::string("true"))); #endif } +#if defined OIS_LINUX_PLATFORM + pl.insert(std::make_pair(std::string("XAutoRepeatOn"), + std::string("true"))); +#endif #if defined(__APPLE__) && !defined(__LP64__) // Give the application window focus to receive input events @@ -175,6 +179,11 @@ namespace MWInput case A_Activate: resetIdleTime(); activate(); + if( MWBase::Environment::get().getWindowManager()->isGuiMode() + && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_InterMessageBox ) { + // Pressing the activation key when a messagebox is prompting for "ok" will activate the ok button + MWBase::Environment::get().getWindowManager()->enterPressed(); + } break; case A_Journal: toggleJournal (); @@ -268,26 +277,29 @@ namespace MWInput // be done in the physics system. if (mControlSwitch["playercontrols"]) { + bool triedToMove = false; if (actionIsActive(A_MoveLeft)) { - mPlayer.setAutoMove (false); - mPlayer.setLeftRight (1); + triedToMove = true; + mPlayer.setLeftRight (-1); } else if (actionIsActive(A_MoveRight)) { - mPlayer.setAutoMove (false); - mPlayer.setLeftRight (-1); + triedToMove = true; + mPlayer.setLeftRight (1); } else mPlayer.setLeftRight (0); if (actionIsActive(A_MoveForward)) { + triedToMove = true; mPlayer.setAutoMove (false); mPlayer.setForwardBackward (1); } else if (actionIsActive(A_MoveBackward)) { + triedToMove = true; mPlayer.setAutoMove (false); mPlayer.setForwardBackward (-1); } @@ -295,12 +307,35 @@ namespace MWInput mPlayer.setForwardBackward (0); if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) + { mPlayer.setUpDown (1); + triedToMove = true; + } else if (actionIsActive(A_Crouch)) mPlayer.setUpDown (-1); else mPlayer.setUpDown (0); + if(actionIsActive(A_Run)) + mPlayer.setRunState(true); + else + mPlayer.setRunState(false); + + // if player tried to start moving, but can't (due to being overencumbered), display a notification. + if (triedToMove) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + mOverencumberedMessageDelay -= dt; + if (MWWorld::Class::get(player).getEncumbrance(player) >= MWWorld::Class::get(player).getCapacity(player)) + { + if (mOverencumberedMessageDelay <= 0) + { + MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}"); + mOverencumberedMessageDelay = 1.0; + } + } + } + if (mControlSwitch["playerviewswitch"]) { // work around preview mode toggle when pressing Alt+Tab @@ -521,6 +556,9 @@ namespace MWInput void InputManager::toggleMainMenu() { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) mWindows.popGuiMode(); else if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video) @@ -599,6 +637,9 @@ namespace MWInput void InputManager::toggleConsole() { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + bool gameMode = !mWindows.isGuiMode(); // Switch to console mode no matter what mode we are currently @@ -705,6 +746,7 @@ namespace MWInput defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1; defaultKeyBindings[A_Console] = OIS::KC_F2; + defaultKeyBindings[A_Run] = OIS::KC_LSHIFT; defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL; defaultKeyBindings[A_AutoMove] = OIS::KC_Q; defaultKeyBindings[A_Jump] = OIS::KC_E; @@ -771,6 +813,7 @@ namespace MWInput descriptions[A_ToggleWeapon] = "sReady_Weapon"; descriptions[A_ToggleSpell] = "sReady_Magic"; descriptions[A_Console] = "sConsoleTitle"; + descriptions[A_Run] = "sRun"; descriptions[A_Crouch] = "sCrouch_Sneak"; descriptions[A_AutoMove] = "sAuto_Run"; descriptions[A_Jump] = "sJump"; @@ -819,6 +862,7 @@ namespace MWInput ret.push_back(A_MoveLeft); ret.push_back(A_MoveRight); ret.push_back(A_TogglePOV); + ret.push_back(A_Run); ret.push_back(A_Crouch); ret.push_back(A_Activate); ret.push_back(A_ToggleWeapon); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 9deed1f28..383fe3035 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -145,6 +145,8 @@ namespace MWInput bool mMouseLookEnabled; bool mGuiCursorEnabled; + float mOverencumberedMessageDelay; + float mMouseX; float mMouseY; int mMouseWheel; @@ -207,7 +209,7 @@ namespace MWInput A_Journal, //Journal A_Weapon, //Draw/Sheath weapon A_Spell, //Ready/Unready Casting - A_AlwaysRun, //Toggle Always Run + A_Run, //Run when held A_CycleSpellLeft, //cycling through spells A_CycleSpellRight, A_CycleWeaponLeft,//Cycling through weapons diff --git a/apps/openmw/mwmechanics/activators.cpp b/apps/openmw/mwmechanics/activators.cpp new file mode 100644 index 000000000..b67fcb216 --- /dev/null +++ b/apps/openmw/mwmechanics/activators.cpp @@ -0,0 +1,76 @@ +#include "activators.hpp" + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +namespace MWMechanics +{ + +Activators::Activators() +{ +} + +void Activators::addActivator(const MWWorld::Ptr& ptr) +{ + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if(anim != NULL) + mActivators.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true))); +} + +void Activators::removeActivator (const MWWorld::Ptr& ptr) +{ + PtrControllerMap::iterator iter = mActivators.find(ptr); + if(iter != mActivators.end()) + mActivators.erase(iter); +} + +void Activators::updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) +{ + PtrControllerMap::iterator iter = mActivators.find(old); + if(iter != mActivators.end()) + { + CharacterController ctrl = iter->second; + mActivators.erase(iter); + + ctrl.updatePtr(ptr); + mActivators.insert(std::make_pair(ptr, ctrl)); + } +} + +void Activators::dropActivators (const MWWorld::Ptr::CellStore *cellStore) +{ + PtrControllerMap::iterator iter = mActivators.begin(); + while(iter != mActivators.end()) + { + if(iter->first.getCell()==cellStore) + mActivators.erase(iter++); + else + ++iter; + } +} + +void Activators::update(float duration, bool paused) +{ + if(!paused) + { + for(PtrControllerMap::iterator iter(mActivators.begin());iter != mActivators.end();++iter) + iter->second.update(duration); + } +} + +void Activators::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) +{ + PtrControllerMap::iterator iter = mActivators.find(ptr); + if(iter != mActivators.end()) + iter->second.playGroup(groupName, mode, number); +} +void Activators::skipAnimation(const MWWorld::Ptr& ptr) +{ + PtrControllerMap::iterator iter = mActivators.find(ptr); + if(iter != mActivators.end()) + iter->second.skipAnim(); +} + +} diff --git a/apps/openmw/mwmechanics/activators.hpp b/apps/openmw/mwmechanics/activators.hpp new file mode 100644 index 000000000..137674a57 --- /dev/null +++ b/apps/openmw/mwmechanics/activators.hpp @@ -0,0 +1,45 @@ +#ifndef GAME_MWMECHANICS_ACTIVATORS_H +#define GAME_MWMECHANICS_ACTOVATRS_H + +#include +#include + +#include "character.hpp" + +namespace MWWorld +{ + class Ptr; + class CellStore; +} + +namespace MWMechanics +{ + class Activators + { + typedef std::map PtrControllerMap; + PtrControllerMap mActivators; + + public: + Activators(); + + void addActivator (const MWWorld::Ptr& ptr); + ///< Register an animated activator + + void removeActivator (const MWWorld::Ptr& ptr); + ///< Deregister an activator + + void updateActivator(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); + ///< Updates an activator with a new Ptr + + void dropActivators (const MWWorld::CellStore *cellStore); + ///< Deregister all activators in the given cell. + + void update (float duration, bool paused); + ///< Update activator animations + + void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + void skipAnimation(const MWWorld::Ptr& ptr); + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d541baea9..9632bdf76 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -165,35 +165,46 @@ namespace MWMechanics void Actors::addActor (const MWWorld::Ptr& ptr) { - if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) - mActors.insert (ptr); + MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); + if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead()) + mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true))); else - MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, "death1", 2); + mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1, false))); } void Actors::removeActor (const MWWorld::Ptr& ptr) { - std::set::iterator iter = mActors.find (ptr); + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + mActors.erase(iter); + } - if (iter!=mActors.end()) - mActors.erase (iter); + void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) + { + PtrControllerMap::iterator iter = mActors.find(old); + if(iter != mActors.end()) + { + CharacterController ctrl = iter->second; + mActors.erase(iter); + + ctrl.updatePtr(ptr); + mActors.insert(std::make_pair(ptr, ctrl)); + } } void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore) { - std::set::iterator iter = mActors.begin(); - - while (iter!=mActors.end()) - if (iter->getCell()==cellStore) - { - mActors.erase (iter++); - } + PtrControllerMap::iterator iter = mActors.begin(); + while(iter != mActors.end()) + { + if(iter->first.getCell()==cellStore) + mActors.erase(iter++); else ++iter; + } } - void Actors::update (std::vector >& movement, float duration, - bool paused) + void Actors::update (float duration, bool paused) { mDuration += duration; @@ -201,79 +212,91 @@ namespace MWMechanics { float totalDuration = mDuration; mDuration = 0; - - std::set::iterator iter (mActors.begin()); - while (iter!=mActors.end()) + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { - if (!MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) + if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) { - updateActor (*iter, totalDuration); + if(iter->second.getState() >= CharState_Death1) + iter->second.setState(CharState_Idle, true); - if (iter->getTypeName()==typeid (ESM::NPC).name()) - updateNpc (*iter, totalDuration, paused); + updateActor(iter->first, totalDuration); + if(iter->first.getTypeName() == typeid(ESM::NPC).name()) + updateNpc(iter->first, totalDuration, paused); + + if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) + continue; } - if (MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) + // workaround: always keep player alive for now + // \todo remove workaround, once player death can be handled + if(iter->first.getRefData().getHandle()=="player") { - // workaround: always keep player alive for now - // \todo remove workaround, once player death can be handled - if (iter->getRefData().getHandle()=="player") - { - MWMechanics::DynamicStat stat ( - MWWorld::Class::get (*iter).getCreatureStats (*iter).getHealth()); - - if (stat.getModified()<1) - { - stat.setModified (1, 0); - MWWorld::Class::get (*iter).getCreatureStats (*iter).setHealth (stat); - } + MWMechanics::DynamicStat stat ( + MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth()); - MWWorld::Class::get (*iter).getCreatureStats (*iter).resurrect(); - ++iter; - continue; + if (stat.getModified()<1) + { + stat.setModified (1, 0); + MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat); } - ++mDeathCount[MWWorld::Class::get (*iter).getId (*iter)]; - - MWBase::Environment::get().getWorld()->playAnimationGroup (*iter, "death1", 0); - - if (MWWorld::Class::get (*iter).isEssential (*iter)) - MWBase::Environment::get().getWindowManager()->messageBox ( - "#{sKilledEssential}", std::vector()); - - mActors.erase (iter++); + MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect(); + continue; } - else - ++iter; + + if(iter->second.getState() >= CharState_Death1) + continue; + + iter->second.setState(CharState_Death1, false); + + ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; + + if(MWWorld::Class::get(iter->first).isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox( + "#{sKilledEssential}", std::vector()); } } - for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); - ++iter) + if(!paused) { - Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter); + mMovement.reserve(mActors.size()); - if (vector!=Ogre::Vector3::ZERO) - movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector)); + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + { + Ogre::Vector3 movement = iter->second.update(duration); + mMovement.push_back(std::make_pair(iter->first, movement)); + } + MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration); + + mMovement.clear(); } } void Actors::restoreDynamicStats() { - for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter) - { - calculateRestoration (*iter, 3600); - } + for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) + calculateRestoration(iter->first, 3600); } int Actors::countDeaths (const std::string& id) const { - std::map::const_iterator iter = mDeathCount.find (id); - - if (iter!=mDeathCount.end()) + std::map::const_iterator iter = mDeathCount.find(id); + if(iter != mDeathCount.end()) return iter->second; - return 0; } + + void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + iter->second.playGroup(groupName, mode, number); + } + void Actors::skipAnimation(const MWWorld::Ptr& ptr) + { + PtrControllerMap::iterator iter = mActors.find(ptr); + if(iter != mActors.end()) + iter->second.skipAnim(); + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 79ae16fc3..fc4af8dd6 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -6,6 +6,9 @@ #include #include +#include "character.hpp" +#include "../mwbase/world.hpp" + namespace Ogre { class Vector3; @@ -21,9 +24,14 @@ namespace MWMechanics { class Actors { - std::set mActors; - float mDuration; - std::map mDeathCount; + typedef std::map PtrControllerMap; + PtrControllerMap mActors; + + MWWorld::PtrMovementList mMovement; + + std::map mDeathCount; + + float mDuration; void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused); @@ -50,11 +58,13 @@ namespace MWMechanics /// /// \note Ignored, if \a ptr is not a registered actor. + void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr); + ///< Updates an actor with a new Ptr + void dropActors (const MWWorld::CellStore *cellStore); ///< Deregister all actors in the given cell. - void update (std::vector >& movement, - float duration, bool paused); + void update (float duration, bool paused); ///< Update actor stats and store desired velocity vectors in \a movement void updateActor (const MWWorld::Ptr& ptr, float duration); @@ -66,6 +76,9 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. + + void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + void skipAnimation(const MWWorld::Ptr& ptr); }; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp new file mode 100644 index 000000000..ae0114a35 --- /dev/null +++ b/apps/openmw/mwmechanics/character.cpp @@ -0,0 +1,296 @@ +/* + * OpenMW - The completely unofficial reimplementation of Morrowind + * + * This file (character.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 "character.hpp" + +#include + +#include "../mwrender/animation.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/class.hpp" + + +namespace MWMechanics +{ + +static const struct { + CharacterState state; + const char groupname[32]; +} sStateList[] = { + { CharState_Idle, "idle" }, + { CharState_Idle2, "idle2" }, + { CharState_Idle3, "idle3" }, + { CharState_Idle4, "idle4" }, + { CharState_Idle5, "idle5" }, + { CharState_Idle6, "idle6" }, + { CharState_Idle7, "idle7" }, + { CharState_Idle8, "idle8" }, + { CharState_Idle9, "idle9" }, + { CharState_IdleSwim, "idleswim" }, + + { CharState_WalkForward, "walkforward" }, + { CharState_WalkBack, "walkback" }, + { CharState_WalkLeft, "walkleft" }, + { CharState_WalkRight, "walkright" }, + + { CharState_SwimWalkForward, "swimwalkforward" }, + { CharState_SwimWalkBack, "swimwalkback" }, + { CharState_SwimWalkLeft, "swimwalkleft" }, + { CharState_SwimWalkRight, "swimwalkright" }, + + { CharState_RunForward, "runforward" }, + { CharState_RunBack, "runback" }, + { CharState_RunLeft, "runleft" }, + { CharState_RunRight, "runright" }, + + { CharState_SwimRunForward, "swimrunforward" }, + { CharState_SwimRunBack, "swimrunback" }, + { CharState_SwimRunLeft, "swimrunleft" }, + { CharState_SwimRunRight, "swimrunright" }, + + { CharState_Jump, "jump" }, + + { CharState_Death1, "death1" }, + { CharState_Death2, "death2" }, + { CharState_Death3, "death3" }, + { CharState_Death4, "death4" }, + { CharState_Death5, "death5" }, +}; +static const size_t sStateListSize = sizeof(sStateList)/sizeof(sStateList[0]); + +static void getStateInfo(CharacterState state, std::string *group) +{ + for(size_t i = 0;i < sStateListSize;i++) + { + if(sStateList[i].state == state) + { + *group = sStateList[i].groupname; + return; + } + } + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(state)); +} + + +CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) + : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) +{ + if(!mAnimation) + return; + + mAnimation->setController(this); + + getStateInfo(mState, &mCurrentGroup); + if(ptr.getTypeName() == typeid(ESM::Activator).name()) + { + /* Don't accumulate with activators (they don't get moved). */ + mAnimation->setAccumulation(Ogre::Vector3::ZERO); + } + else + { + /* Accumulate along X/Y only for now, until we can figure out how we should + * handle knockout and death which moves the character down. */ + mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); + } + if(mAnimation->hasAnimation(mCurrentGroup)) + mAnimation->play(mCurrentGroup, "stop", "stop", loop); +} + +CharacterController::CharacterController(const CharacterController &rhs) + : mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue) + , mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState) + , mSkipAnim(rhs.mSkipAnim) +{ + if(!mAnimation) + return; + /* We've been copied. Update the animation with the new controller. */ + mAnimation->setController(this); +} + +CharacterController::~CharacterController() +{ +} + + +void CharacterController::updatePtr(const MWWorld::Ptr &ptr) +{ + mPtr = ptr; +} + + +void CharacterController::markerEvent(float time, const std::string &evt) +{ + if(evt == "stop") + { + if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1]) + { + mAnimQueue.pop_front(); + mAnimation->play(mCurrentGroup, "loop start", "stop", false); + } + else if(mAnimQueue.size() > 0) + { + mAnimQueue.pop_front(); + if(mAnimQueue.size() > 0) + { + mCurrentGroup = mAnimQueue.front(); + mAnimation->play(mCurrentGroup, "start", "stop", false); + } + } + return; + } + + std::cerr<< "Unhandled animation event: "<= CharState_Death1)) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + const Ogre::Vector3 &vec = cls.getMovementVector(mPtr); + + bool onground = world->isOnGround(mPtr); + bool inwater = world->isSwimming(mPtr); + bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run); + speed = cls.getSpeed(mPtr); + + /* FIXME: The state should be set to Jump, and X/Y movement should be disallowed except + * for the initial thrust (which would be carried by "physics" until landing). */ + if(onground && vec.z > 0.0f) + { + float x = cls.getJump(mPtr); + + if(vec.x == 0 && vec.y == 0) + movement.z += x*duration; + else + { + /* FIXME: this would be more correct if we were going into a jumping state, + * rather than normal walking/idle states. */ + //Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy(); + //movement += Ogre::Vector3(lat.x, lat.y, 1.0f) * x * 0.707f * duration; + movement.z += x * 0.707f * duration; + } + + //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; + } + + if(std::abs(vec.x/2.0f) > std::abs(vec.y) && speed > 0.0f) + { + if(vec.x > 0.0f) + setState(isrunning ? + (inwater ? CharState_SwimRunRight : CharState_RunRight) : + (inwater ? CharState_SwimWalkRight : CharState_WalkRight), true); + else if(vec.x < 0.0f) + setState(isrunning ? + (inwater ? CharState_SwimRunLeft: CharState_RunLeft) : + (inwater ? CharState_SwimWalkLeft : CharState_WalkLeft), true); + // Apply any forward/backward movement manually + movement.y += vec.y * (speed*duration); + } + else if(vec.y != 0.0f && speed > 0.0f) + { + if(vec.y > 0.0f) + setState(isrunning ? + (inwater ? CharState_SwimRunForward : CharState_RunForward) : + (inwater ? CharState_SwimWalkForward : CharState_WalkForward), true); + else if(vec.y < 0.0f) + setState(isrunning ? + (inwater ? CharState_SwimRunBack : CharState_RunBack) : + (inwater ? CharState_SwimWalkBack : CharState_WalkBack), true); + // Apply any sideways movement manually + movement.x += vec.x * (speed*duration); + } + else if(mAnimQueue.size() == 0) + setState((inwater ? CharState_IdleSwim : CharState_Idle), true); + } + + if(mAnimation && !mSkipAnim) + { + mAnimation->setSpeed(speed); + movement += mAnimation->runAnimation(duration); + } + mSkipAnim = false; + + return movement; +} + + +void CharacterController::playGroup(const std::string &groupname, int mode, int count) +{ + if(!mAnimation || !mAnimation->hasAnimation(groupname)) + std::cerr<< "Animation "< 0) + mAnimQueue.push_back(groupname); + mCurrentGroup = groupname; + mState = CharState_SpecialIdle; + mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false); + } + else if(mode == 0) + { + mAnimQueue.resize(1); + while(count-- > 0) + mAnimQueue.push_back(groupname); + } + } +} + +void CharacterController::skipAnim() +{ + mSkipAnim = true; +} + + +void CharacterController::setState(CharacterState state, bool loop) +{ + if(mState == state) + { + if(mAnimation) + mAnimation->setLooping(loop); + return; + } + mState = state; + + if(!mAnimation) + return; + mAnimQueue.clear(); + + std::string anim; + getStateInfo(mState, &anim); + if(mAnimation->hasAnimation(anim)) + { + mCurrentGroup = anim; + mAnimation->play(mCurrentGroup, "start", "stop", loop); + } +} + +} diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp new file mode 100644 index 000000000..2b3c50864 --- /dev/null +++ b/apps/openmw/mwmechanics/character.hpp @@ -0,0 +1,96 @@ +#ifndef GAME_MWMECHANICS_CHARACTER_HPP +#define GAME_MWMECHANICS_CHARACTER_HPP + +#include + +#include "../mwworld/ptr.hpp" + +namespace MWRender +{ + class Animation; +} + +namespace MWMechanics +{ + +enum CharacterState { + CharState_SpecialIdle, + CharState_Idle, + CharState_Idle2, + CharState_Idle3, + CharState_Idle4, + CharState_Idle5, + CharState_Idle6, + CharState_Idle7, + CharState_Idle8, + CharState_Idle9, + CharState_IdleSwim, + + CharState_WalkForward, + CharState_WalkBack, + CharState_WalkLeft, + CharState_WalkRight, + + CharState_SwimWalkForward, + CharState_SwimWalkBack, + CharState_SwimWalkLeft, + CharState_SwimWalkRight, + + CharState_RunForward, + CharState_RunBack, + CharState_RunLeft, + CharState_RunRight, + + CharState_SwimRunForward, + CharState_SwimRunBack, + CharState_SwimRunLeft, + CharState_SwimRunRight, + + CharState_Jump, + + /* Death states must be last! */ + CharState_Death1, + CharState_Death2, + CharState_Death3, + CharState_Death4, + CharState_Death5 +}; + +class CharacterController +{ + MWWorld::Ptr mPtr; + MWRender::Animation *mAnimation; + + typedef std::deque AnimationQueue; + AnimationQueue mAnimQueue; + + std::string mCurrentGroup; + CharacterState mState; + bool mSkipAnim; + +protected: + /* Called by the animation whenever a new text key is reached. */ + void markerEvent(float time, const std::string &evt); + + friend class MWRender::Animation; + +public: + CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); + CharacterController(const CharacterController &rhs); + virtual ~CharacterController(); + + void updatePtr(const MWWorld::Ptr &ptr); + + Ogre::Vector3 update(float duration); + + void playGroup(const std::string &groupname, int mode, int count); + void skipAnim(); + + void setState(CharacterState state, bool loop); + CharacterState getState() const + { return mState; } +}; + +} + +#endif /* GAME_MWMECHANICS_CHARACTER_HPP */ diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index dae417d44..c9e4c77ea 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -175,34 +175,47 @@ namespace MWMechanics buildPlayer(); } - void MechanicsManager::addActor (const MWWorld::Ptr& ptr) + void MechanicsManager::add(const MWWorld::Ptr& ptr) { - mActors.addActor (ptr); + if(ptr.getTypeName() == typeid(ESM::Activator).name()) + mActivators.addActivator(ptr); + else + mActors.addActor(ptr); } - void MechanicsManager::removeActor (const MWWorld::Ptr& ptr) + void MechanicsManager::remove(const MWWorld::Ptr& ptr) { - if (ptr==mWatched) + if(ptr == mWatched) + mWatched = MWWorld::Ptr(); + mActors.removeActor(ptr); + mActivators.removeActivator(ptr); + } + + void MechanicsManager::updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) + { + if(ptr.getTypeName() == typeid(ESM::Activator).name()) + mActivators.updateActivator(old, ptr); + else + mActors.updateActor(old, ptr); + } + + + void MechanicsManager::drop(const MWWorld::CellStore *cellStore) + { + if(!mWatched.isEmpty() && mWatched.getCell() == cellStore) mWatched = MWWorld::Ptr(); - mActors.removeActor (ptr); + mActors.dropActors(cellStore); + mActivators.dropActivators(cellStore); } - void MechanicsManager::dropActors (const MWWorld::Ptr::CellStore *cellStore) - { - if (!mWatched.isEmpty() && mWatched.getCell()==cellStore) - mWatched = MWWorld::Ptr(); - mActors.dropActors (cellStore); - } - - void MechanicsManager::watchActor (const MWWorld::Ptr& ptr) + void MechanicsManager::watchActor(const MWWorld::Ptr& ptr) { mWatched = ptr; } - void MechanicsManager::update (std::vector >& movement, - float duration, bool paused) + void MechanicsManager::update(float duration, bool paused) { if (!mWatched.isEmpty()) { @@ -296,9 +309,16 @@ namespace MWMechanics } winMgr->configureSkills (majorSkills, minorSkills); + + // HACK? The player has been changed, so a new Animation object may + // have been made for them. Make sure they're properly updated. + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + mActors.removeActor(ptr); + mActors.addActor(ptr); } - mActors.update (movement, duration, paused); + mActors.update(duration, paused); + mActivators.update(duration, paused); } void MechanicsManager::restoreDynamicStats() @@ -471,8 +491,10 @@ namespace MWMechanics if(buying) x = buyTerm; else x = std::min(buyTerm, sellTerm); int offerPrice; - if (x < 1) offerPrice = int(x * basePrice); - if (x >= 1) offerPrice = basePrice + int((x - 1) * basePrice); + if (x < 1) + offerPrice = int(x * basePrice); + else + offerPrice = basePrice + int((x - 1) * basePrice); offerPrice = std::max(1, offerPrice); return offerPrice; } @@ -535,7 +557,8 @@ namespace MWMechanics float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat(); float fPerTempMult = gmst.find("fPerTempMult")->getFloat(); - float x,y; + float x = 0; + float y = 0; float roll = static_cast (std::rand()) / RAND_MAX * 100; @@ -629,4 +652,20 @@ namespace MWMechanics permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y; } } + + void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) + { + if(ptr.getTypeName() == typeid(ESM::Activator).name()) + mActivators.playAnimationGroup(ptr, groupName, mode, number); + else + mActors.playAnimationGroup(ptr, groupName, mode, number); + } + void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr) + { + if(ptr.getTypeName() == typeid(ESM::Activator).name()) + mActivators.skipAnimation(ptr); + else + mActors.skipAnimation(ptr); + } + } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index d3a97db36..5ad870571 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -7,6 +7,7 @@ #include "creaturestats.hpp" #include "npcstats.hpp" +#include "activators.hpp" #include "actors.hpp" namespace Ogre @@ -29,6 +30,8 @@ namespace MWMechanics bool mUpdatePlayer; bool mClassSelected; bool mRaceSelected; + + Activators mActivators; Actors mActors; void buildPlayer(); @@ -39,24 +42,24 @@ namespace MWMechanics MechanicsManager(); - virtual void addActor (const MWWorld::Ptr& ptr); - ///< Register an actor for stats management - /// - /// \note Dead actors are ignored. + virtual void add (const MWWorld::Ptr& ptr); + ///< Register an object for management - virtual void removeActor (const MWWorld::Ptr& ptr); - ///< Deregister an actor for stats management + virtual void remove (const MWWorld::Ptr& ptr); + ///< Deregister an object for management - virtual void dropActors (const MWWorld::CellStore *cellStore); - ///< Deregister all actors in the given cell. + virtual void updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr); + ///< Moves an object to a new cell - virtual void watchActor (const MWWorld::Ptr& ptr); + virtual void drop(const MWWorld::CellStore *cellStore); + ///< Deregister all objects in the given cell. + + virtual void watchActor(const MWWorld::Ptr& ptr); ///< On each update look for changes in a previously registered actor and update the /// GUI accordingly. - virtual void update (std::vector >& movement, - float duration, bool paused); - ///< Update actor stats and store desired velocity vectors in \a movement + virtual void update (float duration, bool paused); + ///< Update objects /// /// \param paused In game type does not currently advance (this usually means some GUI /// component is up). @@ -92,6 +95,9 @@ namespace MWMechanics float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange); void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC + + virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); + virtual void skipAnimation(const MWWorld::Ptr& ptr); }; } diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp new file mode 100644 index 000000000..0dc16ecb6 --- /dev/null +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -0,0 +1,56 @@ +#include "activatoranimation.hpp" + +#include +#include +#include + +#include "renderconst.hpp" + +#include "../mwbase/world.hpp" + +namespace MWRender +{ + +ActivatorAnimation::~ActivatorAnimation() +{ +} + +ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) + : Animation(ptr) +{ + MWWorld::LiveCellRef *ref = mPtr.get(); + + assert (ref->mBase != NULL); + if(!ref->mBase->mModel.empty()) + { + std::string mesh = "meshes\\" + ref->mBase->mModel; + + createEntityList(mPtr.getRefData().getBaseNode(), mesh); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) + { + Ogre::Entity *ent = mEntityList.mEntities[i]; + + bool transparent = false; + for(unsigned int j=0;!transparent && j < ent->getNumSubEntities(); ++j) + { + Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while(!transparent && techIt.hasMoreElements()) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while(!transparent && passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); + transparent = pass->isTransparent(); + } + } + } + ent->setVisibilityFlags(RV_Misc); + ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + } + setAnimationSource(mesh); + } +} + +} diff --git a/apps/openmw/mwrender/activatoranimation.hpp b/apps/openmw/mwrender/activatoranimation.hpp new file mode 100644 index 000000000..f3ea38f44 --- /dev/null +++ b/apps/openmw/mwrender/activatoranimation.hpp @@ -0,0 +1,21 @@ +#ifndef _GAME_RENDER_ACTIVATORANIMATION_H +#define _GAME_RENDER_ACTIVATORANIMATION_H + +#include "animation.hpp" + +namespace MWWorld +{ + class Ptr; +} + +namespace MWRender +{ + class ActivatorAnimation : public Animation + { + public: + ActivatorAnimation(const MWWorld::Ptr& ptr); + virtual ~ActivatorAnimation(); + }; +} + +#endif diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 92f5cbe28..83c07737c 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -3,14 +3,23 @@ #include #include +#include "../mwworld/ptr.hpp" +#include "../mwworld/class.hpp" + +#include "animation.hpp" +#include "activatoranimation.hpp" +#include "creatureanimation.hpp" +#include "npcanimation.hpp" + #include "renderconst.hpp" namespace MWRender { +using namespace Ogre; -Actors::~Actors(){ - +Actors::~Actors() +{ PtrAnimationMap::iterator it = mAllActors.begin(); for(;it != mAllActors.end();++it) { @@ -19,18 +28,10 @@ Actors::~Actors(){ } } -void Actors::setMwRoot(Ogre::SceneNode* root) -{ mMwRoot = root; } +void Actors::setRootNode(Ogre::SceneNode* root) +{ mRootNode = root; } -void Actors::insertNPC(const MWWorld::Ptr &ptr, MWWorld::InventoryStore &inv) -{ - insertBegin(ptr, true, true); - NpcAnimation* anim = new MWRender::NpcAnimation(ptr, ptr.getRefData ().getBaseNode (), inv, RV_Actors); - - mAllActors[ptr] = anim; -} - -void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) +void Actors::insertBegin(const MWWorld::Ptr &ptr) { Ogre::SceneNode* cellnode; CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(ptr.getCell()); @@ -39,7 +40,7 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) else { //Create the scenenode and put it in the map - cellnode = mMwRoot->createChildSceneNode(); + cellnode = mRootNode->createChildSceneNode(); mCellSceneNodes[ptr.getCell()] = cellnode; } @@ -62,17 +63,27 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); - if (!enabled) - insert->setVisible (false); ptr.getRefData().setBaseNode(insert); - - } -void Actors::insertCreature (const MWWorld::Ptr& ptr){ - - insertBegin(ptr, true, true); - CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr); +void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv) +{ + insertBegin(ptr); + NpcAnimation* anim = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), inv, RV_Actors); + delete mAllActors[ptr]; + mAllActors[ptr] = anim; +} +void Actors::insertCreature (const MWWorld::Ptr& ptr) +{ + insertBegin(ptr); + CreatureAnimation* anim = new CreatureAnimation(ptr); + delete mAllActors[ptr]; + mAllActors[ptr] = anim; +} +void Actors::insertActivator (const MWWorld::Ptr& ptr) +{ + insertBegin(ptr); + ActivatorAnimation* anim = new ActivatorAnimation(ptr); delete mAllActors[ptr]; mAllActors[ptr] = anim; } @@ -125,47 +136,41 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store) } } -void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) -{ - PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); - if(iter != mAllActors.end()) - iter->second->playGroup(groupName, mode, number); -} -void Actors::skipAnimation (const MWWorld::Ptr& ptr) -{ - PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); - if(iter != mAllActors.end()) - iter->second->skipAnim(); -} void Actors::update (float duration) { - for(PtrAnimationMap::const_iterator iter = mAllActors.begin();iter != mAllActors.end();iter++) - iter->second->runAnimation(duration); + // Nothing to do } -void Actors::updateObjectCell(const MWWorld::Ptr &ptr) +Animation* Actors::getAnimation(const MWWorld::Ptr &ptr) +{ + PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); + if(iter != mAllActors.end()) + return iter->second; + return NULL; +} + +void Actors::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *node; - MWWorld::CellStore *newCell = ptr.getCell(); + MWWorld::CellStore *newCell = cur.getCell(); CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell); if(celliter != mCellSceneNodes.end()) node = celliter->second; else { - node = mMwRoot->createChildSceneNode(); + node = mRootNode->createChildSceneNode(); mCellSceneNodes[newCell] = node; } - node->addChild(ptr.getRefData().getBaseNode()); + node->addChild(cur.getRefData().getBaseNode()); - PtrAnimationMap::iterator iter = mAllActors.find(ptr); + PtrAnimationMap::iterator iter = mAllActors.find(old); if(iter != mAllActors.end()) { - /// \note Update key (Ptr's are compared only with refdata so mCell - /// on key is outdated), maybe redundant Animation *anim = iter->second; mAllActors.erase(iter); - mAllActors[ptr] = anim; + anim->updatePtr(cur); + mAllActors[cur] = anim; } } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index efb6247e4..75a18ba91 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -1,24 +1,27 @@ #ifndef _GAME_RENDER_ACTORS_H #define _GAME_RENDER_ACTORS_H -#include "npcanimation.hpp" -#include "creatureanimation.hpp" +#include namespace MWWorld { class Ptr; class CellStore; + class InventoryStore; } namespace MWRender { + class Animation; + class Actors { typedef std::map CellSceneNodeMap; typedef std::map PtrAnimationMap; OEngine::Render::OgreRenderer &mRend; - Ogre::SceneNode* mMwRoot; + Ogre::SceneNode* mRootNode; + CellSceneNodeMap mCellSceneNodes; PtrAnimationMap mAllActors; @@ -26,31 +29,22 @@ namespace MWRender Actors(OEngine::Render::OgreRenderer& _rend): mRend(_rend) {} ~Actors(); - void setMwRoot(Ogre::SceneNode* root); - void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); - void insertCreature (const MWWorld::Ptr& ptr); + void setRootNode(Ogre::SceneNode* root); + void insertBegin (const MWWorld::Ptr& ptr); void insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv); + void insertCreature (const MWWorld::Ptr& ptr); + void insertActivator (const MWWorld::Ptr& ptr); bool deleteObject (const MWWorld::Ptr& ptr); ///< \return found? void removeCell(MWWorld::CellStore* store); - void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, - int number = 1); - ///< Run animation for a MW-reference. Calls to this function for references that are currently not - /// in the rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run - - void skipAnimation (const MWWorld::Ptr& ptr); - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the rendered scene should be ignored. - void update (float duration); /// Updates containing cell for object rendering data - void updateObjectCell(const MWWorld::Ptr &ptr); + void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); + + Animation* getAnimation(const MWWorld::Ptr &ptr); }; } #endif diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2a3b8cf43..cc926e685 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1,148 +1,453 @@ #include "animation.hpp" -#include +#include #include #include #include #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwmechanics/character.hpp" + namespace MWRender { -Animation::Animation() - : mInsert(NULL) - , mTime(0.0f) - , mSkipFrame(false) +Animation::Animation(const MWWorld::Ptr &ptr) + : mPtr(ptr) + , mController(NULL) + , mInsert(NULL) + , mAccumRoot(NULL) + , mNonAccumRoot(NULL) + , mAccumulate(Ogre::Vector3::ZERO) + , mLastPosition(0.0f) + , mCurrentKeys(NULL) + , mCurrentAnim(NULL) + , mCurrentTime(0.0f) + , mStopTime(0.0f) + , mPlaying(false) + , mLooping(false) + , mAnimVelocity(0.0f) + , mAnimSpeedMult(1.0f) { } Animation::~Animation() { - Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - for(size_t i = 0;i < mEntityList.mEntities.size();i++) - sceneMgr->destroyEntity(mEntityList.mEntities[i]); + if(mInsert) + { + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); + for(size_t i = 0;i < mEntityList.mEntities.size();i++) + sceneMgr->destroyEntity(mEntityList.mEntities[i]); + } mEntityList.mEntities.clear(); + mEntityList.mSkelBase = NULL; } -struct checklow { - bool operator()(const char &a, const char &b) const - { - return ::tolower(a) == ::tolower(b); - } -}; - -bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTimes *times) +void Animation::setAnimationSources(const std::vector &names) { - 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"; + if(!mEntityList.mSkelBase) + return; - NifOgre::TextKeyMap::const_iterator iter; - for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++) + mCurrentAnim = NULL; + mCurrentKeys = NULL; + mAnimVelocity = 0.0f; + mAccumRoot = NULL; + mNonAccumRoot = NULL; + mSkeletonSources.clear(); + + std::vector::const_iterator nameiter; + for(nameiter = names.begin();nameiter != names.end();nameiter++) { - if(times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f) - return true; + Ogre::SkeletonPtr skel = NifOgre::Loader::getSkeleton(*nameiter); + if(skel.isNull()) + { + std::cerr<< "Failed to get skeleton source "<<*nameiter <touch(); - std::string::const_iterator strpos = iter->second.begin(); - std::string::const_iterator strend = iter->second.end(); - size_t strlen = strend-strpos; + Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); + while(boneiter.hasMoreElements()) + { + Ogre::Bone *bone = boneiter.getNext(); + Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); + const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); + if(data.isEmpty() || !Ogre::any_cast(data)) + continue; + + if(!mNonAccumRoot) + { + mAccumRoot = mInsert; + mNonAccumRoot = mEntityList.mSkelBase->getSkeleton()->getBone(bone->getName()); + } + + mSkeletonSources.push_back(skel); + for(int i = 0;i < skel->getNumAnimations();i++) + { + Ogre::Animation *anim = skel->getAnimation(i); + const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ + "@"+anim->getName()); + if(!groupdata.isEmpty()) + mTextKeys[anim->getName()] = Ogre::any_cast(groupdata); + } - if(start.size() <= strlen && std::mismatch(strpos, strend, start.begin(), checklow()).first == strend) - { - times->mStart = iter->first; - times->mLoopStart = iter->first; - } - else if(startloop.size() <= strlen && std::mismatch(strpos, strend, startloop.begin(), checklow()).first == strend) - { - times->mLoopStart = iter->first; - } - else if(stoploop.size() <= strlen && std::mismatch(strpos, strend, stoploop.begin(), checklow()).first == strend) - { - times->mLoopStop = iter->first; - } - else if(stop.size() <= strlen && std::mismatch(strpos, strend, stop.begin(), checklow()).first == strend) - { - times->mStop = iter->first; - if(times->mLoopStop < 0.0f) - times->mLoopStop = iter->first; break; } } +} - return (times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f); +void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model) +{ + mInsert = node->createChildSceneNode(); + assert(mInsert); + + mEntityList = NifOgre::Loader::createEntities(mInsert, model); + if(mEntityList.mSkelBase) + { + Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); + while(asiter.hasMoreElements()) + { + Ogre::AnimationState *state = asiter.getNext(); + state->setEnabled(false); + state->setLoop(false); + } + + // Set the bones as manually controlled since we're applying the + // transformations manually (needed if we want to apply an animation + // from one skeleton onto another). + Ogre::SkeletonInstance *skelinst = mEntityList.mSkelBase->getSkeleton(); + Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); + while(boneiter.hasMoreElements()) + boneiter.getNext()->setManuallyControlled(true); + } } -void Animation::playGroup(std::string groupname, int mode, int loops) +bool Animation::hasAnimation(const std::string &anim) { - GroupTimes times; - times.mLoops = loops; - - if(groupname == "all") + for(std::vector::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++) { - times.mStart = times.mLoopStart = 0.0f; - times.mLoopStop = times.mStop = 0.0f; - - NifOgre::TextKeyMap::const_reverse_iterator iter = mTextKeys.rbegin(); - if(iter != mTextKeys.rend()) - times.mLoopStop = times.mStop = iter->first; + if((*iter)->hasAnimation(anim)) + return true; } - else if(!findGroupTimes(groupname, ×)) - throw std::runtime_error("Failed to find animation group "+groupname); + return false; +} - if(mode == 0 && mCurGroup.mLoops > 0) - mNextGroup = times; + +void Animation::setController(MWMechanics::CharacterController *controller) +{ + mController = controller; +} + + +void Animation::setAccumulation(const Ogre::Vector3 &accum) +{ + mAccumulate = accum; +} + +void Animation::setSpeed(float speed) +{ + mAnimSpeedMult = 1.0f; + if(mAnimVelocity > 1.0f && speed > 0.0f) + mAnimSpeedMult = speed / mAnimVelocity; +} + +void Animation::setLooping(bool loop) +{ + mLooping = loop; +} + +void Animation::updatePtr(const MWWorld::Ptr &ptr) +{ + mPtr = ptr; +} + + +void Animation::calcAnimVelocity() +{ + const Ogre::NodeAnimationTrack *track = 0; + + Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); + while(!track && trackiter.hasMoreElements()) + { + const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); + if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) + track = cur; + } + + if(track && track->getNumKeyFrames() > 1) + { + float loopstarttime = 0.0f; + float loopstoptime = mCurrentAnim->getLength(); + NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin(); + while(keyiter != mCurrentKeys->end()) + { + if(keyiter->second == "loop start") + loopstarttime = keyiter->first; + else if(keyiter->second == "loop stop") + { + loopstoptime = keyiter->first; + break; + } + keyiter++; + } + + if(loopstoptime > loopstarttime) + { + Ogre::TransformKeyFrame startkf(0, loopstarttime); + Ogre::TransformKeyFrame endkf(0, loopstoptime); + + track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf); + track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf); + + mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) / + (loopstoptime-loopstarttime); + } + } +} + +void Animation::applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel) +{ + Ogre::TimeIndex timeindex = anim->_getTimeIndex(time); + Ogre::Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator(); + while(tracks.hasMoreElements()) + { + Ogre::NodeAnimationTrack *track = tracks.getNext(); + const Ogre::String &targetname = track->getAssociatedNode()->getName(); + if(!skel->hasBone(targetname)) + continue; + Ogre::Bone *bone = skel->getBone(targetname); + bone->setOrientation(Ogre::Quaternion::IDENTITY); + bone->setPosition(Ogre::Vector3::ZERO); + bone->setScale(Ogre::Vector3::UNIT_SCALE); + track->applyToNode(bone, timeindex); + } + + // HACK: Dirty the animation state set so that Ogre will apply the + // transformations to entities this skeleton instance is shared with. + mEntityList.mSkelBase->getAllAnimationStates()->_notifyDirty(); +} + +static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) +{ + if(skelsrc->hasBone(bone->getName())) + { + Ogre::Bone *srcbone = skelsrc->getBone(bone->getName()); + if(!srcbone->getParent() || !bone->getParent()) + { + bone->setOrientation(srcbone->getOrientation()); + bone->setPosition(srcbone->getPosition()); + bone->setScale(srcbone->getScale()); + } + else + { + bone->_setDerivedOrientation(srcbone->_getDerivedOrientation()); + bone->_setDerivedPosition(srcbone->_getDerivedPosition()); + bone->setScale(Ogre::Vector3::UNIT_SCALE); + } + } else { - mCurGroup = times; - mNextGroup = GroupTimes(); - mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart); + // No matching bone in the source. Make sure it stays properly offset + // from its parent. + bone->resetToInitialState(); } + + Ogre::Node::ChildNodeIterator boneiter = bone->getChildIterator(); + while(boneiter.hasMoreElements()) + updateBoneTree(skelsrc, static_cast(boneiter.getNext())); } -void Animation::skipAnim() +void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel) { - mSkipFrame = true; + Ogre::Skeleton::BoneIterator boneiter = skel->getRootBoneIterator(); + while(boneiter.hasMoreElements()) + updateBoneTree(skelsrc, boneiter.getNext()); } -void Animation::runAnimation(float timepassed) + +Ogre::Vector3 Animation::updatePosition(float time) { - if(mCurGroup.mLoops > 0 && !mSkipFrame) + if(mLooping) + mCurrentTime = std::fmod(std::max(time, 0.0f), mCurrentAnim->getLength()); + else + mCurrentTime = std::min(mCurrentAnim->getLength(), std::max(time, 0.0f)); + applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton()); + + Ogre::Vector3 posdiff = Ogre::Vector3::ZERO; + if(mNonAccumRoot) { - mTime += timepassed; - if(mTime >= mCurGroup.mLoopStop) + /* Get the non-accumulation root's difference from the last update. */ + posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate; + + /* Translate the accumulation root back to compensate for the move. */ + mLastPosition += posdiff; + mAccumRoot->setPosition(-mLastPosition); + } + return posdiff; +} + +void Animation::reset(const std::string &start, const std::string &stop) +{ + mNextKey = mCurrentKeys->begin(); + + while(mNextKey != mCurrentKeys->end() && mNextKey->second != start) + mNextKey++; + if(mNextKey != mCurrentKeys->end()) + mCurrentTime = mNextKey->first; + else + { + mNextKey = mCurrentKeys->begin(); + mCurrentTime = 0.0f; + } + + if(stop.length() > 0) + { + NifOgre::TextKeyMap::const_iterator stopKey = mNextKey; + while(stopKey != mCurrentKeys->end() && stopKey->second != stop) + stopKey++; + if(stopKey != mCurrentKeys->end()) + mStopTime = stopKey->first; + else + mStopTime = mCurrentAnim->getLength(); + } + + if(mNonAccumRoot) + { + const Ogre::NodeAnimationTrack *track = 0; + Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); + while(!track && trackiter.hasMoreElements()) { - if(mCurGroup.mLoops > 1) - { - mCurGroup.mLoops--; - mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart; - } - else if(mTime >= mCurGroup.mStop) - { - if(mNextGroup.mLoops > 0) - mTime = mTime - mCurGroup.mStop + mNextGroup.mStart; - else - mTime = mCurGroup.mStop; - mCurGroup = mNextGroup; - mNextGroup = GroupTimes(); - } + const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); + if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) + track = cur; } - if(mEntityList.mSkelBase) + if(track) { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - state->setTimePosition(mTime); - } + Ogre::TransformKeyFrame kf(0, mCurrentTime); + track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf); + mLastPosition = kf.getTranslate() * mAccumulate; } } - mSkipFrame = false; +} + + +bool Animation::handleEvent(float time, const std::string &evt) +{ + if(evt == "start" || evt == "loop start") + { + /* Do nothing */ + return true; + } + + if(evt.compare(0, 7, "sound: ") == 0) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); + return true; + } + if(evt.compare(0, 10, "soundgen: ") == 0) + { + // FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds + // to this actor type + return true; + } + + if(evt == "loop stop") + { + if(mLooping) + { + reset("loop start", ""); + if(mCurrentTime >= time) + return false; + } + return true; + } + if(evt == "stop") + { + if(mLooping) + { + reset("loop start", ""); + if(mCurrentTime >= time) + return false; + return true; + } + // fall-through + } + if(mController) + mController->markerEvent(time, evt); + return true; +} + + +void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop) +{ + try { + bool found = false; + /* Look in reverse; last-inserted source has priority. */ + for(std::vector::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++) + { + if((*iter)->hasAnimation(groupname)) + { + mCurrentAnim = (*iter)->getAnimation(groupname); + mCurrentKeys = &mTextKeys[groupname]; + mAnimVelocity = 0.0f; + + if(mNonAccumRoot) + calcAnimVelocity(); + + found = true; + break; + } + } + if(!found) + throw std::runtime_error("Failed to find animation "+groupname); + + reset(start, stop); + setLooping(loop); + mPlaying = true; + } + catch(std::exception &e) { + std::cerr<< e.what() <end() || mNextKey->first > targetTime) + { + movement += updatePosition(targetTime); + mPlaying = (mLooping || mStopTime > targetTime); + break; + } + + float time = mNextKey->first; + const std::string &evt = mNextKey->second; + mNextKey++; + + movement += updatePosition(time); + mPlaying = (mLooping || mStopTime > time); + + timepassed = targetTime - time; + + if(!handleEvent(time, evt)) + break; + } + + return movement; } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 07583db78..810ca869f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -1,55 +1,99 @@ #ifndef _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H -#include - #include -#include -#include "../mwworld/actiontalk.hpp" -#include -#include +#include "../mwworld/ptr.hpp" +namespace MWMechanics +{ + class CharacterController; +} +namespace MWRender +{ -namespace MWRender { - -class Animation { - struct GroupTimes { - float mStart; - float mStop; - float mLoopStart; - float mLoopStop; - - size_t mLoops; - - GroupTimes() - : mStart(-1.0f), mStop(-1.0f), mLoopStart(-1.0f), mLoopStop(-1.0f), - mLoops(0) - { } - }; - +class Animation +{ protected: + MWWorld::Ptr mPtr; + MWMechanics::CharacterController *mController; + Ogre::SceneNode* mInsert; - - float mTime; - GroupTimes mCurGroup; - GroupTimes mNextGroup; - - bool mSkipFrame; - NifOgre::EntityList mEntityList; - NifOgre::TextKeyMap mTextKeys; + std::map mTextKeys; + Ogre::Node *mAccumRoot; + Ogre::Bone *mNonAccumRoot; + Ogre::Vector3 mAccumulate; + Ogre::Vector3 mLastPosition; - bool findGroupTimes(const std::string &groupname, GroupTimes *times); + std::vector mSkeletonSources; + + NifOgre::TextKeyMap *mCurrentKeys; + NifOgre::TextKeyMap::const_iterator mNextKey; + Ogre::Animation *mCurrentAnim; + float mCurrentTime; + float mStopTime; + bool mPlaying; + bool mLooping; + + float mAnimVelocity; + float mAnimSpeedMult; + + void calcAnimVelocity(); + + /* Applies the given animation to the given skeleton instance, using the specified time. */ + void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel); + + /* Updates a skeleton instance so that all bones matching the source skeleton (based on + * bone names) are positioned identically. */ + void updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel); + + /* Updates the animation to the specified time, and returns the movement + * vector since the last update or reset. */ + Ogre::Vector3 updatePosition(float time); + + /* Resets the animation to the time of the specified start marker, without + * moving anything, and set the end time to the specified stop marker. If + * the marker is not found, it resets to the beginning or end respectively. + */ + void reset(const std::string &start, const std::string &stop); + + bool handleEvent(float time, const std::string &evt); + + /* Specifies a list of skeleton names to use as animation sources. */ + void setAnimationSources(const std::vector &names); + + /* Specifies a single skeleton name to use as an animation source. */ + void setAnimationSource(const std::string &name) + { + std::vector names(1, name); + setAnimationSources(names); + } + + void createEntityList(Ogre::SceneNode *node, const std::string &model); public: - Animation(); + Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); - void playGroup(std::string groupname, int mode, int loops); - void skipAnim(); - virtual void runAnimation(float timepassed); + void setController(MWMechanics::CharacterController *controller); + + void updatePtr(const MWWorld::Ptr &ptr); + + bool hasAnimation(const std::string &anim); + + // Specifies the axis' to accumulate on. Non-accumulated axis will just + // move visually, but not affect the actual movement. Each x/y/z value + // should be on the scale of 0 to 1. + void setAccumulation(const Ogre::Vector3 &accum); + + void setSpeed(float speed); + + void setLooping(bool loop); + + void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); + virtual Ogre::Vector3 runAnimation(float timepassed); }; } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index b034e098b..c32e9d1d6 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" #include "renderconst.hpp" #include "npcanimation.hpp" @@ -120,7 +121,8 @@ namespace MWRender void InventoryPreview::update(int sizeX, int sizeY) { - mAnimation->forceUpdate (); + mAnimation->forceUpdate(); + mAnimation->runAnimation(0.0f); mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); @@ -143,8 +145,7 @@ namespace MWRender { mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview); - mAnimation->playGroup ("inventoryhandtohand", 0, 1); - mAnimation->runAnimation (0); + mAnimation->play("inventoryhandtohand", "start", "stop", false); } // -------------------------------------------------------------------------------------------------- @@ -160,6 +161,7 @@ namespace MWRender void RaceSelectionPreview::update(float angle) { + mAnimation->runAnimation(0.0f); mNode->roll(Ogre::Radian(angle), Ogre::SceneNode::TS_LOCAL); mNode->setVisible (true); @@ -174,4 +176,9 @@ namespace MWRender rebuild(); update(0); } + + void RaceSelectionPreview::onSetup () + { + mAnimation->play("idle", "start", "stop", false); + } } diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index d07a03be7..cf1e25069 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -85,6 +85,8 @@ namespace MWRender public: RaceSelectionPreview(); + virtual void onSetup(); + void update(float angle); const ESM::NPC &getPrototype() const { diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 7ee361a6f..73bb80547 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -8,70 +8,54 @@ #include "../mwbase/world.hpp" -using namespace Ogre; -using namespace NifOgre; -namespace MWRender{ +namespace MWRender +{ CreatureAnimation::~CreatureAnimation() { } -CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr): Animation() +CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) + : Animation(ptr) { - mInsert = ptr.getRefData().getBaseNode(); - MWWorld::LiveCellRef *ref = ptr.get(); + MWWorld::LiveCellRef *ref = mPtr.get(); assert (ref->mBase != NULL); if(!ref->mBase->mModel.empty()) { - std::string mesh = "meshes\\" + ref->mBase->mModel; + std::string model = "meshes\\"+ref->mBase->mModel; - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, mesh); + createEntityList(mPtr.getRefData().getBaseNode(), model); for(size_t i = 0;i < mEntityList.mEntities.size();i++) { Ogre::Entity *ent = mEntityList.mEntities[i]; ent->setVisibilityFlags(RV_Actors); bool transparent = false; - for (unsigned int j=0;j < ent->getNumSubEntities() && !transparent; ++j) + for(unsigned int j=0;!transparent && j < ent->getNumSubEntities(); ++j) { Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial(); Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements() && !transparent) + while(!transparent && techIt.hasMoreElements()) { Ogre::Technique* tech = techIt.getNext(); Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements() && !transparent) + while(!transparent && passIt.hasMoreElements()) { Ogre::Pass* pass = passIt.getNext(); - - if (pass->getDepthWriteEnabled() == false) - transparent = true; + transparent = pass->isTransparent(); } } } ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } - if(mEntityList.mSkelBase) - { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - state->setEnabled(true); - state->setLoop(false); - } - } + std::vector names; + if((ref->mBase->mFlags&ESM::Creature::Biped)) + names.push_back("meshes\\base_anim.nif"); + names.push_back(model); + setAnimationSources(names); } } -void CreatureAnimation::runAnimation(float timepassed) -{ - // Placeholder - - Animation::runAnimation(timepassed); -} - } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index e1a7bbb8f..0c277d198 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -3,18 +3,19 @@ #include "animation.hpp" -#include "components/nifogre/ogre_nif_loader.hpp" +namespace MWWorld +{ + class Ptr; +} - -namespace MWRender{ - - class CreatureAnimation: public Animation +namespace MWRender +{ + class CreatureAnimation : public Animation { public: - virtual ~CreatureAnimation(); CreatureAnimation(const MWWorld::Ptr& ptr); - virtual void runAnimation(float timepassed); - + virtual ~CreatureAnimation(); }; } + #endif diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 1548cc1b0..54f288bff 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include @@ -148,9 +150,9 @@ ManualObject *Debugging::createPathgridPoints(const ESM::Pathgrid *pathgrid) return result; } -Debugging::Debugging(SceneNode *mwRoot, OEngine::Physic::PhysicEngine *engine) : - mMwRoot(mwRoot), mEngine(engine), - mSceneMgr(mwRoot->getCreator()), +Debugging::Debugging(SceneNode *root, OEngine::Physic::PhysicEngine *engine) : + mRootNode(root), mEngine(engine), + mSceneMgr(root->getCreator()), mPathgridEnabled(false), mInteriorPathgridNode(NULL), mPathGridRoot(NULL), mGridMatsCreated(false) @@ -206,7 +208,7 @@ void Debugging::togglePathgrid() createGridMaterials(); // add path grid meshes to already loaded cells - mPathGridRoot = mMwRoot->createChildSceneNode(); + mPathGridRoot = mRootNode->createChildSceneNode(); for(CellList::iterator it = mActiveCells.begin(); it != mActiveCells.end(); ++it) { enableCellPathgrid(*it); diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index d312b6d54..6a4eef58f 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -13,6 +12,14 @@ namespace ESM struct Pathgrid; } +namespace OEngine +{ + namespace Physic + { + class PhysicEngine; + } +} + namespace Ogre { class Camera; @@ -47,7 +54,7 @@ namespace MWRender typedef std::vector CellList; CellList mActiveCells; - Ogre::SceneNode *mMwRoot; + Ogre::SceneNode *mRootNode; Ogre::SceneNode *mPathGridRoot; @@ -71,7 +78,7 @@ namespace MWRender Ogre::ManualObject *createPathgridLines(const ESM::Pathgrid *pathgrid); Ogre::ManualObject *createPathgridPoints(const ESM::Pathgrid *pathgrid); public: - Debugging(Ogre::SceneNode* mwRoot, OEngine::Physic::PhysicEngine *engine); + Debugging(Ogre::SceneNode* root, OEngine::Physic::PhysicEngine *engine); ~Debugging(); bool toggleRenderMode (int mode); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 072015f9a..055faaa1f 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -190,7 +190,7 @@ namespace MWRender { imageX = float(x / 8192.f - mMinX) / (mMaxX - mMinX + 1); - imageY = 1.f-float(-z / 8192.f - mMinY) / (mMaxY - mMinY + 1); + imageY = 1.f-float(z / 8192.f - mMinY) / (mMaxY - mMinY + 1); } void GlobalMap::cellTopLeftCornerToImageSpace(int x, int y, float& imageX, float& imageY) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index d878cb86e..918ec4580 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -28,9 +28,6 @@ LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManag mCellCamera = mRendering->getScene()->createCamera("CellCamera"); mCellCamera->setProjectionType(PT_ORTHOGRAPHIC); - // look down -y - const float sqrt0pt5 = 0.707106781; - mCellCamera->setOrientation(Quaternion(sqrt0pt5, -sqrt0pt5, 0, 0)); mCameraNode->attachObject(mCellCamera); } @@ -82,8 +79,8 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell) } else { - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); - Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z); + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); + Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().y); Vector2 length = max-min; // divide into segments @@ -107,6 +104,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell) mInterior = false; mCameraRotNode->setOrientation(Quaternion::IDENTITY); + mCellCamera->setOrientation(Quaternion(Ogre::Math::Cos(Ogre::Degree(0)/2.f), 0, 0, -Ogre::Math::Sin(Ogre::Degree(0)/2.f))); int x = cell->mCell->getGridX(); int y = cell->mCell->getGridY(); @@ -115,49 +113,60 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell) mCameraPosNode->setPosition(Vector3(0,0,0)); - render((x+0.5)*sSize, (-y-0.5)*sSize, -10000, 10000, sSize, sSize, name); + render((x+0.5)*sSize, (y+0.5)*sSize, -10000, 10000, sSize, sSize, name); } void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, AxisAlignedBox bounds) { + // if we're in an empty cell, don't bother rendering anything + if (bounds.isNull ()) + return; + mInterior = true; mBounds = bounds; - Vector2 z(mBounds.getMaximum().y, mBounds.getMinimum().y); + float zMin = mBounds.getMinimum().z; + float zMax = mBounds.getMaximum().z; const Vector2& north = MWBase::Environment::get().getWorld()->getNorthVector(cell); - Radian angle(std::atan2(-north.x, -north.y)); + Radian angle = Ogre::Math::ATan2 (north.x, north.y); mAngle = angle.valueRadians(); - mCameraRotNode->setOrientation(Quaternion(Math::Cos(angle/2.f), 0, Math::Sin(angle/2.f), 0)); + + mCellCamera->setOrientation(Quaternion::IDENTITY); + mCameraRotNode->setOrientation(Quaternion(Math::Cos(mAngle/2.f), 0, 0, -Math::Sin(mAngle/2.f))); // rotate the cell and merge the rotated corners to the bounding box - Vector2 _center(bounds.getCenter().x, bounds.getCenter().z); - Vector3 _c1 = bounds.getCorner(AxisAlignedBox::NEAR_LEFT_BOTTOM); - Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM); - Vector3 _c3 = bounds.getCorner(AxisAlignedBox::NEAR_RIGHT_BOTTOM); - Vector3 _c4 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM); - Vector2 c1(_c1.x, _c1.z); - Vector2 c2(_c2.x, _c2.z); - Vector2 c3(_c3.x, _c3.z); - Vector2 c4(_c4.x, _c4.z); + Vector2 _center(bounds.getCenter().x, bounds.getCenter().y); + Vector3 _c1 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM); + Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM); + Vector3 _c3 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_TOP); + Vector3 _c4 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_TOP); + + Vector2 c1(_c1.x, _c1.y); + Vector2 c2(_c2.x, _c2.y); + Vector2 c3(_c3.x, _c3.y); + Vector2 c4(_c4.x, _c4.y); c1 = rotatePoint(c1, _center, mAngle); c2 = rotatePoint(c2, _center, mAngle); c3 = rotatePoint(c3, _center, mAngle); c4 = rotatePoint(c4, _center, mAngle); - mBounds.merge(Vector3(c1.x, 0, c1.y)); - mBounds.merge(Vector3(c2.x, 0, c2.y)); - mBounds.merge(Vector3(c3.x, 0, c3.y)); - mBounds.merge(Vector3(c4.x, 0, c4.y)); + mBounds.merge(Vector3(c1.x, c1.y, 0)); + mBounds.merge(Vector3(c2.x, c2.y, 0)); + mBounds.merge(Vector3(c3.x, c3.y, 0)); + mBounds.merge(Vector3(c4.x, c4.y, 0)); - Vector2 center(mBounds.getCenter().x, mBounds.getCenter().z); + // apply a little padding + mBounds.scale ((mBounds.getSize ()+Ogre::Vector3(1000,1000,0)) / mBounds.getSize ()); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); - Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z); + Vector2 center(mBounds.getCenter().x, mBounds.getCenter().y); + + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); + Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().y); Vector2 length = max-min; - mCameraPosNode->setPosition(Vector3(center.x, 0, center.y)); + mCameraPosNode->setPosition(Vector3(center.x, center.y, 0)); // divide into segments const int segsX = std::ceil( length.x / sSize ); @@ -172,7 +181,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell, Vector2 start = min + Vector2(sSize*x,sSize*y); Vector2 newcenter = start + 4096; - render(newcenter.x - center.x, newcenter.y - center.y, z.y, z.x, sSize, sSize, + render(newcenter.x - center.x, newcenter.y - center.y, zMin, zMax, sSize, sSize, cell->mCell->mName + "_" + coordStr(x,y)); } } @@ -193,7 +202,7 @@ void LocalMap::render(const float x, const float y, mRendering->getScene()->setAmbientLight(ColourValue(1,1,1)); mRenderingManager->disableLights(); - mCameraNode->setPosition(Vector3(x, zhigh+100000, y)); + mCameraNode->setPosition(Vector3(x, y, zhigh+100000)); //mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 ); mCellCamera->setFarClipDistance(0); // infinite @@ -272,15 +281,15 @@ void LocalMap::render(const float x, const float y, void LocalMap::getInteriorMapPosition (Ogre::Vector2 pos, float& nX, float& nY, int& x, int& y) { - pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle); + pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().y), mAngle); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); x = std::ceil((pos.x - min.x)/sSize)-1; y = std::ceil((pos.y - min.y)/sSize)-1; nX = (pos.x - min.x - sSize*x)/sSize; - nY = (pos.y - min.y - sSize*y)/sSize; + nY = 1.0-(pos.y - min.y - sSize*y)/sSize; } bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interior) @@ -311,19 +320,19 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni int x,y; float u,v; - Vector2 pos(position.x, position.z); + Vector2 pos(position.x, position.y); if (mInterior) getInteriorMapPosition(pos, u,v, x,y); - Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis(); + Vector3 playerdirection = mCameraRotNode->convertWorldToLocalOrientation(orientation).yAxis(); - Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z); + Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().y); if (!mInterior) { x = std::ceil(pos.x / sSize)-1; - y = std::ceil(-pos.y / sSize)-1; + y = std::ceil(pos.y / sSize)-1; mCellX = x; mCellY = y; } @@ -337,7 +346,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni if (!mInterior) { u = std::abs((pos.x - (sSize*x))/sSize); - v = 1-std::abs((pos.y + (sSize*y))/sSize); + v = 1.0-std::abs((pos.y - (sSize*y))/sSize); texBaseName = "Cell_"; } else @@ -346,15 +355,13 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni } MWBase::Environment::get().getWindowManager()->setPlayerPos(u, v); - MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, -playerdirection.z); + MWBase::Environment::get().getWindowManager()->setPlayerDir(playerdirection.x, playerdirection.y); // explore radius (squared) const float sqrExploreRadius = (mInterior ? 0.01 : 0.09) * sFogOfWarResolution*sFogOfWarResolution; const float exploreRadius = (mInterior ? 0.1 : 0.3) * sFogOfWarResolution; // explore radius from 0 to sFogOfWarResolution const float exploreRadiusUV = exploreRadius / sFogOfWarResolution; // explore radius from 0 to 1 (UV space) - int intExtMult = mInterior ? 1 : -1; // interior and exterior have reversed Y coordinates (interior: top to bottom) - // change the affected fog of war textures (in a 3x3 grid around the player) for (int mx = -1; mx<2; ++mx) { @@ -375,7 +382,7 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni if (!affected) continue; - std::string texName = texBaseName + coordStr(x+mx,y+my*intExtMult); + std::string texName = texBaseName + coordStr(x+mx,y+my*-1); TexturePtr tex = TextureManager::getSingleton().getByName(texName+"_fog"); if (!tex.isNull()) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d33bdda91..1f1cd0530 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -5,295 +5,257 @@ #include #include "../mwworld/esmstore.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/class.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "renderconst.hpp" -using namespace Ogre; -using namespace NifOgre; -namespace MWRender{ +namespace MWRender +{ + +const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize] = { + { ESM::PRT_Head, "Head" }, + { ESM::PRT_Hair, "Head" }, + { ESM::PRT_Neck, "Neck" }, + { ESM::PRT_Cuirass, "Chest" }, + { ESM::PRT_Groin, "Groin" }, + { ESM::PRT_Skirt, "Groin" }, + { ESM::PRT_RHand, "Right Hand" }, + { ESM::PRT_LHand, "Left Hand" }, + { ESM::PRT_RWrist, "Right Wrist" }, + { ESM::PRT_LWrist, "Left Wrist" }, + { ESM::PRT_Shield, "Shield" }, + { ESM::PRT_RForearm, "Right Forearm" }, + { ESM::PRT_LForearm, "Left Forearm" }, + { ESM::PRT_RUpperarm, "Right Upper Arm" }, + { ESM::PRT_LUpperarm, "Left Upper Arm" }, + { ESM::PRT_RFoot, "Right Foot" }, + { ESM::PRT_LFoot, "Left Foot" }, + { ESM::PRT_RAnkle, "Right Ankle" }, + { ESM::PRT_LAnkle, "Left Ankle" }, + { ESM::PRT_RKnee, "Right Knee" }, + { ESM::PRT_LKnee, "Left Knee" }, + { ESM::PRT_RLeg, "Right Upper Leg" }, + { ESM::PRT_LLeg, "Left Upper Leg" }, + { ESM::PRT_RPauldron, "Right Clavicle" }, + { ESM::PRT_LPauldron, "Left Clavicle" }, + { ESM::PRT_Weapon, "Weapon" }, + { ESM::PRT_Tail, "Tail" } +}; + NpcAnimation::~NpcAnimation() { - removeEntities(mHead); - removeEntities(mHair); - removeEntities(mNeck); - removeEntities(mChest); - removeEntities(mGroin); - removeEntities(mSkirt); - removeEntities(mHandL); - removeEntities(mHandR); - removeEntities(mWristL); - removeEntities(mWristR); - removeEntities(mForearmL); - removeEntities(mForearmR); - removeEntities(mUpperArmL); - removeEntities(mUpperArmR); - removeEntities(mFootL); - removeEntities(mFootR); - removeEntities(mAnkleL); - removeEntities(mAnkleR); - removeEntities(mKneeL); - removeEntities(mKneeR); - removeEntities(mUpperLegL); - removeEntities(mUpperLegR); - removeEntities(mClavicleL); - removeEntities(mClavicleR); - removeEntities(mTail); + for(size_t i = 0;i < sPartListSize;i++) + removeEntities(mEntityParts[i]); } NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags) - : Animation(), + : Animation(ptr), mStateID(-1), - mInv(inv), mTimeToChange(0), mVisibilityFlags(visibilityFlags), - mRobe(mInv.end()), - mHelmet(mInv.end()), - mShirt(mInv.end()), - mCuirass(mInv.end()), - mGreaves(mInv.end()), - mPauldronL(mInv.end()), - mPauldronR(mInv.end()), - mBoots(mInv.end()), - mPants(mInv.end()), - mGloveL(mInv.end()), - mGloveR(mInv.end()), - mSkirtIter(mInv.end()) + mRobe(inv.end()), + mHelmet(inv.end()), + mShirt(inv.end()), + mCuirass(inv.end()), + mGreaves(inv.end()), + mPauldronL(inv.end()), + mPauldronR(inv.end()), + mBoots(inv.end()), + mPants(inv.end()), + mGloveL(inv.end()), + mGloveR(inv.end()), + mSkirtIter(inv.end()) { - mNpc = ptr.get()->mBase; + mNpc = mPtr.get()->mBase; - for (int init = 0; init < 27; init++) + for(size_t i = 0;i < sPartListSize;i++) { - mPartslots[init] = -1; //each slot is empty - mPartPriorities[init] = 0; + mPartslots[i] = -1; //each slot is empty + mPartPriorities[i] = 0; } const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); + float scale = race->mData.mHeight.mMale; + if(!mNpc->isMale()) + scale = race->mData.mHeight.mFemale; + node->scale(Ogre::Vector3(scale)); + mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; mBodyPrefix = "b_n_" + mNpc->mRace; Misc::StringUtils::toLower(mBodyPrefix); - mInsert = node; - assert(mInsert); - bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, smodel); + createEntityList(node, smodel); for(size_t i = 0;i < mEntityList.mEntities.size();i++) { Ogre::Entity *base = mEntityList.mEntities[i]; - base->getUserObjectBindings ().setUserAny (Ogre::Any(-1)); - + base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); base->setVisibilityFlags(mVisibilityFlags); + bool transparent = false; - for(unsigned int j=0;j < base->getNumSubEntities();++j) + for(unsigned int j=0;!transparent && j < base->getNumSubEntities();++j) { Ogre::MaterialPtr mat = base->getSubEntity(j)->getMaterial(); Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) + while(!transparent && techIt.hasMoreElements()) { Ogre::Technique* tech = techIt.getNext(); Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) + while(!transparent && passIt.hasMoreElements()) { Ogre::Pass* pass = passIt.getNext(); - if (pass->getDepthWriteEnabled() == false) - transparent = true; + transparent = pass->isTransparent(); } } } base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); } - if(mEntityList.mSkelBase) - { - Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); - Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); - while(as.hasMoreElements()) - { - Ogre::AnimationState *state = as.getNext(); - state->setEnabled(true); - state->setLoop(false); - } - } + std::vector skelnames(1, smodel); + if(!mNpc->isMale() && !isBeast) + skelnames.push_back("meshes\\base_anim_female.nif"); + else if(mBodyPrefix.find("argonian") != std::string::npos) + skelnames.push_back("meshes\\argonian_swimkna.nif"); + if(mNpc->mModel.length() > 0) + skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); + setAnimationSources(skelnames); - float scale = race->mData.mHeight.mMale; - if (!mNpc->isMale()) { - scale = race->mData.mHeight.mFemale; - } - mInsert->scale(scale, scale, scale); - - updateParts(); + updateParts(true); } -void NpcAnimation::updateParts() +void NpcAnimation::updateParts(bool forceupdate) { - bool apparelChanged = false; + static const struct { + int numRemoveParts; // Max: 1 + ESM::PartReferenceType removeParts[1]; - const struct { - MWWorld::ContainerStoreIterator *iter; + MWWorld::ContainerStoreIterator NpcAnimation::*part; int slot; + + int numReserveParts; // Max: 12 + ESM::PartReferenceType reserveParts[12]; } slotlist[] = { - { &mRobe, MWWorld::InventoryStore::Slot_Robe }, - { &mSkirtIter, MWWorld::InventoryStore::Slot_Skirt }, - { &mHelmet, MWWorld::InventoryStore::Slot_Helmet }, - { &mCuirass, MWWorld::InventoryStore::Slot_Cuirass }, - { &mGreaves, MWWorld::InventoryStore::Slot_Greaves }, - { &mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron }, - { &mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron }, - { &mBoots, MWWorld::InventoryStore::Slot_Boots }, - { &mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet }, - { &mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet }, - { &mShirt, MWWorld::InventoryStore::Slot_Shirt }, - { &mPants, MWWorld::InventoryStore::Slot_Pants }, + { 0, { }, + &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, + 12, { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, + ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, + ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron } + }, + + { 0, { }, + &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, + 3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg } + }, + + { 1, { ESM::PRT_Hair }, + &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, + 0, { } + }, + + { 0, { }, + &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, + 0, { } + }, }; - for(size_t i = 0;i < sizeof(slotlist)/sizeof(slotlist[0]);i++) + static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); + + MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + for(size_t i = 0;!forceupdate && i < slotlistsize;i++) { - MWWorld::ContainerStoreIterator iter = mInv.getSlot(slotlist[i].slot); - if(*slotlist[i].iter != iter) + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); + if(this->*slotlist[i].part != iter) { - *slotlist[i].iter = iter; - removePartGroup(slotlist[i].slot); - apparelChanged = true; + forceupdate = true; + break; } } + if(!forceupdate) + return; - if(apparelChanged) + for(size_t i = 0;i < slotlistsize;i++) { - if(mRobe != mInv.end()) - { - MWWorld::Ptr ptr = *mRobe; + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); - const ESM::Clothing *clothes = (ptr.get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5); - reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5); + this->*slotlist[i].part = iter; + removePartGroup(slotlist[i].slot); + + if(this->*slotlist[i].part == inv.end()) + continue; + + for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++) + removeIndividualPart(slotlist[i].removeParts[rem]); + + int prio = 1; + MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part; + if(store->getTypeName() == typeid(ESM::Clothing).name()) + { + prio = ((slotlist[i].numReserveParts+1)<<1) + 0; + const ESM::Clothing *clothes = store->get()->mBase; + addPartGroup(slotlist[i].slot, prio, clothes->mParts.mParts); } - if(mSkirtIter != mInv.end()) + else if(store->getTypeName() == typeid(ESM::Armor).name()) { - MWWorld::Ptr ptr = *mSkirtIter; - - const ESM::Clothing *clothes = (ptr.get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts); - reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4); - reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4); + prio = ((slotlist[i].numReserveParts+1)<<1) + 1; + const ESM::Armor *armor = store->get()->mBase; + addPartGroup(slotlist[i].slot, prio, armor->mParts.mParts); } - if(mHelmet != mInv.end()) - { - removeIndividualPart(ESM::PRT_Hair); - const ESM::Armor *armor = (mHelmet->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts); - } - if(mCuirass != mInv.end()) - { - const ESM::Armor *armor = (mCuirass->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts); - } - if(mGreaves != mInv.end()) - { - const ESM::Armor *armor = (mGreaves->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts); - } - - if(mPauldronL != mInv.end()) - { - const ESM::Armor *armor = (mPauldronL->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts); - } - if(mPauldronR != mInv.end()) - { - const ESM::Armor *armor = (mPauldronR->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts); - } - if(mBoots != mInv.end()) - { - if(mBoots->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = (mBoots->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts); - } - else if(mBoots->getTypeName() == typeid(ESM::Armor).name()) - { - const ESM::Armor *armor = (mBoots->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts); - } - } - if(mGloveL != mInv.end()) - { - if(mGloveL->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = (mGloveL->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (mGloveL->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts); - } - } - if(mGloveR != mInv.end()) - { - if(mGloveR->getTypeName() == typeid(ESM::Clothing).name()) - { - const ESM::Clothing *clothes = (mGloveR->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts); - } - else - { - const ESM::Armor *armor = (mGloveR->get())->mBase; - std::vector parts = armor->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts); - } - - } - - if(mShirt != mInv.end()) - { - const ESM::Clothing *clothes = (mShirt->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts); - } - if(mPants != mInv.end()) - { - const ESM::Clothing *clothes = (mPants->get())->mBase; - std::vector parts = clothes->mParts.mParts; - addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts); - } + for(int res = 0;res < slotlist[i].numReserveParts;res++) + reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio); } if(mPartPriorities[ESM::PRT_Head] < 1) @@ -333,21 +295,18 @@ void NpcAnimation::updateParts() if(mPartPriorities[PartTypeList[i].type] < 1) { const ESM::BodyPart *part = NULL; - const MWWorld::Store &partStore = - store.get(); + const MWWorld::Store &partStore = store.get(); - if (!mNpc->isMale()) { + if(!mNpc->isMale()) + { part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]); - if (part == 0) { + if(part == 0) part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]); - } } - if (part == 0) { + if(part == 0) part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]); - } - if (part == 0) { + if(part == 0) part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]); - } if(part) addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); @@ -357,27 +316,69 @@ void NpcAnimation::updateParts() NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename) { - NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, - mInsert, mesh); + NifOgre::EntityList entities = NifOgre::Loader::createEntities(mEntityList.mSkelBase, bonename, + mInsert, mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) { + parts[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); parts[i]->setVisibilityFlags(mVisibilityFlags); - parts[i]->getUserObjectBindings ().setUserAny (Ogre::Any(group)); + + bool transparent = false; + for(unsigned int j=0;!transparent && j < parts[i]->getNumSubEntities();++j) + { + Ogre::MaterialPtr mat = parts[i]->getSubEntity(j)->getMaterial(); + Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); + while(!transparent && techIt.hasMoreElements()) + { + Ogre::Technique* tech = techIt.getNext(); + Ogre::Technique::PassIterator passIt = tech->getPassIterator(); + while(!transparent && passIt.hasMoreElements()) + { + Ogre::Pass* pass = passIt.getNext(); + transparent = pass->isTransparent(); + } + } + } + parts[i]->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); + } + if(entities.mSkelBase) + { + Ogre::AnimationStateSet *aset = entities.mSkelBase->getAllAnimationStates(); + Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); + while(asiter.hasMoreElements()) + { + Ogre::AnimationState *state = asiter.getNext(); + state->setEnabled(false); + state->setLoop(false); + } + Ogre::SkeletonInstance *skelinst = entities.mSkelBase->getSkeleton(); + Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); + while(boneiter.hasMoreElements()) + boneiter.getNext()->setManuallyControlled(true); } return entities; } -void NpcAnimation::runAnimation(float timepassed) +Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) { - if(mTimeToChange > .2) + if(mTimeToChange <= 0.0f) { - mTimeToChange = 0; + mTimeToChange = 0.2f; updateParts(); } - mTimeToChange += timepassed; + mTimeToChange -= timepassed; - Animation::runAnimation(timepassed); + Ogre::Vector3 ret = Animation::runAnimation(timepassed); + const Ogre::SkeletonInstance *skelsrc = mEntityList.mSkelBase->getSkeleton(); + for(size_t i = 0;i < sPartListSize;i++) + { + Ogre::Entity *ent = mEntityParts[i].mSkelBase; + if(!ent) continue; + updateSkeletonInstance(skelsrc, ent->getSkeleton()); + ent->getAllAnimationStates()->_notifyDirty(); + } + return ret; } void NpcAnimation::removeEntities(NifOgre::EntityList &entities) @@ -399,62 +400,14 @@ void NpcAnimation::removeIndividualPart(int type) mPartPriorities[type] = 0; mPartslots[type] = -1; - if(type == ESM::PRT_Head) //0 - removeEntities(mHead); - else if(type == ESM::PRT_Hair) //1 - removeEntities(mHair); - else if(type == ESM::PRT_Neck) //2 - removeEntities(mNeck); - else if(type == ESM::PRT_Cuirass)//3 - removeEntities(mChest); - else if(type == ESM::PRT_Groin)//4 - removeEntities(mGroin); - else if(type == ESM::PRT_Skirt)//5 - removeEntities(mSkirt); - else if(type == ESM::PRT_RHand)//6 - removeEntities(mHandR); - else if(type == ESM::PRT_LHand)//7 - removeEntities(mHandL); - else if(type == ESM::PRT_RWrist)//8 - removeEntities(mWristR); - else if(type == ESM::PRT_LWrist) //9 - removeEntities(mWristL); - else if(type == ESM::PRT_Shield) //10 + for(size_t i = 0;i < sPartListSize;i++) { + if(type == sPartList[i].type) + { + removeEntities(mEntityParts[i]); + break; + } } - else if(type == ESM::PRT_RForearm) //11 - removeEntities(mForearmR); - else if(type == ESM::PRT_LForearm) //12 - removeEntities(mForearmL); - else if(type == ESM::PRT_RUpperarm) //13 - removeEntities(mUpperArmR); - else if(type == ESM::PRT_LUpperarm) //14 - removeEntities(mUpperArmL); - else if(type == ESM::PRT_RFoot) //15 - removeEntities(mFootR); - else if(type == ESM::PRT_LFoot) //16 - removeEntities(mFootL); - else if(type == ESM::PRT_RAnkle) //17 - removeEntities(mAnkleR); - else if(type == ESM::PRT_LAnkle) //18 - removeEntities(mAnkleL); - else if(type == ESM::PRT_RKnee) //19 - removeEntities(mKneeR); - else if(type == ESM::PRT_LKnee) //20 - removeEntities(mKneeL); - else if(type == ESM::PRT_RLeg) //21 - removeEntities(mUpperLegR); - else if(type == ESM::PRT_LLeg) //22 - removeEntities(mUpperLegL); - else if(type == ESM::PRT_RPauldron) //23 - removeEntities(mClavicleR); - else if(type == ESM::PRT_LPauldron) //24 - removeEntities(mClavicleL); - else if(type == ESM::PRT_Weapon) //25 - { - } - else if(type == ESM::PRT_Tail) //26 - removeEntities(mTail); } void NpcAnimation::reserveIndividualPart(int type, int group, int priority) @@ -484,96 +437,23 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, removeIndividualPart(type); mPartslots[type] = group; mPartPriorities[type] = priority; - switch(type) + + for(size_t i = 0;i < sPartListSize;i++) { - case ESM::PRT_Head: //0 - mHead = insertBoundedPart(mesh, group, "Head"); - break; - case ESM::PRT_Hair: //1 - mHair = insertBoundedPart(mesh, group, "Head"); - break; - case ESM::PRT_Neck: //2 - mNeck = insertBoundedPart(mesh, group, "Neck"); - break; - case ESM::PRT_Cuirass: //3 - mChest = insertBoundedPart(mesh, group, "Chest"); - break; - case ESM::PRT_Groin: //4 - mGroin = insertBoundedPart(mesh, group, "Groin"); - break; - case ESM::PRT_Skirt: //5 - mSkirt = insertBoundedPart(mesh, group, "Groin"); - break; - case ESM::PRT_RHand: //6 - mHandR = insertBoundedPart(mesh, group, "Right Hand"); - break; - case ESM::PRT_LHand: //7 - mHandL = insertBoundedPart(mesh, group, "Left Hand"); - break; - case ESM::PRT_RWrist: //8 - mWristR = insertBoundedPart(mesh, group, "Right Wrist"); - break; - case ESM::PRT_LWrist: //9 - mWristL = insertBoundedPart(mesh, group, "Left Wrist"); - break; - case ESM::PRT_Shield: //10 - break; - case ESM::PRT_RForearm: //11 - mForearmR = insertBoundedPart(mesh, group, "Right Forearm"); - break; - case ESM::PRT_LForearm: //12 - mForearmL = insertBoundedPart(mesh, group, "Left Forearm"); - break; - case ESM::PRT_RUpperarm: //13 - mUpperArmR = insertBoundedPart(mesh, group, "Right Upper Arm"); - break; - case ESM::PRT_LUpperarm: //14 - mUpperArmL = insertBoundedPart(mesh, group, "Left Upper Arm"); - break; - case ESM::PRT_RFoot: //15 - mFootR = insertBoundedPart(mesh, group, "Right Foot"); - break; - case ESM::PRT_LFoot: //16 - mFootL = insertBoundedPart(mesh, group, "Left Foot"); - break; - case ESM::PRT_RAnkle: //17 - mAnkleR = insertBoundedPart(mesh, group, "Right Ankle"); - break; - case ESM::PRT_LAnkle: //18 - mAnkleL = insertBoundedPart(mesh, group, "Left Ankle"); - break; - case ESM::PRT_RKnee: //19 - mKneeR = insertBoundedPart(mesh, group, "Right Knee"); - break; - case ESM::PRT_LKnee: //20 - mKneeL = insertBoundedPart(mesh, group, "Left Knee"); - break; - case ESM::PRT_RLeg: //21 - mUpperLegR = insertBoundedPart(mesh, group, "Right Upper Leg"); - break; - case ESM::PRT_LLeg: //22 - mUpperLegL = insertBoundedPart(mesh, group, "Left Upper Leg"); - break; - case ESM::PRT_RPauldron: //23 - mClavicleR = insertBoundedPart(mesh , group, "Right Clavicle"); - break; - case ESM::PRT_LPauldron: //24 - mClavicleL = insertBoundedPart(mesh, group, "Left Clavicle"); - break; - case ESM::PRT_Weapon: //25 - break; - case ESM::PRT_Tail: //26 - mTail = insertBoundedPart(mesh, group, "Tail"); + if(type == sPartList[i].type) + { + mEntityParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); break; + } } return true; } -void NpcAnimation::addPartGroup(int group, int priority, std::vector &parts) +void NpcAnimation::addPartGroup(int group, int priority, const std::vector &parts) { for(std::size_t i = 0; i < parts.size(); i++) { - ESM::PartReference &part = parts[i]; + const ESM::PartReference &part = parts[i]; const MWWorld::Store &partStore = MWBase::Environment::get().getWorld()->getStore().get(); @@ -585,15 +465,10 @@ void NpcAnimation::addPartGroup(int group, int priority, std::vectormModel); + addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel); else reserveIndividualPart(part.mPart, group, priority); } } -void NpcAnimation::forceUpdate () -{ - updateParts(); -} - } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index ca76dcc22..aed4868bd 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -3,9 +3,6 @@ #include "animation.hpp" -#include "components/nifogre/ogre_nif_loader.hpp" -#include "../mwworld/inventorystore.hpp" -#include "../mwclass/npc.hpp" #include "../mwworld/containerstore.hpp" namespace ESM @@ -13,49 +10,36 @@ namespace ESM struct NPC; } -namespace MWRender{ +namespace MWWorld +{ + class InventoryStore; +} + +namespace MWRender +{ + +class NpcAnimation : public Animation +{ +public: +struct PartInfo { + ESM::PartReferenceType type; + const char name[32]; +}; -class NpcAnimation: public Animation{ private: - MWWorld::InventoryStore& mInv; + static const size_t sPartListSize = 27; + static const PartInfo sPartList[sPartListSize]; + int mStateID; - int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty - int mPartPriorities[27]; - - //Bounded Parts - NifOgre::EntityList mClavicleL; - NifOgre::EntityList mClavicleR; - NifOgre::EntityList mUpperArmL; - NifOgre::EntityList mUpperArmR; - NifOgre::EntityList mUpperLegL; - NifOgre::EntityList mUpperLegR; - NifOgre::EntityList mForearmL; - NifOgre::EntityList mForearmR; - NifOgre::EntityList mWristL; - NifOgre::EntityList mWristR; - NifOgre::EntityList mKneeR; - NifOgre::EntityList mKneeL; - NifOgre::EntityList mNeck; - NifOgre::EntityList mAnkleL; - NifOgre::EntityList mAnkleR; - NifOgre::EntityList mGroin; - NifOgre::EntityList mSkirt; - NifOgre::EntityList mFootL; - NifOgre::EntityList mFootR; - NifOgre::EntityList mHair; - NifOgre::EntityList mHandL; - NifOgre::EntityList mHandR; - NifOgre::EntityList mHead; - NifOgre::EntityList mChest; - NifOgre::EntityList mTail; + // Bounded Parts + NifOgre::EntityList mEntityParts[sPartListSize]; const ESM::NPC *mNpc; std::string mHeadModel; std::string mHairModel; std::string mBodyPrefix; - float mTimeToChange; MWWorld::ContainerStoreIterator mRobe; MWWorld::ContainerStoreIterator mHelmet; @@ -72,23 +56,32 @@ private: int mVisibilityFlags; -public: - NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, - MWWorld::InventoryStore& inv, int visibilityFlags); - virtual ~NpcAnimation(); + int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty + int mPartPriorities[sPartListSize]; + NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename); - virtual void runAnimation(float timepassed); - void updateParts(); + + void updateParts(bool forceupdate = false); + 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); + void addPartGroup(int group, int priority, const std::vector &parts); - void forceUpdate(); +public: + NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, + MWWorld::InventoryStore& inv, int visibilityFlags); + virtual ~NpcAnimation(); + + virtual Ogre::Vector3 runAnimation(float timepassed); + + void forceUpdate() + { updateParts(true); } }; } + #endif diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index ee36126f8..e91180a42 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -34,19 +34,37 @@ void Objects::clearSceneNode (Ogre::SceneNode *node) for (int i=node->numAttachedObjects()-1; i>=0; --i) { Ogre::MovableObject *object = node->getAttachedObject (i); + + // for entities, destroy any objects attached to bones + if (object->getTypeFlags () == Ogre::SceneManager::ENTITY_TYPE_MASK) + { + Ogre::Entity* ent = static_cast(object); + Ogre::Entity::ChildObjectListIterator children = ent->getAttachedObjectIterator (); + while (children.hasMoreElements()) + { + mRenderer.getScene ()->destroyMovableObject (children.getNext ()); + } + } + node->detachObject (object); mRenderer.getScene()->destroyMovableObject (object); } + + Ogre::Node::ChildNodeIterator it = node->getChildIterator (); + while (it.hasMoreElements ()) + { + clearSceneNode(static_cast(it.getNext ())); + } } -void Objects::setMwRoot(Ogre::SceneNode* root) +void Objects::setRootNode(Ogre::SceneNode* root) { - mMwRoot = root; + mRootNode = root; } void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) { - Ogre::SceneNode* root = mMwRoot; + Ogre::SceneNode* root = mRootNode; Ogre::SceneNode* cellnode; if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) { @@ -87,20 +105,16 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) mIsStatic = static_; } -void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) +void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light) { Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); assert(insert); Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, NULL, mesh); + NifOgre::EntityList entities = NifOgre::Loader::createEntities(insert, 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()) - ); - } + bounds.merge(entities.mEntities[i]->getWorldBoundingBox(true)); + Ogre::Vector3 extents = bounds.getSize(); extents *= insert->getScale(); float size = std::max(std::max(extents.x, extents.y), extents.z); @@ -116,23 +130,21 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) mBounds[ptr.getCell()].merge(bounds); bool transparent = false; - for(size_t i = 0;i < entities.mEntities.size();i++) + for(size_t i = 0;!transparent && i < entities.mEntities.size();i++) { Ogre::Entity *ent = entities.mEntities[i]; - for (unsigned int i=0; igetNumSubEntities(); ++i) + for(unsigned int i=0;!transparent && i < ent->getNumSubEntities(); ++i) { Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial(); Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator(); - while (techIt.hasMoreElements()) + while(!transparent && techIt.hasMoreElements()) { Ogre::Technique* tech = techIt.getNext(); Ogre::Technique::PassIterator passIt = tech->getPassIterator(); - while (passIt.hasMoreElements()) + while(!transparent && passIt.hasMoreElements()) { Ogre::Pass* pass = passIt.getNext(); - - if (pass->getDepthWriteEnabled() == false) - transparent = true; + transparent = pass->isTransparent(); } } } @@ -193,26 +205,40 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); - for(size_t i = 0;i < entities.mEntities.size();i++) + std::vector::reverse_iterator iter = entities.mEntities.rbegin(); + while(iter != entities.mEntities.rend()) { - Ogre::Entity *ent = entities.mEntities[i]; - insert->detachObject(ent); - sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); + Ogre::Node *node = (*iter)->getParentNode(); + sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); - mRenderer.getScene()->destroyEntity(ent); + (*iter)->detachFromParent(); + mRenderer.getScene()->destroyEntity(*iter); + iter++; } } + + if (light) + { + insertLight(ptr, entities.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition()); + } } -void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius) +void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter) { Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle()); assert(insert); - Ogre::Light *light = mRenderer.getScene()->createLight(); - light->setDiffuseColour (r, g, b); MWWorld::LiveCellRef *ref = ptr.get(); + const int color = ref->mBase->mData.mColor; + const float r = ((color >> 0) & 0xFF) / 255.0f; + const float g = ((color >> 8) & 0xFF) / 255.0f; + const float b = ((color >> 16) & 0xFF) / 255.0f; + const float radius = float (ref->mBase->mData.mRadius); + + Ogre::Light *light = mRenderer.getScene()->createLight(); + light->setDiffuseColour (r, g, b); + LightInfo info; info.name = light->getName(); info.radius = radius; @@ -263,7 +289,17 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f light->setAttenuation(r*10, 0, 0, attenuation); } - insert->attachObject(light); + // If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node + if (skelBase && skelBase->getSkeleton ()->hasBone ("AttachLight")) + { + skelBase->attachObjectToBone ("AttachLight", light); + } + else + { + Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter); + childNode->attachObject(light); + } + mLights.push_back(info); } @@ -348,9 +384,9 @@ void Objects::enableLights() std::vector::iterator it = mLights.begin(); while (it != mLights.end()) { - if (mMwRoot->getCreator()->hasLight(it->name)) + if (mRootNode->getCreator()->hasLight(it->name)) { - mMwRoot->getCreator()->getLight(it->name)->setVisible(true); + mRootNode->getCreator()->getLight(it->name)->setVisible(true); ++it; } else @@ -363,9 +399,9 @@ void Objects::disableLights() std::vector::iterator it = mLights.begin(); while (it != mLights.end()) { - if (mMwRoot->getCreator()->hasLight(it->name)) + if (mRootNode->getCreator()->hasLight(it->name)) { - mMwRoot->getCreator()->getLight(it->name)->setVisible(false); + mRootNode->getCreator()->getLight(it->name)->setVisible(false); ++it; } else @@ -418,9 +454,9 @@ void Objects::update(const float dt) std::vector::iterator it = mLights.begin(); while (it != mLights.end()) { - if (mMwRoot->getCreator()->hasLight(it->name)) + if (mRootNode->getCreator()->hasLight(it->name)) { - Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name); + Ogre::Light* light = mRootNode->getCreator()->getLight(it->name); float brightness; float cycle_time; @@ -502,21 +538,17 @@ void Objects::rebuildStaticGeometry() } } -void Objects::updateObjectCell(const MWWorld::Ptr &ptr) +void Objects::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *node; - MWWorld::CellStore *newCell = ptr.getCell(); + MWWorld::CellStore *newCell = cur.getCell(); if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { - node = mMwRoot->createChildSceneNode(); + node = mRootNode->createChildSceneNode(); mCellSceneNodes[newCell] = node; } else { node = mCellSceneNodes[newCell]; } - node->addChild(ptr.getRefData().getBaseNode()); - - /// \note Still unaware how to move aabb and static w/o full rebuild, - /// moving static objects may cause problems - insertMesh(ptr, MWWorld::Class::get(ptr).getModel(ptr)); + node->addChild(cur.getRefData().getBaseNode()); } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 48632dba8..73e95a3c5 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -2,6 +2,7 @@ #define _GAME_RENDER_OBJECTS_H #include +#include #include @@ -52,7 +53,7 @@ class Objects{ std::map mStaticGeometrySmall; std::map mBounds; std::vector mLights; - Ogre::SceneNode* mMwRoot; + Ogre::SceneNode* mRootNode; bool mIsStatic; static int uniqueID; @@ -72,8 +73,8 @@ public: Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer), mIsStatic(false) {} ~Objects(){} void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); - void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh); - void insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius); + void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light=false); + void insertLight (const MWWorld::Ptr& ptr, Ogre::Entity *skelBase=0, Ogre::Vector3 fallbackCenter=Ogre::Vector3(0.0f)); void enableLights(); void disableLights(); @@ -89,12 +90,12 @@ public: void removeCell(MWWorld::CellStore* store); void buildStaticGeometry(MWWorld::CellStore &cell); - void setMwRoot(Ogre::SceneNode* root); + void setRootNode(Ogre::SceneNode* root); void rebuildStaticGeometry(); /// Updates containing cell for object rendering data - void updateObjectCell(const MWWorld::Ptr &ptr); + void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); }; } #endif diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 0e4c76b0e..1ac3b072f 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -113,11 +113,6 @@ namespace MWRender Ogre::Vector3 dir = mCamera->getRealDirection(); Ogre::Vector3 up = mCamera->getRealUp(); - Ogre::Real xch; - xch = pos.y, pos.y = -pos.z, pos.z = xch; - xch = dir.y, dir.y = -dir.z, dir.z = xch; - xch = up.y, up.y = -up.z, up.z = xch; - MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); } @@ -129,14 +124,8 @@ namespace MWRender MWBase::Environment::get().getWindowManager ()->showCrosshair (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); - if (mAnimation) { - mAnimation->runAnimation(duration); - } - mPlayerNode->setVisible( - mVanity.enabled || mPreviewMode || !mFirstPersonView, - false - ); - + /// \fixme We shouldn't hide the whole model, just certain components of the character (head, chest, feet, etc) + mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView); if (mFirstPersonView && !mVanity.enabled) { return; } @@ -313,10 +302,7 @@ namespace MWRender delete mAnimation; mAnimation = anim; - mPlayerNode->setVisible( - mVanity.enabled || mPreviewMode || !mFirstPersonView, - false - ); + mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView); } void Player::setHeight(float height) @@ -332,10 +318,8 @@ namespace MWRender bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) { - float xch; mCamera->getParentSceneNode ()->needUpdate(true); camera = mCamera->getRealPosition(); - xch = camera.z, camera.z = camera.y, camera.y = -xch; player = mPlayerNode->getPosition(); return mFirstPersonView && !mVanity.enabled && !mPreviewMode; diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 29a68ca69..e24f44d68 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -95,7 +95,9 @@ namespace MWRender /// Restore default camera distance for current mode. void setCameraDistance(); - void setAnimation(MWRender::NpcAnimation *anim); + void setAnimation(NpcAnimation *anim); + NpcAnimation *getAnimation() const + { return mAnimation; } void setHeight(float height); float getHeight(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ae7d6612b..fe3dc776d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -18,9 +18,12 @@ #include #include +#include + #include -#include "../mwworld/esmstore.hpp" #include +#include "../mwworld/esmstore.hpp" +#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" @@ -138,26 +141,20 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const applyCompositors(); - // 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 = mRendering.getScene()->getRootSceneNode(); - mMwRoot = rt->createChildSceneNode("mwRoot"); - mMwRoot->pitch(Degree(-90)); + mRootNode = rt; - mObjects.setMwRoot(mMwRoot); - mActors.setMwRoot(mMwRoot); + mObjects.setRootNode(mRootNode); + mActors.setRootNode(mRootNode); - Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player"); + Ogre::SceneNode *playerNode = mRootNode->createChildSceneNode ("player"); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mShadows = new Shadows(&mRendering); mTerrainManager = new TerrainManager(mRendering.getScene(), this); - mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera()); + mSkyManager = new SkyManager(mRootNode, mRendering.getCamera()); mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); @@ -166,7 +163,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mSun = 0; - mDebugging = new Debugging(mMwRoot, engine); + mDebugging = new Debugging(mRootNode, engine); mLocalMap = new MWRender::LocalMap(&mRendering, this); setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); @@ -252,8 +249,7 @@ void RenderingManager::removeObject (const MWWorld::Ptr& ptr) void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position) { /// \todo move this to the rendering-subsystems - mRendering.getScene()->getSceneNode (ptr.getRefData().getHandle())-> - setPosition (position); + ptr.getRefData().getBaseNode()->setPosition(position); } void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale) @@ -300,23 +296,19 @@ bool RenderingManager::rotateObject( const MWWorld::Ptr &ptr, Ogre::Vector3 &rot } void -RenderingManager::moveObjectToCell( - const MWWorld::Ptr& ptr, - const Ogre::Vector3& pos, - MWWorld::CellStore *store) +RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *child = - mRendering.getScene()->getSceneNode(ptr.getRefData().getHandle()); + mRendering.getScene()->getSceneNode(old.getRefData().getHandle()); Ogre::SceneNode *parent = child->getParentSceneNode(); parent->removeChild(child); - if (MWWorld::Class::get(ptr).isActor()) { - mActors.updateObjectCell(ptr); + if (MWWorld::Class::get(old).isActor()) { + mActors.updateObjectCell(old, cur); } else { - mObjects.updateObjectCell(ptr); + mObjects.updateObjectCell(old, cur); } - child->setPosition(pos); } void RenderingManager::update (float duration, bool paused) @@ -324,7 +316,7 @@ void RenderingManager::update (float duration, bool paused) Ogre::Vector3 orig, dest; mPlayer->setCameraDistance(); if (!mPlayer->getPosition(orig, dest)) { - orig.z += mPlayer->getHeight() * mMwRoot->getScale().z; + orig.z += mPlayer->getHeight() * mRootNode->getScale().z; btVector3 btOrig(orig.x, orig.y, orig.z); btVector3 btDest(dest.x, dest.y, dest.z); @@ -368,11 +360,13 @@ void RenderingManager::update (float duration, bool paused) float *fpos = data.getPosition().pos; // only for LocalMap::updatePlayer() - Ogre::Vector3 pos(fpos[0], -fpos[2], -fpos[1]); + Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); Ogre::SceneNode *node = data.getBaseNode(); + //Ogre::Quaternion orient = + //node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); Ogre::Quaternion orient = - node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); +node->_getDerivedOrientation(); mLocalMap->updatePlayer(pos, orient); @@ -383,8 +377,8 @@ void RenderingManager::update (float duration, bool paused) mWater->updateUnderwater( world->isUnderwater( - *world->getPlayer().getPlayer().getCell()->mCell, - Ogre::Vector3(cam.x, -cam.z, cam.y)) + world->getPlayer().getPlayer().getCell(), + cam) ); mWater->update(duration); } @@ -580,17 +574,6 @@ void RenderingManager::toggleLight() setAmbientMode(); } -void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, - int mode, int number) -{ - mActors.playAnimationGroup(ptr, groupName, mode, number); -} - -void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr) -{ - mActors.skipAnimation(ptr); -} - void RenderingManager::setSunColour(const Ogre::ColourValue& colour) { if (!mSunEnabled) return; @@ -627,8 +610,7 @@ void RenderingManager::sunDisable() void RenderingManager::setSunDirection(const Ogre::Vector3& direction) { // direction * -1 (because 'direction' is camera to sun vector and not sun to camera), - // then convert from MW to ogre coordinates (swap y,z and make y negative) - if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.z, direction.y)); + if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.y, -direction.z)); mSkyManager->setSunDirection(direction); } @@ -929,6 +911,15 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } +Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) +{ + Animation *anim = mActors.getAnimation(ptr); + if(!anim && ptr.getRefData().getHandle() == "player") + anim = mPlayer->getAnimation(); + return anim; +} + + void RenderingManager::playVideo(const std::string& name, bool allowSkipping) { mVideoPlayer->playVideo ("video/" + name, allowSkipping); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 68f2d79c3..53c63cfed 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -46,6 +46,7 @@ namespace MWRender class ExternalRendering; class GlobalMap; class VideoPlayer; + class Animation; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -122,9 +123,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setWaterHeight(const float height); void toggleWater(); - /// Moves object rendering part to proper container - /// \param store Cell the object was in previously (\a ptr has already been updated to the new cell). - void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store); + /// Updates object rendering after cell change + /// \param old Object reference in previous cell + /// \param cur Object reference in new cell + void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); void update (float duration, bool paused); @@ -166,18 +168,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList /// configure fog manually void configureFog(const float density, const Ogre::ColourValue& colour); - void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, - int number = 1); - ///< Run animation for a MW-reference. Calls to this function for references that are currently not - /// in the rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run - - void skipAnimation (const MWWorld::Ptr& ptr); - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the rendered scene should be ignored. - Ogre::Vector4 boundingBoxToScreen(Ogre::AxisAlignedBox bounds); ///< transform the specified bounding box (in world coordinates) into screen coordinates. /// @return packed vector4 (min_x, min_y, max_x, max_y) @@ -196,6 +186,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); + Animation* getAnimation(const MWWorld::Ptr &ptr); + void playVideo(const std::string& name, bool allowSkipping); void stopVideo(); @@ -236,10 +228,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList Ogre::ColourValue mAmbientColor; Ogre::Light* mSun; - /// Root node for all objects added to the scene. This is rotated so - /// that the OGRE coordinate system matches that used internally in - /// Morrowind. - Ogre::SceneNode *mMwRoot; + Ogre::SceneNode *mRootNode; OEngine::Physic::PhysicEngine* mPhysicsEngine; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index f8499d1e5..062079693 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -110,7 +110,7 @@ void BillboardObject::setPosition(const Vector3& pPosition) Vector3 BillboardObject::getPosition() const { Vector3 p = mNode->_getDerivedPosition() - mNode->getParentSceneNode()->_getDerivedPosition(); - return Vector3(p.x, -p.z, p.y); + return p; } void BillboardObject::setVisibilityFlags(int flags) @@ -203,7 +203,7 @@ unsigned int Moon::getPhaseInt() const return 0; } -SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) +SkyManager::SkyManager (SceneNode* root, Camera* pCamera) : mHour(0.0f) , mDay(0) , mMonth(0) @@ -234,9 +234,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera) , mCloudAnimationTimer(0.f) , mMoonRed(false) { - mSceneMgr = pMwRoot->getCreator(); + mSceneMgr = root->getCreator(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates mRootNode->setInheritOrientation(false); } @@ -282,7 +281,7 @@ void SkyManager::create() // Stars mAtmosphereNight = mRootNode->createChildSceneNode(); - NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, NULL, "meshes\\sky_night_01.nif"); + NifOgre::EntityList entities = NifOgre::Loader::createEntities(mAtmosphereNight, "meshes\\sky_night_01.nif"); for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++) { Entity* night1_ent = entities.mEntities[i]; @@ -307,26 +306,28 @@ void SkyManager::create() // Atmosphere (day) mAtmosphereDay = mRootNode->createChildSceneNode(); - entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, NULL, "meshes\\sky_atmosphere.nif"); + entities = NifOgre::Loader::createEntities(mAtmosphereDay, "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"); + for(unsigned int j = 0;j < atmosphere_ent->getNumSubEntities();j++) + atmosphere_ent->getSubEntity (j)->setMaterialName("openmw_atmosphere"); } // Clouds SceneNode* clouds_node = mRootNode->createChildSceneNode(); - entities = NifOgre::NIFLoader::createEntities(clouds_node, NULL, "meshes\\sky_clouds_01.nif"); + entities = NifOgre::Loader::createEntities(clouds_node, "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"); + for(unsigned int j = 0;j < clouds_ent->getNumSubEntities();j++) + clouds_ent->getSubEntity(j)->setMaterialName("openmw_clouds"); clouds_ent->setCastShadows(false); } @@ -389,7 +390,6 @@ void SkyManager::update(float duration) // increase the strength of the sun glare effect depending // on how directly the player is looking at the sun Vector3 sun = mSunGlare->getPosition(); - sun = Vector3(sun.x, sun.z, -sun.y); Vector3 cam = mCamera->getRealDirection(); const Degree angle = sun.angleBetween( cam ); float val = 1- (angle.valueDegrees() / 180.f); diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 52fd7b4aa..feaba3d96 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -112,7 +112,7 @@ namespace MWRender class SkyManager { public: - SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera); + SkyManager(Ogre::SceneNode* root, Ogre::Camera* pCamera); ~SkyManager(); void update(float duration); diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 2c2e9e6fc..47ee6f06f 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -26,7 +26,7 @@ namespace MWRender //---------------------------------------------------------------------------------------------- TerrainManager::TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend) : - mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)), mRendering(rend) + mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Y, mLandSize, mWorldSize)), mRendering(rend) { mTerrainGlobals = OGRE_NEW TerrainGlobalOptions(); @@ -54,8 +54,8 @@ namespace MWRender mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); mTerrainGroup.setOrigin(Vector3(mWorldSize/2, - 0, - -mWorldSize/2)); + mWorldSize/2, + 0)); Terrain::ImportData& importSettings = mTerrainGroup.getDefaultImportSettings(); diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b71022504..a0dedb6bc 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -404,6 +404,8 @@ public: *type = MWSound::SampleType_UInt8; else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = MWSound::SampleType_Int16; + else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_FLT) + *type = MWSound::SampleType_Float32; else fail(std::string("Unsupported sample format: ")+ av_get_sample_fmt_name(mAVStream->codec->sample_fmt)); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index e8f099640..e8ccbb2b4 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -42,9 +42,9 @@ Water::Water (Ogre::Camera *camera, RenderingManager* rend, const ESM::Cell* cel mIsUnderwater = false; - mWaterPlane = Plane(Vector3::UNIT_Y, 0); + mWaterPlane = Plane(Vector3::UNIT_Z, 0); - MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Z); + MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Y); mWater = mSceneManager->createEntity("water"); mWater->setVisibilityFlags(RV_Water); @@ -168,12 +168,12 @@ void Water::setHeight(const float height) { mTop = height; - mWaterPlane = Plane(Vector3::UNIT_Y, height); + mWaterPlane = Plane(Vector3::UNIT_Z, height); // small error due to reflection texture size & reflection distortion - mErrorPlane = Plane(Vector3::UNIT_Y, height - 5); + mErrorPlane = Plane(Vector3::UNIT_Z, height - 5); - mWaterNode->setPosition(0, height, 0); + mWaterNode->setPosition(0, 0, height); sh::Factory::getInstance ().setSharedParameter ("waterLevel", sh::makeProperty(new sh::FloatValue(height))); } @@ -199,7 +199,7 @@ Water::updateUnderwater(bool underwater) Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY) { - return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2)); + return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), gridY * CELL_SIZE + (CELL_SIZE / 2), mTop); } void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) @@ -216,7 +216,7 @@ void Water::preRenderTargetUpdate(const RenderTargetEvent& evt) mReflectionRenderActive = true; Vector3 pos = mCamera->getRealPosition(); - pos.y = mTop*2 - pos.y; + pos.z = mTop*2 - pos.z; mSky->setSkyPosition(pos); mReflectionCamera->enableReflection(mWaterPlane); } diff --git a/apps/openmw/mwscript/animationextensions.cpp b/apps/openmw/mwscript/animationextensions.cpp index 6f9253e5d..fc52e5e16 100644 --- a/apps/openmw/mwscript/animationextensions.cpp +++ b/apps/openmw/mwscript/animationextensions.cpp @@ -9,7 +9,7 @@ #include #include -#include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -27,7 +27,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - MWBase::Environment::get().getWorld()->skipAnimation (ptr); + MWBase::Environment::get().getMechanicsManager()->skipAnimation (ptr); } }; @@ -54,7 +54,7 @@ namespace MWScript throw std::runtime_error ("animation mode out of range"); } - MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, 1); + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, 1); } }; @@ -87,7 +87,7 @@ namespace MWScript throw std::runtime_error ("animation mode out of range"); } - MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, loops); + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops); } }; diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 1f338edbd..fed5877c4 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -140,25 +140,42 @@ namespace MWScript Compiler::Locals& ScriptManager::getLocals (const std::string& name) { - ScriptCollection::iterator iter = mScripts.find (name); - - if (iter==mScripts.end()) { - if (!compile (name)) - { - /// \todo Handle case of cyclic member variable access. Currently this could look up - /// the whole application in an endless recursion. + ScriptCollection::iterator iter = mScripts.find (name); - // failed -> ignore script from now on. - std::vector empty; - mScripts.insert (std::make_pair (name, std::make_pair (empty, Compiler::Locals()))); - throw std::runtime_error ("failed to compile script " + name); - } - - iter = mScripts.find (name); + if (iter!=mScripts.end()) + return iter->second.second; } - return iter->second.second; + { + std::map::iterator iter = mOtherLocals.find (name); + + if (iter!=mOtherLocals.end()) + return iter->second; + } + + Compiler::Locals locals; + + if (const ESM::Script *script = mStore.get().find (name)) + { + int index = 0; + + for (int i=0; imData.mNumShorts; ++i) + locals.declare ('s', script->mVarNames[index++]); + + for (int i=0; imData.mNumLongs; ++i) + locals.declare ('l', script->mVarNames[index++]); + + for (int i=0; imData.mNumFloats; ++i) + locals.declare ('f', script->mVarNames[index++]); + + std::map::iterator iter = + mOtherLocals.insert (std::make_pair (name, locals)).first; + + return iter->second; + } + + throw std::logic_error ("script " + name + " does not exist"); } GlobalScripts& ScriptManager::getGlobalScripts() diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index c4a016eb7..1a856e0c5 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -47,6 +47,7 @@ namespace MWScript ScriptCollection mScripts; GlobalScripts mGlobalScripts; + std::map mOtherLocals; public: diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 00530a962..d5c382a41 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -134,6 +134,18 @@ size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length) return dec; } +static AVSampleFormat ffmpegNonPlanarSampleFormat (AVSampleFormat format) +{ + switch (format) + { + case AV_SAMPLE_FMT_U8P: return AV_SAMPLE_FMT_U8; + case AV_SAMPLE_FMT_S16P: return AV_SAMPLE_FMT_S16; + case AV_SAMPLE_FMT_S32P: return AV_SAMPLE_FMT_S32; + case AV_SAMPLE_FMT_FLTP: return AV_SAMPLE_FMT_FLT; + case AV_SAMPLE_FMT_DBLP: return AV_SAMPLE_FMT_DBL; + default:return format; + } +} void FFmpeg_Decoder::open(const std::string &fname) { @@ -160,7 +172,6 @@ void FFmpeg_Decoder::open(const std::string &fname) { if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - mFormatCtx->streams[j]->codec->request_sample_fmt = AV_SAMPLE_FMT_S16; mStream = &mFormatCtx->streams[j]; break; } @@ -168,6 +179,8 @@ void FFmpeg_Decoder::open(const std::string &fname) if(!mStream) fail("No audio streams in "+fname); + (*mStream)->codec->request_sample_fmt = ffmpegNonPlanarSampleFormat ((*mStream)->codec->sample_fmt); + AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); if(!codec) { @@ -220,6 +233,8 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * *type = SampleType_UInt8; else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; + else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT) + *type = SampleType_Float32; else fail(std::string("Unsupported sample format: ")+ av_get_sample_fmt_name((*mStream)->codec->sample_fmt)); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 67008e2bc..1dc5f9974 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -88,6 +88,51 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type) } } } + if(alIsExtensionPresent("AL_EXT_FLOAT32")) + { + static const struct { + char name[32]; + ChannelConfig chans; + SampleType type; + } fltfmtlist[] = { + { "AL_FORMAT_MONO_FLOAT32", ChannelConfig_Mono, SampleType_Float32 }, + { "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 }, + }; + static const size_t fltfmtlistsize = sizeof(fltfmtlist)/sizeof(fltfmtlist[0]); + + for(size_t i = 0;i < fltfmtlistsize;i++) + { + if(fltfmtlist[i].chans == chans && fltfmtlist[i].type == type) + { + ALenum format = alGetEnumValue(fltfmtlist[i].name); + if(format != 0 && format != -1) + return format; + } + } + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + static const struct { + char name[32]; + ChannelConfig chans; + SampleType type; + } fltmcfmtlist[] = { + { "AL_FORMAT_QUAD32", ChannelConfig_Quad, SampleType_Float32 }, + { "AL_FORMAT_51CHN32", ChannelConfig_5point1, SampleType_Float32 }, + { "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 }, + }; + static const size_t fltmcfmtlistsize = sizeof(fltmcfmtlist)/sizeof(fltmcfmtlist[0]); + + for(size_t i = 0;i < fltmcfmtlistsize;i++) + { + if(fltmcfmtlist[i].chans == chans && fltmcfmtlist[i].type == type) + { + ALenum format = alGetEnumValue(fltmcfmtlist[i].name); + if(format != 0 && format != -1) + return format; + } + } + } + } fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")"); return AL_NONE; diff --git a/apps/openmw/mwsound/sound_decoder.hpp b/apps/openmw/mwsound/sound_decoder.hpp index 29d99e8fa..151b58036 100644 --- a/apps/openmw/mwsound/sound_decoder.hpp +++ b/apps/openmw/mwsound/sound_decoder.hpp @@ -9,7 +9,8 @@ namespace MWSound { enum SampleType { SampleType_UInt8, - SampleType_Int16 + SampleType_Int16, + SampleType_Float32 }; const char *getSampleTypeName(SampleType type); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index c50290680..1b07dfe62 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -607,6 +607,7 @@ namespace MWSound { case SampleType_UInt8: return "U8"; case SampleType_Int16: return "S16"; + case SampleType_Float32: return "Float32"; } return "(unknown sample type)"; } @@ -638,6 +639,7 @@ namespace MWSound { case SampleType_UInt8: frames *= 1; break; case SampleType_Int16: frames *= 2; break; + case SampleType_Float32: frames *= 4; break; } return frames; } diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 59c62e37d..b912f5ccc 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -86,7 +86,7 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), - mIdCache (20, std::pair ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable + mIdCache (40, std::pair ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable mIdCacheIndex (0) {} diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index c76019149..71b24b65d 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -122,6 +122,11 @@ namespace MWWorld return 0; } + float Class::getJump (const Ptr& ptr) const + { + return 0; + } + MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const { throw std::runtime_error ("movement settings not supported by class"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 07dcb8fe0..1a6a16ca0 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -140,6 +140,9 @@ namespace MWWorld virtual float getSpeed (const Ptr& ptr) const; ///< Return movement speed. + virtual float getJump(const MWWorld::Ptr &ptr) const; + ///< Return jump velocity (not accounting for movement) + virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; ///< Return desired movement. diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 9e2e94143..65cbc1164 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -9,6 +9,10 @@ #include #include +#include +#include +#include + #include //#include "../mwbase/world.hpp" // FIXME @@ -21,23 +25,191 @@ using namespace Ogre; namespace MWWorld { - PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : - mRender(_rend), mEngine(0), mFreeFly (true) - { + static const float sMaxSlope = 60.0f; + static const float sStepSize = 30.0f; + // Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared. + static const int sMaxIterations = 50; - playerphysics = new playerMove; + class MovementSolver + { + private: + static bool stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime, + const Ogre::Vector3 &halfExtents, bool isInterior, + OEngine::Physic::PhysicEngine *engine) + { + traceResults trace; // no initialization needed + + newtrace(&trace, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), + halfExtents, isInterior, engine); + if(trace.fraction == 0.0f) + return false; + + newtrace(&trace, trace.endpos, trace.endpos + velocity*remainingTime, + halfExtents, isInterior, engine); + if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope)) + return false; + + newtrace(&trace, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine); + if(getSlope(trace.planenormal) <= sMaxSlope) + { + // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. + position = trace.endpos; + return true; + } + + return false; + } + + static void clipVelocity(Ogre::Vector3& inout, const Ogre::Vector3& normal, float overbounce=1.0f) + { + //Math stuff. Basically just project the velocity vector onto the plane represented by the normal. + //More specifically, it projects velocity onto the normal, takes that result, multiplies it by overbounce and then subtracts it from velocity. + float backoff = inout.dotProduct(normal); + if(backoff < 0.0f) + backoff *= overbounce; + else + backoff /= overbounce; + + inout -= normal*backoff; + } + + static void projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction) + { + Ogre::Vector3 normalizedDirection(direction); + normalizedDirection.normalise(); + + // no divide by normalizedDirection.length necessary because it's normalized + velocity = normalizedDirection * velocity.dotProduct(normalizedDirection); + } + + static float getSlope(const Ogre::Vector3 &normal) + { + return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees(); + } + + public: + static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, + bool gravity, OEngine::Physic::PhysicEngine *engine) + { + const ESM::Position &refpos = ptr.getRefData().getPosition(); + Ogre::Vector3 position(refpos.pos); + + /* Anything to collide with? */ + OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + if(!physicActor || !physicActor->getCollisionMode()) + { + // FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why? + return position + (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)* + Ogre::Quaternion(Ogre::Radian( refpos.rot[1]), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + movement; + } + + traceResults trace; //no initialization needed + bool onground = false; + float remainingTime = time; + bool isInterior = !ptr.getCell()->isExterior(); + Ogre::Vector3 halfExtents = physicActor->getHalfExtents(); + + Ogre::Vector3 velocity; + if(!gravity) + { + velocity = (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)* + Ogre::Quaternion(Ogre::Radian( refpos.rot[1]), Ogre::Vector3::UNIT_Y)* + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + movement / time; + } + else + { + if(!(movement.z > 0.0f)) + { + newtrace(&trace, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine); + if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope) + onground = true; + } + + velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z) * + movement / time; + velocity.z += physicActor->getVerticalForce(); + } + + Ogre::Vector3 clippedVelocity(velocity); + if(onground) + { + // if we're on the ground, force velocity to track it + clippedVelocity.z = velocity.z = std::max(0.0f, velocity.z); + clipVelocity(clippedVelocity, trace.planenormal); + } + + const Ogre::Vector3 up(0.0f, 0.0f, 1.0f); + Ogre::Vector3 newPosition = position; + int iterations = 0; + do { + // trace to where character would go if there were no obstructions + newtrace(&trace, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, isInterior, engine); + newPosition = trace.endpos; + remainingTime = remainingTime * (1.0f-trace.fraction); + + // check for obstructions + if(trace.fraction < 1.0f) + { + //std::cout<<"angle: "< 0.0f); + + if(onground) + { + newtrace(&trace, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), halfExtents, isInterior, engine); + if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope) + newPosition.z = trace.endpos.z + 2.0f; + else + onground = false; + } + physicActor->setOnGround(onground); + physicActor->setVerticalForce(!onground ? clippedVelocity.z - time*627.2f : 0.0f); + + return newPosition; + } + }; + + + PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : + mRender(_rend), mEngine(0) + { // Create physics. shapeLoader is deleted by the physic engine NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); - playerphysics->mEngine = mEngine; } PhysicsSystem::~PhysicsSystem() { delete mEngine; - delete playerphysics; - } + OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine() { return mEngine; @@ -92,9 +264,8 @@ namespace MWWorld Ogre::Vector3 to = ray.getPoint(queryDistance); btVector3 _from, _to; - // OGRE to MW coordinates - _from = btVector3(from.x, -from.z, from.y); - _to = btVector3(to.x, -to.z, to.y); + _from = btVector3(from.x, from.y, from.z); + _to = btVector3(to.x, to.y, to.z); std::vector < std::pair > results; /* auto */ results = mEngine->rayTest2(_from,_to); @@ -106,15 +277,7 @@ namespace MWWorld void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight) { - playerphysics->hasWater = hasWater; - if(hasWater){ - playerphysics->waterHeight = waterHeight; - } - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) - { - it->second->setCurrentWater(hasWater, waterHeight); - } - + // TODO: store and use } btVector3 PhysicsSystem::getRayPoint(float extent) @@ -123,7 +286,7 @@ namespace MWWorld Ray centerRay = mRender.getCamera()->getCameraToViewportRay( mRender.getViewport()->getWidth()/2, mRender.getViewport()->getHeight()/2); - btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); + btVector3 result(centerRay.getPoint(extent).x,centerRay.getPoint(extent).y,centerRay.getPoint(extent).z); return result; } @@ -131,7 +294,7 @@ namespace MWWorld { //get a ray pointing to the center of the viewport Ray centerRay = mRender.getCamera()->getCameraToViewportRay(mouseX, mouseY); - btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); + btVector3 result(centerRay.getPoint(extent).x,centerRay.getPoint(extent).y,centerRay.getPoint(extent).z); return result; } @@ -171,9 +334,8 @@ namespace MWWorld Ogre::Vector3 to = ray.getPoint(200); /// \todo make this distance (ray length) configurable btVector3 _from, _to; - // OGRE to MW coordinates - _from = btVector3(from.x, -from.z, from.y); - _to = btVector3(to.x, -to.z, to.y); + _from = btVector3(from.x, from.y, from.z); + _to = btVector3(to.x, to.y, to.z); std::pair result = mEngine->rayTest(_from, _to); @@ -185,71 +347,11 @@ namespace MWWorld } } - void PhysicsSystem::doPhysics(float dt, const std::vector >& actors) + Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity) { - //set the DebugRenderingMode. To disable it,set it to 0 - //eng->setDebugRenderingMode(1); - - //set the movement keys to 0 (no movement) for every actor) - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) - { - OEngine::Physic::PhysicActor* act = it->second; - act->setMovement(0,0,0); - } - - playerMove::playercmd& pm_ref = playerphysics->cmd; - - - pm_ref.rightmove = 0; - pm_ref.forwardmove = 0; - pm_ref.upmove = 0; - - - //playerphysics->ps.move_type = PM_NOCLIP; - for (std::vector >::const_iterator iter (actors.begin()); - iter!=actors.end(); ++iter) - { - //dirty stuff to get the camera orientation. Must be changed! - if (iter->first == "player") { - playerphysics->ps.viewangles.x = - Ogre::Radian(mPlayerData.pitch).valueDegrees(); - - - - playerphysics->ps.viewangles.y = - Ogre::Radian(mPlayerData.yaw).valueDegrees() + 90; - - pm_ref.rightmove = iter->second.x; - pm_ref.forwardmove = -iter->second.y; - pm_ref.upmove = iter->second.z; - } - } - mEngine->stepSimulation(dt); + return MovementSolver::move(ptr, movement, time, gravity, mEngine); } - std::vector< std::pair > PhysicsSystem::doPhysicsFixed ( - const std::vector >& actors) - { - Pmove(playerphysics); - - - std::vector< std::pair > response; - for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) - { - - Ogre::Vector3 coord = it->second->getPosition(); - if(it->first == "player"){ - - coord = playerphysics->ps.origin ; - - } - - - response.push_back(std::pair(it->first, coord)); - } - - return response; - } void PhysicsSystem::addHeightField (float* heights, int x, int y, float yoffset, @@ -291,46 +393,20 @@ namespace MWWorld void PhysicsSystem::moveObject (const Ptr& ptr) { - Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - std::string handle = node->getName(); - Ogre::Vector3 position = node->getPosition(); - if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) - { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow - // start positions others than 0, 0, 0 - - - if(dynamic_cast(body->getCollisionShape()) == NULL){ - btTransform tr = body->getWorldTransform(); - tr.setOrigin(btVector3(position.x,position.y,position.z)); - body->setWorldTransform(tr); - } - else{ - //For objects that contain a box shape. - //Do any such objects exist? Perhaps animated objects? - mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, position, node->getOrientation()); - } - } - if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) - { - // TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow - // start positions others than 0, 0, 0 - if (handle == "player") - { - playerphysics->ps.origin = position; - } - else - { - act->setPosition(position); - } - } + Ogre::SceneNode *node = ptr.getRefData().getBaseNode(); + const std::string &handle = node->getName(); + const Ogre::Vector3 &position = node->getPosition(); + if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle)) + body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z)); + else if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle)) + physact->setPosition(position); } void PhysicsSystem::rotateObject (const Ptr& ptr) { Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - std::string handle = node->getName(); - Ogre::Quaternion rotation = node->getOrientation(); + const std::string &handle = node->getName(); + const Ogre::Quaternion &rotation = node->getOrientation(); if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { //Needs to be changed @@ -348,7 +424,7 @@ namespace MWWorld void PhysicsSystem::scaleObject (const Ptr& ptr) { Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); - std::string handle = node->getName(); + const std::string &handle = node->getName(); if(handleToMesh.find(handle) != handleToMesh.end()) { removeObject(handle); @@ -361,7 +437,6 @@ namespace MWWorld bool PhysicsSystem::toggleCollisionMode() { - playerphysics->ps.move_type = (playerphysics->ps.move_type == PM_NOCLIP ? PM_NORMAL : PM_NOCLIP); for(std::map::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) { if (it->first=="player") @@ -372,12 +447,10 @@ namespace MWWorld if(cmode) { act->enableCollisions(false); - mFreeFly = true; return false; } else { - mFreeFly = false; act->enableCollisions(true); return true; } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 81715c31e..60c8246ae 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -1,12 +1,27 @@ #ifndef GAME_MWWORLD_PHYSICSSYSTEM_H #define GAME_MWWORLD_PHYSICSSYSTEM_H -#include -#include "ptr.hpp" -#include +#include + +#include + + +namespace OEngine +{ + namespace Render + { + class OgreRenderer; + } + namespace Physic + { + class PhysicEngine; + } +} namespace MWWorld { + class World; + class Ptr; class PhysicsSystem { @@ -14,12 +29,6 @@ namespace MWWorld PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); - void doPhysics(float duration, const std::vector >& actors); - ///< do physics with dt - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed - - std::vector< std::pair > doPhysicsFixed (const std::vector >& actors); - ///< do physics with fixed timestep - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed - void addObject (const MWWorld::Ptr& ptr); void addActor (const MWWorld::Ptr& ptr); @@ -41,6 +50,8 @@ namespace MWWorld bool toggleCollisionMode(); + Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity); + std::pair getFacedHandle (MWWorld::World& world, float queryDistance); std::vector < std::pair > getFacedHandles (float queryDistance); std::vector < std::pair > getFacedHandles (float mouseX, float mouseY, float queryDistance); @@ -74,8 +85,6 @@ namespace MWWorld OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; - bool mFreeFly; - playerMove* playerphysics; std::map handleToMesh; PhysicsSystem (const PhysicsSystem&); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 3414ba448..76d44a5d7 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -71,6 +71,12 @@ namespace MWWorld MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value; } + void Player::setRunState(bool run) + { + MWWorld::Ptr ptr = getPlayer(); + MWWorld::Class::get(ptr).setStance(ptr, MWWorld::Class::Run, run); + } + void Player::toggleRunning() { MWWorld::Ptr ptr = getPlayer(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 1c1ef76cf..4ef4d6771 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -65,6 +65,7 @@ namespace MWWorld void setForwardBackward (int value); void setUpDown(int value); + void setRunState(bool run); void toggleRunning(); }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b917a8916..ecf783d6c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -1,5 +1,6 @@ #include "scene.hpp" +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" /// FIXME @@ -7,9 +8,11 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "physicssystem.hpp" #include "player.hpp" #include "localscripts.hpp" #include "esmstore.hpp" +#include "class.hpp" #include "cellfunctors.hpp" @@ -24,13 +27,10 @@ namespace { const MWWorld::Class& class_ = MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.mList.begin(), &cell)); - - int numRefs = cellRefList.mList.size(); int current = 0; for (typename T::List::iterator it = cellRefList.mList.begin(); it != cellRefList.mList.end(); it++) { - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, current, numRefs); ++current; if (it->mData.getCount() || it->mData.isEnabled()) @@ -52,10 +52,6 @@ namespace } } } - else - { - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 1, 0, 1); - } } } @@ -98,7 +94,7 @@ namespace MWWorld //mPhysics->removeObject("Unnamed_43"); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); - MWBase::Environment::get().getMechanicsManager()->dropActors (*iter); + MWBase::Environment::get().getMechanicsManager()->drop (*iter); MWBase::Environment::get().getSoundManager()->stopSound (*iter); mActiveCells.erase(*iter); } @@ -164,7 +160,7 @@ namespace MWWorld MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); - mechMgr->addActor(player); + mechMgr->add(player); mechMgr->watchActor(player); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); @@ -173,11 +169,17 @@ namespace MWWorld void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { Nif::NIFFile::CacheLock cachelock; + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); mRendering.preCellChange(mCurrentCell); // remove active - MWBase::Environment::get().getMechanicsManager()->removeActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + MWBase::Environment::get().getMechanicsManager()->remove(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); + + std::string loadingExteriorText; + + loadingExteriorText = gmst.find ("sLoadingMessage3")->getString(); CellStoreCollection::iterator active = mActiveCells.begin(); @@ -213,8 +215,6 @@ namespace MWWorld continue; } } - - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload); unloadCell (active++); ++current; } @@ -263,7 +263,9 @@ namespace MWWorld { CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y); - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, current, numLoad); + //Loading Exterior loading text + MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingExteriorText, 0, current, numLoad); + loadCell (cell); ++current; } @@ -322,6 +324,13 @@ namespace MWWorld void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position) { + + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + std::string loadingInteriorText; + loadingInteriorText = gmst.find ("sLoadingMessage2")->getString(); + CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName); bool loadcell = (mCurrentCell == NULL); if(!loadcell) @@ -357,8 +366,6 @@ namespace MWWorld active = mActiveCells.begin(); while (active!=mActiveCells.end()) { - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Unloading cells", 0, current, numUnload); - unloadCell (active++); ++current; } @@ -366,7 +373,9 @@ namespace MWWorld // Load cell. std::cout << "cellName: " << cell->mCell->mName << std::endl; - MWBase::Environment::get().getWindowManager ()->setLoadingProgress ("Loading cells", 0, 0, 1); + //Loading Interior loading text + MWBase::Environment::get().getWindowManager ()->setLoadingProgress (loadingInteriorText, 0, 0, 1); + loadCell (cell); mCurrentCell = cell; @@ -441,7 +450,7 @@ namespace MWWorld void Scene::removeObjectFromScene (const Ptr& ptr) { - MWBase::Environment::get().getMechanicsManager()->removeActor (ptr); + MWBase::Environment::get().getMechanicsManager()->remove (ptr); MWBase::Environment::get().getSoundManager()->stopSound3D (ptr); mPhysics->removeObject (ptr.getRefData().getHandle()); mRendering.removeObject (ptr); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index ad6ad1559..2333a95ab 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -3,7 +3,7 @@ #include "../mwrender/renderingmanager.hpp" -#include "physicssystem.hpp" +#include "ptr.hpp" #include "globals.hpp" namespace Ogre @@ -34,9 +34,9 @@ namespace MWRender namespace MWWorld { + class PhysicsSystem; class Player; class CellStore; - class Ptr; class Scene { diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index cb18873cc..5fad63ff5 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -17,9 +17,9 @@ namespace MWWorld virtual void setUp() {} virtual void listIdentifier(std::vector &list) const {} - virtual int getSize() const = 0; + virtual size_t getSize() const = 0; virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; - + virtual bool eraseStatic(const std::string &id) {return false;} }; @@ -110,7 +110,7 @@ namespace MWWorld item.mId = Misc::StringUtils::lowerCase(id); typename std::map::const_iterator it = mStatic.find(item.mId); - + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { return &(it->second); } @@ -158,7 +158,7 @@ namespace MWWorld return mShared.end(); } - int getSize() const { + size_t getSize() const { return mShared.size(); } @@ -188,14 +188,14 @@ namespace MWWorld item.mId = Misc::StringUtils::lowerCase(id); typename std::map::iterator it = mStatic.find(item.mId); - + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { mStatic.erase(it); } return true; } - + bool erase(const std::string &id) { std::string key = Misc::StringUtils::lowerCase(id); typename Dynamic::iterator it = mDynamic.find(key); @@ -220,9 +220,15 @@ namespace MWWorld template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { std::string idLower = Misc::StringUtils::lowerCase(id); - mStatic[idLower] = ESM::Dialogue(); - mStatic[idLower].mId = id; // don't smash case here, as this line is printed... I think - mStatic[idLower].load(esm); + + std::map::iterator it = mStatic.find(idLower); + if (it == mStatic.end()) { + it = mStatic.insert( std::make_pair( idLower, ESM::Dialogue() ) ).first; + it->second.mId = id; // don't smash case here, as this line is printed... I think + } + + //I am not sure is it need to load the dialog from a plugin if it was already loaded from prevois plugins + it->second.load(esm); } template <> @@ -269,11 +275,11 @@ namespace MWWorld return ptr; } - int getSize() const { + size_t getSize() const { return mStatic.size(); } - int getSize(size_t plugin) const { + size_t getSize(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].size(); } @@ -338,7 +344,7 @@ namespace MWWorld } - int getSize() const { + size_t getSize() const { return mStatic.size(); } @@ -409,7 +415,7 @@ namespace MWWorld DynamicInt mDynamicInt; DynamicExt mDynamicExt; - + const ESM::Cell *search(const ESM::Cell &cell) const { if (cell.isExterior()) { return search(cell.getGridX(), cell.getGridY()); @@ -481,7 +487,7 @@ namespace MWWorld newCell->mData.mY = y; mExt[std::make_pair(x, y)] = *newCell; delete newCell; - + return &mExt[std::make_pair(x, y)]; } @@ -528,7 +534,7 @@ namespace MWWorld // There some nasty three-way cyclic header dependency involved, which I could only fix by moving // this method. void load(ESM::ESMReader &esm, const std::string &id); - + iterator intBegin() const { return iterator(mSharedInt.begin()); } @@ -567,7 +573,7 @@ namespace MWWorld return 0; } - int getSize() const { + size_t getSize() const { return mSharedInt.size() + mSharedExt.size(); } @@ -701,7 +707,7 @@ namespace MWWorld mStatic.back().load(esm); } - int getSize() const { + size_t getSize() const { return mStatic.size(); } @@ -930,7 +936,7 @@ namespace MWWorld } } - int getSize() const { + size_t getSize() const { return mStatic.size(); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ca054e776..f8efcdbd4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,5 +1,7 @@ #include "worldimp.hpp" +#include + #include #include #include @@ -10,6 +12,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" @@ -18,6 +22,7 @@ #include "player.hpp" #include "manualref.hpp" #include "cellfunctors.hpp" +#include "containerstore.hpp" using namespace Ogre; @@ -183,6 +188,8 @@ namespace MWWorld mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine); + mPhysEngine->setSceneManager(renderer.getScene()); + mWeatherManager = new MWWorld::WeatherManager(mRendering); int idx = 0; @@ -732,6 +739,7 @@ namespace MWWorld removeContainerScripts(ptr); if (isPlayer) + { if (!newCell.isExterior()) changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos); else @@ -740,13 +748,18 @@ namespace MWWorld int cellY = newCell.mCell->getGridY(); mWorldScene->changeCell(cellX, cellY, pos, false); } + } else { if (!mWorldScene->isCellActive(*currCell)) copyObjectToCell(ptr, newCell, pos); else if (!mWorldScene->isCellActive(newCell)) { - MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + MWWorld::Class::get(ptr) + .copyToCell(ptr, newCell) + .getRefData() + .setBaseNode(0); + mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); removeContainerScripts (ptr); @@ -757,7 +770,10 @@ namespace MWWorld MWWorld::Ptr copy = MWWorld::Class::get(ptr).copyToCell(ptr, newCell); - mRendering->moveObjectToCell(copy, vec, currCell); + mRendering->updateObjectCell(ptr, copy); + + MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager(); + mechMgr->updateCell(ptr, copy); std::string script = MWWorld::Class::get(ptr).getScript(ptr); @@ -768,15 +784,6 @@ namespace MWWorld mLocalScripts.add(script, copy); addContainerScripts (copy, &newCell); } - - if (MWWorld::Class::get(ptr).isActor()) - { - MWBase::MechanicsManager *mechMgr = - MWBase::Environment::get().getMechanicsManager(); - - mechMgr->removeActor(ptr); - mechMgr->addActor(copy); - } } ptr.getRefData().setCount(0); } @@ -825,16 +832,16 @@ namespace MWWorld rot.y = Ogre::Degree(y).valueRadians(); rot.z = Ogre::Degree(z).valueRadians(); - float *objRot = ptr.getRefData().getPosition().rot; - if(ptr.getRefData().getBaseNode() == 0 || !mRendering->rotateObject(ptr, rot, adjust)) + if (mRendering->rotateObject(ptr, rot, adjust)) { - objRot[0] = (adjust ? objRot[0] + rot.x : rot.x), objRot[1] = (adjust ? objRot[1] + rot.y : rot.y), objRot[2] = (adjust ? objRot[2] + rot.z : rot.z); - return; - } + // rotate physically iff renderer confirm so + float *objRot = ptr.getRefData().getPosition().rot; + objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; - // do this after rendering rotated the object so it gets changed by Class->adjustRotation - objRot[0] = rot.x, objRot[1] = rot.y, objRot[2] = rot.z; - mPhysics->rotateObject(ptr); + if (ptr.getRefData().getBaseNode() != 0) { + mPhysics->rotateObject(ptr); + } + } } void World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) @@ -871,53 +878,33 @@ namespace MWWorld --cellY; } - void World::doPhysics (const std::vector >& actors, - float duration) + void World::doPhysics(const PtrMovementList &actors, float duration) { - mPhysics->doPhysics(duration, actors); + /* No duration? Shouldn't be any movement, then. */ + if(duration <= 0.0f) + return; - const int tick = 16; // 16 ms ^= 60 Hz - - // Game clock part of the loop, contains everything that has to be executed in a fixed timestep - long long dt = mTimer.getMilliseconds() - lastTick; - if (dt >= 100) + PtrMovementList::const_iterator player(actors.end()); + for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++) { - // throw away wall clock time if necessary to keep the framerate above the minimum of 10 fps - lastTick += (dt - 100); - dt = 100; + if(iter->first.getRefData().getHandle() == "player") + { + /* Handle player last, in case a cell transition occurs */ + player = iter; + continue; + } + Ogre::Vector3 vec = mPhysics->move(iter->first, iter->second, duration, + !isSwimming(iter->first) && !isFlying(iter->first)); + moveObjectImp(iter->first, vec.x, vec.y, vec.z); } - while (dt >= tick) + if(player != actors.end()) { - dt -= tick; - lastTick += tick; - - std::vector< std::pair > vectors = mPhysics->doPhysicsFixed (actors); - - std::vector< std::pair >::iterator player = vectors.end(); - - for (std::vector< std::pair >::iterator it = vectors.begin(); - it!= vectors.end(); ++it) - { - if (it->first=="player") - { - player = it; - } - else - { - MWWorld::Ptr ptr = getPtrViaHandle (it->first); - moveObjectImp (ptr, it->second.x, it->second.y, it->second.z); - } - } - - // Make sure player is moved last (otherwise the cell might change in the middle of an update - // loop) - if (player!=vectors.end()) - { - if (moveObjectImp (getPtrViaHandle (player->first), - player->second.x, player->second.y, player->second.z) == true) - return; // abort the current loop if the cell has changed - } + Ogre::Vector3 vec = mPhysics->move(player->first, player->second, duration, + !isSwimming(player->first) && !isFlying(player->first)); + moveObjectImp(player->first, vec.x, vec.y, vec.z); } + // the only purpose this has currently is to update the debug drawer + mPhysEngine->stepSimulation (duration); } bool World::toggleCollisionMode() @@ -985,17 +972,6 @@ namespace MWWorld return ret; } - void World::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, - int number) - { - mRendering->playAnimationGroup (ptr, groupName, mode, number); - } - - void World::skipAnimation (const MWWorld::Ptr& ptr) - { - mRendering->skipAnimation (ptr); - } - void World::update (float duration, bool paused) { mWorldScene->update (duration, paused); @@ -1048,7 +1024,6 @@ namespace MWWorld // currently its here because we need to access the physics system float* p = mPlayer->getPlayer().getRefData().getPosition().pos; Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); - sun = Vector3(sun.x, -sun.z, sun.y); mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); } @@ -1146,7 +1121,7 @@ namespace MWWorld } else p = mPhysics->getRayPoint(results.front().first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::Vector3 pos(p.x(), p.y(), p.z()); Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode(); //std::cout << "Num facing 1 : " << mFaced1Name << std::endl; @@ -1174,7 +1149,7 @@ namespace MWWorld } else p = mPhysics->getRayPoint(results.at (1).first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::Vector3 pos(p.x(), p.y(), p.z()); Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode(); Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode(); @@ -1252,8 +1227,8 @@ namespace MWWorld if (!ref) return Vector2(0, 1); Ogre::SceneNode* node = ref->mData.getBaseNode(); - Vector3 dir = node->_getDerivedOrientation().yAxis(); - Vector2 d = Vector2(dir.x, dir.z); + Vector3 dir = node->_getDerivedOrientation() * Ogre::Vector3(0,1,0); + Vector2 d = Vector2(dir.x, dir.y); return d; } @@ -1323,7 +1298,7 @@ namespace MWWorld if (isCellExterior()) { int cellX, cellY; - positionToIndex(result.second[0], -result.second[2], cellX, cellY); + positionToIndex(result.second[0], result.second[1], cellX, cellY); cell = mCells.getExterior(cellX, cellY); } else @@ -1331,8 +1306,8 @@ namespace MWWorld 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]; + pos.pos[1] = result.second[1]; + pos.pos[2] = result.second[2]; Ptr dropped = copyObjectToCell(object, *cell, pos); PCDropped(dropped); @@ -1416,25 +1391,42 @@ namespace MWWorld } bool - World::isSwimming(const MWWorld::Ptr &object) + World::isFlying(const MWWorld::Ptr &ptr) const + { + const MWWorld::Class &cls = MWWorld::Class::get(ptr); + if(cls.isActor() && cls.getCreatureStats(ptr).getMagicEffects().get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0) + return true; + return false; + } + + bool + World::isSwimming(const MWWorld::Ptr &object) const { /// \todo add check ifActor() - only actors can swim float *fpos = object.getRefData().getPosition().pos; Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); - /// \fixme should rely on object height - pos.z += 30; + /// \fixme 3/4ths submerged? + const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); + if(actor) pos.z += actor->getHalfExtents().z * 1.5; - return isUnderwater(*object.getCell()->mCell, pos); + return isUnderwater(object.getCell(), pos); } bool - World::isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) + World::isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const { - if (!(cell.mData.mFlags & ESM::Cell::HasWater)) { + if (!(cell->mCell->mData.mFlags & ESM::Cell::HasWater)) { return false; } - return pos.z < cell.mWater; + return pos.z < cell->mWaterLevel; + } + + bool World::isOnGround(const MWWorld::Ptr &ptr) const + { + RefData &refdata = ptr.getRefData(); + const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + return physactor && physactor->getOnGround(); } void World::renderPlayer() @@ -1451,24 +1443,21 @@ namespace MWWorld { Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); - Ogre::Vector3 playerPos; - float* pos = mPlayer->getPlayer ().getRefData ().getPosition ().pos; - playerPos.x = pos[0]; - playerPos.y = pos[1]; - playerPos.z = pos[2]; + RefData &refdata = mPlayer->getPlayer().getRefData(); + Ogre::Vector3 playerPos(refdata.getPosition().pos); - std::pair hit = - mPhysics->castRay(playerPos, Ogre::Vector3(0,0,-1), 50); - bool isOnGround = (hit.first ? (hit.second.distance (playerPos) < 25) : false); - - if (!isOnGround || isUnderwater (*currentCell->mCell, playerPos)) + const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + if(!physactor->getOnGround() || isUnderwater(currentCell, playerPos)) return 2; - - if (currentCell->mCell->mData.mFlags & ESM::Cell::NoSleep) + if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep)) return 1; return 0; + } + MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr) + { + return mRendering->getAnimation(ptr); } void World::playVideo (const std::string &name, bool allowSkipping) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6f0a1d6ed..0ae81b33a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -37,6 +37,7 @@ namespace MWRender { class SkyManager; class CellRender; + class Animation; } namespace MWWorld @@ -264,8 +265,7 @@ namespace MWWorld virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const; ///< Convert position to cell numbers - virtual void doPhysics (const std::vector >& actors, - float duration); + virtual void doPhysics(const PtrMovementList &actors, float duration); ///< Run physics simulation and modify \a world accordingly. virtual bool toggleCollisionMode(); @@ -298,18 +298,6 @@ namespace MWWorld /// \return pointer to created record - virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, - int mode, int number = 1); - ///< Run animation for a MW-reference. Calls to this function for references that are - /// currently not in the rendered scene should be ignored. - /// - /// \param mode: 0 normal, 1 immediate start, 2 immediate loop - /// \param number How offen the animation should be run - - virtual void skipAnimation (const MWWorld::Ptr& ptr); - ///< Skip the animation for the given MW-reference for one frame. Calls to this function for - /// references that are currently not in the rendered scene should be ignored. - virtual void update (float duration, bool paused); virtual bool placeObject (const Ptr& object, float cursorX, float cursorY); @@ -326,8 +314,10 @@ namespace MWWorld virtual void processChangedSettings(const Settings::CategorySettingVector& settings); - virtual bool isSwimming(const MWWorld::Ptr &object); - virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); + virtual bool isFlying(const MWWorld::Ptr &ptr) const; + virtual bool isSwimming(const MWWorld::Ptr &object) const; + virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; + virtual bool isOnGround(const MWWorld::Ptr &ptr) const; virtual void togglePOV() { mRendering->togglePOV(); @@ -360,6 +350,9 @@ namespace MWWorld /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + /// \todo Probably shouldn't be here + virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr); + /// \todo this does not belong here virtual void playVideo(const std::string& name, bool allowSkipping); virtual void stopVideo(); diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake index c80203a25..4147590d6 100644 --- a/cmake/FindFFmpeg.cmake +++ b/cmake/FindFFmpeg.cmake @@ -68,6 +68,7 @@ macro(find_component _component _pkgconfig _library _header) find_path(${_component}_INCLUDE_DIRS ${_header} HINTS + ${FFMPEGSDK_INC} ${PC_LIB${_component}_INCLUDEDIR} ${PC_LIB${_component}_INCLUDE_DIRS} PATH_SUFFIXES @@ -76,6 +77,7 @@ macro(find_component _component _pkgconfig _library _header) find_library(${_component}_LIBRARIES NAMES ${_library} HINTS + ${FFMPEGSDK_LIB} ${PC_LIB${_component}_LIBDIR} ${PC_LIB${_component}_LIBRARY_DIRS} ) @@ -97,6 +99,12 @@ endmacro() # Check for cached results. If there are skip the costly part. if (NOT FFMPEG_LIBRARIES) + set (FFMPEGSDK ENV${FFMPEG_HOME}) + if (FFMPEGSDK) + set (FFMPEGSDK_INC "${FFMPEGSDK}/include") + set (FFMPEGSDK_LIB "${FFMPEGSDK}/lib") + endif () + # Check for all possible component. find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) find_component(AVFORMAT libavformat avformat libavformat/avformat.h) diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index f2382fd8a..f66dbf2c4 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -2,7 +2,7 @@ macro (add_openmw_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") +file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND OPENMW_FILES "${f}") @@ -14,7 +14,7 @@ endmacro (add_openmw_dir) macro (add_component_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") +file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND COMPONENT_FILES "${f}") @@ -26,12 +26,12 @@ endmacro (add_component_dir) macro (add_component_qt_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") +file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND COMPONENT_FILES "${f}") endforeach (f) -file (GLOB MOC_H ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.hpp") +file (GLOB MOC_H "${dir}/${u}.hpp") foreach (fi ${MOC_H}) list (APPEND COMPONENT_MOC_FILES "${fi}") endforeach (fi) diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp index 9b184f1ff..98be2d3d1 100644 --- a/components/compiler/fileparser.cpp +++ b/components/compiler/fileparser.cpp @@ -45,7 +45,10 @@ namespace Compiler reportWarning ("Names for script " + mName + " do not match", loc); mState = EndCompleteState; - return true; + return false; // we are stopping here, because there might be more garbage on the end line, + // that we must ignore. + // + /// \todo allow this workaround to be disabled for newer scripts } return Parser::parseName (name, loc, scanner); diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index eca0d7854..b4f581d7e 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -77,8 +77,7 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) // Get the header getHNT(mCtx.header, "HEDR", 300); - if (mCtx.header.version != VER_12 && mCtx.header.version != VER_13) - fail("Unsupported file format version"); + // Some mods abuse the header.version field for the version of the mod instead of the version of the file format, so we can only ignore it. while (isNextSub("MAST")) { diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 45d6d9164..ae876edf8 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -37,14 +37,14 @@ public: *************************************************************************/ int getVer() const { return mCtx.header.version; } - float getFVer() { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } - int getSpecial() { return mSpf; } - int getType() { return mCtx.header.type; } - const std::string getAuthor() { return mCtx.header.author.toString(); } - const std::string getDesc() { return mCtx.header.desc.toString(); } + float getFVer() const { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } + int getSpecial() const { return mSpf; } + int getType() const { return mCtx.header.type; } + const std::string getAuthor() const { return mCtx.header.author.toString(); } + const std::string getDesc() const { return mCtx.header.desc.toString(); } const SaveData &getSaveData() const { return mSaveData; } - const MasterList &getMasters() { return mMasters; } - const NAME &retSubName() { return mCtx.subName; } + const MasterList &getMasters() const { return mMasters; } + const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } /************************************************************************* @@ -85,7 +85,7 @@ public: int mIdx; void setIndex(const int index) {mIdx = index; mCtx.index = index;} const int getIndex() {return mIdx;} - + void setGlobalReaderList(std::vector *list) {mGlobalReaderList = list;} std::vector *getGlobalReaderList() {return mGlobalReaderList;} diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index e2f878a25..b9dd5b57b 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -136,15 +136,15 @@ void ESMWriter::writeHNString(const std::string& name, const std::string& data) endRecord(name); } -void ESMWriter::writeHNString(const std::string& name, const std::string& data, int size) +void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) { - assert(static_cast (data.size()) <= size); + assert(data.size() <= size); startSubRecord(name); writeHString(data); - if (static_cast (data.size()) < size) + if (data.size() < size) { - for (int i = data.size(); i < size; ++i) + for (size_t i = data.size(); i < size; ++i) write("\0",1); } @@ -177,7 +177,7 @@ void ESMWriter::writeName(const std::string& name) write(name.c_str(), name.size()); } -void ESMWriter::write(const char* data, int size) +void ESMWriter::write(const char* data, size_t size) { if (count && !m_records.empty()) { diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index b557a29ad..94e173b4c 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -16,7 +16,7 @@ class ESMWriter { std::string name; std::streampos position; - int size; + size_t size; }; public: @@ -35,7 +35,7 @@ public: void close(); void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, int size); + void writeHNString(const std::string& name, const std::string& data, size_t size); void writeHNCString(const std::string& name, const std::string& data) { startSubRecord(name); @@ -76,7 +76,7 @@ public: } template - void writeT(const T& data, int size) + void writeT(const T& data, size_t size) { write((char*)&data, size); } @@ -87,7 +87,7 @@ public: void writeHString(const std::string& data); void writeHCString(const std::string& data); void writeName(const std::string& data); - void write(const char* data, int size); + void write(const char* data, size_t size); private: std::list m_masters; diff --git a/components/esm/loadappa.cpp b/components/esm/loadappa.cpp index f5e7e10e1..643fefda5 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -7,12 +7,27 @@ namespace ESM { void Apparatus::load(ESMReader &esm) { - mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); - esm.getHNT(mData, "AADT", 16); - mScript = esm.getHNOString("SCRI"); - mIcon = esm.getHNString("ITEX"); + // we will not treat duplicated subrecords as errors here + while (esm.hasMoreSubs()) + { + esm.getSubName(); + NAME subName = esm.retSubName(); + + if (subName == "MODL") + mModel = esm.getHString(); + else if (subName == "FNAM") + mName = esm.getHString(); + else if (subName == "AADT") + esm.getHT(mData); + else if (subName == "SCRI") + mScript = esm.getHString(); + else if (subName == "ITEX") + mIcon = esm.getHString(); + else + esm.fail("wrong subrecord type " + subName.toString() + " for APPA record"); + } } + void Apparatus::save(ESMWriter &esm) { esm.writeHNCString("MODL", mModel); diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 7400fd026..92cb7d5ce 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -112,8 +112,8 @@ void Cell::load(ESMReader &esm, MWWorld::ESMStore &store) // instead. if (mData.mFlags & QuasiEx) mRegion = esm.getHNOString("RGNN"); - else - esm.getHNT(mAmbi, "AMBI", 16); + else if (esm.isNextSub("AMBI")) + esm.getHT(mAmbi); } else { @@ -126,7 +126,7 @@ void Cell::load(ESMReader &esm, MWWorld::ESMStore &store) if (esm.isNextSub("NAM0")) { esm.getHT(mNAM0); } - + // preload moved references while (esm.isNextSub("MVRF")) { CellRef ref; @@ -135,7 +135,7 @@ void Cell::load(ESMReader &esm, MWWorld::ESMStore &store) MWWorld::Store &cStore = const_cast&>(store.get()); ESM::Cell *cellAlt = const_cast(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); - + // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. getNextRef(esm, ref); @@ -143,7 +143,7 @@ void Cell::load(ESMReader &esm, MWWorld::ESMStore &store) std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), (int(*)(int)) std::tolower); - + // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. mMovedRefs.push_back(cMRef); @@ -186,7 +186,7 @@ void Cell::save(ESMWriter &esm) if (mMapColor != 0) esm.writeHNT("NAM5", mMapColor); } - + if (mNAM0 != 0) esm.writeHNT("NAM0", mNAM0); } @@ -226,7 +226,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) esm.getHNT(ref.mRefnum, "FRMR"); ref.mRefID = esm.getHNString("NAME"); - + // Identify references belonging to a parent file and adapt the ID accordingly. int local = (ref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; @@ -249,7 +249,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // missing ref.mScale = 1.0; esm.getHNOT(ref.mScale, "XSCL"); - + // TODO: support loading references from saves, there are tons of keys not recognized yet. // The following is just an incomplete list. if (esm.isNextSub("ACTN")) @@ -266,7 +266,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) esm.skipHSub(); else if (esm.isNextSub("CRED")) // ??? esm.skipHSub(); - + ref.mOwner = esm.getHNOString("ANAM"); ref.mGlob = esm.getHNOString("BNAM"); ref.mSoul = esm.getHNOString("XSOL"); @@ -305,7 +305,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) esm.getHNOT(ref.mFltv, "FLTV"); esm.getHNOT(ref.mPos, "DATA", 24); - + // Number of references in the cell? Maximum once in each cell, // but not always at the beginning, and not always right. In other // words, completely useless. @@ -318,7 +318,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) esm.getHT(ref.mNam0); //esm.getHNOT(NAM0, "NAM0"); } - + if (esm.isNextSub("DELE")) { esm.skipHSub(); ref.mDeleted = 2; // Deleted, will not respawn. @@ -333,7 +333,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) { esm.getHT(mref.mRefnum); esm.getHNOT(mref.mTarget, "CNDT"); - + // Identify references belonging to a parent file and adapt the ID accordingly. int local = (mref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index de467666e..6bde9972d 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -3,17 +3,17 @@ #include #include +#include #include "esmcommon.hpp" #include "defs.hpp" -#include "apps/openmw/mwbase/world.hpp" -/* -namespace MWWorld { - class ESMStore; - class CellStore; + +namespace MWWorld +{ + class ESMStore; } -*/ + namespace ESM { diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index dce4f96c2..a309dc9fb 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -/** \file components/files/fixedpath.hpp */ - #ifndef COMPONENTS_FILES_FIXEDPATH_HPP #define COMPONENTS_FILES_FIXEDPATH_HPP diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 0f08b67fe..c974a91d3 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -/** \file components/files/linuxpath.cpp */ - #include "linuxpath.hpp" #if defined(__linux__) || defined(__FreeBSD__) diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 09acd2be7..6acf2a2d5 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -/** \file components/files/linuxpath.hpp */ - #ifndef COMPONENTS_FILES_LINUXPATH_H #define COMPONENTS_FILES_LINUXPATH_H diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 9625612ad..9edcd6ef2 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -/** \file components/files/macospath.cpp */ - #include "macospath.hpp" #if defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 591c978aa..576ec1681 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -/** \file components/files/macospath.hpp */ - #ifndef COMPONENTS_FILES_MACOSPATH_H #define COMPONENTS_FILES_MACOSPATH_H diff --git a/components/files/ogreplugin.hpp b/components/files/ogreplugin.hpp index 2d56bfb47..6fcf61376 100644 --- a/components/files/ogreplugin.hpp +++ b/components/files/ogreplugin.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -/** \file components/files/ogreplugin.hpp */ - #ifndef COMPONENTS_FILES_OGREPLUGIN_H #define COMPONENTS_FILES_OGREPLUGIN_H diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 7fe8bc955..6044b67c2 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -1,25 +1,3 @@ -/** - * Open Morrowind - an opensource Elder Scrolls III: Morrowind - * engine implementation. - * - * Copyright (C) 2011 Open Morrowind Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -/** \file components/files/windowspath.hpp */ - #ifndef COMPONENTS_FILES_WINDOWSPATH_HPP #define COMPONENTS_FILES_WINDOWSPATH_HPP diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 63df23b27..46b58da8f 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -142,20 +142,18 @@ public: { ShapeData::read(nif); - int tris = nif->getUShort(); - if(tris) - { - // We have three times as many vertices as triangles, so this - // is always equal to tris*3. - int cnt = nif->getInt(); - nif->getShorts(triangles, cnt); - } + /*int tris =*/ nif->getUShort(); + + // We have three times as many vertices as triangles, so this + // is always equal to tris*3. + int cnt = nif->getInt(); + nif->getShorts(triangles, cnt); // Read the match list, which lists the vertices that are equal to // vertices. We don't actually need need this for anything, so // just skip it. int verts = nif->getUShort(); - for(int i=0;igetUShort(); diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 58d1bc9b8..6e806e7ec 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -220,11 +220,10 @@ void NIFFile::parse() for(size_t i = 0;i < recNum;i++) { - std::string rec = getString(); - //cout << i << ": " << rec.toString() << endl; - Record *r = NULL; + std::string rec = getString(); + /* These are all the record types we know how to read. This can be heavily optimized later if needed. For example, a @@ -256,6 +255,7 @@ void NIFFile::parse() else if(rec == "NiDitherProperty") { r = new NiDitherProperty; r->recType = RC_NiDitherProperty; } else if(rec == "NiWireframeProperty") { r = new NiWireframeProperty; r->recType = RC_NiWireframeProperty; } else if(rec == "NiSpecularProperty") { r = new NiSpecularProperty; r->recType = RC_NiSpecularProperty; } + else if(rec == "NiStencilProperty") { r = new NiStencilProperty; r->recType = RC_NiStencilProperty; } // Controllers else if(rec == "NiVisController") { r = new NiVisController; r->recType = RC_NiVisController; } diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index e8884cd4d..5e9694f4b 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -158,6 +158,7 @@ public: short getShort() { return read_le16(); } unsigned short getUShort() { return read_le16(); } int getInt() { return read_le32(); } + unsigned int getUInt() { return read_le32(); } float getFloat() { return read_le32f(); } Ogre::Vector2 getVector2() { diff --git a/components/nif/property.hpp b/components/nif/property.hpp index b24e49b47..046fb0465 100644 --- a/components/nif/property.hpp +++ b/components/nif/property.hpp @@ -257,9 +257,66 @@ struct S_AlphaProperty } }; +/* + Docs taken from: + http://niftools.sourceforge.net/doc/nif/NiStencilProperty.html + */ +struct S_StencilProperty +{ + // Is stencil test enabled? + unsigned char enabled; + + /* + 0 TEST_NEVER + 1 TEST_LESS + 2 TEST_EQUAL + 3 TEST_LESS_EQUAL + 4 TEST_GREATER + 5 TEST_NOT_EQUAL + 6 TEST_GREATER_EQUAL + 7 TEST_ALWAYS + */ + int compareFunc; + unsigned stencilRef; + unsigned stencilMask; + /* + Stencil test fail action, depth test fail action and depth test pass action: + 0 ACTION_KEEP + 1 ACTION_ZERO + 2 ACTION_REPLACE + 3 ACTION_INCREMENT + 4 ACTION_DECREMENT + 5 ACTION_INVERT + */ + int failAction; + int zFailAction; + int zPassAction; + /* + Face draw mode: + 0 DRAW_CCW_OR_BOTH + 1 DRAW_CCW [default] + 2 DRAW_CW + 3 DRAW_BOTH + */ + int drawMode; + + void read(NIFFile *nif) + { + enabled = nif->getChar(); + compareFunc = nif->getInt(); + stencilRef = nif->getUInt(); + stencilMask = nif->getUInt(); + failAction = nif->getInt(); + zFailAction = nif->getInt(); + zPassAction = nif->getInt(); + drawMode = nif->getInt(); + } +}; + typedef StructPropT NiAlphaProperty; typedef StructPropT NiMaterialProperty; typedef StructPropT NiVertexColorProperty; +typedef StructPropT NiStencilProperty; } // Namespace #endif diff --git a/components/nif/record.hpp b/components/nif/record.hpp index d5f65e83a..073f4657c 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -48,6 +48,7 @@ enum RecordType RC_NiDitherProperty, RC_NiWireframeProperty, RC_NiSpecularProperty, + RC_NiStencilProperty, RC_NiVisController, RC_NiGeomMorpherController, RC_NiKeyframeController, diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index a619bdda2..3d9c16ebb 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -155,6 +155,8 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, // the flags we currently use, at least. flags |= node->flags; + isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); + // Marker objects: no collision /// \todo don't do this in the editor if (node->name.find("marker") != std::string::npos) @@ -191,25 +193,26 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, } } - if(node->hasBounds) + if(!hasCollisionNode || isCollisionNode) { - cShape->boxTranslation = node->boundPos; - cShape->boxRotation = node->boundRot; - mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ)); - } + if(node->hasBounds) + { + cShape->boxTranslation = node->boundPos; + cShape->boxRotation = node->boundRot; + mBoundingBox = new btBoxShape(getbtVector(node->boundXYZ)); + } - if(node->recType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) - { - cShape->mCollide = !(flags&0x800); - handleNiTriShape(static_cast(node), flags, node->getWorldTransform(), raycastingOnly); + if(node->recType == Nif::RC_NiTriShape) + { + cShape->mCollide = !(flags&0x800); + handleNiTriShape(static_cast(node), flags, node->getWorldTransform(), raycastingOnly); + } } // For NiNodes, loop through children const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { - isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); - const Nif::NodeList &list = ninode->children; for(size_t i = 0;i < list.length();i++) { diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index dfbc93ee9..d33243dd4 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -21,8 +21,6 @@ */ -//loadResource->handleNode->handleNiTriShape->createSubMesh - #include "ogre_nif_loader.hpp" #include @@ -35,6 +33,7 @@ #include #include #include +#include #include #include @@ -53,9 +52,6 @@ typedef unsigned char ubyte; namespace std { -// These operators allow extra data types to be stored in an Ogre::Any -// object, which can then be stored in user object bindings on the nodes - // TODO: Do something useful ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) { return o; } @@ -149,8 +145,12 @@ public: }; -class NIFSkeletonLoader : public Ogre::ManualResourceLoader { - +/** Manual resource loader for NIF skeletons. This is the main class + responsible for translating the internal NIF skeleton structure into + something Ogre can use (includes animations and node TextKeyData). + */ +class NIFSkeletonLoader : public Ogre::ManualResourceLoader +{ static void warn(const std::string &msg) { std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl; @@ -163,168 +163,49 @@ static void fail(const std::string &msg) } -static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) +static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime) { - TextKeyMap textkeys; - for(size_t i = 0;i < tk->list.size();i++) - { - const std::string &str = tk->list[i].text; - std::string::size_type pos = 0; - while(pos < str.length()) - { - while(pos < str.length() && ::isspace(str[pos])) - pos++; - if(pos >= str.length()) - break; - - std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); - textkeys.insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); - - pos = nextpos; - } - } - return textkeys; -} - - -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; - } - - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData) - { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk))); - } - e = e->extra; - } - - 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::ptr pnif(Nif::NIFFile::create (skel->getName())); - Nif::NIFFile & nif = *pnif.get (); - 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 const *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)); + Ogre::Animation *anim = skel->createAnimation(name, stopTime-startTime); for(size_t i = 0;i < ctrls.size();i++) { - Nif::NiKeyframeController const *kfc = ctrls[i]; - Nif::NiKeyframeData const *kf = kfc->data.getPtr(); + const Nif::NiKeyframeController *kfc = ctrls[i]; + if(kfc->data.empty()) + continue; + const Nif::NiKeyframeData *kf = kfc->data.getPtr(); /* Get the keyframes and make sure they're sorted first to last */ - Nif::QuaternionKeyList quatkeys = kf->mRotations; - Nif::Vector3KeyList trankeys = kf->mTranslations; - Nif::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()); + const Nif::QuaternionKeyList &quatkeys = kf->mRotations; + const Nif::Vector3KeyList &trankeys = kf->mTranslations; + const Nif::FloatKeyList &scalekeys = kf->mScales; Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); Nif::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()); + // NOTE: For some reason, Ogre doesn't like the node track ID being different from + // the bone ID + Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ? + anim->getNodeTrack(bone->getHandle()) : + anim->createNodeTrack(bone->getHandle(), bone); 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; + lastquat = curquat = quatiter->mValue; if(traniter != trankeys.mKeys.end()) - lasttrans = curtrans = traniter->mValue - starttrans; + lasttrans = curtrans = traniter->mValue; if(scaleiter != scalekeys.mKeys.end()) - lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + lastscale = curscale = Ogre::Vector3(scaleiter->mValue); + float begTime = std::max(kfc->timeStart, startTime); + float endTime = std::min(kfc->timeStop, stopTime); bool didlast = false; while(!didlast) { - float curtime = kfc->timeStop; + float curtime = std::numeric_limits::max(); //Get latest time if(quatiter != quatkeys.mKeys.end()) @@ -334,11 +215,11 @@ void loadResource(Ogre::Resource *resource) if(scaleiter != scalekeys.mKeys.end()) curtime = std::min(curtime, scaleiter->mTime); - curtime = std::max(curtime, kfc->timeStart); - if(curtime >= kfc->timeStop) + curtime = std::max(curtime, begTime); + if(curtime >= endTime) { didlast = true; - curtime = kfc->timeStop; + curtime = endTime; } // Get the latest quaternions, translations, and scales for the @@ -346,27 +227,24 @@ void loadResource(Ogre::Resource *resource) while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime) { lastquat = curquat; - quatiter++; - if(quatiter != quatkeys.mKeys.end()) - curquat = startquat.Inverse() * quatiter->mValue ; + if(++quatiter != quatkeys.mKeys.end()) + curquat = quatiter->mValue; } while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime) { lasttrans = curtrans; - traniter++; - if(traniter != trankeys.mKeys.end()) - curtrans = traniter->mValue - starttrans; + if(++traniter != trankeys.mKeys.end()) + curtrans = traniter->mValue; } while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime) { lastscale = curscale; - scaleiter++; - if(scaleiter != scalekeys.mKeys.end()) - curscale = Ogre::Vector3(scaleiter->mValue) / startscale; + if(++scaleiter != scalekeys.mKeys.end()) + curscale = Ogre::Vector3(scaleiter->mValue); } Ogre::TransformKeyFrame *kframe; - kframe = nodetrack->createNodeKeyFrame(curtime); + kframe = nodetrack->createNodeKeyFrame(curtime-startTime); if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin()) kframe->setRotation(curquat); else @@ -396,18 +274,73 @@ void loadResource(Ogre::Resource *resource) anim->optimise(); } -bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) + +static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) { - if(node->boneTrafo != NULL) + TextKeyMap textkeys; + for(size_t i = 0;i < tk->list.size();i++) { - Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - Ogre::SkeletonPtr skel = skelMgr.getByName(name); - if(skel.isNull()) + const std::string &str = tk->list[i].text; + std::string::size_type pos = 0; + while(pos < str.length()) { - NIFSkeletonLoader *loader = &sLoaders[name]; - skel = skelMgr.create(name, group, true, loader); + if(::isspace(str[pos])) + { + pos++; + continue; + } + + std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); + std::string result = str.substr(pos, nextpos-pos); + textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); + + pos = nextpos; } - return true; + } + return textkeys; +} + + +void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, 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(); + + if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ + node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ + node->recType == Nif::RC_NiTriShape /* Handled in the mesh loader */ + )) + warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName()); + + Nif::ControllerPtr ctrl = node->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiKeyframeController) + ctrls.push_back(static_cast(ctrl.getPtr())); + else + warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); + ctrl = ctrl->next; + } + + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + textkeys = extractTextKeys(tk); + animroot = bone; + } + e = e->extra; } const Nif::NiNode *ninode = dynamic_cast(node); @@ -417,62 +350,186 @@ bool createSkeleton(const std::string &name, const std::string &group, const Nif for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) + buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone); + } + } +} + + +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::ptr nif(Nif::NIFFile::create(skel->getName())); + const Nif::Node *node = static_cast(nif->getRecord(0)); + + std::vector ctrls; + Ogre::Bone *animroot = NULL; + TextKeyMap textkeys; + try { + buildBones(skel, node, animroot, textkeys, ctrls); + } + catch(std::exception &e) { + std::cerr<< "Exception while loading "<getName() < 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++) + { + const 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; + } + + if(!animroot) + { + warn(Ogre::StringConverter::toString(ctrls.size())+" animated node(s) in "+ + skel->getName()+", but no text keys."); + return; + } + + Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings(); + bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true)); + + std::string currentgroup; + TextKeyMap::const_iterator keyiter = textkeys.begin(); + for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++) + { + std::string::size_type sep = keyiter->second.find(':'); + if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) || + (sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) || + (sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0)) + continue; + currentgroup = keyiter->second.substr(0, sep); + + if(skel->hasAnimation(currentgroup)) + continue; + + TextKeyMap::const_iterator lastkeyiter = textkeys.end(); + while((--lastkeyiter)->first > keyiter->first) + { + if(lastkeyiter->second.find(':') == currentgroup.length() && + lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0) + break; + } + + buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first); + + TextKeyMap::const_iterator insiter = keyiter; + TextKeyMap groupkeys; + do { + sep = insiter->second.find(':'); + if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0) + groupkeys.insert(std::make_pair(insiter->first - keyiter->first, + insiter->second.substr(sep+2))); + else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) || + (sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0)) + groupkeys.insert(std::make_pair(insiter->first - keyiter->first, insiter->second)); + } while(insiter++ != lastkeyiter); + + bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys)); + } +} + + +static Ogre::SkeletonPtr createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) +{ + /* We need to be a little aggressive here, since some NIFs have a crap-ton + * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: + * There are no bones used for skinning, there are no controllers on non- + * NiTriShape nodes, there are no nodes named "AttachLight", and the tree + * consists of NiNode, NiTriShape, and RootCollisionNode types only. + */ + if(!node->boneTrafo) + { + if(node->recType == Nif::RC_NiTriShape) + return Ogre::SkeletonPtr(); + if(node->controller.empty() && node->name != "AttachLight") + { + if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) { - if(createSkeleton(name, group, children[i].getPtr())) - return true; + const Nif::NiNode *ninode = static_cast(node); + const Nif::NodeList &children = ninode->children; + for(size_t i = 0;i < children.length();i++) + { + if(!children[i].empty()) + { + Ogre::SkeletonPtr skel = createSkeleton(name, group, children[i].getPtr()); + if(!skel.isNull()) + return skel; + } + } + return Ogre::SkeletonPtr(); } } } - return false; + + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + return skelMgr.create(name, group, true, &sLoaders[name]); } }; NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders; -// Conversion of blend / test mode from NIF -> OGRE. -// Not in use yet, so let's comment it out. -/* -static SceneBlendFactor getBlendFactor(int mode) +// Conversion of blend / test mode from NIF +static const char *getBlendFactor(int mode) { - switch(mode) + switch(mode) { - case 0: return SBF_ONE; - case 1: return SBF_ZERO; - case 2: return SBF_SOURCE_COLOUR; - case 3: return SBF_ONE_MINUS_SOURCE_COLOUR; - case 4: return SBF_DEST_COLOUR; - case 5: return SBF_ONE_MINUS_DEST_COLOUR; - case 6: return SBF_SOURCE_ALPHA; - case 7: return SBF_ONE_MINUS_SOURCE_ALPHA; - case 8: return SBF_DEST_ALPHA; - case 9: return SBF_ONE_MINUS_DEST_ALPHA; - // [Comment from Chris Robinson:] Can't handle this mode? :/ - // case 10: return SBF_SOURCE_ALPHA_SATURATE; - default: - return SBF_SOURCE_ALPHA; + case 0: return "one"; + case 1: return "zero"; + case 2: return "src_colour"; + case 3: return "one_minus_src_colour"; + case 4: return "dest_colour"; + case 5: return "one_minus_dest_colour"; + case 6: return "src_alpha"; + case 7: return "one_minus_src_alpha"; + case 8: return "dest_alpha"; + case 9: return "one_minus_dest_alpha"; + case 10: return "src_alpha_saturate"; } + std::cerr<< "Unexpected blend mode: "<data->colors.size() != 0); @@ -530,6 +587,8 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String m = static_cast(pr); else if (pr->recType == Nif::RC_NiAlphaProperty) a = static_cast(pr); + else if (pr->recType == Nif::RC_NiStencilProperty) + /* unused */; else warn("Skipped property type: "+pr->recName); } @@ -540,13 +599,18 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String Nif::NiSourceTexture *st = t->textures[0].texture.getPtr(); if (st->external) { - /* Bethesda at some at some point converted all their BSA + /* Bethesda 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. */ static const char path[] = "textures\\"; - texName = path + st->filename; + texName = st->filename; + Misc::StringUtils::toLower(texName); + + if(texName.compare(0, sizeof(path)-1, path) != 0) + texName = path + texName; + Ogre::String::size_type pos = texName.rfind('.'); if(pos != Ogre::String::npos && texName.compare(pos, texName.length() - pos, ".dds") != 0) { @@ -557,7 +621,12 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) // verify, and revert if false (this call succeeds quickly, but fails slowly) if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) - texName = path + st->filename; + { + texName = st->filename; + Misc::StringUtils::toLower(texName); + if(texName.compare(0, sizeof(path)-1, path) != 0) + texName = path + texName; + } } } else warn("Found internal texture, ignoring."); @@ -567,7 +636,7 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String if (a) { alphaFlags = a->flags; -// alphaTest = a->data.threshold; + alphaTest = a->data.threshold; } // Material @@ -601,6 +670,7 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String boost::hash_combine(h, texName); boost::hash_combine(h, vertexColour); boost::hash_combine(h, alphaFlags); + boost::hash_combine(h, alphaTest); std::map::iterator itr = MaterialMap.find(h); if (itr != MaterialMap.end()) @@ -621,7 +691,7 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha))); instance->setProperty ("specular", sh::makeProperty ( - new sh::Vector4(specular.x, specular.y, specular.z, glossiness))); + new sh::Vector4(specular.x, specular.y, specular.z, glossiness*255.0f))); instance->setProperty ("emissive", sh::makeProperty ( new sh::Vector3(emissive.x, emissive.y, emissive.z))); @@ -632,58 +702,37 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String instance->setProperty ("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); // Add transparency if NiAlphaProperty was present - if (alphaFlags != -1) + NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); + if (result.first) { - // The 237 alpha flags are by far the most common. Check - // NiAlphaProperty in nif/property.h if you need to decode - // other values. 237 basically means normal transparencly. - if (alphaFlags == 237) - { - NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName); - if (result.first) - { - instance->setProperty("alpha_rejection", - sh::makeProperty(new sh::StringValue("greater_equal " + boost::lexical_cast(result.second)))); - } - else - { - // Enable transparency - instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); - instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue("off"))); - } - } - else - warn("Unhandled alpha setting for texture " + texName); + alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ + alphaTest = result.second; + } + + if((alphaFlags&1)) + { + std::string blend_mode; + blend_mode += getBlendFactor((alphaFlags>>1)&0xf); + blend_mode += " "; + blend_mode += getBlendFactor((alphaFlags>>5)&0xf); + instance->setProperty("scene_blend", sh::makeProperty(new sh::StringValue(blend_mode))); } else - instance->getMaterial ()->setShadowCasterMaterial ("openmw_shadowcaster_noalpha"); + instance->getMaterial()->setShadowCasterMaterial("openmw_shadowcaster_noalpha"); - // As of yet UNTESTED code from Chris: - /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); - pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); - pass->setDepthCheckEnabled(true); - - // Add transparency if NiAlphaProperty was present - if (alphaFlags != -1) + if((alphaFlags>>9)&1) { - std::cout << "Alpha flags set!" << endl; - if ((alphaFlags&1)) - { - pass->setDepthWriteEnabled(false); - pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf), - getBlendFactor((alphaFlags>>5)&0xf)); - } - else - pass->setDepthWriteEnabled(true); - - if ((alphaFlags>>9)&1) - pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7), - alphaTest); - - pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1)); + std::string reject; + reject += getTestMode((alphaFlags>>10)&0x7); + reject += " "; + reject += Ogre::StringConverter::toString(alphaTest); + instance->setProperty("alpha_rejection", sh::makeProperty(new sh::StringValue(reject))); } -*/ + instance->setProperty("transparent_sorting", sh::makeProperty(new sh::StringValue(((alphaFlags>>13)&1) ? + "off" : "on"))); + + sh::Factory::getInstance()._ensureMaterial(matname, "Default"); return matname; } @@ -691,14 +740,16 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String std::map NIFMaterialLoader::MaterialMap; +/** Manual resource loader for NIF meshes. This is the main class + responsible for translating the internal NIF mesh structure into + something Ogre can use. + */ class NIFMeshLoader : Ogre::ManualResourceLoader { std::string mName; std::string mGroup; size_t mShapeIndex; - std::string mSkelName; std::string mMaterialName; - std::string mShapeName; void warn(const std::string &msg) { @@ -724,12 +775,11 @@ class NIFMeshLoader : Ogre::ManualResourceLoader { // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be // explicitly attached later. - mesh->setSkeletonName(mSkelName); + mesh->setSkeletonName(mName); // 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(); + skel = skelMgr->getByName(mName); // 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 @@ -742,12 +792,10 @@ class NIFMeshLoader : Ogre::ManualResourceLoader for(size_t b = 0;b < bones.length();b++) { Ogre::Bone *bone = skel->getBone(bones[b]->name); - Ogre::Matrix4 mat, mat2; + Ogre::Matrix4 mat; 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; + mat = bone->_getFullTransform() * mat; const std::vector &weights = data->bones[b].weights; for(size_t i = 0;i < weights.size();i++) @@ -768,31 +816,38 @@ class NIFMeshLoader : Ogre::ManualResourceLoader srcVerts = newVerts; srcNorms = newNorms; } - else if(mSkelName.length() == 0) + else { - // 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::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + if(skelMgr->getByName(mName).isNull()) { - 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]); + // 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. + if(!bounds.isValid()) + { + float v[3] = { 0.0f, 0.0f, 0.0f }; + bounds.add(&v[0], 1); + } + 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()); @@ -806,8 +861,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader Ogre::VertexDeclaration *decl; int nextBuf = 0; - Ogre::SubMesh *sub = ((mShapeName.length() > 0) ? mesh->createSubMesh(mShapeName) : - mesh->createSubMesh()); + Ogre::SubMesh *sub = mesh->createSubMesh(); // Add vertices sub->useSharedVertices = false; @@ -943,19 +997,21 @@ class NIFMeshLoader : Ogre::ManualResourceLoader public: NIFMeshLoader() { } - NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName) - : mName(name), mGroup(group), mShapeIndex(~(size_t)0), mSkelName(skelName) + NIFMeshLoader(const std::string &name, const std::string &group) + : mName(name), mGroup(group), mShapeIndex(~(size_t)0) { } virtual void loadResource(Ogre::Resource *resource) { Ogre::Mesh *mesh = dynamic_cast(resource); - assert(mesh && "Attempting to load a mesh into a non-mesh resource!"); + OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); if(mShapeIndex >= nif->numRecords()) { - mesh->setSkeletonName(mSkelName); + Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); + if(!skelMgr->getByName(mName).isNull()) + mesh->setSkeletonName(mName); return; } @@ -963,8 +1019,12 @@ public: findTriShape(mesh, node); } - void createMeshes(const Nif::Node *node, MeshPairList &meshes, int flags=0) + void createMeshes(const Nif::Node *node, MeshInfoList &meshes, int flags=0) { + // Do not create meshes for the collision shape (includes all children) + if(node->recType == Nif::RC_RootCollisionNode) + return; + flags |= node->flags; // Marker objects: just skip the entire node @@ -976,7 +1036,6 @@ public: while(!e.empty()) { Nif::NiStringExtraData *sd; - Nif::NiTextKeyExtraData *td; if((sd=dynamic_cast(e.getPtr())) != NULL) { // String markers may contain important information @@ -988,26 +1047,17 @@ public: flags |= 0x01; } } - else if((td=dynamic_cast(e.getPtr())) != NULL) - { - // TODO: Read and store text keys somewhere - } - else - warn("Unhandled extra data type "+e->recName); e = e->extra; } - if(node->recType == Nif::RC_NiTriShape) + if(node->recType == Nif::RC_NiTriShape && !(flags&0x01)) // Not hidden { const Nif::NiTriShape *shape = dynamic_cast(node); - mShapeName = shape->name; Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex); - if(mShapeName.length() > 0) - fullname += "@shape="+mShapeName; - if(mSkelName.length() > 0 && mName != mSkelName) - fullname += "@skel="+mSkelName; + if(shape->name.length() > 0) + fullname += "@shape="+shape->name; Misc::StringUtils::toLower(fullname); Ogre::MeshPtr mesh = meshMgr.getByName(fullname); @@ -1015,21 +1065,15 @@ public: { NIFMeshLoader *loader = &sLoaders[fullname]; *loader = *this; - if(!(flags&0x01)) // Not hidden - { - loader->mShapeIndex = shape->recIndex; - loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup); - } + loader->mShapeIndex = shape->recIndex; + loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup); mesh = meshMgr.createManual(fullname, mGroup, loader); mesh->setAutoBuildEdgeLists(false); } - meshes.push_back(std::make_pair(mesh->getName(), shape->name)); + meshes.push_back(MeshInfo(mesh->getName(), 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); const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) @@ -1042,28 +1086,47 @@ public: } } } + + void createEmptyMesh(const Nif::Node *node, MeshInfoList &meshes) + { + /* This creates an empty mesh to which a skeleton gets attached. This + * is to ensure we have an entity with a skeleton instance, even if all + * other meshes are hidden or entities attached to a specific node + * instead of skinned. */ + std::string fullname = mName; + Misc::StringUtils::toLower(fullname); + + Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); + Ogre::MeshPtr mesh = meshMgr.getByName(fullname); + if(mesh.isNull()) + { + NIFMeshLoader *loader = &sLoaders[fullname]; + *loader = *this; + + mesh = meshMgr.createManual(fullname, mGroup, loader); + mesh->setAutoBuildEdgeLists(false); + } + meshes.push_back(MeshInfo(mesh->getName(), node->name)); + } }; NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -typedef std::map MeshPairMap; -static MeshPairMap sMeshPairMap; +typedef std::map MeshInfoMap; +static MeshInfoMap sMeshInfoMap; -MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) +MeshInfoList Loader::load(const std::string &name, const std::string &group) { - Misc::StringUtils::toLower(name); - Misc::StringUtils::toLower(skelName); - - MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName); - if(meshiter != sMeshPairMap.end()) + MeshInfoMap::const_iterator meshiter = sMeshInfoMap.find(name); + if(meshiter != sMeshInfoMap.end()) return meshiter->second; - MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName]; - Nif::NIFFile::ptr pnif = Nif::NIFFile::create (name); - Nif::NIFFile &nif = *pnif.get (); - if (nif.numRecords() < 1) + MeshInfoList &meshes = sMeshInfoMap[name]; + Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name); + Nif::NIFFile &nif = *pnif.get(); + if(nif.numRecords() < 1) { - nif.warn("Found no records in NIF."); + nif.warn("Found no NIF records in "+name+"."); return meshes; } @@ -1074,109 +1137,90 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: Nif::Node const *node = dynamic_cast(r); if(node == NULL) { - nif.warn("First record in file was not a node, but a "+ - r->recName+". Skipping file."); + nif.warn("First record in "+name+" was not a node, but a "+ + r->recName+"."); return meshes; } - NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(name, group, node); + bool hasSkel = Ogre::SkeletonManager::getSingleton().resourceExists(name); + if(!hasSkel) + hasSkel = !NIFSkeletonLoader::createSkeleton(name, group, node).isNull(); - NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); - meshldr.createMeshes(node, meshes); + NIFMeshLoader meshldr(name, group); + if(hasSkel) + meshldr.createEmptyMesh(node, meshes); + meshldr.createMeshes(node, meshes, 0); return meshes; } -EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textkeys, const std::string &name, const std::string &group) +EntityList Loader::createEntities(Ogre::SceneNode *parentNode, std::string name, const std::string &group) { EntityList entitylist; - MeshPairList meshes = load(name, name, group); + Misc::StringUtils::toLower(name); + MeshInfoList meshes = load(name, group); if(meshes.size() == 0) return entitylist; - Ogre::SceneManager *sceneMgr = parent->getCreator(); + Ogre::SceneManager *sceneMgr = parentNode->getCreator(); for(size_t i = 0;i < meshes.size();i++) { - entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first)); + entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].mMeshName)); Ogre::Entity *entity = entitylist.mEntities.back(); if(!entitylist.mSkelBase && entity->hasSkeleton()) entitylist.mSkelBase = entity; } - if(entitylist.mSkelBase && textkeys) - { - // Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr. - Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - Ogre::SkeletonPtr skel = skelMgr.getByName(entitylist.mSkelBase->getSkeleton()->getName()); - Ogre::Skeleton::BoneIterator iter = skel->getBoneIterator(); - while(iter.hasMoreElements()) - { - Ogre::Bone *bone = iter.getNext(); - const Ogre::Any &data = bone->getUserObjectBindings().getUserAny("TextKeyExtraData"); - if(!data.isEmpty()) - { - *textkeys = Ogre::any_cast(data); - break; - } - } - } - if(entitylist.mSkelBase) { - parent->attachObject(entitylist.mSkelBase); + 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(entitylist.mSkelBase); - parent->attachObject(entity); + parentNode->attachObject(entity); } else if(entity != entitylist.mSkelBase) - entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity); + entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity); } } else { for(size_t i = 0;i < entitylist.mEntities.size();i++) - parent->attachObject(entitylist.mEntities[i]); + parentNode->attachObject(entitylist.mEntities[i]); } return entitylist; } -EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, - Ogre::SceneNode *parentNode, - const std::string &name, - const std::string &group) +EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonename, + Ogre::SceneNode *parentNode, + std::string name, const std::string &group) { EntityList entitylist; - MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group); + Misc::StringUtils::toLower(name); + MeshInfoList meshes = load(name, group); if(meshes.size() == 0) return entitylist; + bool isskinned = false; Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - std::string filter = "tri "+bonename; - std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower); + std::string filter = "@shape=tri "+bonename; + Misc::StringUtils::toLower(filter); for(size_t i = 0;i < meshes.size();i++) { - Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first); - if(ent->hasSkeleton()) + Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].mMeshName); + if(!entitylist.mSkelBase) { - Misc::StringUtils::toLower(meshes[i].second); - - if(meshes[i].second.length() < filter.length() || - meshes[i].second.compare(0, filter.length(), filter) != 0) - { - sceneMgr->destroyEntity(ent); - continue; - } - if(!entitylist.mSkelBase) + if(ent->hasSkeleton()) entitylist.mSkelBase = ent; } + else if(!isskinned && ent->hasSkeleton()) + isskinned = true; entitylist.mEntities.push_back(ent); } @@ -1184,22 +1228,22 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo if(bonename.find("Left") != std::string::npos) scale.x *= -1.0f; - if(entitylist.mSkelBase) + if(isskinned) { - 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()) + if(entity->hasSkeleton()) { - entity->shareSkeletonInstanceWith(parent); - parentNode->attachObject(entity); + if(entity != entitylist.mSkelBase) + entity->shareSkeletonInstanceWith(entitylist.mSkelBase); + if(entity->getMesh()->getName().find(filter) != std::string::npos) + parentNode->attachObject(entity); } - else if(entity != entitylist.mSkelBase) + else { - Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity); - tag->setScale(scale); + if(entity->getMesh()->getName().find(filter) != std::string::npos) + entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity); } } } @@ -1216,6 +1260,38 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo } +Ogre::SkeletonPtr Loader::getSkeleton(std::string name, const std::string &group) +{ + Ogre::SkeletonPtr skel; + + Misc::StringUtils::toLower(name); + skel = Ogre::SkeletonManager::getSingleton().getByName(name); + if(!skel.isNull()) + return skel; + + Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + if(nif->numRecords() < 1) + { + nif->warn("Found no NIF records in "+name+"."); + return skel; + } + + // The first record is assumed to be the root node + const Nif::Record *r = nif->getRecord(0); + assert(r != NULL); + + const Nif::Node *node = dynamic_cast(r); + if(node == NULL) + { + nif->warn("First record in "+name+" was not a node, but a "+ + r->recName+"."); + return skel; + } + + return NIFSkeletonLoader::createSkeleton(name, group, node); +} + + /* 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 3e05c5873..eae37dd8a 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -31,18 +31,14 @@ #include #include -namespace Nif -{ - class Node; - class Transformation; - class NiTriShape; -} +// FIXME: This namespace really doesn't do anything Nif-specific. Any supportable +// model format should go through this. namespace NifOgre { -// FIXME: These should not be in NifOgre, it works agnostic of what model format is used typedef std::multimap TextKeyMap; +static const char sTextKeyExtraDataID[] = "TextKeyExtraData"; struct EntityList { std::vector mEntities; Ogre::Entity *mSkelBase; @@ -52,39 +48,45 @@ struct EntityList { }; -/** This holds a list of mesh names along with the names of their parent nodes */ -typedef std::vector< std::pair > MeshPairList; +/* This holds a list of mesh names, the names of their parent nodes, and the offset + * from their parent nodes. */ +struct MeshInfo { + std::string mMeshName; + std::string mTargetNode; -/** Manual resource loader for NIF meshes. This is the main class - responsible for translating the internal NIF mesh structure into - something Ogre can use. + MeshInfo(const std::string &name, const std::string &target) + : mMeshName(name), mTargetNode(target) + { } +}; +typedef std::vector MeshInfoList; - You have to insert meshes manually into Ogre like this: - - NIFLoader::load("somemesh.nif"); - - 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 +class Loader { - static MeshPairList load(std::string name, std::string skelName, const std::string &group); + static MeshInfoList load(const std::string &name, const std::string &group); public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, - const std::string &name, + std::string name, const std::string &group="General"); - static EntityList createEntities(Ogre::SceneNode *parent, - TextKeyMap *textkeys, - const std::string &name, + static EntityList createEntities(Ogre::SceneNode *parentNode, + std::string name, const std::string &group="General"); + + static Ogre::SkeletonPtr getSkeleton(std::string name, const std::string &group="General"); }; } +namespace std +{ + +// These operators allow extra data types to be stored in an Ogre::Any +// object, which can then be stored in user object bindings on the nodes + +ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&); + +} + #endif - - diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index e2a1b1220..1de15d79c 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -70,7 +70,7 @@ Utf8Encoder::Utf8Encoder(const FromType sourceEncoding): } } -std::string Utf8Encoder::getUtf8(const char* input, int size) +std::string Utf8Encoder::getUtf8(const char* input, size_t size) { // Double check that the input string stops at some point (it might // contain zero terminators before this, inside its own data, which @@ -111,7 +111,7 @@ std::string Utf8Encoder::getUtf8(const char* input, int size) return std::string(&mOutput[0], outlen); } -std::string Utf8Encoder::getLegacyEnc(const char *input, int size) +std::string Utf8Encoder::getLegacyEnc(const char *input, size_t size) { // Double check that the input string stops at some point (it might // contain zero terminators before this, inside its own data, which diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index d975b3e4d..839aa36aa 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -27,13 +27,13 @@ namespace ToUTF8 Utf8Encoder(FromType sourceEncoding); // Convert to UTF8 from the previously given code page. - std::string getUtf8(const char *input, int size); + std::string getUtf8(const char *input, size_t size); inline std::string getUtf8(const std::string &str) { return getUtf8(str.c_str(), str.size()); } - std::string getLegacyEnc(const char *input, int size); + std::string getLegacyEnc(const char *input, size_t size); inline std::string getLegacyEnc(const std::string &str) { return getLegacyEnc(str.c_str(), str.size()); diff --git a/credits.txt b/credits.txt index 5c966d86b..d1e85c690 100644 --- a/credits.txt +++ b/credits.txt @@ -12,63 +12,75 @@ Marc Zinnschlag (Zini) - Lead Programmer/Project Manager Adam Hogan (aurix) Aleksandar Jovanov +Alexander Nadeau (wareya) Alexander Olofsson (Ace) Artem Kotsynyak (greye) athile BrotherBrick -Chris Robinson +Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) Douglas Diniz (Dgdiniz) +Douglas Mencken (dougmencken) +Edmondo Tommasina (edmondo) Eduard Cot (trombonecot) Eli2 -Emanuel "potatoesmaster" Guével -gugus / gus +Emanuel Guével (potatoesmaster) +gugus/gus Jacob Essex (Yacoby) Jannik Heller (scrawl) Jason Hooks (jhooks) Joel Graff (graffy) +Jordan Milne +Julien Voisin (jvoisin/ap0) Karl-Felix Glatzer (k1ll) +Lars Söderberg (Lazaroth) lazydev Leon Saunders (emoose) Lukasz Gromanowski (lgro) Marcin Hulist (Gohan) +Mark Siewert (mark76) +Manuel Edelmann (vorenon) Michael Mc Donnell Michael Papageorgiou (werdanith) Nathan Jeffords (blunted2night) Nikolay Kasyanov (corristo) +Nolan Poe (nopoe) +Paul McElroy (Greendogo) Pieter van der Kloet (pvdk) +Radu-Marius Popovici (rpopovici) Roman Melnik (Kromgart) +Sandy Carter (bwrsandman) Sebastian Wick (swick) -Sylvain T. (Garvek) +Sergey Shambir +Sylvain Thesnieres (Garvek) Tom Mason (wheybags) + Packagers: Alexander Olofsson (Ace) - Windows BrotherBrick - Ubuntu Linux -Edmondo Tommasina - Gentoo Linux +Edmondo Tommasina (edmondo) - Gentoo Linux +Julian Ospald (hasufell) - Gentoo Linux +Karl-Felix Glatzer (k1ll) - Linux Binaries Kenny Armstrong (artorius) - Fedora Linux Nikolay Kasyanov (corristo) - Mac OS X Sandy Carter (bwrsandman) - Arch Linux -Public Relations: -ElderTroll - Release Manager -sir_herrbatka - News Writer +Public Relations and Translations: +Artem Kotsynyak (greye) - Russian News Writer +Julien Voisin (jvoisin/ap0) - French News Writer +Mickey Lyle (raevol) - Release Manager +Pithorn - Chinese News Writer +sir_herrbatka - English/Polish News Writer WeirdSexy - Podcaster Website: -juanmnzsk8 - Spanish News Writer -Julien Voisin (jvoisin/ap0) - French News Writer -Kingpix - Italian News Writer Lukasz Gromanowski (lgro) - Website Administrator -Nikolay Kasyanov (corristo) - Russian News Writer -Okulo - Dutch News Writer -penguinroad - Indonesian News Writer Ryan Sardonic (Wry) - Wiki Editor -sir_herrbatka - Forum Admin/Polish News Writer -spyboot - German News Writer +sir_herrbatka - Forum Administrator Formula Research: @@ -84,20 +96,32 @@ Sadler Artwork: Necrod - OpenMW Logo -raevol - Wordpress Theme - +Mickey Lyle (raevol) - Wordpress Theme +Okulo - OpenMW Editor Icons Inactive Contributors: Ardekantur Armin Preiml +Carl Maxwell Diggory Hardy -Jan Borsodi +Dmitry Marakasov (AMDmi3) +ElderTroll +guidoj Jan-Peter Nilsson (peppe) +Jan Borsodi Josua Grawitter +juanmnzsk8 +Kingpix Lordrea +Michal Sciubidlo Nicolay Korslund +pchan3 +penguinroad +psi29a sergoz +spyboot Star-Demon +Thoronador Yuri Krupenin @@ -115,7 +139,7 @@ Thanks to Kevin Ryan, for creating the icon used for the Data Files tab of the OpenMW Launcher. Thanks to Georg Duffner, -for the open-source EB Garamond fontface. +for his EB Garamond fontface, see OFL.txt for his license terms. Thanks to Dongle, for his Daedric fontface, see Daedric Font License.txt for his license terms. diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 25624351c..0f1b3722f 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -225,9 +225,9 @@ float3 waterEyePos = float3(1,1,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); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - if (worldPos.y >= waterLevel || waterEnabled != 1.f) + if (worldPos.z >= waterLevel || waterEnabled != 1.f) caustics = float3(1,1,1); #endif @@ -269,7 +269,7 @@ #if UNDERWATER // regular fog only if fragment is above water - if (worldPos.y > waterLevel || waterEnabled != 1.f) + if (worldPos.z > waterLevel || waterEnabled != 1.f) #endif shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColour), fogValue); #endif @@ -278,7 +278,7 @@ shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); #if UNDERWATER - float fogAmount = (cameraPos.y > waterLevel) + float fogAmount = (cameraPos.z > waterLevel) ? shSaturate(length(waterEyePos-worldPos) / VISIBILITY) : shSaturate(length(cameraPos.xyz-worldPos)/ VISIBILITY); @@ -292,14 +292,14 @@ waterGradient = clamp((waterGradient*0.5+0.5),0.2,1.0); float3 watercolour = ( gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); - watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; + watercolour = (cameraPos.z <= waterLevel) ? watercolour : watercolour*0.3; float darkness = VISIBILITY*2.0; - darkness = clamp((waterEyePos.y - waterLevel + darkness)/darkness,0.2,1.0); + darkness = clamp((waterEyePos.z - waterLevel + darkness)/darkness,0.2,1.0); watercolour *= darkness; - float isUnderwater = (worldPos.y < waterLevel) ? 1.0 : 0.0; + float isUnderwater = (worldPos.z < waterLevel) ? 1.0 : 0.0; shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater * waterEnabled); #endif diff --git a/files/materials/terrain.shader b/files/materials/terrain.shader index dee733263..d8bc00c18 100644 --- a/files/materials/terrain.shader +++ b/files/materials/terrain.shader @@ -88,8 +88,8 @@ float toMorph = -min(0, sign(uv1.y - lodMorph.y)); // morph - // this assumes XZ terrain alignment - worldPos.y += uv1.x * toMorph * lodMorph.x; + // this assumes XY terrain alignment + worldPos.z += uv1.x * toMorph * lodMorph.x; shOutputPosition = shMatrixMult(viewProjMatrix, worldPos); @@ -233,9 +233,9 @@ float3 waterEyePos = float3(1,1,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); + waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel); caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - if (worldPos.y >= waterLevel) + if (worldPos.z >= waterLevel) caustics = float3(1,1,1); @@ -341,7 +341,7 @@ #if UNDERWATER // regular fog only if fragment is above water - if (worldPos.y > waterLevel) + if (worldPos.z > waterLevel) #endif shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColour), fogValue); #endif @@ -350,7 +350,7 @@ shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0)); #if UNDERWATER - float fogAmount = (cameraPos.y > waterLevel) + float fogAmount = (cameraPos.z > waterLevel) ? shSaturate(length(waterEyePos-worldPos) / VISIBILITY) : shSaturate(length(cameraPos.xyz-worldPos)/ VISIBILITY); @@ -365,14 +365,14 @@ float3 watercolour = (gammaCorrectRead(float3(0.0078, 0.5176, 0.700))+waterSunColour)*waterGradient*2.0; float3 waterext = gammaCorrectRead(float3(0.6, 0.9, 1.0));//water extinction watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); - watercolour = (cameraPos.y <= waterLevel) ? watercolour : watercolour*0.3; + watercolour = (cameraPos.z <= waterLevel) ? watercolour : watercolour*0.3; float darkness = VISIBILITY*2.0; - darkness = clamp((waterEyePos.y - waterLevel + darkness)/darkness,0.2,1.0); + darkness = clamp((waterEyePos.z - waterLevel + darkness)/darkness,0.2,1.0); watercolour *= darkness; - float isUnderwater = (worldPos.y < waterLevel) ? 1.0 : 0.0; + float isUnderwater = (worldPos.z < waterLevel) ? 1.0 : 0.0; shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, watercolour, fogAmount * isUnderwater); #endif diff --git a/files/materials/underwater.h b/files/materials/underwater.h index 18052a98d..a760202fa 100644 --- a/files/materials/underwater.h +++ b/files/materials/underwater.h @@ -79,9 +79,9 @@ float3 perturb(shTexture2D tex, float2 coords, float bend, float2 windDir, float float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 waterEyePos, float3 worldNormal, float3 lightDirectionWS0, float waterLevel, float waterTimer, float3 windDir_windSpeed) { - float waterDepth = shSaturate((waterEyePos.y - worldPos.y) / 50.0); + float waterDepth = shSaturate((waterEyePos.z - worldPos.z) / 50.0); - float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,1,0), waterLevel); + float3 causticPos = intercept(worldPos.xyz, lightDirectionWS0.xyz, float3(0,0,1), waterLevel); ///\ todo clean this up float causticdepth = length(causticPos-worldPos.xyz); @@ -91,20 +91,21 @@ float3 getCaustics (shTexture2D causticMap, float3 worldPos, float3 waterEyePos, // NOTE: the original shader calculated a tangent space basis here, // but using only the world normal is cheaper and i couldn't see a visual difference // also, if this effect gets moved to screen-space some day, it's unlikely to have tangent information - float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xzy * 2 - 1; + float3 causticNorm = worldNormal.xyz * perturb(causticMap, causticPos.xy, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).xyz * 2 - 1; + causticNorm = float3(causticNorm.x, causticNorm.y, -causticNorm.z); //float fresnel = pow(clamp(dot(LV,causticnorm),0.0,1.0),2.0); float NdotL = max(dot(worldNormal.xyz, lightDirectionWS0.xyz),0.0); - float causticR = 1.0-perturb(causticMap, causticPos.xz, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticR = 1.0-perturb(causticMap, causticPos.xy, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; /// \todo sunFade // float3 caustics = clamp(pow(float3(causticR)*5.5,float3(5.5*causticdepth)),0.0,1.0)*NdotL*sunFade*causticdepth; float3 caustics = clamp(pow(float3(causticR,causticR,causticR)*5.5,float3(5.5*causticdepth,5.5*causticdepth,5.5*causticdepth)),0.0,1.0)*NdotL*causticdepth; - float causticG = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; - float causticB = 1.0-perturb(causticMap,causticPos.xz+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticG = 1.0-perturb(causticMap,causticPos.xy+(1.0-causticdepth)*ABBERATION, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; + float causticB = 1.0-perturb(causticMap,causticPos.xy+(1.0-causticdepth)*ABBERATION*2.0, causticdepth, windDir_windSpeed.xy, windDir_windSpeed.z, waterTimer).z; //caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth)))*NdotL*sunFade*causticdepth; caustics = shSaturate(pow(float3(causticR,causticG,causticB)*5.5,float3(5.5*causticdepth,5.5*causticdepth,5.5*causticdepth)))*NdotL*causticdepth; diff --git a/files/materials/water.shader b/files/materials/water.shader index 6bd277eab..a58a9c38b 100644 --- a/files/materials/water.shader +++ b/files/materials/water.shader @@ -122,7 +122,7 @@ #define REFL_BUMP 0.08 // reflection distortion amount #define REFR_BUMP 0.06 // refraction distortion amount - #define SCATTER_AMOUNT 3.0 // amount of sunlight scattering + #define SCATTER_AMOUNT 0.3 // amount of sunlight scattering #define SCATTER_COLOUR gammaCorrectRead(float3(0.0,1.0,0.95)) // colour of sunlight scattering #define SUN_EXT gammaCorrectRead(float3(0.45, 0.55, 0.68)) //sunlight extinction @@ -219,25 +219,27 @@ float3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + - normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y).xzy; + normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y).xyz; - normal = normalize(float3(normal.x * BUMP, normal.y, normal.z * BUMP)); - + normal = normalize(float3(normal.x * BUMP, normal.y * BUMP, normal.z)); + normal = float3(normal.x, normal.y, -normal.z); + // normal for sunlight scattering float3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 + normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 + - normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xzy; - lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y, lNormal.z * BUMP)); - + normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1).xyz; + lNormal = normalize(float3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z)); + lNormal = float3(lNormal.x, lNormal.y, -lNormal.z); + float3 lVec = normalize(sunPosition.xyz); float3 vVec = normalize(position.xyz - cameraPos.xyz); - float isUnderwater = (cameraPos.y > 0) ? 0.0 : 1.0; + float isUnderwater = (cameraPos.z > 0) ? 0.0 : 1.0; // sunlight scattering - float3 pNormal = float3(0,1,0); + float3 pNormal = float3(0,0,1); float3 lR = reflect(lVec, lNormal); float3 llR = reflect(lVec, pNormal); @@ -246,13 +248,13 @@ float3 scatterColour = shLerp(float3(SCATTER_COLOUR)*gammaCorrectRead(float3(1.0,0.4,0.0)), SCATTER_COLOUR, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); // fresnel - float ior = (cameraPos.y>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air + float ior = (cameraPos.z>0)?(1.333/1.0):(1.0/1.333); //air to water; water to air float fresnel = fresnel_dielectric(-vVec, normal, ior); fresnel = shSaturate(fresnel); // reflection - float3 reflection = gammaCorrectRead(shSample(reflectionMap, screenCoords+(normal.xz*REFL_BUMP)).rgb); + float3 reflection = gammaCorrectRead(shSample(reflectionMap, screenCoords+(normal.xy*REFL_BUMP)).rgb); // refraction float3 R = reflect(vVec, normal); @@ -260,13 +262,13 @@ // check the depth at the refracted coords, and don't do any normal distortion for the refraction if the object to refract // is actually above the water (objectDepth < waterDepth) // this solves silhouettes around objects above the water - float refractDepth = shSample(depthMap, screenCoords-(shoreFade * normal.xz*REFR_BUMP)).x * far - depthPassthrough; + float refractDepth = shSample(depthMap, screenCoords-(shoreFade * normal.xy*REFR_BUMP)).x * far - depthPassthrough; float doRefraction = (refractDepth < 0) ? 0.f : 1.f; - float3 refraction = gammaCorrectRead(shSample(refractionMap, (screenCoords-(shoreFade * normal.xz*REFR_BUMP * doRefraction))*1.0).rgb); + float3 refraction = gammaCorrectRead(shSample(refractionMap, (screenCoords-(shoreFade * normal.xy*REFR_BUMP * doRefraction))*1.0).rgb); // brighten up the refraction underwater - refraction = (cameraPos.y < 0) ? shSaturate(refraction * 1.5) : refraction; + refraction = (cameraPos.z < 0) ? shSaturate(refraction * 1.5) : refraction; // specular float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS); @@ -290,7 +292,7 @@ watercolour = shLerp(watercolour*0.3*waterSunFade_sunHeight.x, watercolour, shSaturate(1.0-exp(-waterSunFade_sunHeight.y*SUN_EXT))); float darkness = VISIBILITY*2.0; - darkness = clamp((cameraPos.y+darkness)/darkness,0.2,1.0); + darkness = clamp((cameraPos.z+darkness)/darkness,0.2,1.0); float fog = shSaturate(length(cameraPos.xyz-position.xyz) / VISIBILITY); diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index e7e5b695e..beace5b81 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -9,7 +9,6 @@ set(MYGUI_FILES core.skin core.xml EBGaramond-Regular.ttf - mwgui.png Obliviontt.zip openmw_alchemy_window.layout openmw_book.layout diff --git a/files/mygui/mwgui.png b/files/mygui/mwgui.png deleted file mode 100644 index 318f16e41..000000000 Binary files a/files/mygui/mwgui.png and /dev/null differ diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 894c1dbeb..96d1153f0 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -2,41 +2,45 @@ - + - + + - + + - + - + + - + - + + - + - + - - + + diff --git a/files/mygui/openmw_chargen_class_description.layout b/files/mygui/openmw_chargen_class_description.layout index 11031eb4e..8823e1e76 100644 --- a/files/mygui/openmw_chargen_class_description.layout +++ b/files/mygui/openmw_chargen_class_description.layout @@ -4,6 +4,9 @@ + + + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index a9ec9905d..4ef8da0f3 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -8,33 +8,52 @@ - + - + - - + + + + + + + + + - - - + + + + + + + + + - - - + + + + + + + + + - + @@ -42,11 +61,11 @@ - + - + diff --git a/files/mygui/openmw_dialogue_window.layout b/files/mygui/openmw_dialogue_window.layout index 1271a287b..9a9da72d4 100644 --- a/files/mygui/openmw_dialogue_window.layout +++ b/files/mygui/openmw_dialogue_window.layout @@ -3,13 +3,15 @@ + + - + - + diff --git a/files/mygui/openmw_edit.skin.xml b/files/mygui/openmw_edit.skin.xml index 02fee4b17..da21385e2 100644 --- a/files/mygui/openmw_edit.skin.xml +++ b/files/mygui/openmw_edit.skin.xml @@ -1,55 +1,52 @@ + + + + + + - - - - + - - - - - - - - - - - - + + + + + + + + + + + + + - + + + + - - - - - - - - - - - - - + + + diff --git a/files/mygui/openmw_edit_effect.layout b/files/mygui/openmw_edit_effect.layout index 45ecb63ed..cad22c064 100644 --- a/files/mygui/openmw_edit_effect.layout +++ b/files/mygui/openmw_edit_effect.layout @@ -31,7 +31,7 @@ - + @@ -39,7 +39,7 @@ - + @@ -56,7 +56,7 @@ - + @@ -72,7 +72,7 @@ - + diff --git a/files/mygui/openmw_hud_box.skin.xml b/files/mygui/openmw_hud_box.skin.xml index ce231e5bb..23480e8d3 100644 --- a/files/mygui/openmw_hud_box.skin.xml +++ b/files/mygui/openmw_hud_box.skin.xml @@ -1,78 +1,31 @@ + - - - - - - - - - - - - - - - + - - - - - - - + - - - - - - - + + + - - - - - - - - + - - - - - - - + - - - - - - - - - - - - - - - + + + + diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 64435451a..6631424cc 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -1,228 +1,107 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + + + + + @@ -234,81 +113,85 @@ - - - - + + + + - - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - + - - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index 6862702ca..1e4bba5ed 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -6,12 +6,13 @@ - + - + + - + diff --git a/files/mygui/openmw_map_window_skin.xml b/files/mygui/openmw_map_window_skin.xml index 0c6050969..13f18c6d3 100644 --- a/files/mygui/openmw_map_window_skin.xml +++ b/files/mygui/openmw_map_window_skin.xml @@ -1,7 +1,7 @@ - + diff --git a/files/mygui/openmw_pointer.xml b/files/mygui/openmw_pointer.xml index 42ee5d435..cf21037f8 100644 --- a/files/mygui/openmw_pointer.xml +++ b/files/mygui/openmw_pointer.xml @@ -5,26 +5,31 @@ + + - + + - + + - + + diff --git a/files/mygui/openmw_progress.skin.xml b/files/mygui/openmw_progress.skin.xml index c4b94e28e..4666be221 100644 --- a/files/mygui/openmw_progress.skin.xml +++ b/files/mygui/openmw_progress.skin.xml @@ -17,6 +17,11 @@ + + + + + @@ -51,4 +56,12 @@ + + + + + + + + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml index 5a695515d..e47ff6386 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -17,27 +17,6 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_scroll_skin.xml b/files/mygui/openmw_scroll_skin.xml index 70fad3f4b..1b94f0c29 100644 --- a/files/mygui/openmw_scroll_skin.xml +++ b/files/mygui/openmw_scroll_skin.xml @@ -2,12 +2,12 @@ - + - + - + diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 2f9b5a67f..8d435bfd5 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -15,7 +15,7 @@ - + @@ -30,7 +30,7 @@ - + @@ -64,35 +64,35 @@ - + - + - + - + - + @@ -117,7 +117,7 @@ - + @@ -133,7 +133,7 @@ - + @@ -192,7 +192,7 @@ - + @@ -208,7 +208,7 @@ - + @@ -233,7 +233,7 @@ - + @@ -241,7 +241,7 @@ - + diff --git a/files/mygui/smallbars.png b/files/mygui/smallbars.png index a9ed572ef..f938412c2 100644 Binary files a/files/mygui/smallbars.png and b/files/mygui/smallbars.png differ diff --git a/libs/openengine/bullet/BtOgreExtras.h b/libs/openengine/bullet/BtOgreExtras.h index 423924eda..b20a3ff98 100644 --- a/libs/openengine/bullet/BtOgreExtras.h +++ b/libs/openengine/bullet/BtOgreExtras.h @@ -207,7 +207,7 @@ public: mLineDrawer->setMaterial("BtOgre/DebugLines"); - mLineDrawer->setVisibilityFlags (1024); + //mLineDrawer->setVisibilityFlags (1024); } ~DebugDrawer() diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index b39ba53a2..f993ce68e 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "pmove.h" #include #include "CMotionState.h" #include "OgreRoot.h" @@ -27,91 +26,48 @@ namespace Physic COL_RAYCASTING = BIT(3) }; - PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale): - mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0), mBody(0), collisionMode(false), mBoxRotation(0,0,0,0) + PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) + : mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0) + , mBody(0), onGround(false), collisionMode(true), mBoxRotation(0,0,0,0), verticalForce(0.0f) { + // FIXME: Force player to start in no-collision mode for now, until he spawns at a proper door marker. + if(name == "player") + collisionMode = false; mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); Ogre::Quaternion inverse = mBoxRotation.Inverse(); mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map - pmove = new playerMove; - pmove->mEngine = mEngine; - btBoxShape* box = static_cast (mBody->getCollisionShape()); - if(box != NULL){ - btVector3 size = box->getHalfExtentsWithMargin(); - Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); - pmove->ps.halfExtents = halfExtents; - } } PhysicActor::~PhysicActor() { - if(mBody){ + if(mBody) + { mEngine->dynamicsWorld->removeRigidBody(mBody); delete mBody; } - delete pmove; - } - - void PhysicActor::setCurrentWater(bool hasWater, int waterHeight){ - pmove->hasWater = hasWater; - if(hasWater){ - pmove->waterHeight = waterHeight; - } - } - - void PhysicActor::setGravity(float gravity) - { - pmove->ps.gravity = gravity; - } - - void PhysicActor::setSpeed(float speed) - { - pmove->ps.speed = speed; } void PhysicActor::enableCollisions(bool collision) { collisionMode = collision; - if(collisionMode) - pmove->ps.move_type=PM_NORMAL; - else - pmove->ps.move_type=PM_NOCLIP; } - void PhysicActor::setJumpVelocity(float velocity) + + void PhysicActor::setPosition(const Ogre::Vector3 &pos) { - pmove->ps.jump_velocity = velocity; + if(pos != getPosition()) + mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); } - bool PhysicActor::getCollisionMode() - { - return collisionMode; - } - - void PhysicActor::setMovement(signed char rightmove, signed char forwardmove, signed char upmove) - { - playerMove::playercmd& pm_ref = pmove->cmd; - pm_ref.rightmove = rightmove; - pm_ref.forwardmove = forwardmove; - pm_ref.upmove = upmove; - } - - void PhysicActor::setPmoveViewAngles(float pitch, float yaw, float roll){ - pmove->ps.viewangles.x = pitch; - pmove->ps.viewangles.y = yaw; - pmove->ps.viewangles.z = roll; - } - - - - void PhysicActor::setRotation(const Ogre::Quaternion quat) + void PhysicActor::setRotation(const Ogre::Quaternion &quat) { if(!quat.equals(getRotation(), Ogre::Radian(0))){ mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); } } + Ogre::Vector3 PhysicActor::getPosition() { btVector3 vec = mBody->getWorldTransform().getOrigin(); @@ -128,13 +84,6 @@ namespace Physic return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); } - void PhysicActor::setPosition(const Ogre::Vector3 pos) - { - mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); - btVector3 vec = mBody->getWorldTransform().getOrigin(); - pmove->ps.origin = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()); - } - void PhysicActor::setScale(float scale){ Ogre::Vector3 position = getPosition(); Ogre::Quaternion rotation = getRotation(); @@ -148,18 +97,40 @@ namespace Physic //Create the newly scaled rigid body mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation); mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map - btBoxShape* box = static_cast (mBody->getCollisionShape()); - if(box != NULL){ - btVector3 size = box->getHalfExtentsWithMargin(); - Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); - pmove->ps.halfExtents = halfExtents; - } } - void PhysicActor::runPmove(){ - Pmove(pmove); - Ogre::Vector3 newpos = pmove->ps.origin; - mBody->getWorldTransform().setOrigin(btVector3(newpos.x, newpos.y, newpos.z)); + Ogre::Vector3 PhysicActor::getHalfExtents() const + { + if(mBody) + { + btBoxShape *box = static_cast(mBody->getCollisionShape()); + if(box != NULL) + { + btVector3 size = box->getHalfExtentsWithMargin(); + return Ogre::Vector3(size.getX(), size.getY(), size.getZ()); + } + } + return Ogre::Vector3(0.0f); + } + + void PhysicActor::setVerticalForce(float force) + { + verticalForce = force; + } + + float PhysicActor::getVerticalForce() const + { + return verticalForce; + } + + void PhysicActor::setOnGround(bool grounded) + { + onGround = grounded; + } + + bool PhysicActor::getOnGround() const + { + return collisionMode && onGround; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -221,11 +192,7 @@ namespace Physic { if(!isDebugCreated) { - Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator(); - iter.begin(); - Ogre::SceneManager* scn = iter.getNext(); - Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode(); - node->pitch(Ogre::Degree(-90)); + Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); dynamicsWorld->setDebugDrawer(mDebugDrawer); isDebugCreated = true; @@ -249,6 +216,11 @@ namespace Physic return mDebugActive; } + void PhysicEngine::setSceneManager(Ogre::SceneManager* sceneMgr) + { + mSceneMgr = sceneMgr; + } + PhysicEngine::~PhysicEngine() { HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); @@ -356,18 +328,21 @@ namespace Physic mHeightFieldMap.erase(name); } - void PhysicEngine::adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3 scaledBoxTranslation, Ogre::Quaternion boxRotation){ + void PhysicEngine::adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, + const Ogre::Vector3 &scaledBoxTranslation, const Ogre::Quaternion &boxRotation) + { btTransform tr; - rotation = rotation * boxRotation; - Ogre::Vector3 transrot = rotation * scaledBoxTranslation; + Ogre::Quaternion boxrot = rotation * boxRotation; + Ogre::Vector3 transrot = boxrot * scaledBoxTranslation; Ogre::Vector3 newPosition = transrot + position; - + tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); - tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); + tr.setRotation(btQuaternion(boxrot.x,boxrot.y,boxrot.z,boxrot.w)); body->setWorldTransform(tr); } - void PhysicEngine::boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation){ + void PhysicEngine::boxAdjustExternal(const std::string &mesh, RigidBody* body, + float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation) + { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; //std::cout << "The string" << outputstring << "\n"; @@ -380,7 +355,8 @@ namespace Physic adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); } - RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, + RigidBody* PhysicEngine::createAndAdjustRigidBody(const std::string &mesh, const std::string &name, + float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation) { std::string sid = (boost::format("%07.3f") % scale).str(); @@ -441,7 +417,7 @@ namespace Physic } } - void PhysicEngine::removeRigidBody(std::string name) + void PhysicEngine::removeRigidBody(const std::string &name) { RigidBodyContainer::iterator it = ObjectMap.find(name); if (it != ObjectMap.end() ) @@ -461,7 +437,7 @@ namespace Physic } } - void PhysicEngine::deleteRigidBody(std::string name) + void PhysicEngine::deleteRigidBody(const std::string &name) { RigidBodyContainer::iterator it = ObjectMap.find(name); if (it != ObjectMap.end() ) @@ -481,7 +457,7 @@ namespace Physic } } - RigidBody* PhysicEngine::getRigidBody(std::string name) + RigidBody* PhysicEngine::getRigidBody(const std::string &name) { RigidBodyContainer::iterator it = ObjectMap.find(name); if (it != ObjectMap.end() ) @@ -497,15 +473,16 @@ namespace Physic void PhysicEngine::stepSimulation(double deltaT) { - dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); + // This isn't needed as there are no dynamic objects at this point + //dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { mDebugDrawer->step(); } } - void PhysicEngine::addCharacter(std::string name, std::string mesh, - Ogre::Vector3 position, float scale, Ogre::Quaternion rotation) + void PhysicEngine::addCharacter(const std::string &name, const std::string &mesh, + const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation) { // Remove character with given name, so we don't make memory // leak when character would be added twice @@ -518,9 +495,8 @@ namespace Physic PhysicActorMap[name] = newActor; } - void PhysicEngine::removeCharacter(std::string name) + void PhysicEngine::removeCharacter(const std::string &name) { - //std::cout << "remove"; PhysicActorContainer::iterator it = PhysicActorMap.find(name); if (it != PhysicActorMap.end() ) { @@ -534,7 +510,7 @@ namespace Physic } } - PhysicActor* PhysicEngine::getCharacter(std::string name) + PhysicActor* PhysicEngine::getCharacter(const std::string &name) { PhysicActorContainer::iterator it = PhysicActorMap.find(name); if (it != PhysicActorMap.end() ) diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 76bdb491d..bd5d3d50a 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -18,13 +18,17 @@ class btSequentialImpulseConstraintSolver; class btCollisionDispatcher; class btDiscreteDynamicsWorld; class btHeightfieldTerrainShape; -struct playerMove; namespace BtOgre { class DebugDrawer; } +namespace Ogre +{ + class SceneManager; +} + namespace MWWorld { class World; @@ -61,33 +65,26 @@ namespace Physic class PhysicActor { public: - PhysicActor(std::string name, std::string mesh, PhysicEngine *engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale); + PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale); ~PhysicActor(); - void setCurrentWater(bool hasWater, int waterHeight); - - /** - * This function sets the movement keys for pmove - */ - void setMovement(signed char rightmove, signed char forwardmove, signed char upmove); + void setPosition(const Ogre::Vector3 &pos); /** * This adjusts the rotation of a PhysicActor * If we have any problems with this (getting stuck in pmove) we should change it * from setting the visual orientation to setting the orientation of the rigid body directly. */ - void setRotation(const Ogre::Quaternion quat); - - void setGravity(float gravity); - - void setSpeed(float speed); - - void setJumpVelocity(float velocity); + void setRotation(const Ogre::Quaternion &quat); void enableCollisions(bool collision); - bool getCollisionMode(); + bool getCollisionMode() const + { + return collisionMode; + } + /** * This returns the visual position of the PhysicActor (used to position a scenenode). @@ -100,27 +97,29 @@ namespace Physic */ Ogre::Quaternion getRotation(); - /** - * Sets the position of mBody from a visual position input. - * For most cases this should not be used. We should instead let pmove move the PhysicActor around for us - */ - void setPosition(const Ogre::Vector3 pos); - - /** - * Sets the view angles for pmove directly. - * Remember, add 90 for yaw. Set roll to 0. - */ - void setPmoveViewAngles(float pitch, float yaw, float roll); - /** * Sets the scale of the PhysicActor */ void setScale(float scale); /** - * Runs pmove for this PhysicActor + * Returns the half extents for this PhysiActor */ - void runPmove(); + Ogre::Vector3 getHalfExtents() const; + + /** + * Sets the current amount of vertical force (gravity) affecting this physic actor + */ + void setVerticalForce(float force); + + /** + * Gets the current amount of vertical force (gravity) affecting this physic actor + */ + float getVerticalForce() const; + + void setOnGround(bool grounded); + + bool getOnGround() const; //HACK: in Visual Studio 2010 and presumably above, this structures alignment // must be 16, but the built in operator new & delete don't properly @@ -136,12 +135,12 @@ namespace Physic Ogre::Vector3 mBoxScaledTranslation; btQuaternion mBoxRotationInverse; Ogre::Quaternion mBoxRotation; + float verticalForce; + bool onGround; bool collisionMode; std::string mMesh; PhysicEngine* mEngine; std::string mName; - playerMove* pmove; - }; /** @@ -190,19 +189,21 @@ namespace Physic * Creates a RigidBody. It does not add it to the simulation. * After created, the body is set to the correct rotation, position, and scale */ - RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, + RigidBody* createAndAdjustRigidBody(const std::string &mesh, const std::string &name, + float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0); /** * Adjusts a rigid body to the right position and rotation */ - void adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation, - Ogre::Vector3 scaledBoxTranslation = Ogre::Vector3::ZERO, Ogre::Quaternion boxRotation = Ogre::Quaternion::IDENTITY); + void adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, + const Ogre::Vector3 &scaledBoxTranslation = Ogre::Vector3::ZERO, + const Ogre::Quaternion &boxRotation = Ogre::Quaternion::IDENTITY); /** Mainly used to (but not limited to) adjust rigid bodies based on box shapes to the right position and rotation. */ - void boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); + void boxAdjustExternal(const std::string &mesh, RigidBody* body, float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation); /** * Add a HeightField to the simulation */ @@ -223,35 +224,35 @@ namespace Physic /** * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. */ - void removeRigidBody(std::string name); + void removeRigidBody(const std::string &name); /** * Delete a RigidBody, and remove it from RigidBodyMap. */ - void deleteRigidBody(std::string name); + void deleteRigidBody(const std::string &name); /** * Return a pointer to a given rigid body. * TODO:check if exist */ - RigidBody* getRigidBody(std::string name); + RigidBody* getRigidBody(const std::string &name); /** * Create and add a character to the scene, and add it to the ActorMap. */ - void addCharacter(std::string name, std::string mesh, - Ogre::Vector3 position, float scale, Ogre::Quaternion rotation); + void addCharacter(const std::string &name, const std::string &mesh, + const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation); /** * Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done? */ - void removeCharacter(std::string name); + void removeCharacter(const std::string &name); /** * Return a pointer to a character * TODO:check if the actor exist... */ - PhysicActor* getCharacter(std::string name); + PhysicActor* getCharacter(const std::string &name); /** * This step the simulation of a given time. @@ -279,6 +280,8 @@ namespace Physic void getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max); + void setSceneManager(Ogre::SceneManager* sceneMgr); + /** * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). */ @@ -315,11 +318,12 @@ namespace Physic typedef std::map PhysicActorContainer; PhysicActorContainer PhysicActorMap; + Ogre::SceneManager* mSceneMgr; + //debug rendering BtOgre::DebugDrawer* mDebugDrawer; bool isDebugCreated; bool mDebugActive; - }; diff --git a/libs/openengine/bullet/pmove.cpp b/libs/openengine/bullet/pmove.cpp deleted file mode 100644 index 8d98a482e..000000000 --- a/libs/openengine/bullet/pmove.cpp +++ /dev/null @@ -1,2115 +0,0 @@ -/* -This source file is a *modified* version of bg_pmove.c from the Quake 3 Arena source code, -which was released under the GNU GPL (v2) in 2005. -Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. -*/ - - -#include "pmove.h" - - - -//#include "bprintf.h" - -//#include "..\..\ESMParser\ESMParser\CELL.h" - -//#include "GameTime.h" - -//#include "Sound.h" - -//#include "..\..\ESMParser\ESMParser\SNDG.h" -//#include "..\..\ESMParser\ESMParser\SOUN.h" - -#include - -//SceneInstance* global_lastscene = NULL; - -// Forward declaration: -void PM_AirMove(); - -static playerMove* pm = NULL; - -//extern std::map ExtCellLookup; - -static struct playermoveLocal -{ - playermoveLocal() : frametime(1.0f / 20.0f), groundPlane(true), walking(true), msec(50) - { - forward = Ogre::Vector3(0.0f, 0.0f, 0.0f); - right = Ogre::Vector3(0.0f, 0.0f, 0.0f); - up = Ogre::Vector3(0.0f, 0.0f, 0.0f); - - previous_origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); - previous_velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); - } - - traceResults groundTrace; - - //SceneInstance* scene; - - float frametime; // in seconds (usually something like 0.01f) - float impactSpeed; - - Ogre::Vector3 forward; - Ogre::Vector3 right; - Ogre::Vector3 up; - - int msec; - - Ogre::Vector3 previous_origin, previous_velocity; - - int previous_waterlevel; // the waterlevel before this pmove - - bool groundPlane; // if we're standing on a groundplane this frame - - bool walking; - int waterHeight; - bool hasWater; - bool isInterior; - -} pml; - -static inline void PM_ClipVelocity(const Ogre::Vector3& in, const Ogre::Vector3& normal, Ogre::Vector3& out, const float overbounce) -{ - float backoff; - //float change; - //int i; - - // backoff = in dot normal - //backoff = DotProduct (in, normal); - backoff = in.dotProduct(normal); - - if ( backoff < 0 ) - backoff *= overbounce; - else - backoff /= overbounce; - - // change = normal * backoff - // out = in - change - /*for ( i=0 ; i<3 ; i++ ) - { - change = normal[i]*backoff; - out[i] = in[i] - change; - - }*/ - float changex = normal.x * backoff; - out.x = in.x - changex; - float changey = normal.y * backoff; - out.y = in.y - changey; - float changez = normal.z * backoff; - out.z = in.z - changez; -} - -float VectorNormalize2( const Ogre::Vector3& v, Ogre::Vector3& out) -{ - float length, ilength; - - length = v.x * v.x+ v.y * v.y + v.z * v.z; - length = sqrt(length); - - if (length) - { -#ifndef Q3_VM // bk0101022 - FPE related -// assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) ); -#endif - ilength = 1 / length; - out.x= v.x * ilength; - out.y = v.y * ilength; - out.z = v.z * ilength; - } else - { -#ifndef Q3_VM // bk0101022 - FPE related -// assert( ((Q_fabs(v[0])==0.0f) && (Q_fabs(v[1])==0.0f) && (Q_fabs(v[2])==0.0f)) ); -#endif - //VectorClear( out ); - out.x = 0; out.y = 0; out.z = 0; - } - - return length; - -} - - -float VectorNormalize(Ogre::Vector3& out) -{ - float length, ilength; - - length = out.x * out.x + out.y * out.y + out.z * out.z; - length = sqrt(length); - - if (length) - { -#ifndef Q3_VM // bk0101022 - FPE related -// assert( ((Q_fabs(v[0])!=0.0f) || (Q_fabs(v[1])!=0.0f) || (Q_fabs(v[2])!=0.0f)) ); -#endif - ilength = 1 / length; - out.x = out.x * ilength; - out.y = out.y * ilength; - out.z = out.z * ilength; - } - - return length; - -} - -/* -================== -PM_SlideMove - -Returns qtrue if the velocity was clipped in some way -================== -*/ - -bool PM_SlideMove( bool gravity ) -{ - int bumpcount, numbumps; - Ogre::Vector3 dir; - float d; - int numplanes; - Ogre::Vector3 planes[MAX_CLIP_PLANES]; - Ogre::Vector3 primal_velocity; - Ogre::Vector3 clipVelocity; - int i, j, k; - struct traceResults trace; - Ogre::Vector3 end(0,0,0); - float time_left; - float into; - Ogre::Vector3 endVelocity(0,0,0); - Ogre::Vector3 endClipVelocity(0,0,0); - - numbumps = 4; - - // primal_velocity = pm->ps->velocity - //VectorCopy (pm->ps->velocity, primal_velocity); - primal_velocity = pm->ps.velocity; - - if ( gravity ) - { - // endVelocity = pm->ps->velocity - vec3(0, 0, pm->ps->gravity * pml.frametime) - //VectorCopy( pm->ps->velocity, endVelocity ); - endVelocity = pm->ps.velocity; - //endVelocity[2] -= pm->ps->gravity * pml.frametime; - endVelocity.z -= pm->ps.gravity * pml.frametime; - - // pm->ps->velocity = avg(pm->ps->velocity.z, endVelocity.z) - //pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; - pm->ps.velocity.z= (pm->ps.velocity.z + endVelocity.z) * 0.5f; - - //primal_velocity[2] = endVelocity[2]; - primal_velocity.z = endVelocity.z; - - if ( pml.groundPlane ) - // slide along the ground plane - //PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP); - } - - time_left = pml.frametime; - - // never turn against the ground plane - if ( pml.groundPlane ) - { - numplanes = 1; - - // planes[0] = pml.groundTrace.plane.normal - //VectorCopy( pml.groundTrace.plane.normal, planes[0] ); - planes[0] = pml.groundTrace.planenormal; - } else - numplanes = 0; - - // never turn against original velocity - VectorNormalize2( pm->ps.velocity, planes[numplanes] ); - numplanes++; - - for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) - { - - // calculate position we are trying to move to - //VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); - end = pm->ps.origin + pm->ps.velocity * time_left; - - // see if we can make it there - //pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemaskg); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&(end), *(const D3DXVECTOR3* const)&(pm->ps.velocity), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, end, pm->ps.halfExtents, Ogre::Math::DegreesToRadians (pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - - if (trace.allsolid) - { - // entity is completely trapped in another solid - //pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration - pm->ps.velocity = Ogre::Vector3(0,0,0); - return true; - } - - if (trace.fraction > 0) - // actually covered some distance - //VectorCopy (trace.endpos, pm->ps->origin); - pm->ps.origin = trace.endpos; - - if (trace.fraction == 1) - break; // moved the entire distance - - // save entity for contact8 - //PM_AddTouchEnt( trace.entityNum ); - - time_left -= time_left * trace.fraction; - - if (numplanes >= MAX_CLIP_PLANES) - { - // this shouldn't really happen - //VectorClear( pm->ps->velocity ); - pm->ps.velocity = Ogre::Vector3(0,0,0); - return true; - } - - // - // if this is the same plane we hit before, nudge velocity - // out along it, which fixes some epsilon issues with - // non-axial planes - // - for ( i = 0 ; i < numplanes ; i++ ) - { - if (trace.planenormal.dotProduct(planes[i]) > 0.99) //OGRE::VECTOR3 ? - //if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) - { - // pm->ps->velocity += (trace.plane.normal + pm->ps->velocity) - //VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); - pm->ps.velocity = trace.planenormal + pm->ps.velocity; - break; - } - } - - if ( i < numplanes ) - continue; - - //VectorCopy (trace.plane.normal, planes[numplanes]); - planes[numplanes] = trace.planenormal; - numplanes++; - - // - // modify velocity so it parallels all of the clip planes - // - - // find a plane that it enters - for ( i = 0 ; i < numplanes ; i++ ) - { - //into = DotProduct( pm->ps->velocity, planes[i] ); - into = pm->ps.velocity.dotProduct(planes[i]); - if ( into >= 0.1 ) - continue; // move doesn't interact with the plane - - - if(planes[i].x >= .70) - { - pm->ps.velocity.z = 0; - return true; - } - // see how hard we are hitting things - if ( -into > pml.impactSpeed ) - pml.impactSpeed = -into; - - // slide along the plane - //PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, planes[i], clipVelocity, OVERCLIP); - - // slide along the plane - PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); - - // see if there is a second plane that the new move enters - for ( j = 0 ; j < numplanes ; j++ ) - { - if ( j == i ) - continue; - - if (clipVelocity.dotProduct(planes[j]) >= 0.1) - //if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) - continue; // move doesn't interact with the plane - - - - - //pm->ps.velocity = Ogre::Vector3(0,0,0); - //return true; - - - // try clipping the move to the plane - PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); - PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); - - // see if it goes back into the first clip plane - if (clipVelocity.dotProduct(planes[i]) >= 0) - //if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) - continue; - - - // slide the original velocity along the crease - //dProduct (planes[i], planes[j], dir); - dir = planes[i].crossProduct(planes[j]) ; - - //VectorNormalize( dir ); - //D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir); - VectorNormalize(dir); - - //d = DotProduct( dir, pm->ps->velocity ); - d = dir.dotProduct(pm->ps.velocity); - - //VectorScale( dir, d, clipVelocity ); - clipVelocity = dir * d; - - //CrossProduct (planes[i], planes[j], dir); - dir = planes[i].crossProduct(planes[j]) ; - - - //VectorNormalize( dir ); - //D3DXVec3Normalize( (D3DXVECTOR3* const)&dir, (const D3DXVECTOR3* const)&dir); - VectorNormalize(dir); - - //d = DotProduct( dir, endVelocity ); - d = dir.dotProduct(endVelocity); - - //VectorScale( dir, d, endClipVelocity ); - endClipVelocity = dir * d; - - // see if there is a third plane the the new move enters - for ( k = 0 ; k < numplanes ; k++ ) - { - - if ( k == i || k == j ) - continue; - - if (clipVelocity.dotProduct(planes[k]) >= 0.1) - //if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) - continue; // move doesn't interact with the plane - - // stop dead at a tripple plane interaction - //VectorClear( pm->ps->velocity ); - //printf("Stop dead at a triple plane interaction\n"); - pm->ps.velocity = Ogre::Vector3(0,0,0); - return true; - } - } - - // if we have fixed all interactions, try another move - //VectorCopy( clipVelocity, pm->ps->velocity ); - pm->ps.velocity = clipVelocity; - - //VectorCopy( endClipVelocity, endVelocity ); - endVelocity = endClipVelocity; - break; - } - } - - if ( gravity ) - //VectorCopy( endVelocity, pm->ps->velocity ); - pm->ps.velocity = endVelocity; - - // don't change velocity if in a timer (FIXME: is this correct?) - if ( pm->ps.pm_time ) - //VectorCopy( primal_velocity, pm->ps->velocity ); - pm->ps.velocity = primal_velocity; - - //return ( (qboolean)(bumpcount != 0) ); - return bumpcount != 0; -} - -/* -================== -PM_StepSlideMove - -================== -*/ -int PM_StepSlideMove( bool gravity ) -{ - Ogre::Vector3 start_o, start_v; - Ogre::Vector3 down_o, down_v; - traceResults trace; -// float down_dist, up_dist; -// vec3_t delta, delta2; - Ogre::Vector3 up, down; - float stepSize; - - //std::cout << "StepSlideMove\n"; - // start_o = pm->ps->origin - //VectorCopy (pm->ps->origin, start_o); - start_o = pm->ps.origin; - - // start_v = pm->ps->velocity - //VectorCopy (pm->ps->velocity, start_v); - start_v = pm->ps.velocity; - - if ( PM_SlideMove( gravity ) == false ) - return 1; // we got exactly where we wanted to go first try - - - // down = start_o - vec3(0, 0, STEPSIZE) - //VectorCopy(start_o, down); - down = start_o; - down.z -= STEPSIZE; - - //pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, start_o, down, , 0, pml.scene); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, down, start_o, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - - // up = vec3(0, 0, 1) - //VectorSet(up, 0, 0, 1); - up = Ogre::Vector3(0.0f, 0.0f, 1.0f); - - // never step up when you still have up velocity - //if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) - if (pm->ps.velocity.z > 0 && ( - trace.fraction == 1.0 || trace.planenormal.dotProduct(up) < 0.7 - ) ) - return 2; - - // down_o = pm->ps->origin - //VectorCopy (pm->ps->origin, down_o); - down_o = pm->ps.origin; - - // down_v = pm->ps->velocity - //VectorCopy (pm->ps->velocity, down_v); - down_v = pm->ps.velocity; - - // up = start_o + vec3(0, 0, STEPSIZE) - //VectorCopy (start_o, up); - up = start_o; - //up[2] += STEPSIZE; - up.z += STEPSIZE; - - // test the player position if they were a stepheight higher - //pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&start_o, *(const D3DXVECTOR3* const)&up, D3DXVECTOR3(0.0f, STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, start_o, up, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - if ( trace.allsolid ) - { - //if ( pm->debugLevel ) - //Com_Printf("%i:bend can't step\n", c_pmove); - //bprintf("bend can't step\n"); - return 3; // can't step up - } - - //stepSize = trace.endpos[2] - start_o[2]; - stepSize = trace.endpos.z - start_o.z; - - // try slidemove from this position - //VectorCopy (trace.endpos, pm->ps->origin); // pm->ps->origin = trace.endpos - pm->ps.origin = trace.endpos; - //VectorCopy (start_v, pm->ps->velocity); // pm->ps->velocity = start_v - pm->ps.velocity = start_v; - - PM_SlideMove( gravity ); - - // push down the final amount - - // down = pm->ps->origin - vec3(0, 0, stepSize) - //VectorCopy (pm->ps->origin, down); - down = pm->ps.origin; - //down[2] -= stepSize; - down.z -= stepSize; - - - //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&down, D3DXVECTOR3(0.0f, -STEPSIZE, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, down, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - if ( !trace.allsolid ) - //VectorCopy (trace.endpos, pm->ps->origin); - pm->ps.origin = trace.endpos; - - if ( trace.fraction < 1.0 ) - //PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, trace.planenormal, pm->ps.velocity, OVERCLIP); - - { - // use the step move - float delta; - - //delta = pm->ps->origin[2] - start_o[2]; - delta = pm->ps.origin.z - start_o.z; - if ( delta > 2 ) - { - pm->ps.counter = 10; - - /* - if (gravity) - printf("g on: %f ", delta); - else - printf("g off: %f ", delta); - - if ( delta < 7 ) - printf("stepped 3 < x < 7\n"); - //PM_AddEvent( EV_STEP_4 ); - else if ( delta < 11 ) - printf("stepped 7 < x < 11\n"); - //PM_AddEvent( EV_STEP_8 ); - else if ( delta < 15 ) - printf("stepped 11 < x < 15\n"); - //PM_AddEvent( EV_STEP_12 ); - else - printf("stepped 15+\n"); - //PM_AddEvent( EV_STEP_16 ); - */ - } - /*if ( pm->debugLevel ) - Com_Printf("%i:stepped\n", c_pmove);*/ - } - - return 4; -} - -void PM_Friction(void) -{ - - Ogre::Vector3 vec; - float* vel; - float speed, newspeed, control; - float drop; - - vel = &(pm->ps.velocity.x); - - // vec = vel - //VectorCopy( vel, vec ); - vec = pm->ps.velocity; - - if ( pml.walking ) - //vec[2] = 0; // ignore slope movement - vec.z = 0; - - //speed = VectorLength(vec); - speed = vec.length(); - if (speed < 1) - { - vel[0] = 0; - vel[1] = 0; // allow sinking underwater - // FIXME: still have z friction underwater? - //bprintf("Static friction (vec = [%f, %f, %f]) (vec.length = %f)\n", vec.x, vec.y, vec.z, speed); - return; - } - - drop = 0; - - // apply ground friction - if ( pm->ps.waterlevel <= 1 ) - { - if ( pml.walking )//&& !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) - { - // if getting knocked back, no friction - //if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) - { - control = (speed < pm_stopspeed) ? pm_stopspeed : speed; - drop += control * pm_friction * pml.frametime; - } - } - } - - // apply water friction even if just wading - if ( pm->ps.waterlevel ) - drop += speed * pm_waterfriction * pm->ps.waterlevel * pml.frametime; - - // apply flying friction - /*if ( pm->ps->powerups[PW_FLIGHT]) - drop += speed * pm_flightfriction * pml.frametime; - - if ( pm->ps->pm_type == PM_SPECTATOR) - drop += speed * pm_spectatorfriction * pml.frametime;*/ - if (pm->ps.move_type == PM_SPECTATOR) - drop += speed * pm_flightfriction * pml.frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - - newspeed /= speed; - - // vel *= newspeed - vel[0] = vel[0] * newspeed; - vel[1] = vel[1] * newspeed; - vel[2] = vel[2] * newspeed; -} - -float PM_CmdScale(playerMove::playercmd* const cmd) -{ - int max; - float total; - float scale; - - max = abs( cmd->forwardmove ); - if ( abs( cmd->rightmove ) > max ) - max = abs( cmd->rightmove ); - - if ( abs( cmd->upmove ) > max ) - max = abs( cmd->upmove ); - - if ( !max ) - return 0; - - total = sqrtf( (const float)(cmd->forwardmove * cmd->forwardmove - + cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove) ); - scale = (float)pm->ps.speed * max / ( 127.0f * total ); - if(pm->ps.move_type == PM_NOCLIP) - scale *= 10; - - return scale; -} - -static void PM_Accelerate( Ogre::Vector3& wishdir, float wishspeed, float accel ) -{ -// int i; - float addspeed, accelspeed, currentspeed; - - - // currentspeed = pm->ps->velocity dot wishdir - //currentspeed = DotProduct (pm->ps->velocity, wishdir); - currentspeed = pm->ps.velocity.dotProduct(wishdir); - - addspeed = wishspeed - currentspeed; - if (addspeed <= 0) - return; - - accelspeed = accel * pml.frametime * wishspeed; - - // Clamp accelspeed at addspeed - if (accelspeed > addspeed) - accelspeed = addspeed; - - // pm->ps->velocity += accelspeed * wishdir - //for (i=0 ; i<3 ; i++) - //pm->ps->velocity[i] += accelspeed * wishdir[i]; - pm->ps.velocity += (wishdir * accelspeed); - //pm->ps.velocity = wishdir * wishspeed; //New, for instant acceleration - -} - -static bool PM_CheckJump(void) -{ - //if ( pm->ps->pm_flags & PMF_RESPAWNED ) - //return qfalse; // don't allow jump until all buttons are up - - if ( pm->cmd.upmove < 10 ) - // not holding jump - return false; - - pm->cmd.upmove = 0; - - // must wait for jump to be released - /*if ( pm->ps->pm_flags & PMF_JUMP_HELD ) - { - // clear upmove so cmdscale doesn't lower running speed - pm->cmd.upmove = 0; - return false; - }*/ - - pml.groundPlane = false; // jumping away - pml.walking = false; - //pm->ps->pm_flags |= PMF_JUMP_HELD; - - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pm->ps.velocity.z = pm->ps.jump_velocity; - pm->ps.bSnap = false; - //PM_AddEvent( EV_JUMP ); - - /*if ( pm->cmd.forwardmove >= 0 ) - { - PM_ForceLegsAnim( LEGS_JUMP ); - pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; - } - else - { - PM_ForceLegsAnim( LEGS_JUMPB ); - pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; - }*/ - - return true; -} - -static void PM_WaterMove( playerMove* const pm ) -{ - //int i; - //vec3_t wishvel; - Ogre::Vector3 wishvel; - float wishspeed; - //vec3_t wishdir; - Ogre::Vector3 wishdir; - float scale; - float vel; - - pm->ps.bSnap = false; - - /*if ( PM_CheckWaterJump() ) - { - PM_WaterJumpMove(); - return; - }*/ -#if 0 - // jump = head for surface - if ( pm->cmd.upmove >= 10 ) { - if (pm->ps->velocity[2] > -300) { - if ( pm->watertype == CONTENTS_WATER ) { - pm->ps->velocity[2] = 100; - } else if (pm->watertype == CONTENTS_SLIME) { - pm->ps->velocity[2] = 80; - } else { - pm->ps->velocity[2] = 50; - } - } - } -#endif - PM_Friction (); - - if (pm->cmd.forwardmove || pm->cmd.rightmove) - { - //NEEDS TO BE REWRITTEN FOR OGRE TIME--------------------------------------------------- - /* - static const TimeTicks footstep_duration = GetTimeFreq(); // make each splash last 1.0s - static TimeTicks lastStepTime = 0; - const TimeTicks thisStepTime = GetTimeQPC(); - static bool lastWasLeft = false; - if (thisStepTime > lastStepTime) - { - if (pm->cmd.ducking) - lastStepTime = thisStepTime + footstep_duration * 2; // splashes while ducking are twice as slow - else - lastStepTime = thisStepTime + footstep_duration; - - lastWasLeft = !lastWasLeft; - */ - //-----------------jhooks1 - - /* - namestruct defaultCreature; - const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_swim : SNDG::l_swim); - if (sndg) - { - const namestruct& SOUNID = sndg->soundID; - const SOUN* const soun = SOUN::GetSound(SOUNID); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - } - }*/ - //Sound, ignore for now -- jhooks1 - //} - } - - scale = PM_CmdScale( &pm->cmd ); - // - // user intentions - // - if ( !scale ) - { - /*wishvel[0] = 0; - wishvel[1] = 0; - wishvel[2] = -60; // sink towards bottom - */ - wishvel.x = 0; - wishvel.z = -60; - wishvel.y = 0; - } - else - { - /*for (i=0 ; i<3 ; i++) - wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;*/ - wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove; - - //wishvel[2] += scale * pm->cmd.upmove; - wishvel.z += pm->cmd.upmove * scale; - } - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - wishspeed = VectorNormalize(wishdir); - - if ( wishspeed > pm->ps.speed * pm_swimScale ) - wishspeed = pm->ps.speed * pm_swimScale; - - PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate); - - // make sure we can go up slopes easily under water - //if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) - if (pml.groundPlane && pm->ps.velocity.dotProduct(pml.groundTrace.planenormal) < 0.0f) - { - //vel = VectorLength(pm->ps->velocity); - vel = pm->ps.velocity.length(); - - // slide along the ground plane - //PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); - PM_ClipVelocity(pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP); - - VectorNormalize(pm->ps.velocity); - //VectorScale(pm->ps->velocity, vel, pm->ps->velocity); - pm->ps.velocity = pm->ps.velocity * vel; - } - - PM_SlideMove( false ); -} - -/* -=================== -PM_WalkMove - -=================== -*/ -static void PM_WalkMove( playerMove* const pmove ) -{ -// int i; - Ogre::Vector3 wishvel; - float fmove, smove; - Ogre::Vector3 wishdir; - float wishspeed; - float scale; - playerMove::playercmd cmd; - float accelerate; - float vel; - //pm->ps.gravity = 4000; - - //std::cout << "Player is walking\n"; - - if ( pm->ps.waterlevel > 2 && //DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) - pml.forward.dotProduct(pml.groundTrace.planenormal) > 0.0f) - { - // begin swimming - PM_WaterMove(pmove); - return; - } - - - if ( PM_CheckJump () ) - { - - // jumped away - if ( pm->ps.waterlevel > 1 ) - PM_WaterMove(pmove); - else - PM_AirMove(); - //printf("Jumped away\n"); - return; - } - - // Footsteps time - if (pmove->cmd.forwardmove || pmove->cmd.rightmove) - { - bool step_underwater = false; - //if (pmove->traceObj) - //{ - - - //jhooks1 - Water handling, deal with later - - - - if (pmove->hasWater) - { - if (pmove->hasWater ) - { - const float waterHeight = pmove->waterHeight; - const float waterSoundStepHeight = waterHeight + pm->ps.halfExtents.y; - if (pmove->ps.origin.y < waterSoundStepHeight) - step_underwater = true; - } - } - //} - - /* - static const TimeTicks footstep_duration = GetTimeFreq() / 2; // make each footstep last 500ms - static TimeTicks lastStepTime = 0; - const TimeTicks thisStepTime = GetTimeQPC(); - static bool lastWasLeft = false; - if (thisStepTime > lastStepTime) - { - if (pmove->cmd.ducking) - lastStepTime = thisStepTime + footstep_duration * 2; // footsteps while ducking are twice as slow - else - lastStepTime = thisStepTime + footstep_duration; - - lastWasLeft = !lastWasLeft; - */ - - if (step_underwater) - { - /* - const namestruct ns(lastWasLeft ? "FootWaterRight" : "FootWaterLeft"); - const SOUN* const soun = SOUN::GetSound(ns); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - }*/ - } - else - { - /* - namestruct defaultCreature; - const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, lastWasLeft ? SNDG::r_foot : SNDG::l_foot); - if (sndg) - { - const namestruct& SOUNID = sndg->soundID; - const SOUN* const soun = SOUN::GetSound(SOUNID); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - } - }*/ - } - } - - - PM_Friction (); - - - //bprintf("vel: (%f, %f, %f)\n", pm->ps.velocity.x, pm->ps.velocity.y, pm->ps.velocity.z); - - fmove = pm->cmd.forwardmove; - smove = pm->cmd.rightmove; - - - cmd = pm->cmd; - scale = PM_CmdScale( &cmd ); - - // set the movementDir so clients can rotate the legs for strafing - //PM_SetMovementDir(); - - // project moves down to flat plane - //pml.forward[2] = 0; - pml.forward.z = 0; - - //pml.right[2] = 0; - pml.right.z = 0; - //std::cout << "Further down" << pm->ps.velocity << "\n"; - - - // project the forward and right directions onto the ground plane - PM_ClipVelocity (pml.forward, pml.groundTrace.planenormal, pml.forward, OVERCLIP ); - PM_ClipVelocity (pml.right, pml.groundTrace.planenormal, pml.right, OVERCLIP ); - //std::cout << "Clip velocity" << pm->ps.velocity << "\n"; - // - - VectorNormalize (pml.forward); - VectorNormalize (pml.right); - //pml.forward = pml.forward.normalise(); - //pml.right = pml.right.normalise(); - //std::cout << "forward2" << pml.forward << "\n"; - //std::cout << "right2" << pml.right << "\n"; - - - // wishvel = (pml.forward * fmove) + (pml.right * smove); - //for ( i = 0 ; i < 3 ; i++ ) - //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; - wishvel = pml.forward * fmove + pml.right * smove; - - - //bprintf("f: (%f, %f, %f), s: (%f, %f, %f)\n", fmove, smove); - - - // when going up or down slopes the wish velocity should Not be zero -// wishvel[2] = 0; - - // wishdir = wishvel - //VectorCopy (wishvel, wishdir); - //wishvel = wishdir; - wishdir = wishvel; - - wishspeed = VectorNormalize(wishdir); - //std::cout << "Wishspeed: " << wishspeed << "\n"; - wishspeed *= scale; - //std::cout << "Wishspeed scaled:" << wishspeed << "\n"; - - // clamp the speed lower if ducking - if ( pm->cmd.ducking ) - if ( wishspeed > pm->ps.speed * pm_duckScale ) - wishspeed = pm->ps.speed * pm_duckScale; - - // clamp the speed lower if wading or walking on the bottom - if ( pm->ps.waterlevel ) - { - float waterScale; - - waterScale = pm->ps.waterlevel / 3.0f; - waterScale = 1.0f - ( 1.0f - pm_swimScale ) * waterScale; - if ( wishspeed > pm->ps.speed * waterScale ) - wishspeed = pm->ps.speed * waterScale; - } - - // when a player gets hit, they temporarily lose - // full control, which allows them to be moved a bit - //if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) - //accelerate = pm_airaccelerate; - //else - accelerate = pm_accelerate; - - - PM_Accelerate (wishdir, wishspeed, accelerate); - //std::cout << "Velocityafter: " << pm->ps.velocity << "\n"; - - //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); - //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); - - //if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) - //pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; - //else - //{ - // don't reset the z velocity for slopes -// pm->ps->velocity[2] = 0; - //} - - //vel = VectorLength(pm->ps->velocity); - vel = pm->ps.velocity.length(); - //std::cout << "The length" << vel << "\n"; - - // slide along the ground plane - PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, - pm->ps.velocity, OVERCLIP ); - //std::cout << "Velocity clipped" << pm->ps.velocity << "\n"; - - // don't decrease velocity when going up or down a slope - VectorNormalize(pm->ps.velocity); - //pm->ps.velocity = pm->ps.velocity.normalise(); - - //std::cout << "Final:" << pm->ps.velocity << "\n"; - //VectorScale(pm->ps->velocity, vel, pm->ps->velocity); - pm->ps.velocity = pm->ps.velocity * vel; - - // don't do anything if standing still - //if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) - if (!pm->ps.velocity.x && !pm->ps.velocity.z) - return; - - PM_StepSlideMove( false ); - - //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); - - -} - -void PM_UpdateViewAngles( playerMove::playerStruct* const ps, playerMove::playercmd* const cmd ) -{ - short temp; - int i; - - //while(1); - - //if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) - //return; // no view changes at all - - //if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) - //return; // no view changes at all - - // circularly clamp the angles with deltas - //bprintf("View angles: %i, %i, %i\n", cmd->angles[0], cmd->angles[1], cmd->angles[2]); - for (i = 0 ; i < 3 ; i++) - { - temp = cmd->angles[i];// + ps->delta_angles[i]; - //if ( i == PITCH ) - { - // don't let the player look up or down more than 90 degrees - /*if ( temp > 16000 ) - { - ps->delta_angles[i] = 16000 - cmd->angles[i]; - temp = 16000; - } - else if ( temp < -16000 ) - { - ps->delta_angles[i] = -16000 - cmd->angles[i]; - temp = -16000; - }*/ - } - (&(ps->viewangles.x) )[i] = SHORT2ANGLE(temp); - //cmd->angles[i] += ps->delta_angles[i]; - } - //ps->delta_angles[0] = ps->delta_angles[1] = ps->delta_angles[2] = 0; - -} - -void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) -{ - float angle; - static float sr, sp, sy, cr, cp, cy; - // static to help MS compiler fp bugs - - //angle = angles[YAW] * (M_PI*2 / 360); - angle = angles.x * (M_PI * 2.0f / 360.0f); - sp = sinf(angle); - cp = cosf(angle); - - //angle = angles[PITCH] * (M_PI*2 / 360); - angle = angles.y * (-M_PI * 2.0f / 360.0f); - sy = sinf(angle); - cy = cosf(angle); - - //angle = angles[ROLL] * (M_PI*2 / 360); - angle = angles.z * (M_PI * 2.0f / 360.0f); - sr = sinf(angle); - cr = cosf(angle); - - if (forward) - { - forward->x = cp * cy; - forward->y = cp * sy; - forward->z = -sp; - } - if (right) - { - right->x = (-1 * sr * sp * cy + -1 * cr * -sy); - right->y = (-1 * sr * sp * sy + -1 * cr * cy); - right->z = 0; - } - if (up) - { - up->x =(cr * sp * cy + -sr * -sy); - up->y=(cr * sp * sy + -sr * cy); - up->z = cr * cp; - } - -} - -void PM_GroundTraceMissed() -{ - traceResults trace; - Ogre::Vector3 point; - //We should not have significant upwards velocity when in the air, unless we jumped. - //This code protects against flying into the air when moving at high speeds. - //Z velocity is set to 50, instead of 0, to help move up certain steps. - - //std::cout << "Ground trace missed\n"; - // we just transitioned into freefall - //if ( pm->debugLevel ) - //Com_Printf("%i:lift\n", c_pmove); - - // if they aren't in a jumping animation and the ground is a ways away, force into it - // if we didn't do the trace, the player would be backflipping down staircases - //VectorCopy( pm->ps->origin, point ); - point = pm->ps.origin; - //point[2] -= 64; - point.z -= 32; - - //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -64.0f, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - //It hit the ground below - if ( trace.fraction < 1.0 && pm->ps.origin.z > trace.endpos.z) - { - pm->ps.origin = trace.endpos; - pml.walking = true; - pml.groundPlane = true; - pm->ps.groundEntityNum = trace.entityNum; - - } - else{ - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = false; - pml.walking = false; - pm->ps.bSnap = false; - } - - -} - -static bool PM_CorrectAllSolid(traceResults* const trace) -{ - int i, j, k; - Ogre::Vector3 point; - - //if ( pm->debugLevel ) - //Com_Printf("%i:allsolid\n", c_pmove); - //bprintf("allsolid\n"); - - // jitter around - for (i = -1; i <= 1; i++) - { - for (j = -1; j <= 1; j++) - { - for (k = -1; k <= 1; k++) - { - //VectorCopy(pm->ps->origin, point); - point = pm->ps.origin; - - /*point[0] += (float) i; - point[1] += (float) j; - point[2] += (float) k;*/ - point += Ogre::Vector3( (const float)i, (const float)j, (const float)k); - - //pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(trace, *(const D3DXVECTOR3* const)&point, *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, 0.0f, 0.0f), 0, pml.traceObj); - newtrace(trace, point, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - - if ( !trace->allsolid ) - { - /*point[0] = pm->ps->origin[0]; - point[1] = pm->ps->origin[1]; - point[2] = pm->ps->origin[2] - 0.25;*/ - point = pm->ps.origin; - point.z -= 0.25f; - - //pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); - newtrace(trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - pml.groundTrace = *trace; - return true; - } - } - } - } - - //pm->ps->groundEntityNum = ENTITYNUM_NONE; - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = false; - pml.walking = false; - - return false; -} - -static void PM_CrashLand( void ) -{ - float delta; - float dist ; - float vel, acc; - float t; - float a, b, c, den; - - // decide which landing animation to use - /*if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) - PM_ForceLegsAnim( LEGS_LANDB ); - else - PM_ForceLegsAnim( LEGS_LAND ); - - pm->ps->legsTimer = TIMER_LAND;*/ - - // calculate the exact velocity on landing - //dist = pm->ps->origin[2] - pml.previous_origin[2]; - - dist = pm->ps.origin.z - pml.previous_origin.z; - - //vel = pml.previous_velocity[2]; - vel = pml.previous_velocity.z; - - //acc = -pm->ps->gravity; - acc = -pm->ps.gravity; - - a = acc / 2; - b = vel; - c = -dist; - - den = b * b - 4 * a * c; - if ( den < 0 ) - return; - - t = (-b - sqrtf( den ) ) / ( 2 * a ); - - delta = vel + t * acc; - delta = delta * delta * 0.0001f; - - // ducking while falling doubles damage - /*if ( pm->ps->pm_flags & PMF_DUCKED ) - delta *= 2;*/ - if (pm->cmd.upmove < -20) - delta *= 2; - - // never take falling damage if completely underwater - if ( pm->ps.waterlevel == 3 ) - return; - - // reduce falling damage if there is standing water - if ( pm->ps.waterlevel == 2 ) - delta *= 0.25; - if ( pm->ps.waterlevel == 1 ) - delta *= 0.5; - - if ( delta < 1 ) - return; -/* - if (delta > 60) - printf("Far crashland: %f\n", delta); - else if (delta > 40) - printf("Medium crashland: %f\n", delta); - else if (delta > 4) - printf("Short crashland: %f\n", delta); -*/ - if (delta > 60) - { - /* - static const namestruct healthDamage("Health Damage"); - const SOUN* const soun = SOUN::GetSound(healthDamage); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - }*/ - } - - if (delta > 3) // We need at least a short crashland to proc the sound effects: - { - bool splashSound = false; - - if (pm->hasWater) - { - - const float waterHeight = pm->waterHeight; - const float waterHeightSplash = waterHeight + pm->ps.halfExtents.y; - if (pm->ps.origin.z < waterHeightSplash) - { - splashSound = true; - } - - } - - - if (splashSound) - { - //Change this later----------------------------------- - /* - const namestruct ns("DefaultLandWater"); - const SOUN* const soun = SOUN::GetSound(ns); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundDatga->GetVolumeFloat() ); - }*/ - } - else - { - //Change this later--------------------------------- - /* - namestruct defaultCreature; - const SNDG* const sndg = SNDG::GetFromMap(defaultCreature, SNDG::land); - if (sndg) - { - const namestruct& SOUNID = sndg->soundID; - const SOUN* const soun = SOUN::GetSound(SOUNID); - if (soun) - { - PlaySound2D(soun->soundFilename, soun->soundData->GetVolumeFloat() ); - } - }*/ - } - } - - // create a local entity event to play the sound - - // SURF_NODAMAGE is used for bounce pads where you don't ever - // want to take damage or play a crunch sound - //if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) - { - /*if ( delta > 60 ) - PM_AddEvent( EV_FALL_FAR ); - else if ( delta > 40 ) - { - // this is a pain grunt, so don't play it if dead - if ( pm->ps->stats[STAT_HEALTH] > 0 ) - PM_AddEvent( EV_FALL_MEDIUM ); - } - else if ( delta > 7 ) - PM_AddEvent( EV_FALL_SHORT ); - else - PM_AddEvent( PM_FootstepForSurface() );*/ - } - - // start footstep cycle over - //pm->ps->bobCycle = 0; -} - -static void PM_GroundTrace( void ) -{ - Ogre::Vector3 point; - traceResults trace; - - /*point[0] = pm->ps->origin[0]; - point[1] = pm->ps->origin[1]; - point[2] = pm->ps->origin[2] - 0.25;*/ - point = pm->ps.origin; - point.z -= 0.25f; - - //pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask); - //tracefunc(&trace, *(const D3DXVECTOR3* const)&(pm->ps.origin), *(const D3DXVECTOR3* const)&point, D3DXVECTOR3(0.0f, -0.25f, 0.0f), 0, pml.traceObj); - newtrace(&trace, pm->ps.origin, point, pm->ps.halfExtents, Ogre::Math::DegreesToRadians(pm->ps.viewangles.y), pm->isInterior, pm->mEngine); - pml.groundTrace = trace; - - // do something corrective if the trace starts in a solid... - if ( trace.allsolid ) { - //std::cout << "ALL SOLID\n"; - if ( !PM_CorrectAllSolid(&trace) ){ - //std::cout << "Returning after correct all solid\n"; - return; - } - } - // if the trace didn't hit anything, we are in free fall - if ( trace.fraction == 1.0) - { - if(pm->ps.velocity.z > 50.0f && pm->ps.bSnap && pm->ps.speed > 1000.0f) - pm->ps.velocity.z = 50.0f; - if(pm->ps.snappingImplemented){ - if(pm->ps.bSnap && pm->ps.counter <= 0) - PM_GroundTraceMissed(); - } - - - - return; - } - else - { - //It hit something, so we are on the ground - pm->ps.bSnap = true; - - } - // check if getting thrown off the ground - //if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) - if (pm->ps.velocity.z > 0 && pm->ps.velocity.dotProduct(trace.planenormal) > 10.0f ) - { - //if ( pm->debugLevel ) - //Com_Printf("%i:kickoff\n", c_pmove); - - // go into jump animation - /*if ( pm->cmd.forwardmove >= 0 ) - { - PM_ForceLegsAnim( LEGS_JUMP ); - pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; - } - else - { - PM_ForceLegsAnim( LEGS_JUMPB ); - pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; - }*/ - if(!pm->ps.bSnap){ - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = false; - pml.walking = false; - } - else - { - pml.groundPlane = true; - pml.walking = true; - } - return; - } - - - - - // slopes that are too steep will not be considered onground - //if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) - //std::cout << "MinWalkNormal" << trace.planenormal.z; - if (trace.planenormal.z < MIN_WALK_NORMAL) - { - //if ( pm->debugLevel ) - //Com_Printf("%i:steep\n", c_pmove); - - // FIXME: if they can't slide down the slope, let them - // walk (sharp crevices) - pm->ps.groundEntityNum = ENTITYNUM_NONE; - pml.groundPlane = true; - pml.walking = false; - return; - } - - pml.groundPlane = true; - pml.walking = true; - - // hitting solid ground will end a waterjump - /*if (pm->ps.pm_flags & PMF_TIME_WATERJUMP) - { - pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND); - pm->ps->pm_time = 0; - }*/ - - if ( pm->ps.groundEntityNum == ENTITYNUM_NONE ) - { - // just hit the ground - /*if ( pm->debugLevel ) - Com_Printf("%i:Land\n", c_pmove);*/ - //bprintf("Land\n"); - - PM_CrashLand(); - - // don't do landing time if we were just going down a slope - //if ( pml.previous_velocity[2] < -200 ) - if (pml.previous_velocity.z < -200) - { - // don't allow another jump for a little while - //pm->ps->pm_flags |= PMF_TIME_LAND; - pm->ps.pm_time = 250; - } - } - - pm->ps.groundEntityNum = trace.entityNum; - - // don't reset the z velocity for slopes -// pm->ps->velocity[2] = 0; - - //PM_AddTouchEnt( trace.entityNum ); -} - -void PM_AirMove() -{ - //int i; - Ogre::Vector3 wishvel; - float fmove, smove; - Ogre::Vector3 wishdir; - float wishspeed; - float scale; - playerMove::playercmd cmd; - //pm->ps.gravity = 800; - PM_Friction(); - - fmove = pm->cmd.forwardmove; - smove = pm->cmd.rightmove; - - cmd = pm->cmd; - scale = PM_CmdScale( &cmd ); - // set the movementDir so clients can rotate the legs for strafing - //PM_SetMovementDir(); - - // project moves down to flat plane - //pml.forward[2] = 0; - pml.forward.z = 0; //Z or Y? - //pml.right[2] = 0; - pml.right.z = 0; - //VectorNormalize (pml.forward); - VectorNormalize(pml.forward); - VectorNormalize(pml.right); - //VectorNormalize (pml.right); - - //for ( i = 0 ; i < 2 ; i++ ) - //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; - wishvel = pml.forward * fmove + pml.right * smove; - - //wishvel[2] = 0; - wishvel.z = 0; - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - //wishspeed = VectorNormalize(wishdir); - wishspeed = VectorNormalize(wishdir); - - wishspeed *= scale; - - // not on ground, so little effect on velocity - PM_Accelerate (wishdir, wishspeed, pm_airaccelerate); - - // we may have a ground plane that is very steep, even - // though we don't have a groundentity - // slide along the steep plane - if ( pml.groundPlane ) - PM_ClipVelocity (pm->ps.velocity, pml.groundTrace.planenormal, pm->ps.velocity, OVERCLIP ); - -/*#if 0 - //ZOID: If we are on the grapple, try stair-stepping - //this allows a player to use the grapple to pull himself - //over a ledge - if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) - PM_StepSlideMove ( qtrue ); - else - PM_SlideMove ( qtrue ); -#endif*/ - //std::cout << "Moving in the air" << pm->ps.velocity << "\n"; - - /*bprintf("%i ", */PM_StepSlideMove ( true )/* )*/; - - -} - -static void PM_NoclipMove( void ) -{ - float speed, drop, friction, control, newspeed; -// int i; - Ogre::Vector3 wishvel; - float fmove, smove; - Ogre::Vector3 wishdir; - float wishspeed; - float scale; - - //pm->ps->viewheight = DEFAULT_VIEWHEIGHT; - - // friction - - //speed = VectorLength (pm->ps->velocity); - speed = pm->ps.velocity.length(); - if (speed < 1) - //VectorCopy (vec3_origin, pm->ps->velocity); - pm->ps.velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); - else - { - drop = 0; - - friction = pm_friction * 1.5f; // extra friction - control = speed < pm_stopspeed ? pm_stopspeed : speed; - drop += control * friction * pml.frametime; - - // scale the velocity - newspeed = speed - drop; - if (newspeed < 0) - newspeed = 0; - newspeed /= speed; - - //VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity); - pm->ps.velocity = pm->ps.velocity * newspeed; - } - - // accelerate - scale = PM_CmdScale( &pm->cmd ); - - fmove = pm->cmd.forwardmove; - smove = pm->cmd.rightmove; - - //for (i=0 ; i<3 ; i++) - //wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; - - wishvel = pml.forward * fmove + pml.right * smove; - //wishvel[2] += pm->cmd.upmove; - wishvel.z += pm->cmd.upmove; - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - wishspeed = VectorNormalize(wishdir); - wishspeed *= scale; - - - PM_Accelerate( wishdir, wishspeed, pm_accelerate ); - - // move - //VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); - pm->ps.origin = pm->ps.origin + pm->ps.velocity * pml.frametime; -} - -static void PM_DropTimers( void ) -{ - // drop misc timing counter - if ( pm->ps.pm_time ) - { - if ( pml.msec >= pm->ps.pm_time ) - { - //pm->ps->pm_flags &= ~PMF_ALL_TIMES; - pm->ps.pm_time = 0; - } - else - pm->ps.pm_time -= pml.msec; - } - - //bprintf("Time: %i\n", pm->ps.pm_time); - - // drop animation counter - /*if ( pm->ps->legsTimer > 0 ) - { - pm->ps->legsTimer -= pml.msec; - if ( pm->ps->legsTimer < 0 ) - pm->ps->legsTimer = 0; - } - - if ( pm->ps->torsoTimer > 0 ) - { - pm->ps->torsoTimer -= pml.msec; - if ( pm->ps->torsoTimer < 0 ) - pm->ps->torsoTimer = 0; - }*/ -} - -static void PM_FlyMove( void ) -{ - //int i; - Ogre::Vector3 wishvel; - float wishspeed; - Ogre::Vector3 wishdir; - float scale; - - // normal slowdown - PM_Friction (); - - scale = PM_CmdScale( &pm->cmd ); - // - // user intentions - // - if ( !scale ) - { - /*wishvel[0] = 0; - wishvel[1] = 0; - wishvel[2] = 0;*/ - wishvel = Ogre::Vector3(0,0,0); - } - else - { - //for (i=0 ; i<3 ; i++) - //wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; - wishvel = pml.forward * scale * pm->cmd.forwardmove + pml.right * scale * pm->cmd.rightmove; - - //wishvel[2] += scale * pm->cmd.upmove; - wishvel.z += /*6.35f * */pm->cmd.upmove * scale; - } - - //VectorCopy (wishvel, wishdir); - wishdir = wishvel; - - //wishspeed = VectorNormalize(wishdir); - wishspeed = VectorNormalize(wishdir); - - PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate); - - PM_StepSlideMove( false ); -} - - -void PM_SetWaterLevel( playerMove* const pm ) -{ - Ogre::Vector3 point; - //int cont; - int sample1; - int sample2; - - // - // get waterlevel, accounting for ducking - // - - pm->ps.waterlevel = WL_DRYLAND; - pm->ps.watertype = 0; - - /*point[0] = pm->ps->origin[0]; - point[1] = pm->ps->origin[1]; - point[2] = pm->ps->origin[2] + MINS_Z + 1; */ - point.x = pm->ps.origin.x; - point.y = pm->ps.origin.y; - point.z = pm->ps.origin.z + MINS_Z + 1; - - //cont = pm->pointcontents( point, pm->ps->clientNum ); - bool checkWater = (pml.hasWater && pml.waterHeight > point.z); - //if ( cont & MASK_WATER ) - if ( checkWater) - { - sample2 = /*pm->ps.viewheight*/DEFAULT_VIEWHEIGHT - MINS_Z; - sample1 = sample2 / 2; - - pm->ps.watertype = CONTENTS_WATER;//cont; - pm->ps.waterlevel = WL_ANKLE; - //point[2] = pm->ps->origin[2] + MINS_Z + sample1; - point.z = pm->ps.origin.z + MINS_Z + sample1; - checkWater = (pml.hasWater && pml.waterHeight > point.z); - //cont = pm->pointcontents (point, pm->ps->clientNum ); - //if ( cont & MASK_WATER ) - if (checkWater) - { - pm->ps.waterlevel = WL_WAIST; - //point[2] = pm->ps->origin[2] + MINS_Z + sample2; - point.z = pm->ps.origin.z + MINS_Z + sample2; - //cont = pm->pointcontents (point, pm->ps->clientNum ); - //if ( cont & MASK_WATER ) - checkWater = (pml.hasWater && pml.waterHeight > point.z); - if (checkWater ) - pm->ps.waterlevel = WL_UNDERWATER; - } - } -} - -void PmoveSingle (playerMove* const pmove) -{ - pmove->ps.counter--; - //pm = pmove; - - // Aedra doesn't support Q3-style VM traps D: //while(1); - - // this counter lets us debug movement problems with a journal - // by setting a conditional breakpoint fot the previous frame - //c_pmove++; - - // clear results - //pm->numtouch = 0; - pm->ps.watertype = 0; - pm->ps.waterlevel = WL_DRYLAND; - - //if ( pm->ps->stats[STAT_HEALTH] <= 0 ) - //pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies - - - // make sure walking button is clear if they are running, to avoid - // proxy no-footsteps cheats - //if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) - //pm->cmd.buttons &= ~BUTTON_WALKING; - - - // set the talk balloon flag - //if ( pm->cmd.buttons & BUTTON_TALK ) - //pm->ps->eFlags |= EF_TALK; - //else - //pm->ps->eFlags &= ~EF_TALK; - - // set the firing flag for continuous beam weapons - /*if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION - && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) - pm->ps->eFlags |= EF_FIRING; - else - pm->ps->eFlags &= ~EF_FIRING;*/ - - // clear the respawned flag if attack and use are cleared - /*if ( pm->ps->stats[STAT_HEALTH] > 0 && - !( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) - pm->ps->pm_flags &= ~PMF_RESPAWNED;*/ - - // if talk button is down, dissallow all other input - // this is to prevent any possible intercept proxy from - // adding fake talk balloons - /*if ( pmove->cmd.buttons & BUTTON_TALK ) - { - // keep the talk button set tho for when the cmd.serverTime > 66 msec - // and the same cmd is used multiple times in Pmove - pmove->cmd.buttons = BUTTON_TALK; - pmove->cmd.forwardmove = 0; - pmove->cmd.rightmove = 0; - pmove->cmd.upmove = 0; - }*/ - - // clear all pmove local vars - memset (&pml, 0, sizeof(pml) ); - - // Aedra-specific code: - //pml.scene = global_lastscene; - - - // End Aedra-specific code - pml.hasWater = pmove->hasWater; - pml.isInterior = pmove->isInterior; - pml.waterHeight = pmove->waterHeight; - - // determine the time - pml.msec = pmove->cmd.serverTime - pm->ps.commandTime; - if ( pml.msec < 1 ) - pml.msec = 1; - else if ( pml.msec > 200 ) - pml.msec = 200; - - //pm->ps->commandTime = pmove->cmd.serverTime; - - // Commented out as a hack - pm->ps.commandTime = pmove->cmd.serverTime; - - // Handle state change procs: - if (pm->cmd.activating != pm->cmd.lastActivatingState) - { - if (!pm->cmd.lastActivatingState && pm->cmd.activating) - pm->cmd.procActivating = playerMove::playercmd::KEYDOWN; - else - pm->cmd.procActivating = playerMove::playercmd::KEYUP; - } - else - { - pm->cmd.procActivating = playerMove::playercmd::NO_CHANGE; - } - pm->cmd.lastActivatingState = pm->cmd.activating; - - if (pm->cmd.dropping != pm->cmd.lastDroppingState) - { - if (!pm->cmd.lastDroppingState && pm->cmd.dropping) - pm->cmd.procDropping = playerMove::playercmd::KEYDOWN; - else - pm->cmd.procDropping = playerMove::playercmd::KEYUP; - } - else - { - pm->cmd.procDropping = playerMove::playercmd::NO_CHANGE; - } - pm->cmd.lastDroppingState = pm->cmd.dropping; - - // save old org in case we get stuck - //VectorCopy (pm->ps->origin, pml.previous_origin); - pml.previous_origin = pm->ps.origin; - - // Copy over the lastframe origin - pmove->ps.lastframe_origin = pmove->ps.origin; - - //pmove->ps.lastframe_origin = pmove->ps.origin; - - // save old velocity for crashlanding - //VectorCopy (pm->ps->velocity, pml.previous_velocity); - pml.previous_velocity = pm->ps.velocity; - - pml.frametime = pml.msec * 0.001f; - - // update the viewangles - //PM_UpdateViewAngles( &(pm->ps), &(pm->cmd) ); - - AngleVectors (pm->ps.viewangles, &(pml.forward), &(pml.right), &(pml.up) ); - - //if ( pm->cmd.upmove < 10 ) - // not holding jump - //pm->ps->pm_flags &= ~PMF_JUMP_HELD; - - // decide if backpedaling animations should be used - /*if ( pm->cmd.forwardmove < 0 ) - pm->ps->pm_flags |= PMF_BACKWARDS_RUN; - else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) - pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;*/ - - /*if ( pm->ps->pm_type >= PM_DEAD ) - { - pm->cmd.forwardmove = 0; - pm->cmd.rightmove = 0; - pm->cmd.upmove = 0; - }*/ - - if ( pm->ps.move_type == PM_SPECTATOR ) - { - - //PM_CheckDuck (); - PM_FlyMove (); - PM_DropTimers (); - return; - } - - if ( pm->ps.move_type == PM_NOCLIP ) - { - - PM_NoclipMove (); - PM_DropTimers (); - return; - } - - if (pm->ps.move_type == PM_FREEZE){ - - return; // no movement at all - - } - - if ( pm->ps.move_type == PM_INTERMISSION || pm->ps.move_type == PM_SPINTERMISSION){ - return; // no movement at all - } - - // set watertype, and waterlevel - PM_SetWaterLevel(pmove); - pml.previous_waterlevel = pmove->ps.waterlevel; - - // set mins, maxs, and viewheight - //PM_CheckDuck (); - - // set groundentity - PM_GroundTrace(); - - /*if ( pm->ps->pm_type == PM_DEAD ) - PM_DeadMove (); - - PM_DropTimers();*/ - - PM_DropTimers(); - -/*#ifdef MISSIONPACK - if ( pm->ps->powerups[PW_INVULNERABILITY] ) { - PM_InvulnerabilityMove(); - } else -#endif*/ - /*if ( pm->ps->powerups[PW_FLIGHT] ) - // flight powerup doesn't allow jump and has different friction - PM_FlyMove(); - else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) - { - PM_GrappleMove(); - // We can wiggle a bit - PM_AirMove(); - } - else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) - PM_WaterJumpMove();*/ - if ( pmove->ps.waterlevel > 1 ) - // swimming - PM_WaterMove(pmove); - else if ( pml.walking ) - { - - // walking on ground - PM_WalkMove(pmove); - //bprintf("WalkMove\n"); - } - else - { - // airborne - //std::cout << "AIRMOVE\n"; - PM_AirMove(); - //bprintf("AirMove\n"); - } - - //PM_Animate(); - - // set groundentity, watertype, and waterlevel - PM_GroundTrace(); - PM_SetWaterLevel(pmove); - - // weapons - /*PM_Weapon(); - - // torso animation - PM_TorsoAnimation(); - - // footstep events / legs animations - PM_Footsteps(); - - // entering / leaving water splashes - PM_WaterEvents(); - - // snap some parts of playerstate to save network bandwidth - trap_SnapVector( pm->ps->velocity );*/ -} - -void Ext_UpdateViewAngles(playerMove* const pm) -{ - playerMove::playerStruct* const ps = &(pm->ps); - playerMove::playercmd* const cmd = &(pm->cmd); - PM_UpdateViewAngles(ps, cmd); -} - -void Pmove (playerMove* const pmove) -{ - // warning: unused variable ‘fmove’ - //int fmove = pmove->cmd.forwardmove; - - pm = pmove; - - int finalTime; - - finalTime = pmove->cmd.serverTime; - - pmove->ps.commandTime = 40; - - if ( finalTime < pmove->ps.commandTime ) - return; // should not happen - - if ( finalTime > pmove->ps.commandTime + 1000 ) - pmove->ps.commandTime = finalTime - 1000; - - pmove->ps.pmove_framecount = (pmove->ps.pmove_framecount + 1) & ( (1 << PS_PMOVEFRAMECOUNTBITS) - 1); - - // chop the move up if it is too long, to prevent framerate - // dependent behavior - while ( pmove->ps.commandTime != finalTime ) - { - int msec; - - msec = finalTime - pmove->ps.commandTime; - - if ( pmove->pmove_fixed ) - { - if ( msec > pmove->pmove_msec ) - msec = pmove->pmove_msec; - } - else - { - if ( msec > 66 ) - msec = 66; - } - - pmove->cmd.serverTime = pmove->ps.commandTime + msec; - - if (pmove->isInterior) - { - PmoveSingle( pmove ); - } - else - { - PmoveSingle( pmove ); - /* - std::map::const_iterator it = ExtCellLookup.find(PositionToCell(pmove->ps.origin) ); - if (it != ExtCellLookup.end() ) - { - pmove->traceObj->incellptr = it->second; - }*/ - } - - //if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) - //pmove->cmd.upmove = 20; - } - - //pmove->ps.last_compute_time = GetTimeQPC(); - //pmove->ps.lerp_multiplier = (pmove->ps.origin - pmove->ps.lastframe_origin);// * (1.000 / 31.0); - - //PM_CheckStuck(); - -} - - diff --git a/libs/openengine/bullet/pmove.h b/libs/openengine/bullet/pmove.h deleted file mode 100644 index fa303184e..000000000 --- a/libs/openengine/bullet/pmove.h +++ /dev/null @@ -1,200 +0,0 @@ -#ifndef OENGINE_BULLET_PMOVE_H -#define OENGINE_BULLET_PMOVE_H -/* -This source file is a *modified* version of various header files from the Quake 3 Arena source code, -which was released under the GNU GPL (v2) in 2005. -Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc. -*/ - -#include -#include -#include "trace.h" -#include "physic.hpp" - -#include - -//#include "GameMath.h" -//#include "GameTime.h" - -// Forwards-declare it! - -/*#ifndef COMPILING_PMOVE -#include "Scene.h" -extern SceneInstance* global_lastscene; -#endif*/ - -static const Ogre::Vector3 halfExtentsDefault(14.64f * 2, 14.24f * 2, 33.25f * 2); - -#define MAX_CLIP_PLANES 5 -#define OVERCLIP 1.001f -//#define STEPSIZE 18 // 18 is way too much -#define STEPSIZE (9) -#ifndef M_PI - #define M_PI 3.14159265358979323846f -#endif -#define YAW 0 -#define PITCH /*1*/2 -#define ROLL /*2*/1 -#define SHORT2ANGLE(x) ( (x) * (360.0f / 65536.0f) ) -#define ANGLE2SHORT(x) ( (const short)( (x) / (360.0f / 65536.0f) ) ) -#define GENTITYNUM_BITS 10 // don't need to send any more -#define MAX_GENTITIES (1 << GENTITYNUM_BITS) -#define ENTITYNUM_NONE (MAX_GENTITIES - 1) -#define ENTITYNUM_WORLD (MAX_GENTITIES - 2) -#define MIN_WALK_NORMAL .7f // can't walk on very steep slopes -#define PS_PMOVEFRAMECOUNTBITS 6 -#define MINS_Z -24 -#define DEFAULT_VIEWHEIGHT 26 -#define CROUCH_VIEWHEIGHT 12 -#define DEAD_VIEWHEIGHT (-16) -#define CONTENTS_SOLID 1 // an eye is never valid in a solid -#define CONTENTS_LAVA 8 -#define CONTENTS_SLIME 16 -#define CONTENTS_WATER 32 -#define CONTENTS_FOG 64 -static const float pm_accelerate = 10.0f; -static const float pm_stopspeed = 100.0f; -static const float pm_friction = 12.0f; -static const float pm_flightfriction = 3.0f; -static const float pm_waterfriction = 1.0f; -static const float pm_airaccelerate = 1.0f; -static const float pm_swimScale = 0.50f; -static const float pm_duckScale = 0.25f; -static const float pm_flyaccelerate = 8.0f; -static const float pm_wateraccelerate = 4.0f; - -enum pmtype_t -{ - PM_NORMAL, // can accelerate and turn - PM_NOCLIP, // noclip movement - PM_SPECTATOR, // still run into walls - PM_DEAD, // no acceleration or turning, but free falling - PM_FREEZE, // stuck in place with no control - PM_INTERMISSION, // no movement or status bar - PM_SPINTERMISSION // no movement or status bar -}; - -enum waterlevel_t -{ - WL_DRYLAND = 0, - WL_ANKLE, - WL_WAIST, - WL_UNDERWATER -}; - - -//#include "bprintf.h" - -struct playerMove -{ - struct playerStruct - { - playerStruct() : gravity(800.0f), speed(480.0f), jump_velocity(270), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1), halfExtents(halfExtentsDefault) - { - origin = Ogre::Vector3(0.0f, 0.0f, 0.0f); - velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); - - viewangles = Ogre::Vector3(0.0f, 0.0f, 0.0f); - - delta_angles[0] = delta_angles[1] = delta_angles[2] = 0; - - lastframe_origin.x = lastframe_origin.y = lastframe_origin.z = 0; - lerp_multiplier.x = lerp_multiplier.y = lerp_multiplier.z = 0; - } - - inline void SpeedUp(void) - { - //printf("speed up to: %f\n", speed); - speed *= 1.25f; - } - - inline void SpeedDown(void) - { - //printf("speed down to %f\n", speed); - speed /= 1.25f; - } - - Ogre::Vector3 velocity; - Ogre::Vector3 origin; - Ogre::Vector3 halfExtents; - bool bSnap; - bool snappingImplemented; - int counter; - float gravity; // default = 800 - float speed; // default = 320 - float jump_velocity; //default = 270 - - int commandTime; // the time at which this command was issued (in milliseconds) - - int pm_time; - - Ogre::Vector3 viewangles; - - int groundEntityNum; - - int pmove_framecount; - - int watertype; - waterlevel_t waterlevel; - - signed short delta_angles[3]; - - pmtype_t move_type; - - float last_compute_time; - Ogre::Vector3 lastframe_origin; - Ogre::Vector3 lerp_multiplier; - } ps; - - struct playercmd - { - enum CMDstateChange - { - NO_CHANGE, - KEYDOWN, - KEYUP - }; - - playercmd() : forwardmove(0), rightmove(0), upmove(0), serverTime(50), ducking(false), - activating(false), lastActivatingState(false), procActivating(NO_CHANGE), - dropping(false), lastDroppingState(false), procDropping(NO_CHANGE) - { - angles[0] = angles[1] = angles[2] = 0; - } - - int serverTime; - - short angles[3]; - - signed char forwardmove; - signed char rightmove; - signed char upmove; - - bool ducking; - bool activating; // if the user is holding down the activate button - bool dropping; // if the user is dropping an item - - bool lastActivatingState; - bool lastDroppingState; - - CMDstateChange procActivating; - CMDstateChange procDropping; - } cmd; - - playerMove() : msec(50), pmove_fixed(false), pmove_msec(50), waterHeight(0), isInterior(true), hasWater(false) - { - } - - int msec; - int pmove_msec; - bool pmove_fixed; - int waterHeight; - bool hasWater; - bool isInterior; - OEngine::Physic::PhysicEngine* mEngine; -}; - -void Pmove (playerMove* const pmove); -void Ext_UpdateViewAngles(playerMove* const pm); -void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) ; -#endif diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 9f5398574..7664eb418 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -1,196 +1,57 @@ #include "trace.h" - - #include +#include +#include + +#include "physic.hpp" - - - - -void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) //Traceobj was a Aedra Object +enum traceWorldType { - //static float lastyaw = 0.0f; - //static float lastpitch = 0.0f; - //if (!traceobj) - // return; + collisionWorldTrace = 1, + pickWorldTrace = 2, + bothWorldTrace = collisionWorldTrace | pickWorldTrace +}; - //if (!traceobj->incellptr) - // return; - - - const Ogre::Vector3 rayDir = end - start; - - - - - - - - NewPhysTraceResults out; - //std::cout << "Starting trace\n"; - //Ogre::Vector3 startReplace = Ogre::Vector3(650,950, 45); - //Ogre::Vector3 endReplace = startReplace; - //endReplace.z -= .25; - - const bool hasHit = NewPhysicsTrace(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0f, 0.0f), isInterior, enginePass); - - if (out.fraction < 0.001f) - results->startsolid = true; - else - results->startsolid = false; - - - //results->allsolid = out.startSolid; - - // If outside and underground, we're solid - /*if (isInterior) - { - const Ogre::Vector3 height = GetGroundPosition(start, CellCoords(traceCell->data->gridX, traceCell->data->gridY) ); - if (start.yPos - height.yPos < (-2.0f * BBHalfExtents.yPos) ) - { - results->allsolid = true; - } - else - results->allsolid = false; - }*/ - - // If inside and out of the tree, we're solid - //else - //{ - results->allsolid = out.startSolid; - //std::cout << "allsolid" << results->allsolid << "\n"; - //} - - if (!hasHit) - { - results->endpos = end; - results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); - results->entityNum = ENTITYNUM_NONE; - results->fraction = 1.0f; - } - else - { - results->fraction = out.fraction; - results->planenormal = out.hitNormal; - results->endpos = rayDir * results->fraction + start; - results->entityNum = ENTITYNUM_WORLD; - /*bprintf("Start: (%f, %f, %f) End: (%f, %f, %f) TraceDir: (%f, %f, %f) HitNormal: (%f, %f, %f) Fraction: %f Hitpos: (%f, %f, %f) CompensatedHitpos: (%f, %f, %f)\n", - start.xPos, start.yPos, start.zPos, - end.xPos, end.yPos, end.zPos, - rayDir.xPos, rayDir.yPos, rayDir.zPos, - results->planenormal.xPos, results->planenormal.yPos, results->planenormal.zPos, results->fraction, - out.endPos.xPos, out.endPos.yPos, out.endPos.zPos, - results->endpos.xPos, results->endpos.yPos, results->endpos.zPos);*/ - } -} - - - -template -const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, - const Ogre::Vector3& BBHalfExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) +enum collaborativePhysicsType { - //if (!traceobj->incellptr) - // return false; - //if(enginePass->dynamicsWorld->getCollisionObjectArray().at(60)->getCollisionShape()->isConvex()) - // std::cout << "It's convex\n"; - - + No_Physics = 0, // Both are empty (example: statics you can walk through, like tall grass) + Only_Collision = 1, // This object only has collision physics but no pickup physics (example: statics) + Only_Pickup = 2, // This object only has pickup physics but no collision physics (example: items dropped on the ground) + Both_Physics = 3 // This object has both kinds of physics (example: activators) +}; - const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); - const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); - const btQuaternion btrot(rotation.y, rotation.x, rotation.z); //y, x, z +void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine *enginePass) //Traceobj was a Aedra Object +{ + const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); + const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); + const btQuaternion btrot(0.0f, 0.0f, 0.0f); //y, x, z const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); - //const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2); - const btTransform from(btrot, btstart); - const btTransform to(btrot, btend); + //const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2); + const btTransform from(btrot, btstart); + const btTransform to(btrot, btend); - // warning: unused variable ... - /* - float x = from.getOrigin().getX(); - float y = from.getOrigin().getY(); - float z = from.getOrigin().getZ(); - float x2 = to.getOrigin().getX(); - float y2 = to.getOrigin().getY(); - float z2 = to.getOrigin().getZ(); - */ - - //std::cout << "BtFrom: " << x << "," << y << "," << z << "\n"; - //std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n"; - //std::cout << "BtTo: " << to.getOrigin().getX() << "," << to.getOrigin().getY() << "," << to.getOrigin().getZ() << "\n"; + btCollisionWorld::ClosestConvexResultCallback newTraceCallback(btstart, btend); + newTraceCallback.m_collisionFilterMask = Only_Collision; + enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback); - btCollisionWorld::ClosestConvexResultCallback - newTraceCallback(btstart, btend); - - newTraceCallback.m_collisionFilterMask = (traceType == collisionWorldTrace) ? Only_Collision : Only_Pickup; - - - enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback); - //newTraceCallback. - - - //std::cout << "NUM: " << enginePass->dynamicsWorld->getNumCollisionObjects() << "\n"; - - // Copy the hit data over to our trace results struct: - out->fraction = newTraceCallback.m_closestHitFraction; - - Ogre::Vector3& outhitnormal = out->hitNormal; - const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; - - outhitnormal.x = tracehitnormal.x(); - outhitnormal.y = tracehitnormal.y(); - outhitnormal.z = tracehitnormal.z(); - - Ogre::Vector3& outhitpos = out->endPos; - const btVector3& tracehitpos = newTraceCallback.m_hitPointWorld; - - outhitpos.x = tracehitpos.x(); - outhitpos.y = tracehitpos.y(); - outhitpos.z= tracehitpos.z(); - - // StartSolid test: - { - out->startSolid = false; - //btCollisionObject collision; - //collision.setCollisionShape(const_cast(&newshape) ); - - //CustomContactCallback crb; - - //world.world->contactTest(&collision, crb); - //out->startSolid = crb.hit; - - // If outside and underground, we're solid - if (!isInterior) //Check if we are interior - { - } - - // If inside and out of the tree, we're solid - else - { - btVector3 aabbMin, aabbMax; - enginePass->broadphase->getBroadphaseAabb(aabbMin, aabbMax); - //std::cout << "AABBMIN" << aabbMin.getX() <<"," <startSolid = true; - } - } - } - - const bool hasHit = newTraceCallback.hasHit(); - - - - - return hasHit; + // Copy the hit data over to our trace results struct: + if(newTraceCallback.hasHit()) + { + const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; + results->fraction = newTraceCallback.m_closestHitFraction; + results->planenormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); + results->endpos = (end-start)*results->fraction + start; + } + else + { + results->endpos = end; + results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); + results->fraction = 1.0f; + } } diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index 1bfe0c717..f484497d9 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -1,60 +1,26 @@ #ifndef OENGINE_BULLET_TRACE_H #define OENGINE_BULLET_TRACE_H - -#include -#include -#include -#include -#include "pmove.h" +#include -enum traceWorldType +namespace OEngine { - collisionWorldTrace = 1, - pickWorldTrace = 2, - bothWorldTrace = collisionWorldTrace | pickWorldTrace -}; + namespace Physic + { + class PhysicEngine; + } +} -enum collaborativePhysicsType -{ - No_Physics = 0, // Both are empty (example: statics you can walk through, like tall grass) - Only_Collision = 1, // This object only has collision physics but no pickup physics (example: statics) - Only_Pickup = 2, // This object only has pickup physics but no collision physics (example: items dropped on the ground) - Both_Physics = 3 // This object has both kinds of physics (example: activators) -}; -struct NewPhysTraceResults -{ - Ogre::Vector3 endPos; - Ogre::Vector3 hitNormal; - float fraction; - bool startSolid; - //const Object* hitObj; -}; struct traceResults { - Ogre::Vector3 endpos; - Ogre::Vector3 planenormal; + Ogre::Vector3 endpos; + Ogre::Vector3 planenormal; - float fraction; - - int surfaceFlags; - int contents; - int entityNum; - - bool allsolid; - bool startsolid; + float fraction; }; - - -template -const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); -//template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); -//template const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); - -void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); - +void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass); #endif diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index 925891e1b..e5a164b81 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -53,7 +53,7 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool // Create GUI mGui = new Gui(); - mGui->initialise("core.xml"); + mGui->initialise(""); } void MyGUIManager::shutdown() diff --git a/libs/openengine/ogre/imagerotate.cpp b/libs/openengine/ogre/imagerotate.cpp deleted file mode 100644 index 3dd584078..000000000 --- a/libs/openengine/ogre/imagerotate.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "imagerotate.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Ogre; -using namespace OEngine::Render; - -void ImageRotate::rotate(const std::string& sourceImage, const std::string& destImage, const float angle) -{ - Root* root = Ogre::Root::getSingletonPtr(); - - std::string destImageRot = std::string(destImage) + std::string("_rot"); - - SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); - Camera* camera = sceneMgr->createCamera("ImageRotateCamera"); - - MaterialPtr material = MaterialManager::getSingleton().create("ImageRotateMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - material->getTechnique(0)->getPass(0)->setLightingEnabled(false); - material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - TextureUnitState* tus = material->getTechnique(0)->getPass(0)->createTextureUnitState(sourceImage); - Degree deg(angle); - tus->setTextureRotate(Radian(deg.valueRadians())); - tus->setTextureAddressingMode(TextureUnitState::TAM_BORDER); - tus->setTextureBorderColour(ColourValue(0, 0, 0, 0)); - - Rectangle2D* rect = new Rectangle2D(true); - rect->setCorners(-1.0, 1.0, 1.0, -1.0); - rect->setMaterial("ImageRotateMaterial"); - // Render the background before everything else - rect->setRenderQueueGroup(RENDER_QUEUE_BACKGROUND); - - // Use infinite AAB to always stay visible - AxisAlignedBox aabInf; - aabInf.setInfinite(); - rect->setBoundingBox(aabInf); - - // Attach background to the scene - SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); - node->attachObject(rect); - - // retrieve image width and height - TexturePtr sourceTexture = TextureManager::getSingleton().getByName(sourceImage); - unsigned int width = sourceTexture->getWidth(); - unsigned int height = sourceTexture->getHeight(); - - TexturePtr destTextureRot = TextureManager::getSingleton().createManual( - destImageRot, - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - width, height, - 0, - PF_FLOAT16_RGBA, - TU_RENDERTARGET); - - RenderTarget* rtt = destTextureRot->getBuffer()->getRenderTarget(); - rtt->setAutoUpdated(false); - Viewport* vp = rtt->addViewport(camera); - vp->setOverlaysEnabled(false); - vp->setShadowsEnabled(false); - vp->setBackgroundColour(ColourValue(0,0,0,0)); - - rtt->update(); - - //copy the rotated image to a static texture - TexturePtr destTexture = TextureManager::getSingleton().createManual( - destImage, - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - width, height, - 0, - PF_FLOAT16_RGBA, - Ogre::TU_STATIC); - - destTexture->getBuffer()->blit(destTextureRot->getBuffer()); - - // remove all the junk we've created - TextureManager::getSingleton().remove(destImageRot); - MaterialManager::getSingleton().remove("ImageRotateMaterial"); - root->destroySceneManager(sceneMgr); - delete rect; -} diff --git a/libs/openengine/ogre/imagerotate.hpp b/libs/openengine/ogre/imagerotate.hpp deleted file mode 100644 index a3f6d662f..000000000 --- a/libs/openengine/ogre/imagerotate.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef OENGINE_OGRE_IMAGEROTATE_HPP -#define OENGINE_OGRE_IMAGEROTATE_HPP - -#include - -namespace OEngine -{ -namespace Render -{ - - /// Rotate an image by certain degrees and save as file, uses the GPU - /// Make sure Ogre Root is initialised before calling - class ImageRotate - { - public: - /** - * @param source image (file name - has to exist in an resource group) - * @param name of the destination texture to save to (in memory) - * @param angle in degrees to turn - */ - static void rotate(const std::string& sourceImage, const std::string& destImage, const float angle); - }; - -} -} - -#endif diff --git a/readme.txt b/readme.txt index 3124744c3..228278a91 100644 --- a/readme.txt +++ b/readme.txt @@ -9,7 +9,7 @@ Website: http://www.openmw.org Font Licenses: EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) -VeraMono.ttf: custom (see Bitstream Vera License.txt for more information) +DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information)