From 11b05c352fc038dabd41eb8e51d0ebbb411dcff3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 16:00:27 +0200 Subject: [PATCH 01/35] Fix exception when casting Bound Gloves spell --- apps/openmw/mwmechanics/actors.cpp | 8 ++++++-- apps/openmw/mwworld/projectilemanager.cpp | 4 ++-- apps/openmw/mwworld/projectilemanager.hpp | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d72ab7db8..6ea65275b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -530,8 +530,12 @@ namespace MWMechanics itemGmst)->getString(); if (it->first == ESM::MagicEffect::BoundGloves) { - adjustBoundItem("sMagicBoundLeftGauntletID", magnitude > 0, ptr); - adjustBoundItem("sMagicBoundRightGauntletID", magnitude > 0, ptr); + item = MWBase::Environment::get().getWorld()->getStore().get().find( + "sMagicBoundLeftGauntletID")->getString(); + adjustBoundItem(item, magnitude > 0, ptr); + item = MWBase::Environment::get().getWorld()->getStore().get().find( + "sMagicBoundRightGauntletID")->getString(); + adjustBoundItem(item, magnitude > 0, ptr); } else adjustBoundItem(item, magnitude > 0, ptr); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index cfb407b4a..3122cb325 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -78,7 +78,7 @@ namespace MWWorld state.mSourceName = sourceName; state.mId = model; state.mSpellId = spellId; - state.mCaster = caster; + state.mCasterHandle = caster.getRefData().getHandle(); if (caster.getClass().isActor()) state.mActorId = caster.getClass().getCreatureStats(caster).getActorId(); else @@ -162,7 +162,7 @@ namespace MWWorld { MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second); - MWWorld::Ptr caster = it->mCaster; + MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaHandle(it->mCasterHandle); if (caster.isEmpty()) caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId); diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index 4627d3b8a..6e84b9efb 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -67,12 +67,12 @@ namespace MWWorld int mActorId; - // actorId doesn't work for non-actors, so we also keep track of the Ptr. + // actorId doesn't work for non-actors, so we also keep track of the Ogre-handle. // For non-actors, the caster ptr is mainly needed to prevent the projectile // from colliding with its caster. // TODO: this will break when the game is saved and reloaded, since there is currently // no way to write identifiers for non-actors to a savegame. - MWWorld::Ptr mCaster; + std::string mCasterHandle; // MW-id of this projectile std::string mId; From 218f916d6da5c63203fa7001cfbac3cc37d34b2a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 17:17:26 +0200 Subject: [PATCH 02/35] Savegame: Don't write stat modifiers that are zero --- components/esm/statstate.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp index 4b4023bc2..f1a3b4d79 100644 --- a/components/esm/statstate.hpp +++ b/components/esm/statstate.hpp @@ -12,7 +12,8 @@ namespace ESM struct StatState { T mBase; - T mMod; + T mMod; // Note: can either be the modifier, or the modified value. + // A bit inconsistent, but we can't fix this without breaking compatibility. T mCurrent; T mDamage; float mProgress; @@ -30,7 +31,9 @@ namespace ESM void StatState::load (ESMReader &esm) { esm.getHNT (mBase, "STBA"); - esm.getHNT (mMod, "STMO"); + + mMod = 0; + esm.getHNOT (mMod, "STMO"); mCurrent = 0; esm.getHNOT (mCurrent, "STCU"); mDamage = 0; @@ -43,7 +46,9 @@ namespace ESM void StatState::save (ESMWriter &esm) const { esm.writeHNT ("STBA", mBase); - esm.writeHNT ("STMO", mMod); + + if (mMod != 0) + esm.writeHNT ("STMO", mMod); if (mCurrent) esm.writeHNT ("STCU", mCurrent); @@ -56,4 +61,4 @@ namespace ESM } } -#endif \ No newline at end of file +#endif From 8eab3abb15b30770a8f08c25165db53a1f8a5d47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 19:02:42 +0200 Subject: [PATCH 03/35] Fix initial view offset for large world maps (Fixes #1523) --- apps/openmw/mwgui/mapwindow.cpp | 7 +++---- files/mygui/openmw_map_window.layout | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index db3cfadde..af26456f2 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -431,6 +431,9 @@ namespace MWGui { mGlobalMapRender = new MWRender::GlobalMap(""); mGlobalMapRender->render(loadingListener); + mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); + mGlobalMapImage->setImageTexture("GlobalMap.png"); mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); } @@ -512,7 +515,6 @@ namespace MWGui else mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); - mLastDragPos = MyGUI::IntPoint(_left, _top); } @@ -536,9 +538,6 @@ namespace MWGui void MapWindow::open() { - mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); - // force markers to foreground for (unsigned int i=0; igetChildCount (); ++i) { diff --git a/files/mygui/openmw_map_window.layout b/files/mygui/openmw_map_window.layout index 6e0efce7e..b842888a1 100644 --- a/files/mygui/openmw_map_window.layout +++ b/files/mygui/openmw_map_window.layout @@ -17,8 +17,6 @@ - - From 091f9a8fdceb59be6cd37aa716aef04d0460ab0c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 19:14:59 +0200 Subject: [PATCH 04/35] Optimize global map render slightly --- apps/openmw/mwrender/globalmap.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 0537ed516..1ccfd9527 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -62,6 +62,12 @@ namespace MWRender loadingListener->setProgressRange((mMaxX-mMinX+1) * (mMaxY-mMinY+1)); loadingListener->setProgress(0); + const Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19); + const Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13); + const Ogre::ColourValue groundColour(0.254, 0.19, 0.13); + const Ogre::ColourValue mountainColour(0.05, 0.05, 0.05); + const Ogre::ColourValue hillColour(0.16, 0.12, 0.08); + //if (!boost::filesystem::exists(mCacheDir + "/GlobalMap.png")) if (1) { @@ -91,12 +97,6 @@ namespace MWRender int texelX = (x-mMinX) * cellSize + cellX; int texelY = (mHeight-1) - ((y-mMinY) * cellSize + cellY); - Ogre::ColourValue waterShallowColour(0.15, 0.2, 0.19); - Ogre::ColourValue waterDeepColour(0.1, 0.14, 0.13); - Ogre::ColourValue groundColour(0.254, 0.19, 0.13); - Ogre::ColourValue mountainColour(0.05, 0.05, 0.05); - Ogre::ColourValue hillColour(0.16, 0.12, 0.08); - unsigned char r,g,b; if (land) From ad0a182b7e57b90a953d70a4541d2e63b3f249e2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 19 Jun 2014 20:08:53 +0200 Subject: [PATCH 05/35] Improve error message for unknown cells --- apps/openmw/mwscript/transformationextensions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 138326ff0..3860257ad 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -323,7 +323,7 @@ namespace MWScript } else { - throw std::runtime_error ("unknown cell"); + throw std::runtime_error (std::string("unknown cell (") + cellID + ")"); } } }; @@ -420,7 +420,7 @@ namespace MWScript } else { - throw std::runtime_error ("unknown cell"); + throw std::runtime_error ( std::string("unknown cell (") + cellID + ")"); } } }; From 9a26cf22e6a904ef1875d3cc4d23f78ac263b821 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jun 2014 18:49:19 +0200 Subject: [PATCH 06/35] Implement toggleMenus --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 17 ++++++++++++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++++ apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- apps/openmw/mwscript/docs/vmformat.txt | 3 ++- apps/openmw/mwscript/guiextensions.cpp | 10 ++++++++++ components/compiler/extensions0.cpp | 2 ++ components/compiler/opcodes.hpp | 1 + 8 files changed, 37 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 0f45542ba..8b407c9ba 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -228,6 +228,7 @@ namespace MWBase virtual void showCrosshair(bool show) = 0; virtual bool getSubtitlesEnabled() = 0; virtual void toggleHud() = 0; + virtual bool toggleGui() = 0; virtual void disallowMouse() = 0; virtual void allowMouse() = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3f239e0e3..865ad1dca 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -118,6 +118,7 @@ namespace MWGui , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHudEnabled(true) + , mGuiEnabled(true) , mCursorVisible(true) , mPlayerName() , mPlayerRaceId() @@ -420,7 +421,7 @@ namespace MWGui mRecharge->setVisible(false); mVideoBackground->setVisible(false); - mHud->setVisible(mHudEnabled); + mHud->setVisible(mHudEnabled && mGuiEnabled); bool gameMode = !isGuiMode(); @@ -430,6 +431,13 @@ namespace MWGui if (gameMode) setKeyFocusWidget (NULL); + if (!mGuiEnabled) + { + if (containsMode(GM_Console)) + mConsole->setVisible(true); + return; + } + // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); setWeaponVisibility((mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory))); @@ -1345,6 +1353,13 @@ namespace MWGui mHud->setVisible (mHudEnabled); } + bool WindowManager::toggleGui() + { + mGuiEnabled = !mGuiEnabled; + updateVisible(); + return mGuiEnabled; + } + bool WindowManager::getRestEnabled() { //Enable rest dialogue if character creation finished diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index dd41635ad..8093d637e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -223,6 +223,9 @@ namespace MWGui virtual bool getSubtitlesEnabled(); virtual void toggleHud(); + /// Turn visibility of *all* GUI elements on or off (HUD and all windows, except the console) + virtual bool toggleGui(); + virtual void disallowMouse(); virtual void allowMouse(); virtual void notifyInputActionBound(); @@ -381,6 +384,7 @@ namespace MWGui bool mCrosshairEnabled; bool mSubtitlesEnabled; bool mHudEnabled; + bool mGuiEnabled; bool mCursorVisible; void setCursorVisible(bool visible); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index cd3be4b42..afdde6fb0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -277,7 +277,7 @@ namespace MWInput showQuickKeysMenu(); break; case A_ToggleHUD: - MWBase::Environment::get().getWindowManager()->toggleHud(); + MWBase::Environment::get().getWindowManager()->toggleGui(); break; case A_QuickSave: quickSave(); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index c125985c0..4b2a91a95 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -400,5 +400,6 @@ op 0x2000247: BetaComment op 0x2000248: BetaComment, explicit op 0x2000249: OnMurder op 0x200024a: OnMurder, explicit +op 0x200024b: ToggleMenus -opcodes 0x200024b-0x3ffffff unused +opcodes 0x200024c-0x3ffffff unused diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 333be5be6..afc745beb 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -203,6 +203,15 @@ namespace MWScript } }; + class OpToggleMenus : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + bool state = MWBase::Environment::get().getWindowManager()->toggleGui(); + runtime.getContext().report(state ? "GUI -> On" : "GUI -> Off"); + } + }; void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -242,6 +251,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap); interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap); interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest); + interpreter.installSegment5 (Compiler::Gui::opcodeToggleMenus, new OpToggleMenus); } } } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index efd45f912..9fc66900f 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -216,6 +216,8 @@ namespace Compiler extensions.registerInstruction ("showmap", "S", opcodeShowMap); extensions.registerInstruction ("fillmap", "", opcodeFillMap); extensions.registerInstruction ("menutest", "/l", opcodeMenuTest); + extensions.registerInstruction ("togglemenus", "", opcodeToggleMenus); + extensions.registerInstruction ("tm", "", opcodeToggleMenus); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index ed6f1df92..b097a017b 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -178,6 +178,7 @@ namespace Compiler const int opcodeShowMap = 0x20001a0; const int opcodeFillMap = 0x20001a1; const int opcodeMenuTest = 0x2002c; + const int opcodeToggleMenus = 0x200024b; } namespace Misc From 7bf7daa846606774e57fd5320e5af764a9ba35af Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jun 2014 21:33:03 +0200 Subject: [PATCH 07/35] Fix exception (getline will trigger failbit on the last line) --- components/translation/translation.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 976ae926d..5341240af 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -32,9 +32,6 @@ namespace Translation boost::filesystem::ifstream stream ( dataFileCollections.getCollection (extension).getPath (fileName)); - // Configure the stream to throw exception upon error - stream.exceptions ( boost::filesystem::ifstream::failbit | boost::filesystem::ifstream::badbit ); - if (!stream.is_open()) throw std::runtime_error ("failed to open translation file: " + fileName); @@ -44,9 +41,8 @@ namespace Translation void Storage::loadDataFromStream(ContainerType& container, std::istream& stream) { - // NOTE: does not handle failbit/badbit. stream must be set up beforehand to throw in these cases. std::string line; - while (!stream.eof()) + while (!stream.eof() && !stream.fail()) { std::getline( stream, line ); if (!line.empty() && *line.rbegin() == '\r') From a4ce9d6a7fb472549b0a136d431bf312ec34743a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 20 Jun 2014 23:56:21 +0200 Subject: [PATCH 08/35] Always show the script name when a script fails to compile --- apps/openmw/mwscript/scriptmanagerimp.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index 74c85dbbf..7b858dacf 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -64,12 +64,12 @@ namespace MWScript Success = false; } - if (!Success && mVerbose) + if (!Success) { std::cerr - << "compiling failed: " << name << std::endl - << script->mScriptText - << std::endl << std::endl; + << "compiling failed: " << name << std::endl; + if (mVerbose) + std::cerr << script->mScriptText << std::endl << std::endl; } if (Success) From 98329a94b4b73b6ded6cd4ee2dd04cbf2a920762 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 21 Jun 2014 19:54:12 +0200 Subject: [PATCH 09/35] Add case sensitivity workaround for spine bones (Fixes #1547) --- apps/openmw/mwrender/weaponanimation.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 5f953bd83..cd9ae16b8 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -150,9 +150,19 @@ void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel) return; float pitch = xrot * mPitchFactor; - Ogre::Node *node = skel->getBone("Bip01 Spine2"); + Ogre::Node *node; + + // In spherearcher.nif, we have spine, not Spine. Not sure if all bone names should be case insensitive? + if (skel->hasBone("Bip01 spine2")) + node = skel->getBone("Bip01 spine2"); + else + node = skel->getBone("Bip01 Spine2"); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); - node = skel->getBone("Bip01 Spine1"); + + if (skel->hasBone("Bip01 spine1")) // in spherearcher.nif + node = skel->getBone("Bip01 spine1"); + else + node = skel->getBone("Bip01 Spine1"); node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); } From e002acdeaeb3c2559f36b7c9f0ee4ac6abeb7748 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 21 Jun 2014 23:32:58 +0200 Subject: [PATCH 10/35] Physics: Create actor shapes outside of BulletNifLoader This will allow to create a specialised shape instead, such as a capsule, which tends to work better for character controllers. --- apps/openmw/mwworld/physicssystem.cpp | 10 +- apps/openmw/mwworld/worldimp.cpp | 2 +- components/nifbullet/bulletnifloader.cpp | 49 ++++++ components/nifbullet/bulletnifloader.hpp | 5 + libs/openengine/bullet/physic.cpp | 184 +++++++++++------------ libs/openengine/bullet/physic.hpp | 50 +++--- libs/openengine/bullet/trace.cpp | 18 +-- libs/openengine/bullet/trace.h | 3 +- 8 files changed, 177 insertions(+), 144 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 73a704c88..8aca591e8 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -175,7 +175,7 @@ namespace MWWorld const int maxHeight = 200.f; OEngine::Physic::ActorTracer tracer; - tracer.findGround(physicActor->getCollisionBody(), position, position-Ogre::Vector3(0,0,maxHeight), engine); + tracer.findGround(physicActor, position, position-Ogre::Vector3(0,0,maxHeight), engine); if(tracer.mFraction >= 1.0f) { physicActor->setOnGround(false); @@ -607,9 +607,10 @@ namespace MWWorld Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); const std::string &handle = node->getName(); const Ogre::Quaternion &rotation = node->getOrientation(); + + // TODO: map to MWWorld::Ptr for faster access if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) { - //Needs to be changed act->setRotation(rotation); } if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) @@ -740,8 +741,9 @@ namespace MWWorld btCollisionObject object; object.setCollisionShape(&planeShape); + // TODO: this seems to have a slight performance impact if (waterCollision) - mEngine->dynamicsWorld->addCollisionObject(&object); + mEngine->mDynamicsWorld->addCollisionObject(&object); // 100 points of slowfall reduce gravity by 90% (this is just a guess) float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f); @@ -751,7 +753,7 @@ namespace MWWorld waterlevel, slowFall, mEngine); if (waterCollision) - mEngine->dynamicsWorld->removeCollisionObject(&object); + mEngine->mDynamicsWorld->removeCollisionObject(&object); float heightDiff = newpos.z - oldHeight; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bcae8c070..be334646b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1847,7 +1847,7 @@ namespace MWWorld Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); OEngine::Physic::ActorTracer tracer; // a small distance above collision object is considered "on ground" - tracer.findGround(physactor->getCollisionBody(), + tracer.findGround(physactor, pos, pos - Ogre::Vector3(0, 0, 1.5f), // trace a small amount down mPhysEngine); diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index cdddb94d0..3e753dd37 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -304,4 +304,53 @@ void ManualBulletShapeLoader::load(const std::string &name,const std::string &gr OEngine::Physic::BulletShapeManager::getSingleton().create(name,group,true,this); } +bool findBoundingBox (const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation) +{ + if(node->hasBounds) + { + if (!(node->flags & Nif::NiNode::Flag_Hidden)) + { + translation = node->boundPos; + orientation = node->boundRot; + halfExtents = node->boundXYZ; + return true; + } + } + + const Nif::NiNode *ninode = dynamic_cast(node); + if(ninode) + { + const Nif::NodeList &list = ninode->children; + for(size_t i = 0;i < list.length();i++) + { + if(!list[i].empty()) + if (findBoundingBox(list[i].getPtr(), halfExtents, translation, orientation)) + return true; + } + } + return false; +} + +bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation) +{ + Nif::NIFFile::ptr pnif (Nif::NIFFile::create (nifFile)); + Nif::NIFFile & nif = *pnif.get (); + + if (nif.numRoots() < 1) + { + return false; + } + + Nif::Record *r = nif.getRoot(0); + assert(r != NULL); + + Nif::Node *node = dynamic_cast(r); + if (node == NULL) + { + return false; + } + + return findBoundingBox(node, halfExtents, translation, orientation); +} + } // namespace NifBullet diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index d1e876305..fb7d3d70a 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -108,6 +108,11 @@ private: bool mHasShape; }; + +bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation); + +bool findBoundingBox(const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation); + } #endif diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 8d9ac3c22..bc34f2f51 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -16,115 +16,93 @@ namespace Physic { 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), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0) - , mCollisionBody(true) + : mName(name), mEngine(engine), mMesh(mesh) + , mBody(0), mOnGround(false), mInternalCollisionMode(true) + , mExternalCollisionMode(true) , mForce(0.0f) + , mScale(scale) { - mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); - mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); - Ogre::Quaternion inverse = mBoxRotation.Inverse(); - mBoxRotationInverse = Ogre::Quaternion(inverse.w, inverse.x, inverse.y,inverse.z); - mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map + if (!NifBullet::getBoundingBox(mMesh, mHalfExtents, mMeshTranslation, mMeshOrientation)) + { + mHalfExtents = Ogre::Vector3(0.f); + mMeshTranslation = Ogre::Vector3(0.f); + mMeshOrientation = Ogre::Quaternion::IDENTITY; + } + + mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); + mShape->setLocalScaling(btVector3(scale,scale,scale)); + + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo + (0,0, mShape.get()); + mBody = new RigidBody(CI, name); + mBody->mPlaceable = false; + + setPosition(position); + setRotation(rotation); + + mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, + CollisionType_Actor|CollisionType_World|CollisionType_HeightMap); } PhysicActor::~PhysicActor() { if(mBody) { - mEngine->dynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->removeRigidBody(mBody); delete mBody; - } - if(mRaycastingBody) - { - mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody); - delete mRaycastingBody; - } + } } void PhysicActor::enableCollisionMode(bool collision) { - mCollisionMode = collision; + mInternalCollisionMode = collision; } void PhysicActor::enableCollisionBody(bool collision) { assert(mBody); - if(collision && !mCollisionBody) enableCollisionBody(); - if(!collision && mCollisionBody) disableCollisionBody(); - mCollisionBody = collision; + if(collision && !mExternalCollisionMode) enableCollisionBody(); + if(!collision && mExternalCollisionMode) disableCollisionBody(); + mExternalCollisionMode = collision; } - void PhysicActor::setPosition(const Ogre::Vector3 &pos) + const Ogre::Vector3& PhysicActor::getPosition() const { - assert(mBody); - if(pos != getPosition()) - { - mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); - mEngine->adjustRigidBody(mRaycastingBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation); - } + return mPosition; } - void PhysicActor::setRotation(const Ogre::Quaternion &quat) + void PhysicActor::setPosition(const Ogre::Vector3 &position) { assert(mBody); - if(!quat.equals(getRotation(), Ogre::Radian(0))){ - mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); - mEngine->adjustRigidBody(mRaycastingBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); - } - } + mPosition = position; + btTransform tr = mBody->getWorldTransform(); + Ogre::Quaternion meshrot = mMeshOrientation; + Ogre::Vector3 transrot = meshrot * (mMeshTranslation * mScale); + Ogre::Vector3 newPosition = transrot + position; - Ogre::Vector3 PhysicActor::getPosition() - { - assert(mBody); - btVector3 vec = mBody->getWorldTransform().getOrigin(); - Ogre::Quaternion rotation = Ogre::Quaternion(mBody->getWorldTransform().getRotation().getW(), mBody->getWorldTransform().getRotation().getX(), - mBody->getWorldTransform().getRotation().getY(), mBody->getWorldTransform().getRotation().getZ()); - Ogre::Vector3 transrot = rotation * mBoxScaledTranslation; - Ogre::Vector3 visualPosition = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ()) - transrot; - return visualPosition; + tr.setOrigin(BtOgre::Convert::toBullet(newPosition)); + mBody->setWorldTransform(tr); } - Ogre::Quaternion PhysicActor::getRotation() + void PhysicActor::setRotation (const Ogre::Quaternion& rotation) { - assert(mBody); - btQuaternion quat = mBody->getWorldTransform().getRotation(); - return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()) * mBoxRotationInverse; + btTransform tr = mBody->getWorldTransform(); + tr.setRotation(BtOgre::Convert::toBullet(mMeshOrientation * rotation)); + mBody->setWorldTransform(tr); } - void PhysicActor::setScale(float scale){ - //We only need to change the scaled box translation, box rotations remain the same. - assert(mBody); - mBoxScaledTranslation = mBoxScaledTranslation / mBody->getCollisionShape()->getLocalScaling().getX(); - mBoxScaledTranslation *= scale; - Ogre::Vector3 pos = getPosition(); - Ogre::Quaternion rot = getRotation(); - if(mBody){ - mEngine->dynamicsWorld->removeRigidBody(mBody); - mEngine->dynamicsWorld->removeRigidBody(mRaycastingBody); - delete mBody; - delete mRaycastingBody; - } - //Create the newly scaled rigid body - mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot); - mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true); - mEngine->addRigidBody(mCollisionBody ? mBody : 0, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map + void PhysicActor::setScale(float scale) + { + mScale = scale; + mShape->setLocalScaling(btVector3(scale,scale,scale)); + setPosition(mPosition); } 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); + return mHalfExtents; } void PhysicActor::setInertialForce(const Ogre::Vector3 &force) @@ -139,12 +117,16 @@ namespace Physic void PhysicActor::disableCollisionBody() { - mEngine->dynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, + CollisionType_Raycasting); } void PhysicActor::enableCollisionBody() { - mEngine->dynamicsWorld->addRigidBody(mBody,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); + mEngine->mDynamicsWorld->removeRigidBody(mBody); + mEngine->mDynamicsWorld->addRigidBody(mBody, CollisionType_Actor, + CollisionType_Actor|CollisionType_World|CollisionType_HeightMap); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -189,8 +171,8 @@ namespace Physic broadphase = new btDbvtBroadphase(); // The world. - dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); - dynamicsWorld->setGravity(btVector3(0,0,-10)); + mDynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); + mDynamicsWorld->setGravity(btVector3(0,0,-10)); if(BulletShapeManager::getSingletonPtr() == NULL) { @@ -208,10 +190,10 @@ namespace Physic if(!isDebugCreated) { Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); - dynamicsWorld->setDebugDrawer(mDebugDrawer); + mDebugDrawer = new BtOgre::DebugDrawer(node, mDynamicsWorld); + mDynamicsWorld->setDebugDrawer(mDebugDrawer); isDebugCreated = true; - dynamicsWorld->debugDrawWorld(); + mDynamicsWorld->debugDrawWorld(); } } @@ -241,7 +223,7 @@ namespace Physic HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); for (; hf_it != mHeightFieldMap.end(); ++hf_it) { - dynamicsWorld->removeRigidBody(hf_it->second.mBody); + mDynamicsWorld->removeRigidBody(hf_it->second.mBody); delete hf_it->second.mShape; delete hf_it->second.mBody; } @@ -251,7 +233,7 @@ namespace Physic { if (rb_it->second != NULL) { - dynamicsWorld->removeRigidBody(rb_it->second); + mDynamicsWorld->removeRigidBody(rb_it->second); delete rb_it->second; rb_it->second = NULL; @@ -262,7 +244,7 @@ namespace Physic { if (rb_it->second != NULL) { - dynamicsWorld->removeRigidBody(rb_it->second); + mDynamicsWorld->removeRigidBody(rb_it->second); delete rb_it->second; rb_it->second = NULL; @@ -281,7 +263,7 @@ namespace Physic delete mDebugDrawer; - delete dynamicsWorld; + delete mDynamicsWorld; delete solver; delete collisionConfiguration; delete dispatcher; @@ -331,7 +313,7 @@ namespace Physic mHeightFieldMap [name] = hf; - dynamicsWorld->addRigidBody(body,CollisionType_HeightMap|CollisionType_Raycasting, + mDynamicsWorld->addRigidBody(body,CollisionType_HeightMap, CollisionType_World|CollisionType_Actor|CollisionType_Raycasting); } @@ -343,7 +325,7 @@ namespace Physic HeightField hf = mHeightFieldMap [name]; - dynamicsWorld->removeRigidBody(hf.mBody); + mDynamicsWorld->removeRigidBody(hf.mBody); delete hf.mShape; delete hf.mBody; @@ -367,7 +349,6 @@ namespace Physic { std::string sid = (boost::format("%07.3f") % scale).str(); std::string outputstring = mesh + sid; - //std::cout << "The string" << outputstring << "\n"; //get the shape from the .nif mShapeLoader->load(outputstring,"General"); @@ -419,7 +400,7 @@ namespace Physic } - void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody,bool actor) + void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody) { if(!body && !raycastingBody) return; // nothing to do @@ -427,12 +408,11 @@ namespace Physic const std::string& name = (body ? body->mName : raycastingBody->mName); if (body){ - if(actor) dynamicsWorld->addRigidBody(body,CollisionType_Actor,CollisionType_World|CollisionType_HeightMap); - else dynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); + mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); } if (raycastingBody) - dynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting|CollisionType_World); + mDynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting); if(addToMap){ removeRigidBody(name); @@ -453,7 +433,7 @@ namespace Physic RigidBody* body = it->second; if(body != NULL) { - dynamicsWorld->removeRigidBody(body); + mDynamicsWorld->removeRigidBody(body); } } it = mRaycastingObjectMap.find(name); @@ -462,7 +442,7 @@ namespace Physic RigidBody* body = it->second; if(body != NULL) { - dynamicsWorld->removeRigidBody(body); + mDynamicsWorld->removeRigidBody(body); } } } @@ -605,7 +585,7 @@ namespace Physic if (!body) // fall back to raycasting body if there is no collision body body = getRigidBody(name, true); ContactTestResultCallback callback; - dynamicsWorld->contactTest(body, callback); + mDynamicsWorld->contactTest(body, callback); return callback.mResult; } @@ -615,8 +595,9 @@ namespace Physic btCollisionObject *object) { DeepestNotMeContactTestResultCallback callback(filter, origin); + callback.m_collisionFilterGroup = 0xff; callback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; - dynamicsWorld->contactTest(object, callback); + mDynamicsWorld->contactTest(object, callback); return std::make_pair(callback.mObject, callback.mContactPoint); } @@ -624,7 +605,7 @@ namespace Physic void PhysicEngine::stepSimulation(double deltaT) { // This seems to be needed for character controller objects - dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); + mDynamicsWorld->stepSimulation(deltaT,10, 1/60.0); if(isDebugCreated) { mDebugDrawer->step(); @@ -684,14 +665,15 @@ namespace Physic float d = -1; btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); + resultCallback1.m_collisionFilterGroup = 0xff; if(raycastingObjectOnly) - resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor; else resultCallback1.m_collisionFilterMask = CollisionType_World; if(!ignoreHeightMap) resultCallback1.m_collisionFilterMask = resultCallback1.m_collisionFilterMask | CollisionType_HeightMap; - dynamicsWorld->rayTest(from, to, resultCallback1); + mDynamicsWorld->rayTest(from, to, resultCallback1); if (resultCallback1.hasHit()) { name = static_cast(*resultCallback1.m_collisionObject).mName; @@ -724,6 +706,7 @@ namespace Physic std::pair PhysicEngine::sphereCast (float radius, btVector3& from, btVector3& to) { OurClosestConvexResultCallback callback(from, to); + callback.m_collisionFilterGroup = 0xff; callback.m_collisionFilterMask = OEngine::Physic::CollisionType_World|OEngine::Physic::CollisionType_HeightMap; btSphereShape shape(radius); @@ -732,7 +715,7 @@ namespace Physic btTransform from_ (btrot, from); btTransform to_ (btrot, to); - dynamicsWorld->convexSweepTest(&shape, from_, to_, callback); + mDynamicsWorld->convexSweepTest(&shape, from_, to_, callback); if (callback.hasHit()) return std::make_pair(true, callback.m_closestHitFraction); @@ -743,8 +726,9 @@ namespace Physic std::vector< std::pair > PhysicEngine::rayTest2(btVector3& from, btVector3& to) { MyRayResultCallback resultCallback1; - resultCallback1.m_collisionFilterMask = CollisionType_Raycasting; - dynamicsWorld->rayTest(from, to, resultCallback1); + resultCallback1.m_collisionFilterGroup = 0xff; + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor; + mDynamicsWorld->rayTest(from, to, resultCallback1); std::vector< std::pair > results = resultCallback1.results; std::vector< std::pair > results2; diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 803986d5b..f24ef93d0 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -76,6 +76,9 @@ namespace Physic RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); virtual ~RigidBody(); std::string mName; + + // Hack: placeable objects (that can be picked up by the player) have different collision behaviour. + // This variable needs to be passed to BulletNifLoader. bool mPlaceable; }; @@ -92,13 +95,6 @@ namespace Physic 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); - /** * Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry. */ @@ -111,26 +107,18 @@ namespace Physic bool getCollisionMode() const { - return mCollisionMode; + return mInternalCollisionMode; } - - /** - * This returns the visual position of the PhysicActor (used to position a scenenode). - * Note - this is different from the position of the contained mBody. - */ - Ogre::Vector3 getPosition(); - - /** - * Returns the visual orientation of the PhysicActor - */ - Ogre::Quaternion getRotation(); - /** * Sets the scale of the PhysicActor */ void setScale(float scale); + void setRotation (const Ogre::Quaternion& rotation); + + const Ogre::Vector3& getPosition() const; + /** * Returns the half extents for this PhysiActor */ @@ -153,7 +141,7 @@ namespace Physic bool getOnGround() const { - return mCollisionMode && mOnGround; + return mInternalCollisionMode && mOnGround; } btCollisionObject *getCollisionBody() const @@ -165,17 +153,21 @@ namespace Physic void disableCollisionBody(); void enableCollisionBody(); + boost::shared_ptr mShape; + OEngine::Physic::RigidBody* mBody; - OEngine::Physic::RigidBody* mRaycastingBody; - Ogre::Vector3 mBoxScaledTranslation; - Ogre::Quaternion mBoxRotation; - Ogre::Quaternion mBoxRotationInverse; + Ogre::Quaternion mMeshOrientation; + Ogre::Vector3 mMeshTranslation; + Ogre::Vector3 mHalfExtents; + + float mScale; + Ogre::Vector3 mPosition; Ogre::Vector3 mForce; bool mOnGround; - bool mCollisionMode; - bool mCollisionBody; + bool mInternalCollisionMode; + bool mExternalCollisionMode; std::string mMesh; std::string mName; @@ -242,7 +234,7 @@ namespace Physic /** * Add a RigidBody to the simulation */ - void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL,bool actor = false); + void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL); /** * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. @@ -335,7 +327,7 @@ namespace Physic btDefaultCollisionConfiguration* collisionConfiguration; btSequentialImpulseConstraintSolver* solver; btCollisionDispatcher* dispatcher; - btDiscreteDynamicsWorld* dynamicsWorld; + btDiscreteDynamicsWorld* mDynamicsWorld; //the NIF file loader. BulletShapeLoader* mShapeLoader; diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 6eab43a60..78fb5a3a7 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -65,12 +65,13 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, to.setOrigin(btend); ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + newTraceCallback.m_collisionFilterGroup = CollisionType_Actor; newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; btCollisionShape *shape = actor->getCollisionShape(); assert(shape->isConvex()); - enginePass->dynamicsWorld->convexSweepTest(static_cast(shape), + enginePass->mDynamicsWorld->convexSweepTest(static_cast(shape), from, to, newTraceCallback); // Copy the hit data over to our trace results struct: @@ -89,27 +90,26 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, } } -void ActorTracer::findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass) +void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass) { const btVector3 btstart(start.x, start.y, start.z+1.0f); const btVector3 btend(end.x, end.y, end.z+1.0f); - const btTransform &trans = actor->getWorldTransform(); + const btTransform &trans = actor->getCollisionBody()->getWorldTransform(); btTransform from(trans.getBasis(), btstart); btTransform to(trans.getBasis(), btend); - ClosestNotMeConvexResultCallback newTraceCallback(actor, btstart-btend, btScalar(0.0)); + ClosestNotMeConvexResultCallback newTraceCallback(actor->getCollisionBody(), btstart-btend, btScalar(0.0)); + newTraceCallback.m_collisionFilterGroup = CollisionType_Actor; newTraceCallback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; - const btBoxShape *shape = dynamic_cast(actor->getCollisionShape()); - assert(shape); + btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z); - btVector3 halfExtents = shape->getHalfExtentsWithMargin(); halfExtents[2] = 1.0f; - btBoxShape box(halfExtents); + btBoxShape base(halfExtents); - enginePass->dynamicsWorld->convexSweepTest(&box, from, to, newTraceCallback); + enginePass->mDynamicsWorld->convexSweepTest(&base, from, to, newTraceCallback); if(newTraceCallback.hasHit()) { const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld; diff --git a/libs/openengine/bullet/trace.h b/libs/openengine/bullet/trace.h index 92795c87f..b9fbce64d 100644 --- a/libs/openengine/bullet/trace.h +++ b/libs/openengine/bullet/trace.h @@ -12,6 +12,7 @@ namespace OEngine namespace Physic { class PhysicEngine; + class PhysicActor; struct ActorTracer { @@ -22,7 +23,7 @@ namespace Physic void doTrace(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass); - void findGround(btCollisionObject *actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, + void findGround(const OEngine::Physic::PhysicActor* actor, const Ogre::Vector3 &start, const Ogre::Vector3 &end, const PhysicEngine *enginePass); }; } From 0bed6d9d56fd89510f8f869044c76757153c47d4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 21 Jun 2014 23:56:59 +0200 Subject: [PATCH 11/35] Physics: Recognize BBoxCollision flag, but don't use it for raycasting (Fixes #1349) --- components/nifbullet/bulletnifloader.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 3e753dd37..bed3e8869 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -137,12 +137,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) handleNode(mesh2, node,0,true,true,false); - if(mBoundingBox != NULL) - { - mShape->mRaycastingShape = mBoundingBox; - delete mesh2; - } - else if (mHasShape) + if (mHasShape) { mShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); } @@ -227,10 +222,12 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * if ( (isCollisionNode || (!mShape->mHasCollisionNode && !raycasting)) && (!isMarker || (mShape->mHasCollisionNode && !raycasting))) { + // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! + // It must be ignored completely. + // (occurs in tr_ex_imp_wall_arch_04.nif) if(node->hasBounds) { - // Checking for BBoxCollision flag causes issues with some actors :/ - if (!(node->flags & Nif::NiNode::Flag_Hidden)) + if (flags & Nif::NiNode::Flag_BBoxCollision && !raycasting) { mShape->mBoxTranslation = node->boundPos; mShape->mBoxRotation = node->boundRot; From e23a7694f30afcb8ae683fe67bfa6631fab96244 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 00:34:32 +0200 Subject: [PATCH 12/35] Don't throw exception when using ModDisposition on creatures (Fixes #1548) --- apps/openmw/mwscript/statsextensions.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 5a0cd8ae6..1d3a8bc4b 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -690,8 +690,11 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - ptr.getClass().getNpcStats (ptr).setBaseDisposition - (ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value); + if (ptr.getClass().isNpc()) + ptr.getClass().getNpcStats (ptr).setBaseDisposition + (ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value); + + // else: must not throw exception (used by an Almalexia dialogue script) } }; @@ -707,7 +710,8 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - ptr.getClass().getNpcStats (ptr).setBaseDisposition (value); + if (ptr.getClass().isNpc()) + ptr.getClass().getNpcStats (ptr).setBaseDisposition (value); } }; @@ -720,7 +724,10 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); + if (!ptr.getClass().isNpc()) + runtime.push(0); + else + runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); } }; From 33ed11d8e6be6065b4683fbd4103555fc32c9431 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 00:47:14 +0200 Subject: [PATCH 13/35] Physics: Use capsule shapes for actors if possible (Fixes #1437) --- libs/openengine/bullet/physic.cpp | 11 +++++++++-- libs/openengine/bullet/trace.cpp | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index bc34f2f51..d934639e0 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -29,7 +29,14 @@ namespace Physic mMeshOrientation = Ogre::Quaternion::IDENTITY; } - mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); + // Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it) + if (std::abs(mHalfExtents.x-mHalfExtents.y)= mHalfExtents.x) + { + mShape.reset(new btCapsuleShapeZ(mHalfExtents.x, mHalfExtents.z*2.f - mHalfExtents.x*2.f)); + } + else + mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); + mShape->setLocalScaling(btVector3(scale,scale,scale)); btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo @@ -102,7 +109,7 @@ namespace Physic Ogre::Vector3 PhysicActor::getHalfExtents() const { - return mHalfExtents; + return mHalfExtents * mScale; } void PhysicActor::setInertialForce(const Ogre::Vector3 &force) diff --git a/libs/openengine/bullet/trace.cpp b/libs/openengine/bullet/trace.cpp index 78fb5a3a7..46db4c6b8 100644 --- a/libs/openengine/bullet/trace.cpp +++ b/libs/openengine/bullet/trace.cpp @@ -107,7 +107,7 @@ void ActorTracer::findGround(const OEngine::Physic::PhysicActor* actor, const Og btVector3 halfExtents(actor->getHalfExtents().x, actor->getHalfExtents().y, actor->getHalfExtents().z); halfExtents[2] = 1.0f; - btBoxShape base(halfExtents); + btCylinderShapeZ base(halfExtents); enginePass->mDynamicsWorld->convexSweepTest(&base, from, to, newTraceCallback); if(newTraceCallback.hasHit()) From baf490a2b593f89f4469028082dabe784ed46889 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 03:05:10 +0200 Subject: [PATCH 14/35] Change to btCylinderShape --- libs/openengine/bullet/physic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d934639e0..31cb67a58 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -32,7 +32,8 @@ namespace Physic // Use capsule shape only if base is square (nonuniform scaling apparently doesn't work on it) if (std::abs(mHalfExtents.x-mHalfExtents.y)= mHalfExtents.x) { - mShape.reset(new btCapsuleShapeZ(mHalfExtents.x, mHalfExtents.z*2.f - mHalfExtents.x*2.f)); + // Could also be btCapsuleShapeZ, but the movement solver seems to have issues with it (jumping on slopes doesn't work) + mShape.reset(new btCylinderShapeZ(BtOgre::Convert::toBullet(mHalfExtents))); } else mShape.reset(new btBoxShape(BtOgre::Convert::toBullet(mHalfExtents))); From cbec0ffaee7bbb93d5c0346160f1b7c9449d15dd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 03:05:17 +0200 Subject: [PATCH 15/35] Exception fix --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index f4ffa499c..0ad0b4d5a 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1101,7 +1101,7 @@ namespace MWMechanics if (iter->first.getClass().isClass(iter->first, "Guard")) { MWMechanics::AiSequence& aiSeq = iter->first.getClass().getCreatureStats(iter->first).getAiSequence(); - if (aiSeq.getActivePackage()->getTypeId() == MWMechanics::AiPackage::TypeIdPursue) + if (aiSeq.getTypeId() == MWMechanics::AiPackage::TypeIdPursue) { aiSeq.stopPursuit(); aiSeq.stack(MWMechanics::AiCombat(target), ptr); From d296c6e9b7bb7dbceafcb4b0c0ea1ae2d00950fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 16:10:52 +0200 Subject: [PATCH 16/35] Handle fog density values of 0 (Bug #1549) --- apps/openmw/mwrender/renderingmanager.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8a22c63c6..89933414d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -506,10 +506,19 @@ void RenderingManager::configureFog(const float density, const Ogre::ColourValue mFogColour = colour; float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance"); - mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); - mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); + if (density == 0) + { + mFogStart = 0; + mFogEnd = std::numeric_limits().max(); + mRendering.getCamera()->setFarClipDistance (max); + } + else + { + mFogStart = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance"); + mFogEnd = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance"); + mRendering.getCamera()->setFarClipDistance (max / density); + } - mRendering.getCamera()->setFarClipDistance ( Settings::Manager::getFloat("max viewing distance", "Viewing distance") / density ); } void RenderingManager::applyFog (bool underwater) From 8df0effcf5244121ca53fbd5560e0e5b3e915988 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 16:25:29 +0200 Subject: [PATCH 17/35] Remove unused string --- libs/openengine/bullet/BulletShapeLoader.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/openengine/bullet/BulletShapeLoader.h b/libs/openengine/bullet/BulletShapeLoader.h index 0e5c65226..c6f7f727c 100644 --- a/libs/openengine/bullet/BulletShapeLoader.h +++ b/libs/openengine/bullet/BulletShapeLoader.h @@ -15,8 +15,6 @@ namespace Physic */ class BulletShape : public Ogre::Resource { - Ogre::String mString; - protected: void loadImpl(); void unloadImpl(); From 64a4c2785e2883f530d10448bb523d94a8de66ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Jun 2014 16:43:41 +0200 Subject: [PATCH 18/35] Fix water walking --- apps/openmw/mwworld/physicssystem.cpp | 3 ++- libs/openengine/bullet/physic.hpp | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 8aca591e8..ad4300b1f 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -743,7 +743,8 @@ namespace MWWorld // TODO: this seems to have a slight performance impact if (waterCollision) - mEngine->mDynamicsWorld->addCollisionObject(&object); + mEngine->mDynamicsWorld->addCollisionObject(&object, + 0xff, OEngine::Physic::CollisionType_Actor); // 100 points of slowfall reduce gravity by 90% (this is just a guess) float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f24ef93d0..deef21443 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -82,10 +82,7 @@ namespace Physic bool mPlaceable; }; - /** - * A physic actor uses a rigid body based on box shapes. - * Pmove is used to move the physic actor around the dynamic world. - */ + class PhysicActor { public: From 0b34d8d2fddacc6e99435a7ab2ed11446a9796c2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jun 2014 20:43:24 +0200 Subject: [PATCH 19/35] Add support for animated collision shapes (Fixes #1549) --- apps/openmw/mwworld/physicssystem.cpp | 53 ++++++- apps/openmw/mwworld/physicssystem.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 2 +- components/nifbullet/bulletnifloader.cpp | 148 +++++++++++++------ components/nifbullet/bulletnifloader.hpp | 32 +++- libs/openengine/bullet/BulletShapeLoader.cpp | 5 +- libs/openengine/bullet/BulletShapeLoader.h | 8 + libs/openengine/bullet/physic.cpp | 136 ++++++++++++----- libs/openengine/bullet/physic.hpp | 21 ++- 9 files changed, 311 insertions(+), 96 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index ad4300b1f..7f26fa75a 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -26,10 +27,49 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "../apps/openmw/mwrender/animation.hpp" +#include "../apps/openmw/mwbase/world.hpp" +#include "../apps/openmw/mwbase/environment.hpp" + #include "ptr.hpp" #include "class.hpp" using namespace Ogre; + +namespace +{ + +void animateCollisionShapes (std::map& map) +{ + for (std::map::iterator it = map.begin(); + it != map.end(); ++it) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(it->first->mName); + MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr); + + OEngine::Physic::AnimatedShapeInstance& instance = it->second; + + std::map& shapes = instance.mAnimatedShapes; + for (std::map::iterator shapeIt = shapes.begin(); + shapeIt != shapes.end(); ++shapeIt) + { + Ogre::Node* bone = animation->getNode(shapeIt->first); + + btCompoundShape* compound = dynamic_cast(instance.mCompound); + + btTransform trans; + trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition())); + trans.setRotation(BtOgre::Convert::toBullet(bone->_getDerivedOrientation())); + + compound->getChildShape(shapeIt->second)->setLocalScaling(BtOgre::Convert::toBullet(bone->_getDerivedScale())); + compound->updateChildTransform(shapeIt->second, trans); + } + } +} + +} + + namespace MWWorld { @@ -564,11 +604,10 @@ namespace MWWorld std::string mesh = ptr.getClass().getModel(ptr); Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); handleToMesh[node->getName()] = mesh; - OEngine::Physic::RigidBody* body = mEngine->createAndAdjustRigidBody( + mEngine->createAndAdjustRigidBody( mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, false, placeable); - OEngine::Physic::RigidBody* raycastingBody = mEngine->createAndAdjustRigidBody( + mEngine->createAndAdjustRigidBody( mesh, node->getName(), node->getScale().x, node->getPosition(), node->getOrientation(), 0, 0, true, placeable); - mEngine->addRigidBody(body, true, raycastingBody); } void PhysicsSystem::addActor (const Ptr& ptr) @@ -770,4 +809,12 @@ namespace MWWorld return mMovementResults; } + + void PhysicsSystem::stepSimulation(float dt) + { + animateCollisionShapes(mEngine->mAnimatedShapes); + animateCollisionShapes(mEngine->mAnimatedRaycastingShapes); + + mEngine->stepSimulation(dt); + } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index c590b40c8..df9718669 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -53,6 +53,8 @@ namespace MWWorld bool toggleCollisionMode(); + void stepSimulation(float dt); + std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index be334646b..b4957abfd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1231,7 +1231,7 @@ namespace MWWorld if(player != results.end()) moveObjectImp(player->first, player->second.x, player->second.y, player->second.z); - mPhysEngine->stepSimulation(duration); + mPhysics->stepSimulation(duration); } bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index bed3e8869..64febc3c2 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -49,20 +49,6 @@ typedef unsigned char ubyte; namespace NifBullet { -struct TriangleMeshShape : public btBvhTriangleMeshShape -{ - TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression) - : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression) - { - } - - virtual ~TriangleMeshShape() - { - delete getTriangleInfoMap(); - delete m_meshInterface; - } -}; - ManualBulletShapeLoader::~ManualBulletShapeLoader() { } @@ -81,9 +67,8 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mBoundingBox = NULL; mShape->mBoxTranslation = Ogre::Vector3(0,0,0); mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; - mHasShape = false; - - btTriangleMesh* mesh1 = new btTriangleMesh(); + mCompoundShape = NULL; + mStaticMesh = NULL; // Load the NIF. TODO: Wrap this in a try-catch block once we're out // of the early stages of development. Right now we WANT to catch @@ -111,19 +96,35 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mShape->mHasCollisionNode = hasRootCollisionNode(node); //do a first pass - handleNode(mesh1, node,0,false,false,false); + handleNode(node,0,false,false,false); if(mBoundingBox != NULL) { mShape->mCollisionShape = mBoundingBox; - delete mesh1; + delete mStaticMesh; + if (mCompoundShape) + { + int n = mCompoundShape->getNumChildShapes(); + for(int i=0; i getChildShape(i)); + delete mCompoundShape; + } } - else if (mHasShape && mShape->mCollide) + else { - mShape->mCollisionShape = new TriangleMeshShape(mesh1,true); + if (mCompoundShape) + { + mShape->mCollisionShape = mCompoundShape; + if (mStaticMesh) + { + btTransform trans; + trans.setIdentity(); + mCompoundShape->addChildShape(trans, new TriangleMeshShape(mStaticMesh,true)); + } + } + else if (mStaticMesh) + mShape->mCollisionShape = new TriangleMeshShape(mStaticMesh,true); } - else - delete mesh1; //second pass which create a shape for raycasting. mResourceName = mShape->getName(); @@ -131,18 +132,23 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mBoundingBox = NULL; mShape->mBoxTranslation = Ogre::Vector3(0,0,0); mShape->mBoxRotation = Ogre::Quaternion::IDENTITY; - mHasShape = false; - - btTriangleMesh* mesh2 = new btTriangleMesh(); + mStaticMesh = NULL; + mCompoundShape = NULL; - handleNode(mesh2, node,0,true,true,false); + handleNode(node,0,true,true,false); - if (mHasShape) + if (mCompoundShape) { - mShape->mRaycastingShape = new TriangleMeshShape(mesh2,true); + mShape->mRaycastingShape = mCompoundShape; + if (mStaticMesh) + { + btTransform trans; + trans.setIdentity(); + mCompoundShape->addChildShape(trans, new TriangleMeshShape(mStaticMesh,true)); + } } - else - delete mesh2; + else if (mStaticMesh) + mShape->mRaycastingShape = new TriangleMeshShape(mStaticMesh,true); } bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) @@ -167,14 +173,17 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) return false; } -void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *node, int flags, +void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, bool isCollisionNode, - bool raycasting, bool isMarker) + bool raycasting, bool isMarker, bool isAnimated) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. flags |= node->flags; + if (!node->controller.empty() && node->controller->recType == Nif::RC_NiKeyframeController) + isAnimated = true; + if (!raycasting) isCollisionNode = isCollisionNode || (node->recType == Nif::RC_RootCollisionNode); else @@ -237,7 +246,7 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * else if(node->recType == Nif::RC_NiTriShape) { mShape->mCollide = !(flags&0x800); - handleNiTriShape(mesh, static_cast(node), flags, node->getWorldTransform(), raycasting); + handleNiTriShape(static_cast(node), flags, node->getWorldTransform(), raycasting, isAnimated); } } @@ -249,13 +258,13 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(mesh, list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker); + handleNode(list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker, isAnimated); } } } -void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, - bool raycasting) +void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, + bool raycasting, bool isAnimated) { assert(shape != NULL); @@ -278,17 +287,64 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif:: // bother setting it up. return; - mHasShape = true; + if (!shape->skin.empty()) + isAnimated = false; - const Nif::NiTriShapeData *data = shape->data.getPtr(); - const std::vector &vertices = data->vertices; - const short *triangles = &data->triangles[0]; - for(size_t i = 0;i < data->triangles.size();i+=3) + if (isAnimated) { - Ogre::Vector3 b1 = transform*vertices[triangles[i+0]]; - Ogre::Vector3 b2 = transform*vertices[triangles[i+1]]; - Ogre::Vector3 b3 = transform*vertices[triangles[i+2]]; - mesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); + if (!mCompoundShape) + mCompoundShape = new btCompoundShape(); + + btTriangleMesh* childMesh = new btTriangleMesh(); + + const Nif::NiTriShapeData *data = shape->data.getPtr(); + + childMesh->preallocateVertices(data->vertices.size()); + childMesh->preallocateIndices(data->triangles.size()); + + const std::vector &vertices = data->vertices; + const std::vector &triangles = data->triangles; + + for(size_t i = 0;i < data->triangles.size();i+=3) + { + Ogre::Vector3 b1 = vertices[triangles[i+0]]; + Ogre::Vector3 b2 = vertices[triangles[i+1]]; + Ogre::Vector3 b3 = vertices[triangles[i+2]]; + childMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); + } + + TriangleMeshShape* childShape = new TriangleMeshShape(childMesh,true); + + childShape->setLocalScaling(btVector3(transform[0][0], transform[1][1], transform[2][2])); + + Ogre::Quaternion q = transform.extractQuaternion(); + Ogre::Vector3 v = transform.getTrans(); + btTransform trans(btQuaternion(q.x, q.y, q.z, q.w), btVector3(v.x, v.y, v.z)); + + if (raycasting) + mShape->mAnimatedRaycastingShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes())); + else + mShape->mAnimatedShapes.insert(std::make_pair(shape->name, mCompoundShape->getNumChildShapes())); + + mCompoundShape->addChildShape(trans, childShape); + } + else + { + if (!mStaticMesh) + mStaticMesh = new btTriangleMesh(); + + // Static shape, just transform all vertices into position + const Nif::NiTriShapeData *data = shape->data.getPtr(); + const std::vector &vertices = data->vertices; + const std::vector &triangles = data->triangles; + + for(size_t i = 0;i < data->triangles.size();i+=3) + { + Ogre::Vector3 b1 = transform*vertices[triangles[i+0]]; + Ogre::Vector3 b2 = transform*vertices[triangles[i+1]]; + Ogre::Vector3 b3 = transform*vertices[triangles[i+2]]; + mStaticMesh->addTriangle(btVector3(b1.x,b1.y,b1.z),btVector3(b2.x,b2.y,b2.z),btVector3(b3.x,b3.y,b3.z)); + } } } diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index fb7d3d70a..a9ee968b9 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,22 @@ namespace Nif namespace NifBullet { +// Subclass btBhvTriangleMeshShape to auto-delete the meshInterface +struct TriangleMeshShape : public btBvhTriangleMeshShape +{ + TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression) + : btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression) + { + } + + virtual ~TriangleMeshShape() + { + delete getTriangleInfoMap(); + delete m_meshInterface; + } +}; + + /** *Load bulletShape from NIF files. */ @@ -52,8 +69,9 @@ class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader public: ManualBulletShapeLoader() : mShape(NULL) + , mStaticMesh(NULL) + , mCompoundShape(NULL) , mBoundingBox(NULL) - , mHasShape(false) { } @@ -88,7 +106,8 @@ private: /** *Parse a node. */ - void handleNode(btTriangleMesh* mesh, Nif::Node const *node, int flags, bool isCollisionNode, bool raycasting, bool isMarker); + void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, + bool raycasting, bool isMarker, bool isAnimated=false); /** *Helper function @@ -98,14 +117,17 @@ private: /** *convert a NiTriShape to a bullet trishape. */ - void handleNiTriShape(btTriangleMesh* mesh, const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting); + void handleNiTriShape(const Nif::NiTriShape *shape, int flags, const Ogre::Matrix4 &transform, bool raycasting, bool isAnimated); std::string mResourceName; OEngine::Physic::BulletShape* mShape;//current shape - btBoxShape *mBoundingBox; - bool mHasShape; + btCompoundShape* mCompoundShape; + + btTriangleMesh* mStaticMesh; + + btBoxShape *mBoundingBox; }; diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index 5528924a9..bcb2ba6b2 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -42,7 +42,7 @@ void BulletShape::deleteShape(btCollisionShape* shape) { if(shape->isCompound()) { - btCompoundShape* ms = static_cast(mCollisionShape); + btCompoundShape* ms = static_cast(shape); int a = ms->getNumChildShapes(); for(int i=0; i mAnimatedShapes; + + std::map mAnimatedRaycastingShapes; + btCollisionShape* mCollisionShape; btCollisionShape* mRaycastingShape; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 31cb67a58..469e33f25 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -11,6 +11,59 @@ #include #include +namespace +{ + +// Create a copy of the given collision shape (responsibility of user to delete the returned shape). +btCollisionShape *duplicateCollisionShape(btCollisionShape *shape) +{ + if(shape->isCompound()) + { + btCompoundShape *comp = static_cast(shape); + btCompoundShape *newShape = new btCompoundShape; + + int numShapes = comp->getNumChildShapes(); + for(int i = 0;i < numShapes;i++) + { + btCollisionShape *child = duplicateCollisionShape(comp->getChildShape(i)); + btTransform trans = comp->getChildTransform(i); + newShape->addChildShape(trans, child); + } + + return newShape; + } + + if(btBvhTriangleMeshShape *trishape = dynamic_cast(shape)) + { + btTriangleMesh* oldMesh = dynamic_cast(trishape->getMeshInterface()); + btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh); + NifBullet::TriangleMeshShape *newShape = new NifBullet::TriangleMeshShape(newMesh, true); + + return newShape; + } + + throw std::logic_error(std::string("Unhandled Bullet shape duplication: ")+shape->getName()); +} + +void deleteShape(btCollisionShape* shape) +{ + if(shape!=NULL) + { + if(shape->isCompound()) + { + btCompoundShape* ms = static_cast(shape); + int a = ms->getNumChildShapes(); + for(int i=0; i getChildShape(i)); + } + } + delete shape; + } +} + +} + namespace OEngine { namespace Physic { @@ -228,6 +281,11 @@ namespace Physic PhysicEngine::~PhysicEngine() { + for (std::map::iterator it = mAnimatedShapes.begin(); it != mAnimatedShapes.end(); ++it) + deleteShape(it->second.mCompound); + for (std::map::iterator it = mAnimatedRaycastingShapes.begin(); it != mAnimatedRaycastingShapes.end(); ++it) + deleteShape(it->second.mCompound); + HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); for (; hf_it != mHeightFieldMap.end(); ++hf_it) { @@ -386,17 +444,38 @@ namespace Physic if (!shape->mRaycastingShape && raycasting) return NULL; - if (!raycasting) - shape->mCollisionShape->setLocalScaling( btVector3(scale,scale,scale)); - else - shape->mRaycastingShape->setLocalScaling( btVector3(scale,scale,scale)); + btCollisionShape* collisionShape = raycasting ? shape->mRaycastingShape : shape->mCollisionShape; + + // If this is an animated compound shape, we must duplicate it so we can animate + // multiple instances independently. + if (!raycasting && !shape->mAnimatedShapes.empty()) + collisionShape = duplicateCollisionShape(collisionShape); + if (raycasting && !shape->mAnimatedRaycastingShapes.empty()) + collisionShape = duplicateCollisionShape(collisionShape); + + collisionShape->setLocalScaling( btVector3(scale,scale,scale)); //create the real body btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo - (0,0, raycasting ? shape->mRaycastingShape : shape->mCollisionShape); + (0,0, collisionShape); RigidBody* body = new RigidBody(CI,name); body->mPlaceable = placeable; + if (!raycasting && !shape->mAnimatedShapes.empty()) + { + AnimatedShapeInstance instance; + instance.mAnimatedShapes = shape->mAnimatedShapes; + instance.mCompound = collisionShape; + mAnimatedShapes[body] = instance; + } + if (raycasting && !shape->mAnimatedRaycastingShapes.empty()) + { + AnimatedShapeInstance instance; + instance.mAnimatedShapes = shape->mAnimatedRaycastingShapes; + instance.mCompound = collisionShape; + mAnimatedRaycastingShapes[body] = instance; + } + if(scaledBoxTranslation != 0) *scaledBoxTranslation = shape->mBoxTranslation * scale; if(boxRotation != 0) @@ -404,33 +483,20 @@ namespace Physic adjustRigidBody(body, position, rotation, shape->mBoxTranslation * scale, shape->mBoxRotation); - return body; - - } - - void PhysicEngine::addRigidBody(RigidBody* body, bool addToMap, RigidBody* raycastingBody) - { - if(!body && !raycastingBody) - return; // nothing to do - - const std::string& name = (body ? body->mName : raycastingBody->mName); - - if (body){ + if (!raycasting) + { + assert (mCollisionObjectMap.find(name) == mCollisionObjectMap.end()); + mCollisionObjectMap[name] = body; mDynamicsWorld->addRigidBody(body,CollisionType_World,CollisionType_World|CollisionType_Actor|CollisionType_HeightMap); } - - if (raycastingBody) - mDynamicsWorld->addRigidBody(raycastingBody,CollisionType_Raycasting,CollisionType_Raycasting); - - if(addToMap){ - removeRigidBody(name); - deleteRigidBody(name); - - if (body) - mCollisionObjectMap[name] = body; - if (raycastingBody) - mRaycastingObjectMap[name] = raycastingBody; + else + { + assert (mRaycastingObjectMap.find(name) == mRaycastingObjectMap.end()); + mRaycastingObjectMap[name] = body; + mDynamicsWorld->addRigidBody(body,CollisionType_Raycasting,CollisionType_Raycasting); } + + return body; } void PhysicEngine::removeRigidBody(const std::string &name) @@ -464,6 +530,10 @@ namespace Physic if(body != NULL) { + if (mAnimatedShapes.find(body) != mAnimatedShapes.end()) + deleteShape(mAnimatedShapes[body].mCompound); + mAnimatedShapes.erase(body); + delete body; } mCollisionObjectMap.erase(it); @@ -475,6 +545,10 @@ namespace Physic if(body != NULL) { + if (mAnimatedRaycastingShapes.find(body) != mAnimatedRaycastingShapes.end()) + deleteShape(mAnimatedRaycastingShapes[body].mCompound); + mAnimatedRaycastingShapes.erase(body); + delete body; } mRaycastingObjectMap.erase(it); @@ -609,7 +683,6 @@ namespace Physic return std::make_pair(callback.mObject, callback.mContactPoint); } - void PhysicEngine::stepSimulation(double deltaT) { // This seems to be needed for character controller objects @@ -629,8 +702,6 @@ namespace Physic PhysicActor* newActor = new PhysicActor(name, mesh, this, position, rotation, scale); - - //dynamicsWorld->addAction( newActor->mCharacter ); mActorMap[name] = newActor; } @@ -642,7 +713,6 @@ namespace Physic PhysicActor* act = it->second; if(act != NULL) { - delete act; } mActorMap.erase(it); diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index deef21443..e37caee38 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -117,7 +117,7 @@ namespace Physic const Ogre::Vector3& getPosition() const; /** - * Returns the half extents for this PhysiActor + * Returns the (scaled) half extents */ Ogre::Vector3 getHalfExtents() const; @@ -178,6 +178,14 @@ namespace Physic RigidBody* mBody; }; + struct AnimatedShapeInstance + { + btCollisionShape* mCompound; + + // Maps bone name to child index in the compound shape + std::map mAnimatedShapes; + }; + /** * The PhysicEngine class contain everything which is needed for Physic. * It's needed that Ogre Resources are set up before the PhysicEngine is created. @@ -228,11 +236,6 @@ namespace Physic */ void removeHeightField(int x, int y); - /** - * Add a RigidBody to the simulation - */ - void addRigidBody(RigidBody* body, bool addToMap = true, RigidBody* raycastingBody = NULL); - /** * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. */ @@ -335,8 +338,14 @@ namespace Physic typedef std::map RigidBodyContainer; RigidBodyContainer mCollisionObjectMap; + // Compound shapes that must be animated each frame based on bone positions + // the index refers to an element in mCollisionObjectMap + std::map mAnimatedShapes; + RigidBodyContainer mRaycastingObjectMap; + std::map mAnimatedRaycastingShapes; + typedef std::map PhysicActorContainer; PhysicActorContainer mActorMap; From 320ab1b2c1572b20fe193aff921a63524275e837 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jun 2014 21:59:13 +0200 Subject: [PATCH 20/35] Don't say voice when using startcombat for a dead actor (Fixes #1542) --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 0ad0b4d5a..652f5363e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1112,7 +1112,7 @@ namespace MWMechanics } // Must be done after the target is set up, so that CreatureTargetted dialogue filter works properly - if (ptr.getClass().isNpc()) + if (ptr.getClass().isNpc() && !ptr.getClass().getCreatureStats(ptr).isDead()) MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); } From 750d8b55892c0efa495cb0d99d95b9442c1896b0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Jun 2014 22:02:22 +0200 Subject: [PATCH 21/35] Allow garbage integer argument for addSpell (Fixes #1539) --- components/compiler/exprparser.cpp | 19 +++++++++++-------- components/compiler/extensions.hpp | 3 ++- components/compiler/extensions0.cpp | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index ed628278b..e54b2e2a8 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -775,7 +775,7 @@ namespace Compiler { parser.reset(); - if (optional) + if (optional || *iter == 'X') parser.setOptional (true); scanner.scan (parser); @@ -783,17 +783,20 @@ namespace Compiler if (optional && parser.isEmpty()) break; - std::vector tmp; + if (*iter != 'X') + { + std::vector tmp; - char type = parser.append (tmp); + char type = parser.append (tmp); - if (type!=*iter) - Generator::convert (tmp, type, *iter); + if (type!=*iter) + Generator::convert (tmp, type, *iter); - stack.push (tmp); + stack.push (tmp); - if (optional) - ++optionalCount; + if (optional) + ++optionalCount; + } } } diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index 418cbb402..d229751de 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -20,7 +20,8 @@ namespace Compiler l - Integer
s - Short
S - String, case preserved
- x - Optional, ignored argument + x - Optional, ignored string argument + X - Optional, ignored integer argument **/ typedef std::string ScriptArgs; diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 9fc66900f..f38c3320c 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -405,7 +405,7 @@ namespace Compiler extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); - extensions.registerInstruction ("addspell", "cx", opcodeAddSpell, opcodeAddSpellExplicit); + extensions.registerInstruction ("addspell", "cxX", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, From 1d46ac19ff0e488c03be0a3cbd68e9d19c94a736 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 02:24:32 +0200 Subject: [PATCH 22/35] Fix being able to activate through terrain --- apps/openmw/mwworld/worldimp.cpp | 12 +++++++++--- libs/openengine/bullet/physic.cpp | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b4957abfd..c4e4e3d3a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1471,8 +1471,13 @@ namespace MWWorld std::vector < std::pair < float, std::string > >::iterator it = results.begin(); while (it != results.end()) { - if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain - || getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) + if ((*it).second.find("HeightField") != std::string::npos) // Don't attempt to getPtrViaHandle on terrain + { + ++it; + continue; + } + + if (getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) { it = results.erase(it); } @@ -1480,7 +1485,8 @@ namespace MWWorld ++it; } - if (results.empty()) + if (results.empty() + || results.front().second.find("HeightField") != std::string::npos) // Blocked by terrain { mFacedHandle = ""; mFacedDistance = FLT_MAX; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 469e33f25..d00441fdf 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -805,7 +805,7 @@ namespace Physic { MyRayResultCallback resultCallback1; resultCallback1.m_collisionFilterGroup = 0xff; - resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor; + resultCallback1.m_collisionFilterMask = CollisionType_Raycasting|CollisionType_Actor|CollisionType_HeightMap; mDynamicsWorld->rayTest(from, to, resultCallback1); std::vector< std::pair > results = resultCallback1.results; From 30be59c029307937758c75a2a1ba98d1844950d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 03:07:10 +0200 Subject: [PATCH 23/35] Implement auto-adjusting of particle emit rate This makes ashcloud.nif and blightcloud.nif work properly. --- components/nif/controller.hpp | 9 ++++++++- components/nifogre/ogrenifloader.cpp | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index e44f4a6f3..920e634e6 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -89,7 +89,14 @@ public: float lifetime; float lifetimeRandom; - int emitFlags; // Bit 0: Emit Rate toggle bit (0 = auto adjust, 1 = use Emit Rate value) + enum EmitFlags + { + NoAutoAdjust = 0x1 // If this flag is set, we use the emitRate value. Otherwise, + // we calculate an emit rate so that the maximum number of particles + // in the system (numParticles) is never exceeded. + }; + int emitFlags; + Ogre::Vector3 offsetRandom; NodePtr emitter; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 813c1660d..eed320756 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -756,7 +756,12 @@ class NIFObjectLoader Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, partctrl->velocity + partctrl->velocityRandom*0.5f); - emitter->setEmissionRate(partctrl->emitRate); + + if (partctrl->emitFlags & Nif::NiParticleSystemController::NoAutoAdjust) + emitter->setEmissionRate(partctrl->emitRate); + else + emitter->setEmissionRate(partctrl->numParticles / (partctrl->lifetime + partctrl->lifetimeRandom/2)); + emitter->setTimeToLive(partctrl->lifetime, partctrl->lifetime + partctrl->lifetimeRandom); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); From 36132e054afa8e85c5bd2ec4517908c810e580b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 15:00:15 +0200 Subject: [PATCH 24/35] Implement ashstorm, blightstorm, snow and blizzard effects (Feature #41) --- apps/openmw/mwrender/sky.cpp | 41 ++++++++++++++++++++++++++++++++- apps/openmw/mwrender/sky.hpp | 5 ++++ apps/openmw/mwworld/weather.cpp | 23 ++++++++++-------- apps/openmw/mwworld/weather.hpp | 7 +++++- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 90c08c299..545d34bdf 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -8,10 +8,11 @@ #include #include #include -#include +#include #include #include #include +#include #include @@ -234,6 +235,7 @@ SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) , mCreated(false) , mCloudAnimationTimer(0.f) , mMoonRed(false) + , mParticleNode(NULL) { mSceneMgr = root->getCreator(); mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); @@ -372,6 +374,12 @@ void SkyManager::update(float duration) if (!mEnabled) return; const MWWorld::Fallback* fallback=MWBase::Environment::get().getWorld()->getFallback(); + if (!mParticle.isNull()) + { + for (unsigned int i=0; imControllers.size(); ++i) + mParticle->mControllers[i].update(); + } + // UV Scroll the clouds mCloudAnimationTimer += duration * mCloudSpeed; sh::Factory::getInstance().setSharedParameter ("cloudAnimationTimer", @@ -441,6 +449,37 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mCreated) return; + if (mCurrentParticleEffect != weather.mParticleEffect) + { + mCurrentParticleEffect = weather.mParticleEffect; + + if (mCurrentParticleEffect.empty()) + { + mParticle.setNull(); + } + else + { + if (!mParticleNode) + { + mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(); + mParticleNode->setInheritOrientation(false); + } + + mParticle = NifOgre::Loader::createObjects(mParticleNode, mCurrentParticleEffect); + for(size_t i = 0; i < mParticle->mParticles.size(); ++i) + { + ParticleSystem* particle = mParticle->mParticles[i]; + particle->setRenderQueueGroup(RQG_Alpha); + particle->setVisibilityFlags(RV_Sky); + } + for (size_t i = 0; i < mParticle->mControllers.size(); ++i) + { + if (mParticle->mControllers[i].getSource().isNull()) + mParticle->mControllers[i].setSource(Ogre::ControllerManager::getSingleton().getFrameTimeSource()); + } + } + } + if (mClouds != weather.mCloudTexture) { sh::Factory::getInstance().setTextureAlias ("cloud_texture_1", "textures\\"+weather.mCloudTexture); diff --git a/apps/openmw/mwrender/sky.hpp b/apps/openmw/mwrender/sky.hpp index 965907a97..07edc461e 100644 --- a/apps/openmw/mwrender/sky.hpp +++ b/apps/openmw/mwrender/sky.hpp @@ -200,6 +200,9 @@ namespace MWRender std::vector mObjects; + Ogre::SceneNode* mParticleNode; + NifOgre::ObjectScenePtr mParticle; + // remember some settings so we don't have to apply them again if they didnt change Ogre::String mClouds; Ogre::String mNextClouds; @@ -211,6 +214,8 @@ namespace MWRender Ogre::ColourValue mSkyColour; Ogre::ColourValue mFogColour; + std::string mCurrentParticleEffect; + Ogre::Light* mLightning; float mRemainingTransitionTime; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 25f523bee..ab5ec0004 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -58,6 +58,11 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name weather.mWindSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Wind_Speed"); weather.mCloudSpeed = mFallback->getFallbackFloat("Weather_"+upper+"_Cloud_Speed"); weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); + weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture"); + size_t offset = weather.mCloudTexture.find(".tga"); + if (offset != std::string::npos) + weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds"); + mWeatherSettings[name] = weather; } @@ -123,48 +128,42 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa //Weather Weather clear; - clear.mCloudTexture = "tx_sky_clear.dds"; setFallbackWeather(clear,"clear"); Weather cloudy; - cloudy.mCloudTexture = "tx_sky_cloudy.dds"; setFallbackWeather(cloudy,"cloudy"); Weather foggy; - foggy.mCloudTexture = "tx_sky_foggy.dds"; setFallbackWeather(foggy,"foggy"); Weather thunderstorm; - thunderstorm.mCloudTexture = "tx_sky_thunder.dds"; thunderstorm.mRainLoopSoundID = "rain heavy"; setFallbackWeather(thunderstorm,"thunderstorm"); Weather rain; - rain.mCloudTexture = "tx_sky_rainy.dds"; rain.mRainLoopSoundID = "rain"; setFallbackWeather(rain,"rain"); Weather overcast; - overcast.mCloudTexture = "tx_sky_overcast.dds"; setFallbackWeather(overcast,"overcast"); Weather ashstorm; - ashstorm.mCloudTexture = "tx_sky_ashstorm.dds"; ashstorm.mAmbientLoopSoundID = "ashstorm"; + ashstorm.mParticleEffect = "meshes\\ashcloud.nif"; setFallbackWeather(ashstorm,"ashstorm"); Weather blight; - blight.mCloudTexture = "tx_sky_blight.dds"; blight.mAmbientLoopSoundID = "blight"; + blight.mParticleEffect = "meshes\\blightcloud.nif"; setFallbackWeather(blight,"blight"); Weather snow; - snow.mCloudTexture = "tx_bm_sky_snow.dds"; + snow.mParticleEffect = "meshes\\snow.nif"; setFallbackWeather(snow, "snow"); Weather blizzard; - blizzard.mCloudTexture = "tx_bm_sky_blizzard.dds"; blizzard.mAmbientLoopSoundID = "BM Blizzard"; + blizzard.mParticleEffect = "meshes\\blizzard.nif"; setFallbackWeather(blizzard,"blizzard"); } @@ -214,6 +213,8 @@ void WeatherManager::setResult(const String& weatherType) mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mSunColor = current.mSunDiscSunsetColor; + mResult.mParticleEffect = current.mParticleEffect; + mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); mResult.mFogDepth = mResult.mNight ? current.mLandFogNightDepth : current.mLandFogDayDepth; @@ -316,6 +317,8 @@ void WeatherManager::transition(float factor) mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor); mResult.mNight = current.mNight; + + mResult.mParticleEffect = current.mParticleEffect; } void WeatherManager::update(float duration) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 3e9df504b..4d0458249 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -58,6 +58,8 @@ namespace MWWorld float mNightFade; // fading factor for night skybox std::string mAmbientLoopSoundID; + + std::string mParticleEffect; }; @@ -119,7 +121,10 @@ namespace MWWorld // Rain sound effect std::string mRainLoopSoundID; - /// \todo disease chance + std::string mParticleEffect; + + // Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature + // is broken in the vanilla game and was disabled. }; /// From b52977e44c1685f8763b35cc64ff9fe525d9899a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 15:09:13 +0200 Subject: [PATCH 25/35] Add dummy Face implementation for now (Bug #1541) --- apps/openmw/mwscript/aiextensions.cpp | 13 +++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 4 +++- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 2 ++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 9a3387a00..d844138d6 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -477,6 +477,16 @@ namespace MWScript } }; + template + class OpFace : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime& runtime) + { + /// \todo implement + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment3 (Compiler::Ai::opcodeAIActivate, new OpAiActivate); @@ -538,6 +548,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Ai::opcodeGetFleeExplicit, new OpGetAiSetting(2)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarm, new OpGetAiSetting(3)); interpreter.installSegment5 (Compiler::Ai::opcodeGetAlarmExplicit, new OpGetAiSetting(3)); + + interpreter.installSegment5 (Compiler::Ai::opcodeFace, new OpFace); + interpreter.installSegment5 (Compiler::Ai::opcodeFaceExplicit, new OpFace); } } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 4b2a91a95..319feee0e 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -401,5 +401,7 @@ op 0x2000248: BetaComment, explicit op 0x2000249: OnMurder op 0x200024a: OnMurder, explicit op 0x200024b: ToggleMenus +op 0x200024c: Face +op 0x200024d: Face, explicit -opcodes 0x200024c-0x3ffffff unused +opcodes 0x200024e-0x3ffffff unused diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index f38c3320c..4cd77cf1d 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -70,6 +70,7 @@ namespace Compiler extensions.registerFunction ("getlineofsight", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction ("getlos", 'l', "c", opcodeGetLineOfSight, opcodeGetLineOfSightExplicit); extensions.registerFunction("gettarget", 'l', "c", opcodeGetTarget, opcodeGetTargetExplicit); + extensions.registerInstruction("face", "llX", opcodeFace, opcodeFaceExplicit); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index b097a017b..85ac578f3 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -59,6 +59,8 @@ namespace Compiler const int opcodeStartCombatExplicit = 0x200023b; const int opcodeStopCombat = 0x200023c; const int opcodeStopCombatExplicit = 0x200023d; + const int opcodeFace = 0x200024c; + const int opcodeFaceExplicit = 0x200024d; } namespace Animation From 36135293e887ab412acdc7686e71d1f6e61b6ecf Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 15:29:36 +0200 Subject: [PATCH 26/35] Fix moving object from an inactive to another inactive cell --- apps/openmw/mwworld/worldimp.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c4e4e3d3a..30024d310 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -967,7 +967,7 @@ namespace MWWorld Ogre::Vector3 vec(x, y, z); - CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; + CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); @@ -989,7 +989,9 @@ namespace MWWorld } else { - if (!mWorldScene->isCellActive(*currCell) && mWorldScene->isCellActive(*newCell)) + bool currCellActive = mWorldScene->isCellActive(*currCell); + bool newCellActive = mWorldScene->isCellActive(*newCell); + if (!currCellActive && newCellActive) { MWWorld::Ptr newPtr = ptr.getClass().copyToCell(ptr, *newCell, pos); mWorldScene->addObjectToScene(newPtr); @@ -1000,7 +1002,7 @@ namespace MWWorld } addContainerScripts(newPtr, newCell); } - else if (!mWorldScene->isCellActive(*newCell) && mWorldScene->isCellActive(*currCell)) + else if (!newCellActive && currCellActive) { mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); @@ -1011,7 +1013,9 @@ namespace MWWorld .copyToCell(ptr, *newCell); newPtr.getRefData().setBaseNode(0); } - else + else if (!currCellActive && !newCellActive) + ptr.getClass().copyToCell(ptr, *newCell); + else // both cells active { MWWorld::Ptr copy = ptr.getClass().copyToCell(ptr, *newCell, pos); From 1c157b86f6405a90d9a392bef372972ed0d449ba Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 18:14:27 +0200 Subject: [PATCH 27/35] Fix dead bodies blocking hits (again) --- libs/openengine/bullet/physic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index d00441fdf..31530ebad 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -677,7 +677,7 @@ namespace Physic btCollisionObject *object) { DeepestNotMeContactTestResultCallback callback(filter, origin); - callback.m_collisionFilterGroup = 0xff; + callback.m_collisionFilterGroup = CollisionType_Actor; callback.m_collisionFilterMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor; mDynamicsWorld->contactTest(object, callback); return std::make_pair(callback.mObject, callback.mContactPoint); From 693a097b21fef5f89b0601cc2f36a988c9d49248 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 18:37:38 +0200 Subject: [PATCH 28/35] Implement idlestorm animation (Feature #41) --- apps/openmw/mwbase/world.hpp | 6 ++++ apps/openmw/mwmechanics/character.cpp | 42 +++++++++++++++++++++++++++ apps/openmw/mwmechanics/character.hpp | 1 + apps/openmw/mwrender/sky.cpp | 2 +- apps/openmw/mwworld/refdata.hpp | 2 +- apps/openmw/mwworld/weather.cpp | 15 ++++++++++ apps/openmw/mwworld/weather.hpp | 15 +++++++++- apps/openmw/mwworld/worldimp.cpp | 17 +++++++++++ apps/openmw/mwworld/worldimp.hpp | 6 ++++ 9 files changed, 103 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f42edafd4..311d072c5 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -521,6 +521,12 @@ namespace MWBase const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; + + /// @see MWWorld::WeatherManager::isInStorm + virtual bool isInStorm() const = 0; + + /// @see MWWorld::WeatherManager::getStormDirection + virtual Ogre::Vector3 getStormDirection() const = 0; }; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ddfba51ce..81183da3c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -20,6 +20,7 @@ #include "character.hpp" #include +#include #include "movement.hpp" #include "npcstats.hpp" @@ -234,6 +235,8 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat 1.0f, "start", "stop", 0.0f, ~0ul); } + updateIdleStormState(); + if(force && mJumpState != JumpState_None) { std::string jump; @@ -550,6 +553,45 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) mPtr = ptr; } +void CharacterController::updateIdleStormState() +{ + bool inStormDirection = false; + if (MWBase::Environment::get().getWorld()->isInStorm()) + { + Ogre::Vector3 stormDirection = MWBase::Environment::get().getWorld()->getStormDirection(); + Ogre::Vector3 characterDirection = mPtr.getRefData().getBaseNode()->getOrientation().yAxis(); + inStormDirection = stormDirection.angleBetween(characterDirection) < Ogre::Degree(40); + } + if (inStormDirection && mUpperBodyState == UpperCharState_Nothing && mAnimation->hasAnimation("idlestorm")) + { + float complete = 0; + mAnimation->getInfo("idlestorm", &complete); + + if (complete == 0) + mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, false, + 1.0f, "start", "loop start", 0.0f, 0); + else if (complete == 1) + mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, false, + 1.0f, "loop start", "loop stop", 0.0f, ~0ul); + } + else + { + if (mUpperBodyState == UpperCharState_Nothing) + { + if (mAnimation->isPlaying("idlestorm")) + { + if (mAnimation->getCurrentTime("idlestorm") < mAnimation->getTextKeyTime("idlestorm: loop stop")) + { + mAnimation->play("idlestorm", Priority_Torch, MWRender::Animation::Group_RightArm, true, + 1.0f, "loop stop", "stop", 0.0f, 0); + } + } + } + else + mAnimation->disable("idlestorm"); + } +} + bool CharacterController::updateCreatureState() { const MWWorld::Class &cls = mPtr.getClass(); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 5cefe13bc..59c20db8f 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -178,6 +178,7 @@ class CharacterController bool updateWeaponState(); bool updateCreatureState(); + void updateIdleStormState(); void updateVisibility(); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 545d34bdf..000a1d409 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -461,7 +461,7 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) { if (!mParticleNode) { - mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(); + mParticleNode = mCamera->getParentSceneNode()->createChildSceneNode(Ogre::Vector3(0,0,100)); mParticleNode->setInheritOrientation(false); } diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 753230e7f..db66c091b 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -54,7 +54,7 @@ namespace MWWorld RefData(); /// @param cellRef Used to copy constant data such as position into this class where it can - /// be altered without effecting the original data. This makes it possible + /// be altered without affecting the original data. This makes it possible /// to reset the position as the orignal data is still held in the CellRef RefData (const ESM::CellRef& cellRef); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index ab5ec0004..df78711ad 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -63,6 +63,8 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name if (offset != std::string::npos) weather.mCloudTexture.replace(offset, weather.mCloudTexture.length() - offset, ".dds"); + weather.mIsStorm = (name == "ashstorm" || name == "blight"); + mWeatherSettings[name] = weather; } @@ -213,6 +215,8 @@ void WeatherManager::setResult(const String& weatherType) mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID; mResult.mSunColor = current.mSunDiscSunsetColor; + mResult.mIsStorm = current.mIsStorm; + mResult.mParticleEffect = current.mParticleEffect; mResult.mNight = (mHour < mSunriseTime || mHour > mNightStart - 1); @@ -318,6 +322,7 @@ void WeatherManager::transition(float factor) mResult.mNight = current.mNight; + mResult.mIsStorm = current.mIsStorm; mResult.mParticleEffect = current.mParticleEffect; } @@ -771,3 +776,13 @@ void WeatherManager::switchToNextWeather(bool instantly) } } } + +bool WeatherManager::isInStorm() const +{ + return mResult.mIsStorm; +} + +Ogre::Vector3 WeatherManager::getStormDirection() const +{ + return Ogre::Vector3(0,-1,0); +} diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 4d0458249..8aa3d2635 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -57,6 +57,8 @@ namespace MWWorld bool mNight; // use night skybox float mNightFade; // fading factor for night skybox + bool mIsStorm; + std::string mAmbientLoopSoundID; std::string mParticleEffect; @@ -102,7 +104,7 @@ namespace MWWorld // Duration of weather transition (in days) float mTransitionDelta; - // No idea what this one is used for? + // Used by scripts to animate signs, etc based on the wind (GetWindSpeed) float mWindSpeed; // Cloud animation speed multiplier @@ -121,6 +123,12 @@ namespace MWWorld // Rain sound effect std::string mRainLoopSoundID; + // Is this an ash storm / blight storm? This controls two things: + // - The particle node will be oriented so that the particles appear to come from the Red Mountain. (not implemented yet) + // - Characters will animate their hand to protect eyes from the storm when looking in its direction (idlestorm animation) + // Possible effect on movement speed? + bool mIsStorm; + std::string mParticleEffect; // Note: For Weather Blight, there is a "Disease Chance" (=0.1) setting. But according to MWSFD this feature @@ -156,6 +164,11 @@ namespace MWWorld float getWindSpeed() const; + /// Are we in an ash or blight storm? + bool isInStorm() const; + + Ogre::Vector3 getStormDirection() const; + void advanceTime(double hours) { mTimePassed += hours*3600; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 30024d310..35042b3ff 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1998,6 +1998,22 @@ namespace MWWorld return 0.f; } + bool World::isInStorm() const + { + if (isCellExterior() || isCellQuasiExterior()) + return mWeatherManager->isInStorm(); + else + return false; + } + + Ogre::Vector3 World::getStormDirection() const + { + if (isCellExterior() || isCellQuasiExterior()) + return mWeatherManager->getStormDirection(); + else + return Ogre::Vector3(0,1,0); + } + void World::getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector& out) { const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); @@ -2313,6 +2329,7 @@ namespace MWWorld { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + // TODO: this only works for the player MWWorld::Ptr target = getFacedObject(); std::string selectedSpell = stats.getSpells().getSelectedSpell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8cb2ac18a..69b72b533 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -591,6 +591,12 @@ namespace MWWorld const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); + + /// @see MWWorld::WeatherManager::isInStorm + virtual bool isInStorm() const; + + /// @see MWWorld::WeatherManager::getStormDirection + virtual Ogre::Vector3 getStormDirection() const; }; } From e2743145485c125297aaa1d26078375f7b9b5ad6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 24 Jun 2014 19:51:30 +0200 Subject: [PATCH 29/35] Ignore alpha modifier for particle materials This makes the tx_ash_flake.dds particles from ashcloud.nif appear. --- components/nifogre/material.cpp | 6 ++++-- components/nifogre/material.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index b1ae83107..cc06d65f3 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -106,7 +106,7 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents, bool disableLighting) + bool &needTangents, bool particleMaterial) { Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialPtr material = matMgr.getByName(name); @@ -245,8 +245,10 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, } } - if (disableLighting) + if (particleMaterial) { + alpha = 1.f; // Apparently ignored, might be overridden by particle vertex colors? + ambient = Ogre::Vector3(0.f); diffuse = Ogre::Vector3(0.f); specular = Ogre::Vector3(0.f); diff --git a/components/nifogre/material.hpp b/components/nifogre/material.hpp index 890255dcc..abe1982eb 100644 --- a/components/nifogre/material.hpp +++ b/components/nifogre/material.hpp @@ -49,7 +49,7 @@ public: const Nif::NiZBufferProperty *zprop, const Nif::NiSpecularProperty *specprop, const Nif::NiWireframeProperty *wireprop, - bool &needTangents, bool disableLighting=false); + bool &needTangents, bool particleMaterial=false); }; } From 8a8ecce1e550efe9c94696b881024fd6ee10a57e Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 00:11:25 +0200 Subject: [PATCH 30/35] Fix some uninitialized data written to savegames --- apps/openmw/mwstate/statemanagerimp.cpp | 7 +++++++ components/esm/cellref.cpp | 9 +++++---- components/esm/esmwriter.cpp | 5 +++++ components/esm/esmwriter.hpp | 5 +++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a459c87bb..a3604cc66 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -201,6 +201,13 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 writer.setFormat (ESM::Header::CurrentFormat); + + // all unused + writer.setVersion(0); + writer.setType(0); + writer.setAuthor(""); + writer.setDescription(""); + int recordCount = 1 // saved game header +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index d785fb2b6..84c638d9c 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -108,13 +108,13 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons esm.writeHNT("NAM9", mGoldValue); } - if (mTeleport && !inInventory) + if (!inInventory && mTeleport) { esm.writeHNT("DODT", mDoorDest); esm.writeHNOCString("DNAM", mDestCell); } - if (mLockLevel != 0 && !inInventory) { + if (!inInventory && mLockLevel != 0) { esm.writeHNT("FLTV", mLockLevel); } @@ -127,13 +127,13 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mReferenceBlocked != -1) esm.writeHNT("UNAM", mReferenceBlocked); - if (mFltv != 0 && !inInventory) + if (!inInventory && mFltv != 0) esm.writeHNT("FLTV", mFltv); if (!inInventory) esm.writeHNT("DATA", mPos, 24); - if (mNam0 != 0 && !inInventory) + if (!inInventory && mNam0 != 0) esm.writeHNT("NAM0", mNam0); } @@ -158,6 +158,7 @@ void ESM::CellRef::blank() mReferenceBlocked = 0; mFltv = 0; mNam0 = 0; + mTeleport = false; for (int i=0; i<3; ++i) { diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 9d8d943d9..06572ce8f 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -18,6 +18,11 @@ namespace ESM mHeader.mData.version = ver; } + void ESMWriter::setType(int type) + { + mHeader.mData.type = type; + } + void ESMWriter::setAuthor(const std::string& auth) { mHeader.mData.author.assign (auth); diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index ca4f42217..e57c6e45d 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -25,10 +25,15 @@ class ESMWriter ESMWriter(); unsigned int getVersion() const; + + // Set various header data (ESM::Header::Data). All of the below functions must be called before writing, + // otherwise this data will be left uninitialized. void setVersion(unsigned int ver = 0x3fa66666); + void setType(int type); void setEncoder(ToUTF8::Utf8Encoder *encoding); void setAuthor(const std::string& author); void setDescription(const std::string& desc); + // Set the record count for writing it in the file header void setRecordCount (int count); // Counts how many records we have actually written. From 7f1d0fc2a2ef53c82e8696634217a742ef2f6454 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 00:11:51 +0200 Subject: [PATCH 31/35] Fix disposition for npcs in same faction, by Hrnchamd --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 652f5363e..6e515142d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -501,7 +501,8 @@ namespace MWMechanics { if (!playerStats.getExpelled(npcFaction)) { - reaction = playerStats.getFactionReputation(npcFaction); + // faction reaction towards itself. yes, that exists + reaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, npcFaction); rank = playerStats.getFactionRanks().find(npcFaction)->second; } From 3d9bdad8ba55fb525f918b540dcbc11d847a2558 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 00:51:02 +0200 Subject: [PATCH 32/35] Correct run speed for creatures (by Hrnchamd - Fixes #1136) --- apps/openmw/mwclass/creature.cpp | 4 +-- apps/openmw/mwmechanics/character.cpp | 50 ++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 1f286f7e6..feb6a2714 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -537,8 +537,8 @@ namespace MWClass bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); - float runSpeed = walkSpeed*6; - runSpeed = std::min(gmst.fMaxWalkSpeedCreature->getFloat(), runSpeed); // flame atronach runs way too fast without this + // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp) + float runSpeed = walkSpeed; float moveSpeed; if(normalizedEncumbrance >= 1.0f) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 81183da3c..5f8013b14 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -55,6 +55,43 @@ std::string getBestAttack (const ESM::Weapon* weapon) return "thrust"; } +// Converts a movement Run state to its equivalent Walk state. +MWMechanics::CharacterState runStateToWalkState (MWMechanics::CharacterState state) +{ + using namespace MWMechanics; + CharacterState ret = state; + switch (state) + { + case CharState_RunForward: + ret = CharState_WalkForward; + break; + case CharState_RunBack: + ret = CharState_WalkBack; + break; + case CharState_RunLeft: + ret = CharState_WalkLeft; + break; + case CharState_RunRight: + ret = CharState_WalkRight; + break; + case CharState_SwimRunForward: + ret = CharState_SwimWalkForward; + break; + case CharState_SwimRunBack: + ret = CharState_SwimWalkBack; + break; + case CharState_SwimRunLeft: + ret = CharState_SwimWalkLeft; + break; + case CharState_SwimRunRight: + ret = CharState_SwimWalkRight; + break; + default: + break; + } + return ret; +} + } namespace MWMechanics @@ -323,7 +360,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); - if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + // For non-flying creatures, MW uses the Walk animation to calculate the animation velocity + // even if we are running. This must be replicated, otherwise the observed speed would differ drastically. + std::string anim = mCurrentMovement; + if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() + && !(mPtr.get()->mBase->mFlags & ESM::Creature::Flies)) + { + CharacterState walkState = runStateToWalkState(mMovementState); + const StateInfo *stateinfo = std::find_if(sMovementList, sMovementListEnd, FindCharState(walkState)); + anim = stateinfo->groupname; + } + + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f) { mMovementAnimVelocity = vel; speedmult = mMovementSpeed / vel; From b259c5def26cc371b13fdb048591f3a257981f59 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 01:12:57 +0200 Subject: [PATCH 33/35] Make GetFactionReaction garbage argument optional --- apps/openmw/mwscript/dialogueextensions.cpp | 3 --- components/compiler/extensions0.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 45eeccf18..ecaf4253e 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -227,9 +227,6 @@ namespace MWScript std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - // ignore extra garbage argument - runtime.pop(); - runtime.push(MWBase::Environment::get().getDialogueManager() ->getFactionReaction(faction1, faction2)); } diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 4cd77cf1d..e095958d1 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -180,7 +180,7 @@ namespace Compiler extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, opcodeSameFactionExplicit); extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction); - extensions.registerFunction("getfactionreaction", 'l', "ccl", opcodeGetFactionReaction); + extensions.registerFunction("getfactionreaction", 'l', "ccX", opcodeGetFactionReaction); extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit); } } From ee098de0a69c6fbf538fe1841598715e82c3f53a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 01:14:00 +0200 Subject: [PATCH 34/35] Don't ignore lighting values of particles not attached to a character --- apps/openmw/mwrender/animation.cpp | 64 ++++++++++++++++++++++++++---- components/nifogre/material.cpp | 5 --- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 78703172e..83fe766a9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1169,20 +1169,68 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationTime())); } - if (!texture.empty()) + + // Do some manual adjustments on the created entities/particle systems + + // It looks like vanilla MW totally ignores lighting settings for effects attached to characters. + // If we don't do this, some effects will look way too dark depending on the environment + // (e.g. magic_cast_dst.nif). They were clearly meant to use emissive lighting. + // We used to have this hack in the NIF material loader, but for effects not attached to characters + // (e.g. ash storms) the lighting settings do seem to be in use. Is there maybe a flag we have missed? + Ogre::ColourValue ambient = Ogre::ColourValue(0.f, 0.f, 0.f); + Ogre::ColourValue diffuse = Ogre::ColourValue(0.f, 0.f, 0.f); + Ogre::ColourValue specular = Ogre::ColourValue(0.f, 0.f, 0.f); + Ogre::ColourValue emissive = Ogre::ColourValue(1.f, 1.f, 1.f); + for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) { - for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) + Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; + + Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); + + for (int t=0; tgetNumTechniques(); ++t) { - Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; + Ogre::Technique* tech = mat->getTechnique(t); + for (int p=0; pgetNumPasses(); ++p) + { + Ogre::Pass* pass = tech->getPass(p); - Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys); + pass->setAmbient(ambient); + pass->setDiffuse(diffuse); + pass->setSpecular(specular); + pass->setEmissive(emissive); - for (int t=0; tgetNumTechniques(); ++t) + if (!texture.empty()) + { + for (int tex=0; texgetNumTextureUnitStates(); ++tex) + { + Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); + tus->setTextureName("textures\\" + texture); + } + } + } + } + } + for(size_t i = 0;i < params.mObjects->mEntities.size(); ++i) + { + Ogre::Entity* ent = params.mObjects->mEntities[i]; + if (ent == params.mObjects->mSkelBase) + continue; + Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(ent); + + for (int t=0; tgetNumTechniques(); ++t) + { + Ogre::Technique* tech = mat->getTechnique(t); + for (int p=0; pgetNumPasses(); ++p) { - Ogre::Technique* tech = mat->getTechnique(t); - for (int p=0; pgetNumPasses(); ++p) + Ogre::Pass* pass = tech->getPass(p); + + pass->setAmbient(ambient); + pass->setDiffuse(diffuse); + pass->setSpecular(specular); + pass->setEmissive(emissive); + + if (!texture.empty()) { - Ogre::Pass* pass = tech->getPass(p); for (int tex=0; texgetNumTextureUnitStates(); ++tex) { Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index cc06d65f3..44831c13b 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -248,11 +248,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata, if (particleMaterial) { alpha = 1.f; // Apparently ignored, might be overridden by particle vertex colors? - - ambient = Ogre::Vector3(0.f); - diffuse = Ogre::Vector3(0.f); - specular = Ogre::Vector3(0.f); - emissive = Ogre::Vector3(1.f); } { From ec64f1a53a5ab38f39b5879acfe8f393c92e2afa Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 25 Jun 2014 02:46:39 +0200 Subject: [PATCH 35/35] Reset accumulation root when its animation finishes Fixes a position flicker after standing up from knockdown. --- apps/openmw/mwrender/animation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 83fe766a9..8bf2160e3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1027,7 +1027,11 @@ Ogre::Vector3 Animation::runAnimation(float duration) if(!state.mPlaying && state.mAutoDisable) { + if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) + mAccumRoot->setPosition(0.f,0.f,0.f); + mStates.erase(stateiter++); + resetActiveGroups(); } else