diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index d685967da..7a1e7270d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1213,14 +1213,12 @@ namespace MWMechanics // AI and magic effects update for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { + float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); // AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this // (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not) // This distance could be made configurable later, but the setting must be marked with a big warning: // using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876) - bool inProcessingRange = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() - <= sqrAiProcessingDistance; - - iter->second->getCharacterController()->setActive(inProcessingRange); + bool inProcessingRange = distSqr <= sqrAiProcessingDistance; if (iter->first == player) iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell()); @@ -1326,9 +1324,24 @@ namespace MWMechanics CharacterController* playerCharacter = NULL; for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { - if (iter->first != player && - (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2() - > sqrAiProcessingDistance) + const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation. + const float distSqr = (player.getRefData().getPosition().asVec3() - iter->first.getRefData().getPosition().asVec3()).length2(); + bool isPlayer = iter->first == player; + bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance); + int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) + if (isPlayer) + activeFlag = 2; + int active = inAnimationRange ? activeFlag : 0; + bool canFly = iter->first.getClass().canFly(iter->first); + if (canFly) + { + // Keep animating flying creatures so they don't just hover in-air + inAnimationRange = true; + active = std::max(1, active); + } + iter->second->getCharacterController()->setActive(active); + + if (!inAnimationRange) continue; if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed()) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fa261bbc3..676f01922 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2392,7 +2392,7 @@ float CharacterController::getAttackStrength() const return mAttackStrength; } -void CharacterController::setActive(bool active) +void CharacterController::setActive(int active) { mAnimation->setActive(active); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 016d88289..cab51b82f 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -292,7 +292,7 @@ public: float getAttackStrength() const; /// @see Animation::setActive - void setActive(bool active); + void setActive(int active); /// Make this character turn its head towards \a target. To turn off head tracking, pass an empty Ptr. void setHeadTrackTarget(const MWWorld::ConstPtr& target); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 16ce9b436..1bd839ead 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -486,10 +486,10 @@ namespace MWRender return mPtr; } - void Animation::setActive(bool active) + void Animation::setActive(int active) { if (mSkeleton) - mSkeleton->setActive(active); + mSkeleton->setActive(static_cast(active)); } void Animation::updatePtr(const MWWorld::Ptr &ptr) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 8ac78babc..cff98a4b7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -347,7 +347,8 @@ public: /// Set active flag on the object skeleton, if one exists. /// @see SceneUtil::Skeleton::setActive - void setActive(bool active); + /// 0 = Inactive, 1 = Active in place, 2 = Active + void setActive(int active); osg::Group* getOrCreateObjectRoot(); diff --git a/components/sceneutil/skeleton.cpp b/components/sceneutil/skeleton.cpp index 116edfdb4..94ae1f234 100644 --- a/components/sceneutil/skeleton.cpp +++ b/components/sceneutil/skeleton.cpp @@ -36,8 +36,9 @@ private: Skeleton::Skeleton() : mBoneCacheInit(false) , mNeedToUpdateBoneMatrices(true) - , mActive(true) + , mActive(Active) , mLastFrameNumber(0) + , mLastCullFrameNumber(0) { } @@ -48,6 +49,7 @@ Skeleton::Skeleton(const Skeleton ©, const osg::CopyOp ©op) , mNeedToUpdateBoneMatrices(true) , mActive(copy.mActive) , mLastFrameNumber(0) + , mLastCullFrameNumber(0) { } @@ -123,14 +125,14 @@ void Skeleton::updateBoneMatrices(unsigned int traversalNumber) } } -void Skeleton::setActive(bool active) +void Skeleton::setActive(ActiveType active) { mActive = active; } bool Skeleton::getActive() const { - return mActive; + return mActive != Inactive; } void Skeleton::markDirty() @@ -142,8 +144,16 @@ void Skeleton::markDirty() void Skeleton::traverse(osg::NodeVisitor& nv) { - if (!getActive() && nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && mLastFrameNumber != 0) - return; + if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + { + if (mActive == Inactive && mLastFrameNumber != 0) + return; + if (mActive == SemiActive && mLastFrameNumber != 0 && mLastCullFrameNumber+3 <= nv.getTraversalNumber()) + return; + } + else if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + mLastCullFrameNumber = nv.getTraversalNumber(); + osg::Group::traverse(nv); } diff --git a/components/sceneutil/skeleton.hpp b/components/sceneutil/skeleton.hpp index 245e3522c..1d8069f8e 100644 --- a/components/sceneutil/skeleton.hpp +++ b/components/sceneutil/skeleton.hpp @@ -47,9 +47,16 @@ namespace SceneUtil /// Request an update of bone matrices. May be a no-op if already updated in this frame. void updateBoneMatrices(unsigned int traversalNumber); + enum ActiveType + { + Inactive=0, + SemiActive, /// Like Active, but don't bother with Update (including new bounding box) if we're off-screen + Active + }; + /// Set the skinning active flag. Inactive skeletons will not have their child rigs updated. /// You should set this flag to false if you know that bones are not currently moving. - void setActive(bool active); + void setActive(ActiveType active); bool getActive() const; @@ -71,9 +78,10 @@ namespace SceneUtil bool mNeedToUpdateBoneMatrices; - bool mActive; + ActiveType mActive; unsigned int mLastFrameNumber; + unsigned int mLastCullFrameNumber; }; }