diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 179070aed..59e51e461 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -7,6 +7,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/failedaction.hpp" @@ -21,7 +22,7 @@ #include "../mwgui/tooltips.hpp" -#include "../mwrender/objects.hpp" +#include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwmechanics/npcstats.hpp" @@ -87,7 +88,8 @@ namespace MWClass { const std::string model = getModel(ptr); if (!model.empty()) { - renderingInterface.getObjects().insertModel(ptr, model); + MWRender::Actors& actors = renderingInterface.getActors(); + actors.insertActivator(ptr); } } @@ -96,6 +98,7 @@ namespace MWClass const std::string model = getModel(ptr); if(!model.empty()) physics.addObject(ptr); + MWBase::Environment::get().getMechanicsManager()->add(ptr); } std::string Container::getModel(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 4608010ac..396c8fa48 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -633,8 +633,8 @@ namespace MWGui MWWorld::Store::iterator it = skills.begin(); for (; it != skills.end(); ++it) { - if (it->mData.mSpecialization == specId) - specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->mIndex] + "}"; + if (it->second.mData.mSpecialization == specId) + specText += std::string("\n#{") + ESM::Skill::sSkillNameIds[it->first] + "}"; } widget->setUserString("Caption_CenteredCaptionText", specText); widget->setUserString("ToolTipLayout", "TextWithCenteredCaptionToolTip"); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2e4f76c1a..46e06f460 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1072,7 +1072,8 @@ bool CharacterController::updateWeaponState() } else if (mHitState == CharState_KnockDown) { - mUpperBodyState = UpperCharState_WeapEquiped; + if (mUpperBodyState > UpperCharState_WeapEquiped) + mUpperBodyState = UpperCharState_WeapEquiped; mAnimation->disable(mCurrentWeapon); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index b4edf44aa..9cafe9b3c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -143,9 +143,9 @@ namespace MWMechanics MWWorld::Store::iterator iter = skills.begin(); for (; iter != skills.end(); ++iter) { - if (iter->mData.mSpecialization==class_->mData.mSpecialization) + if (iter->second.mData.mSpecialization==class_->mData.mSpecialization) { - int index = iter->mIndex; + int index = iter->first; if (index>=0 && index<27) { diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index de0457e57..540876a3e 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -4,6 +4,8 @@ #include "../mwbase/world.hpp" +#include "../mwworld/class.hpp" + #include "renderconst.hpp" namespace MWRender @@ -16,17 +18,14 @@ ActivatorAnimation::~ActivatorAnimation() ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) : Animation(ptr, ptr.getRefData().getBaseNode()) { - MWWorld::LiveCellRef *ref = mPtr.get(); + const std::string& model = mPtr.getClass().getModel(mPtr); - assert(ref->mBase != NULL); - if(!ref->mBase->mModel.empty()) + if(!model.empty()) { - const std::string name = "meshes\\"+ref->mBase->mModel; - - setObjectRoot(name, false); + setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha); - addAnimSource(name); + addAnimSource(model); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 548906cdf..ecaaba0b9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -114,10 +114,6 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : NifOgre::Loader::createObjectBase(mInsert, mdlname)); - // Fast forward auto-play particles, which will have been set up as Emitting by the loader. - for (unsigned int i=0; imParticles.size(); ++i) - mObjectRoot->mParticles[i]->fastForward(1, 0.1); - if(mObjectRoot->mSkelBase) { mSkelBase = mObjectRoot->mSkelBase; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d20e89324..f2bc3df95 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -571,10 +571,6 @@ NifOgre::ObjectScenePtr NpcAnimation::insertBoundedPart(const std::string &model std::for_each(objects->mEntities.begin(), objects->mEntities.end(), SetObjectGroup(group)); std::for_each(objects->mParticles.begin(), objects->mParticles.end(), SetObjectGroup(group)); - // Fast forward auto-play particles, which will have been set up as Emitting by the loader. - for (unsigned int i=0; imParticles.size(); ++i) - objects->mParticles[i]->fastForward(1, 0.1); - if(objects->mSkelBase) { Ogre::AnimationStateSet *aset = objects->mSkelBase->getAllAnimationStates(); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 469b93f88..5966a99b9 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -842,36 +842,12 @@ namespace MWWorld template <> class Store : public StoreBase { - public: - typedef std::vector::const_iterator iterator; - private: - std::vector mStatic; + typedef std::map Interior; + typedef std::map, ESM::Pathgrid> Exterior; - std::vector::iterator mIntBegin, mIntEnd, mExtBegin, mExtEnd; - - struct IntExtOrdering - { - bool operator()(const ESM::Pathgrid &x, const ESM::Pathgrid &y) const { - // interior pathgrids precedes exterior ones (x < y) - if ((x.mData.mX == 0 && x.mData.mY == 0) && - (y.mData.mX != 0 || y.mData.mY != 0)) - { - return true; - } - return false; - } - }; - - struct ExtCompare - { - bool operator()(const ESM::Pathgrid &x, const ESM::Pathgrid &y) const { - if (x.mData.mX == y.mData.mX) { - return x.mData.mY < y.mData.mY; - } - return x.mData.mX < y.mData.mX; - } - }; + Interior mInt; + Exterior mExt; public: @@ -881,65 +857,33 @@ namespace MWWorld pathgrid.load(esm); // Try to overwrite existing record - // Can't use search() because we aren't sorted yet if (!pathgrid.mCell.empty()) { - for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) - { - if ((*it).mCell == pathgrid.mCell) - { - (*it) = pathgrid; - return; - } - } + std::pair found = mInt.insert(std::make_pair(pathgrid.mCell, pathgrid)); + if (found.second) + found.first->second = pathgrid; } else { - for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) - { - if ((*it).mData.mX == pathgrid.mData.mX && (*it).mData.mY == pathgrid.mData.mY) - { - (*it) = pathgrid; - return; - } - } + std::pair found = mExt.insert(std::make_pair(std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY), + pathgrid)); + if (found.second) + found.first->second = pathgrid; } - - mStatic.push_back(pathgrid); } size_t getSize() const { - return mStatic.size(); + return mInt.size() + mExt.size(); } void setUp() { - IntExtOrdering cmp; - std::sort(mStatic.begin(), mStatic.end(), cmp); - - ESM::Pathgrid pg; - pg.mData.mX = pg.mData.mY = 1; - mExtBegin = - std::lower_bound(mStatic.begin(), mStatic.end(), pg, cmp); - mExtEnd = mStatic.end(); - - mIntBegin = mStatic.begin(); - mIntEnd = mExtBegin; - - std::sort(mIntBegin, mIntEnd, RecordCmp()); - std::sort(mExtBegin, mExtEnd, ExtCompare()); } const ESM::Pathgrid *search(int x, int y) const { - ESM::Pathgrid pg; - pg.mData.mX = x; - pg.mData.mY = y; - - iterator it = - std::lower_bound(mExtBegin, mExtEnd, pg, ExtCompare()); - if (it != mExtEnd && it->mData.mX == x && it->mData.mY == y) { - return &(*it); - } - return 0; + Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); + if (it != mExt.end()) + return &(it->second); + return NULL; } const ESM::Pathgrid *find(int x, int y) const { @@ -953,14 +897,10 @@ namespace MWWorld } const ESM::Pathgrid *search(const std::string &name) const { - ESM::Pathgrid pg; - pg.mCell = name; - - iterator it = std::lower_bound(mIntBegin, mIntEnd, pg, RecordCmp()); - if (it != mIntEnd && Misc::StringUtils::ciEqual(it->mCell, name)) { - return &(*it); - } - return 0; + Interior::const_iterator it = mInt.find(name); + if (it != mInt.end()) + return &(it->second); + return NULL; } const ESM::Pathgrid *find(const std::string &name) const { @@ -986,53 +926,20 @@ namespace MWWorld } return find(cell.mData.mX, cell.mData.mY); } - - iterator begin() const { - return mStatic.begin(); - } - - iterator end() const { - return mStatic.end(); - } - - iterator interiorPathsBegin() const { - return mIntBegin; - } - - iterator interiorPathsEnd() const { - return mIntEnd; - } - - iterator exteriorPathsBegin() const { - return mExtBegin; - } - - iterator exteriorPathsEnd() const { - return mExtEnd; - } }; template class IndexedStore { - struct Compare - { - bool operator()(const T &x, const T &y) const { - return x.mIndex < y.mIndex; - } - }; protected: - std::vector mStatic; + typedef typename std::map Static; + Static mStatic; public: - typedef typename std::vector::const_iterator iterator; + typedef typename std::map::const_iterator iterator; IndexedStore() {} - IndexedStore(unsigned int size) { - mStatic.reserve(size); - } - iterator begin() const { return mStatic.begin(); } @@ -1041,10 +948,14 @@ namespace MWWorld return mStatic.end(); } - /// \todo refine loading order void load(ESM::ESMReader &esm) { - mStatic.push_back(T()); - mStatic.back().load(esm); + T record; + record.load(esm); + + // Try to overwrite existing record + std::pair found = mStatic.insert(std::make_pair(record.mIndex, record)); + if (found.second) + found.first->second = record; } int getSize() const { @@ -1052,40 +963,13 @@ namespace MWWorld } void setUp() { - /// \note This method sorts indexed values for further - /// searches. Every loaded item is present in storage, but - /// latest loaded shadows any previous while searching. - /// If memory cost will be too high, it is possible to remove - /// unused values. - - Compare cmp; - - std::stable_sort(mStatic.begin(), mStatic.end(), cmp); - - typename std::vector::iterator first, next; - next = first = mStatic.begin(); - - while (first != mStatic.end() && ++next != mStatic.end()) { - while (next != mStatic.end() && !cmp(*first, *next)) { - ++next; - } - if (first != --next) { - std::swap(*first, *next); - } - first = ++next; - } } const T *search(int index) const { - T item; - item.mIndex = index; - - iterator it = - std::lower_bound(mStatic.begin(), mStatic.end(), item, Compare()); - if (it != mStatic.end() && it->mIndex == index) { - return &(*it); - } - return 0; + typename Static::const_iterator it = mStatic.find(index); + if (it != mStatic.end()) + return &(it->second); + return NULL; } const T *find(int index) const { @@ -1103,18 +987,12 @@ namespace MWWorld struct Store : public IndexedStore { Store() {} - Store(unsigned int size) - : IndexedStore(size) - {} }; template <> struct Store : public IndexedStore { Store() {} - Store(unsigned int size) - : IndexedStore(size) - {} }; template <> diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 19e3f3bb3..07561c2ea 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -30,7 +30,14 @@ void Script::load(ESMReader &esm) int s = mData.mStringTableSize; std::vector tmp (s); - esm.getHExact (&tmp[0], s); + // not using getHExact, vanilla doesn't seem to mind unused bytes at the end + esm.getSubHeader(); + int left = esm.getSubSize(); + if (left < s) + esm.fail("SCVR string list is smaller than specified"); + esm.getExact(&tmp[0], s); + if (left > s) + esm.skip(left-s); // skip the leftover junk // Set up the list of variable names mVarNames.resize(mData.mNumShorts + mData.mNumLongs + mData.mNumFloats); diff --git a/components/nif/data.hpp b/components/nif/data.hpp index 5efd0d6c9..d9de12fb5 100644 --- a/components/nif/data.hpp +++ b/components/nif/data.hpp @@ -350,8 +350,11 @@ struct NiMorphData : public Record struct NiKeyframeData : public Record { QuaternionKeyMap mRotations; - //\FIXME mXYZ_Keys are read, but not used. - FloatKeyMap mXYZ_Keys; + + FloatKeyMap mXRotations; + FloatKeyMap mYRotations; + FloatKeyMap mZRotations; + Vector3KeyMap mTranslations; FloatKeyMap mScales; @@ -362,12 +365,9 @@ struct NiKeyframeData : public Record { //Chomp unused float nif->getFloat(); - for(size_t i=0;i<3;++i) - { - //Read concatenates items together. - mXYZ_Keys.read(nif,true); - } - nif->file->warn("XYZ_ROTATION_KEY read, but not used!"); + mXRotations.read(nif, true); + mYRotations.read(nif, true); + mZRotations.read(nif, true); } mTranslations.read(nif); mScales.read(nif); diff --git a/components/nif/nifkey.hpp b/components/nif/nifkey.hpp index b0db80914..cb8142720 100644 --- a/components/nif/nifkey.hpp +++ b/components/nif/nifkey.hpp @@ -49,9 +49,7 @@ struct KeyMapT { if(count == 0 && !force) return; - //If we aren't forcing things, make sure that read clears any previous keys - if(!force) - mKeys.clear(); + mKeys.clear(); mInterpolationType = nif->getUInt(); @@ -88,7 +86,7 @@ struct KeyMapT { //XYZ keys aren't actually read here. //data.hpp sees that the last type read was sXYZInterpolation and: // Eats a floating point number, then - // Re-runs the read function 3 more times, with force enabled so that the previous values aren't cleared. + // Re-runs the read function 3 more times. // When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation. else if(mInterpolationType == sXYZInterpolation) { diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 053adfb5b..1d1e1a22d 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -161,6 +161,38 @@ void ObjectScene::rotateBillboardNodes(Ogre::Camera *camera) } } +void ObjectScene::_notifyAttached() +{ + // convert initial particle positions to world space for world-space particle systems + // this can't be done on creation because the particle system is not in its correct world space position yet + for (std::vector::iterator it = mParticles.begin(); it != mParticles.end(); ++it) + { + Ogre::ParticleSystem* psys = *it; + if (psys->getKeepParticlesInLocalSpace()) + continue; + Ogre::ParticleIterator pi = psys->_getIterator(); + while (!pi.end()) + { + Ogre::Particle *p = pi.getNext(); + +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + Ogre::Vector3& position = p->mPosition; + Ogre::Vector3& direction = p->mDirection; +#else + Ogre::Vector3& position = p->position; + Ogre::Vector3& direction = p->direction; +#endif + + position = + (psys->getParentNode()->_getDerivedOrientation() * + (psys->getParentNode()->_getDerivedScale() * position)) + + psys->getParentNode()->_getDerivedPosition(); + direction = + (psys->getParentNode()->_getDerivedOrientation() * direction); + } + } +} + // Animates a texture class FlipController { @@ -410,6 +442,9 @@ public: { private: const Nif::QuaternionKeyMap* mRotations; + const Nif::FloatKeyMap* mXRotations; + const Nif::FloatKeyMap* mYRotations; + const Nif::FloatKeyMap* mZRotations; const Nif::Vector3KeyMap* mTranslations; const Nif::FloatKeyMap* mScales; Nif::NIFFilePtr mNif; // Hold a SharedPtr to make sure key lists stay valid @@ -440,11 +475,25 @@ public: return keys.rbegin()->second.mValue; } + Ogre::Quaternion getXYZRotation(float time) const + { + float xrot = interpKey(mXRotations->mKeys, time); + float yrot = interpKey(mYRotations->mKeys, time); + float zrot = interpKey(mZRotations->mKeys, time); + Ogre::Quaternion xr(Ogre::Radian(xrot), Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr(Ogre::Radian(yrot), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr(Ogre::Radian(zrot), Ogre::Vector3::UNIT_Z); + return (xr*yr*zr); + } + public: /// @note The NiKeyFrameData must be valid as long as this KeyframeController exists. Value(Ogre::Node *target, const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data) : NodeTargetValue(target) , mRotations(&data->mRotations) + , mXRotations(&data->mXRotations) + , mYRotations(&data->mYRotations) + , mZRotations(&data->mZRotations) , mTranslations(&data->mTranslations) , mScales(&data->mScales) , mNif(nif) @@ -454,6 +503,8 @@ public: { if(mRotations->mKeys.size() > 0) return interpKey(mRotations->mKeys, time); + else if (!mXRotations->mKeys.empty() || !mYRotations->mKeys.empty() || !mZRotations->mKeys.empty()) + return getXYZRotation(time); return mNode->getOrientation(); } @@ -481,6 +532,8 @@ public: { if(mRotations->mKeys.size() > 0) mNode->setOrientation(interpKey(mRotations->mKeys, time)); + else if (!mXRotations->mKeys.empty() || !mYRotations->mKeys.empty() || !mZRotations->mKeys.empty()) + mNode->setOrientation(getXYZRotation(time)); if(mTranslations->mKeys.size() > 0) mNode->setPosition(interpKey(mTranslations->mKeys, time)); if(mScales->mKeys.size() > 0) @@ -949,6 +1002,8 @@ class NIFObjectLoader createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); } + createParticleInitialState(partsys, particledata, partctrl); + Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); @@ -975,6 +1030,50 @@ class NIFObjectLoader createMaterialControllers(partnode, partsys, animflags, scene); } + static void createParticleInitialState(Ogre::ParticleSystem* partsys, const Nif::NiAutoNormalParticlesData* particledata, + const Nif::NiParticleSystemController* partctrl) + { + partsys->_update(0.f); // seems to be required to allocate mFreeParticles + int i=0; + for (std::vector::const_iterator it = partctrl->particles.begin(); + iactiveCount && it != partctrl->particles.end(); ++it, ++i) + { + const Nif::NiParticleSystemController::Particle& particle = *it; + + Ogre::Particle* created = partsys->createParticle(); + if (!created) + break; + +#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0) + Ogre::Vector3& position = created->mPosition; + Ogre::Vector3& direction = created->mDirection; + Ogre::ColourValue& colour = created->mColour; + float& totalTimeToLive = created->mTotalTimeToLive; + float& timeToLive = created->mTimeToLive; +#else + Ogre::Vector3& position = created->position; + Ogre::Vector3& direction = created->direction; + Ogre::ColourValue& colour = created->colour; + float& totalTimeToLive = created->totalTimeToLive; + float& timeToLive = created->timeToLive; +#endif + + direction = particle.velocity; + position = particledata->vertices.at(particle.vertex); + + if (particle.vertex < int(particledata->colors.size())) + { + Ogre::Vector4 partcolour = particledata->colors.at(particle.vertex); + colour = Ogre::ColourValue(partcolour.x, partcolour.y, partcolour.z, partcolour.w); + } + else + colour = Ogre::ColourValue(1.f, 1.f, 1.f, 1.f); + float size = particledata->sizes.at(particle.vertex); + created->setDimensions(size, size); + totalTimeToLive = std::max(0.f, particle.lifespan); + timeToLive = std::max(0.f, particle.lifespan - particle.lifetime); + } + } static void createNodeControllers(const Nif::NIFFilePtr& nif, const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags) { @@ -1275,6 +1374,8 @@ ObjectScenePtr Loader::createObjects(Ogre::SceneNode *parentNode, std::string na parentNode->attachObject(entity); } + scene->_notifyAttached(); + return scene; } @@ -1342,6 +1443,8 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo } } + scene->_notifyAttached(); + return scene; } diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index abadd38de..485495a38 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -84,6 +84,10 @@ struct ObjectScene { void rotateBillboardNodes(Ogre::Camera* camera); void setVisibilityFlags (unsigned int flags); + + // This is called internally by the OgreNifLoader once all elements of the + // scene have been attached to their respective nodes. + void _notifyAttached(); }; typedef Ogre::SharedPtr ObjectScenePtr;