forked from teamnwah/openmw-tes3coop
Merge pull request #666 from scrawl/animation
Animation refactoring & fixes
This commit is contained in:
commit
111cf5462b
5 changed files with 204 additions and 187 deletions
|
@ -43,6 +43,7 @@
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -236,7 +237,7 @@ std::string CharacterController::chooseRandomGroup (const std::string& prefix, i
|
||||||
return prefix + toString(roll);
|
return prefix + toString(roll);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force)
|
void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force)
|
||||||
{
|
{
|
||||||
// hit recoils/knockdown animations handling
|
// hit recoils/knockdown animations handling
|
||||||
if(mPtr.getClass().isActor())
|
if(mPtr.getClass().isActor())
|
||||||
|
@ -251,26 +252,28 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
||||||
{
|
{
|
||||||
mHitState = CharState_KnockOut;
|
mHitState = CharState_KnockOut;
|
||||||
mCurrentHit = "knockout";
|
mCurrentHit = "knockout";
|
||||||
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, ~0ul);
|
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul);
|
||||||
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true);
|
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true);
|
||||||
}
|
}
|
||||||
else if(knockdown)
|
else if(knockdown)
|
||||||
{
|
{
|
||||||
mHitState = CharState_KnockDown;
|
mHitState = CharState_KnockDown;
|
||||||
mCurrentHit = "knockdown";
|
mCurrentHit = "knockdown";
|
||||||
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
|
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
else if (recovery)
|
else if (recovery)
|
||||||
{
|
{
|
||||||
mHitState = CharState_Hit;
|
mHitState = CharState_Hit;
|
||||||
mCurrentHit = chooseRandomGroup("hit");
|
mCurrentHit = chooseRandomGroup("hit");
|
||||||
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "start", "stop", 0.0f, 0);
|
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
else if (block)
|
else if (block)
|
||||||
{
|
{
|
||||||
mHitState = CharState_Block;
|
mHitState = CharState_Block;
|
||||||
mCurrentHit = "shield";
|
mCurrentHit = "shield";
|
||||||
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::Group_All, true, 1, "block start", "block stop", 0.0f, 0);
|
MWRender::Animation::AnimPriority priorityBlock (Priority_Hit);
|
||||||
|
priorityBlock.mPriority[MWRender::Animation::BoneGroup_LeftArm] = Priority_Block;
|
||||||
|
mAnimation->play(mCurrentHit, priorityBlock, MWRender::Animation::BlendMask_All, true, 1, "block start", "block stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel upper body animations
|
// Cancel upper body animations
|
||||||
|
@ -303,7 +306,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
||||||
{
|
{
|
||||||
mHitState = CharState_KnockDown;
|
mHitState = CharState_KnockDown;
|
||||||
mAnimation->disable(mCurrentHit);
|
mAnimation->disable(mCurrentHit);
|
||||||
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "loop stop", "stop", 0.0f, 0);
|
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,40 +314,41 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
||||||
if (!mPtr.getClass().isBipedal(mPtr))
|
if (!mPtr.getClass().isBipedal(mPtr))
|
||||||
weap = sWeaponTypeListEnd;
|
weap = sWeaponTypeListEnd;
|
||||||
|
|
||||||
if(force && mJumpState != JumpState_None)
|
if(force || jump != mJumpState)
|
||||||
{
|
{
|
||||||
std::string jump;
|
bool startAtLoop = (jump == mJumpState);
|
||||||
MWRender::Animation::Group jumpgroup = MWRender::Animation::Group_All;
|
mJumpState = jump;
|
||||||
|
|
||||||
|
std::string jumpAnimName;
|
||||||
|
MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All;
|
||||||
if(mJumpState != JumpState_None)
|
if(mJumpState != JumpState_None)
|
||||||
{
|
{
|
||||||
jump = "jump";
|
jumpAnimName = "jump";
|
||||||
if(weap != sWeaponTypeListEnd)
|
if(weap != sWeaponTypeListEnd)
|
||||||
{
|
{
|
||||||
jump += weap->shortgroup;
|
jumpAnimName += weap->shortgroup;
|
||||||
if(!mAnimation->hasAnimation(jump))
|
if(!mAnimation->hasAnimation(jumpAnimName))
|
||||||
{
|
{
|
||||||
jumpgroup = MWRender::Animation::Group_LowerBody;
|
jumpmask = MWRender::Animation::BlendMask_LowerBody;
|
||||||
jump = "jump";
|
jumpAnimName = "jump";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mJumpState == JumpState_InAir)
|
if(mJumpState == JumpState_InAir)
|
||||||
{
|
{
|
||||||
int mode = ((jump == mCurrentJump) ? 2 : 1);
|
|
||||||
|
|
||||||
mAnimation->disable(mCurrentJump);
|
mAnimation->disable(mCurrentJump);
|
||||||
mCurrentJump = jump;
|
mCurrentJump = jumpAnimName;
|
||||||
if (mAnimation->hasAnimation("jump"))
|
if (mAnimation->hasAnimation("jump"))
|
||||||
mAnimation->play(mCurrentJump, Priority_Jump, jumpgroup, false,
|
mAnimation->play(mCurrentJump, Priority_Jump, jumpmask, false,
|
||||||
1.0f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
|
1.0f, (startAtLoop?"loop start":"start"), "stop", 0.0f, ~0ul);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mAnimation->disable(mCurrentJump);
|
mAnimation->disable(mCurrentJump);
|
||||||
mCurrentJump.clear();
|
mCurrentJump.clear();
|
||||||
if (mAnimation->hasAnimation("jump"))
|
if (mAnimation->hasAnimation("jump"))
|
||||||
mAnimation->play(jump, Priority_Jump, jumpgroup, true,
|
mAnimation->play(jumpAnimName, Priority_Jump, jumpmask, true,
|
||||||
1.0f, "loop stop", "stop", 0.0f, 0);
|
1.0f, "loop stop", "stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,55 +357,55 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
||||||
{
|
{
|
||||||
mMovementState = movement;
|
mMovementState = movement;
|
||||||
|
|
||||||
std::string movement;
|
std::string movementAnimName;
|
||||||
MWRender::Animation::Group movegroup = MWRender::Animation::Group_All;
|
MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All;
|
||||||
const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState));
|
const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState));
|
||||||
if(movestate != sMovementListEnd)
|
if(movestate != sMovementListEnd)
|
||||||
{
|
{
|
||||||
movement = movestate->groupname;
|
movementAnimName = movestate->groupname;
|
||||||
if(weap != sWeaponTypeListEnd && movement.find("swim") == std::string::npos)
|
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
|
||||||
{
|
{
|
||||||
movement += weap->shortgroup;
|
movementAnimName += weap->shortgroup;
|
||||||
if(!mAnimation->hasAnimation(movement))
|
if(!mAnimation->hasAnimation(movementAnimName))
|
||||||
{
|
{
|
||||||
movegroup = MWRender::Animation::Group_LowerBody;
|
movemask = MWRender::Animation::BlendMask_LowerBody;
|
||||||
movement = movestate->groupname;
|
movementAnimName = movestate->groupname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mAnimation->hasAnimation(movement))
|
if(!mAnimation->hasAnimation(movementAnimName))
|
||||||
{
|
{
|
||||||
std::string::size_type swimpos = movement.find("swim");
|
std::string::size_type swimpos = movementAnimName.find("swim");
|
||||||
if(swimpos == std::string::npos)
|
if(swimpos == std::string::npos)
|
||||||
{
|
{
|
||||||
std::string::size_type runpos = movement.find("run");
|
std::string::size_type runpos = movementAnimName.find("run");
|
||||||
if (runpos != std::string::npos)
|
if (runpos != std::string::npos)
|
||||||
{
|
{
|
||||||
movement.replace(runpos, runpos+3, "walk");
|
movementAnimName.replace(runpos, runpos+3, "walk");
|
||||||
if (!mAnimation->hasAnimation(movement))
|
if (!mAnimation->hasAnimation(movementAnimName))
|
||||||
movement.clear();
|
movementAnimName.clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
movement.clear();
|
movementAnimName.clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
movegroup = MWRender::Animation::Group_LowerBody;
|
movemask = MWRender::Animation::BlendMask_LowerBody;
|
||||||
movement.erase(swimpos, 4);
|
movementAnimName.erase(swimpos, 4);
|
||||||
if(!mAnimation->hasAnimation(movement))
|
if(!mAnimation->hasAnimation(movementAnimName))
|
||||||
movement.clear();
|
movementAnimName.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're playing the same animation, restart from the loop start instead of the
|
/* If we're playing the same animation, restart from the loop start instead of the
|
||||||
* beginning. */
|
* beginning. */
|
||||||
int mode = ((movement == mCurrentMovement) ? 2 : 1);
|
int mode = ((movementAnimName == mCurrentMovement) ? 2 : 1);
|
||||||
|
|
||||||
mMovementAnimationControlled = true;
|
mMovementAnimationControlled = true;
|
||||||
|
|
||||||
mAnimation->disable(mCurrentMovement);
|
mAnimation->disable(mCurrentMovement);
|
||||||
mCurrentMovement = movement;
|
mCurrentMovement = movementAnimName;
|
||||||
if(!mCurrentMovement.empty())
|
if(!mCurrentMovement.empty())
|
||||||
{
|
{
|
||||||
float vel, speedmult = 1.0f;
|
float vel, speedmult = 1.0f;
|
||||||
|
@ -447,7 +451,17 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
|
MWRender::Animation::AnimPriority priorityMovement (Priority_Movement);
|
||||||
|
if ((movement == CharState_TurnLeft || movement == CharState_TurnRight)
|
||||||
|
&& mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr()
|
||||||
|
&& MWBase::Environment::get().getWorld()->isFirstPerson())
|
||||||
|
{
|
||||||
|
priorityMovement.mPriority[MWRender::Animation::BoneGroup_Torso] = 0;
|
||||||
|
priorityMovement.mPriority[MWRender::Animation::BoneGroup_LeftArm] = 0;
|
||||||
|
priorityMovement.mPriority[MWRender::Animation::BoneGroup_RightArm] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAnimation->play(mCurrentMovement, priorityMovement, movemask, false,
|
||||||
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
|
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,7 +470,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
||||||
// FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
|
// FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update),
|
||||||
// the idle animation should be displayed
|
// the idle animation should be displayed
|
||||||
if ((mUpperBodyState != UpperCharState_Nothing
|
if ((mUpperBodyState != UpperCharState_Nothing
|
||||||
|| mMovementState != CharState_None
|
|| (mMovementState != CharState_None && mMovementState != CharState_TurnLeft && mMovementState != CharState_TurnRight)
|
||||||
|| mHitState != CharState_None)
|
|| mHitState != CharState_None)
|
||||||
&& !mPtr.getClass().isBipedal(mPtr))
|
&& !mPtr.getClass().isBipedal(mPtr))
|
||||||
idle = CharState_None;
|
idle = CharState_None;
|
||||||
|
@ -486,7 +500,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
||||||
mAnimation->disable(mCurrentIdle);
|
mAnimation->disable(mCurrentIdle);
|
||||||
mCurrentIdle = idle;
|
mCurrentIdle = idle;
|
||||||
if(!mCurrentIdle.empty())
|
if(!mCurrentIdle.empty())
|
||||||
mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false,
|
mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::BlendMask_All, false,
|
||||||
1.0f, "start", "stop", 0.0f, ~0ul, true);
|
1.0f, "start", "stop", 0.0f, ~0ul, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -602,7 +616,7 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
|
||||||
mCurrentJump = "";
|
mCurrentJump = "";
|
||||||
mMovementAnimationControlled = true;
|
mMovementAnimationControlled = true;
|
||||||
|
|
||||||
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
|
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::BlendMask_All,
|
||||||
false, 1.0f, "start", "stop", startpoint, 0);
|
false, 1.0f, "start", "stop", startpoint, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,7 +718,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
||||||
|
|
||||||
|
|
||||||
if(mDeathState == CharState_None)
|
if(mDeathState == CharState_None)
|
||||||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||||
|
|
||||||
mAnimation->runAnimation(0.f);
|
mAnimation->runAnimation(0.f);
|
||||||
}
|
}
|
||||||
|
@ -868,10 +882,10 @@ void CharacterController::updateIdleStormState()
|
||||||
mAnimation->getInfo("idlestorm", &complete);
|
mAnimation->getInfo("idlestorm", &complete);
|
||||||
|
|
||||||
if (complete == 0)
|
if (complete == 0)
|
||||||
mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::Group_RightArm, false,
|
mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::BlendMask_RightArm, false,
|
||||||
1.0f, "start", "loop start", 0.0f, 0);
|
1.0f, "start", "loop start", 0.0f, 0);
|
||||||
else if (complete == 1)
|
else if (complete == 1)
|
||||||
mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::Group_RightArm, false,
|
mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::BlendMask_RightArm, false,
|
||||||
1.0f, "loop start", "loop stop", 0.0f, ~0ul);
|
1.0f, "loop start", "loop stop", 0.0f, ~0ul);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -882,7 +896,7 @@ void CharacterController::updateIdleStormState()
|
||||||
{
|
{
|
||||||
if (mAnimation->getCurrentTime("idlestorm") < mAnimation->getTextKeyTime("idlestorm: loop stop"))
|
if (mAnimation->getCurrentTime("idlestorm") < mAnimation->getTextKeyTime("idlestorm: loop stop"))
|
||||||
{
|
{
|
||||||
mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::Group_RightArm, true,
|
mAnimation->play("idlestorm", Priority_Storm, MWRender::Animation::BlendMask_RightArm, true,
|
||||||
1.0f, "loop stop", "stop", 0.0f, 0);
|
1.0f, "loop stop", "stop", 0.0f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -989,7 +1003,7 @@ bool CharacterController::updateCreatureState()
|
||||||
if (!mCurrentWeapon.empty())
|
if (!mCurrentWeapon.empty())
|
||||||
{
|
{
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||||
MWRender::Animation::Group_All, true,
|
MWRender::Animation::BlendMask_All, true,
|
||||||
1, startKey, stopKey,
|
1, startKey, stopKey,
|
||||||
0.0f, 0);
|
0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_StartToMinAttack;
|
mUpperBodyState = UpperCharState_StartToMinAttack;
|
||||||
|
@ -1051,6 +1065,9 @@ bool CharacterController::updateWeaponState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWRender::Animation::AnimPriority priorityWeapon(Priority_Weapon);
|
||||||
|
priorityWeapon.mPriority[MWRender::Animation::BoneGroup_LowerBody] = 0;
|
||||||
|
|
||||||
bool forcestateupdate = false;
|
bool forcestateupdate = false;
|
||||||
if(weaptype != mWeaponType && mHitState != CharState_KnockDown && mHitState != CharState_KnockOut
|
if(weaptype != mWeaponType && mHitState != CharState_KnockDown && mHitState != CharState_KnockOut
|
||||||
&& mHitState != CharState_Hit)
|
&& mHitState != CharState_Hit)
|
||||||
|
@ -1063,8 +1080,8 @@ bool CharacterController::updateWeaponState()
|
||||||
if(weaptype == WeapType_None)
|
if(weaptype == WeapType_None)
|
||||||
{
|
{
|
||||||
getWeaponGroup(mWeaponType, weapgroup);
|
getWeaponGroup(mWeaponType, weapgroup);
|
||||||
mAnimation->play(weapgroup, Priority_Weapon,
|
mAnimation->play(weapgroup, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, true,
|
MWRender::Animation::BlendMask_All, true,
|
||||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||||
}
|
}
|
||||||
|
@ -1074,8 +1091,8 @@ bool CharacterController::updateWeaponState()
|
||||||
mAnimation->showWeapons(false);
|
mAnimation->showWeapons(false);
|
||||||
mAnimation->setWeaponGroup(weapgroup);
|
mAnimation->setWeaponGroup(weapgroup);
|
||||||
|
|
||||||
mAnimation->play(weapgroup, Priority_Weapon,
|
mAnimation->play(weapgroup, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, true,
|
MWRender::Animation::BlendMask_All, true,
|
||||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
mUpperBodyState = UpperCharState_EquipingWeap;
|
||||||
|
|
||||||
|
@ -1145,7 +1162,7 @@ bool CharacterController::updateWeaponState()
|
||||||
bool animPlaying;
|
bool animPlaying;
|
||||||
if(mAttackingOrSpell)
|
if(mAttackingOrSpell)
|
||||||
{
|
{
|
||||||
if(mUpperBodyState == UpperCharState_WeapEquiped && mHitState == CharState_None)
|
if(mUpperBodyState == UpperCharState_WeapEquiped && (mHitState == CharState_None || mHitState == CharState_Block))
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
|
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
|
||||||
mAttackType.clear();
|
mAttackType.clear();
|
||||||
|
@ -1154,6 +1171,10 @@ bool CharacterController::updateWeaponState()
|
||||||
// Unset casting flag, otherwise pressing the mouse button down would
|
// Unset casting flag, otherwise pressing the mouse button down would
|
||||||
// continue casting every frame if there is no animation
|
// continue casting every frame if there is no animation
|
||||||
mAttackingOrSpell = false;
|
mAttackingOrSpell = false;
|
||||||
|
if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);
|
||||||
|
}
|
||||||
|
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
|
@ -1191,8 +1212,8 @@ bool CharacterController::updateWeaponState()
|
||||||
case 2: mAttackType = "target"; break;
|
case 2: mAttackType = "target"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, true,
|
MWRender::Animation::BlendMask_All, true,
|
||||||
weapSpeed, mAttackType+" start", mAttackType+" stop",
|
weapSpeed, mAttackType+" start", mAttackType+" stop",
|
||||||
0.0f, 0);
|
0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_CastingSpell;
|
mUpperBodyState = UpperCharState_CastingSpell;
|
||||||
|
@ -1223,8 +1244,8 @@ bool CharacterController::updateWeaponState()
|
||||||
else if(item.getTypeName() == typeid(ESM::Probe).name())
|
else if(item.getTypeName() == typeid(ESM::Probe).name())
|
||||||
Security(mPtr).probeTrap(target, item, resultMessage, resultSound);
|
Security(mPtr).probeTrap(target, item, resultMessage, resultSound);
|
||||||
}
|
}
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, true,
|
MWRender::Animation::BlendMask_All, true,
|
||||||
1.0f, "start", "stop", 0.0, 0);
|
1.0f, "start", "stop", 0.0, 0);
|
||||||
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
|
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
|
||||||
|
|
||||||
|
@ -1250,8 +1271,8 @@ bool CharacterController::updateWeaponState()
|
||||||
determineAttackType();
|
determineAttackType();
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
weapSpeed, mAttackType+" start", mAttackType+" min attack",
|
weapSpeed, mAttackType+" start", mAttackType+" min attack",
|
||||||
0.0f, 0);
|
0.0f, 0);
|
||||||
mUpperBodyState = UpperCharState_StartToMinAttack;
|
mUpperBodyState = UpperCharState_StartToMinAttack;
|
||||||
|
@ -1301,8 +1322,8 @@ bool CharacterController::updateWeaponState()
|
||||||
mAttackStrength = attackStrength;
|
mAttackStrength = attackStrength;
|
||||||
|
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
weapSpeed, mAttackType+" max attack", mAttackType+" min hit",
|
weapSpeed, mAttackType+" max attack", mAttackType+" min hit",
|
||||||
1.0f-complete, 0);
|
1.0f-complete, 0);
|
||||||
|
|
||||||
|
@ -1371,15 +1392,6 @@ bool CharacterController::updateWeaponState()
|
||||||
mAnimation->attachArrow();
|
mAnimation->attachArrow();
|
||||||
|
|
||||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||||
//don't allow to continue playing hit animation on UpperBody after actor had attacked during it
|
|
||||||
if(mHitState == CharState_Hit)
|
|
||||||
{
|
|
||||||
mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody);
|
|
||||||
//commenting out following 2 lines will give a bit different combat dynamics(slower)
|
|
||||||
mHitState = CharState_None;
|
|
||||||
mCurrentHit.clear();
|
|
||||||
mPtr.getClass().getCreatureStats(mPtr).setHitRecovery(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(mUpperBodyState == UpperCharState_UnEquipingWeap)
|
else if(mUpperBodyState == UpperCharState_UnEquipingWeap)
|
||||||
mUpperBodyState = UpperCharState_Nothing;
|
mUpperBodyState = UpperCharState_Nothing;
|
||||||
|
@ -1397,8 +1409,8 @@ bool CharacterController::updateWeaponState()
|
||||||
case UpperCharState_MinAttackToMaxAttack:
|
case UpperCharState_MinAttackToMaxAttack:
|
||||||
//hack to avoid body pos desync when jumping/sneaking in 'max attack' state
|
//hack to avoid body pos desync when jumping/sneaking in 'max attack' state
|
||||||
if(!mAnimation->isPlaying(mCurrentWeapon))
|
if(!mAnimation->isPlaying(mCurrentWeapon))
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
|
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
|
||||||
break;
|
break;
|
||||||
case UpperCharState_MaxAttackToMinHit:
|
case UpperCharState_MaxAttackToMinHit:
|
||||||
|
@ -1440,33 +1452,16 @@ bool CharacterController::updateWeaponState()
|
||||||
{
|
{
|
||||||
mAnimation->disable(mCurrentWeapon);
|
mAnimation->disable(mCurrentWeapon);
|
||||||
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, true,
|
MWRender::Animation::BlendMask_All, true,
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
weapSpeed, start, stop, 0.0f, 0);
|
||||||
else
|
else
|
||||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||||
MWRender::Animation::Group_UpperBody, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
weapSpeed, start, stop, 0.0f, 0);
|
weapSpeed, start, stop, 0.0f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//if playing combat animation and lowerbody is not busy switch to whole body animation
|
|
||||||
if((weaptype != WeapType_None || mUpperBodyState == UpperCharState_UnEquipingWeap) && animPlaying)
|
|
||||||
{
|
|
||||||
if( mMovementState != CharState_None ||
|
|
||||||
mJumpState != JumpState_None ||
|
|
||||||
mHitState != CharState_None ||
|
|
||||||
MWBase::Environment::get().getWorld()->isSwimming(mPtr) ||
|
|
||||||
cls.getCreatureStats(mPtr).getMovementFlag(CreatureStats::Flag_Sneak))
|
|
||||||
{
|
|
||||||
mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_UpperBody);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mAnimation->changeGroups(mCurrentWeapon, MWRender::Animation::Group_All);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPtr.getClass().hasInventoryStore(mPtr))
|
if (mPtr.getClass().hasInventoryStore(mPtr))
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
||||||
|
@ -1475,7 +1470,7 @@ bool CharacterController::updateWeaponState()
|
||||||
&& updateCarriedLeftVisible(mWeaponType))
|
&& updateCarriedLeftVisible(mWeaponType))
|
||||||
|
|
||||||
{
|
{
|
||||||
mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm,
|
mAnimation->play("torch", Priority_Torch, MWRender::Animation::BlendMask_LeftArm,
|
||||||
false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true);
|
false, 1.0f, "start", "stop", 0.0f, (~(size_t)0), true);
|
||||||
}
|
}
|
||||||
else if (mAnimation->isPlaying("torch"))
|
else if (mAnimation->isPlaying("torch"))
|
||||||
|
@ -1505,7 +1500,7 @@ void CharacterController::update(float duration)
|
||||||
mAnimQueue.pop_front();
|
mAnimQueue.pop_front();
|
||||||
|
|
||||||
mAnimation->play(mAnimQueue.front().first, Priority_Default,
|
mAnimation->play(mAnimQueue.front().first, Priority_Default,
|
||||||
MWRender::Animation::Group_All, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
1.0f, "start", "stop", 0.0f, mAnimQueue.front().second);
|
1.0f, "start", "stop", 0.0f, mAnimQueue.front().second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1562,6 +1557,8 @@ void CharacterController::update(float duration)
|
||||||
|
|
||||||
CharacterState movestate = CharState_None;
|
CharacterState movestate = CharState_None;
|
||||||
CharacterState idlestate = CharState_SpecialIdle;
|
CharacterState idlestate = CharState_SpecialIdle;
|
||||||
|
JumpingState jumpstate = JumpState_None;
|
||||||
|
|
||||||
bool forcestateupdate = false;
|
bool forcestateupdate = false;
|
||||||
|
|
||||||
mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f;
|
mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f;
|
||||||
|
@ -1644,7 +1641,7 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
forcestateupdate = (mJumpState != JumpState_InAir);
|
forcestateupdate = (mJumpState != JumpState_InAir);
|
||||||
mJumpState = JumpState_InAir;
|
jumpstate = JumpState_InAir;
|
||||||
|
|
||||||
static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat();
|
static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat();
|
||||||
static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat();
|
static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat();
|
||||||
|
@ -1689,7 +1686,7 @@ void CharacterController::update(float duration)
|
||||||
else if(mJumpState == JumpState_InAir)
|
else if(mJumpState == JumpState_InAir)
|
||||||
{
|
{
|
||||||
forcestateupdate = true;
|
forcestateupdate = true;
|
||||||
mJumpState = JumpState_Landing;
|
jumpstate = JumpState_Landing;
|
||||||
vec.z() = 0.0f;
|
vec.z() = 0.0f;
|
||||||
|
|
||||||
float height = cls.getCreatureStats(mPtr).land();
|
float height = cls.getCreatureStats(mPtr).land();
|
||||||
|
@ -1720,7 +1717,7 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mJumpState = JumpState_None;
|
jumpstate = JumpState_None;
|
||||||
vec.z() = 0.0f;
|
vec.z() = 0.0f;
|
||||||
|
|
||||||
inJump = false;
|
inJump = false;
|
||||||
|
@ -1786,19 +1783,22 @@ void CharacterController::update(float duration)
|
||||||
mAnimQueue.pop_front();
|
mAnimQueue.pop_front();
|
||||||
|
|
||||||
mAnimation->play(mAnimQueue.front().first, Priority_Default,
|
mAnimation->play(mAnimQueue.front().first, Priority_Default,
|
||||||
MWRender::Animation::Group_All, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
1.0f, "start", "stop", 0.0f, mAnimQueue.front().second);
|
1.0f, "start", "stop", 0.0f, mAnimQueue.front().second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mSkipAnim)
|
||||||
|
{
|
||||||
// bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used.
|
// bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used.
|
||||||
if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr))
|
if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr))
|
||||||
forcestateupdate = updateWeaponState() || forcestateupdate;
|
forcestateupdate = updateWeaponState() || forcestateupdate;
|
||||||
else
|
else
|
||||||
forcestateupdate = updateCreatureState() || forcestateupdate;
|
forcestateupdate = updateCreatureState() || forcestateupdate;
|
||||||
|
|
||||||
if (!mSkipAnim)
|
refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate);
|
||||||
refreshCurrentAnims(idlestate, movestate, forcestateupdate);
|
}
|
||||||
|
|
||||||
if (inJump)
|
if (inJump)
|
||||||
mMovementAnimationControlled = false;
|
mMovementAnimationControlled = false;
|
||||||
|
|
||||||
|
@ -1894,7 +1894,7 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int
|
||||||
|
|
||||||
mIdleState = CharState_SpecialIdle;
|
mIdleState = CharState_SpecialIdle;
|
||||||
mAnimation->play(groupname, Priority_Default,
|
mAnimation->play(groupname, Priority_Default,
|
||||||
MWRender::Animation::Group_All, false, 1.0f,
|
MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||||
((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1);
|
((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1);
|
||||||
}
|
}
|
||||||
else if(mode == 0)
|
else if(mode == 0)
|
||||||
|
@ -1934,7 +1934,7 @@ void CharacterController::forceStateUpdate()
|
||||||
return;
|
return;
|
||||||
clearAnimQueue();
|
clearAnimQueue();
|
||||||
|
|
||||||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||||
if(mDeathState != CharState_None)
|
if(mDeathState != CharState_None)
|
||||||
{
|
{
|
||||||
playRandomDeath();
|
playRandomDeath();
|
||||||
|
@ -2052,12 +2052,13 @@ void CharacterController::setAttackingOrSpell(bool attackingOrSpell)
|
||||||
|
|
||||||
bool CharacterController::readyToPrepareAttack() const
|
bool CharacterController::readyToPrepareAttack() const
|
||||||
{
|
{
|
||||||
return mHitState == CharState_None && mUpperBodyState <= UpperCharState_WeapEquiped;
|
return (mHitState == CharState_None || mHitState == CharState_Block)
|
||||||
|
&& mUpperBodyState <= UpperCharState_WeapEquiped;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterController::readyToStartAttack() const
|
bool CharacterController::readyToStartAttack() const
|
||||||
{
|
{
|
||||||
if (mHitState != CharState_None)
|
if (mHitState != CharState_None && mHitState != CharState_Block)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mPtr.getClass().hasInventoryStore(mPtr) || mPtr.getClass().isBipedal(mPtr))
|
if (mPtr.getClass().hasInventoryStore(mPtr) || mPtr.getClass().isBipedal(mPtr))
|
||||||
|
|
|
@ -32,6 +32,7 @@ enum Priority {
|
||||||
Priority_Movement,
|
Priority_Movement,
|
||||||
Priority_Hit,
|
Priority_Hit,
|
||||||
Priority_Weapon,
|
Priority_Weapon,
|
||||||
|
Priority_Block,
|
||||||
Priority_Knockdown,
|
Priority_Knockdown,
|
||||||
Priority_Torch,
|
Priority_Torch,
|
||||||
Priority_Storm,
|
Priority_Storm,
|
||||||
|
@ -185,7 +186,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
||||||
|
|
||||||
void determineAttackType();
|
void determineAttackType();
|
||||||
|
|
||||||
void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false);
|
void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);
|
||||||
|
|
||||||
void clearAnimQueue();
|
void clearAnimQueue();
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,7 @@ namespace MWRender
|
||||||
|
|
||||||
typedef std::map<std::string, osg::ref_ptr<NifOsg::KeyframeController> > ControllerMap;
|
typedef std::map<std::string, osg::ref_ptr<NifOsg::KeyframeController> > ControllerMap;
|
||||||
|
|
||||||
ControllerMap mControllerMap[Animation::sNumGroups];
|
ControllerMap mControllerMap[Animation::sNumBlendMasks];
|
||||||
|
|
||||||
const std::multimap<float, std::string>& getTextKeys();
|
const std::multimap<float, std::string>& getTextKeys();
|
||||||
};
|
};
|
||||||
|
@ -261,7 +261,7 @@ namespace MWRender
|
||||||
, mHeadYawRadians(0.f)
|
, mHeadYawRadians(0.f)
|
||||||
, mHeadPitchRadians(0.f)
|
, mHeadPitchRadians(0.f)
|
||||||
{
|
{
|
||||||
for(size_t i = 0;i < sNumGroups;i++)
|
for(size_t i = 0;i < sNumBlendMasks;i++)
|
||||||
mAnimationTimePtr[i].reset(new AnimationTime);
|
mAnimationTimePtr[i].reset(new AnimationTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,9 +299,9 @@ namespace MWRender
|
||||||
mResetAccumRootCallback->setAccumulate(mAccumulate);
|
mResetAccumRootCallback->setAccumulate(mAccumulate);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Animation::detectAnimGroup(osg::Node* node)
|
size_t Animation::detectBlendMask(osg::Node* node)
|
||||||
{
|
{
|
||||||
static const char sGroupRoots[sNumGroups][32] = {
|
static const char sBlendMaskRoots[sNumBlendMasks][32] = {
|
||||||
"", /* Lower body / character root */
|
"", /* Lower body / character root */
|
||||||
"Bip01 Spine1", /* Torso */
|
"Bip01 Spine1", /* Torso */
|
||||||
"Bip01 L Clavicle", /* Left arm */
|
"Bip01 L Clavicle", /* Left arm */
|
||||||
|
@ -311,9 +311,9 @@ namespace MWRender
|
||||||
while(node != mObjectRoot)
|
while(node != mObjectRoot)
|
||||||
{
|
{
|
||||||
const std::string &name = node->getName();
|
const std::string &name = node->getName();
|
||||||
for(size_t i = 1;i < sNumGroups;i++)
|
for(size_t i = 1;i < sNumBlendMasks;i++)
|
||||||
{
|
{
|
||||||
if(name == sGroupRoots[i])
|
if(name == sBlendMaskRoots[i])
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,13 +361,13 @@ namespace MWRender
|
||||||
|
|
||||||
osg::Node* node = found->second;
|
osg::Node* node = found->second;
|
||||||
|
|
||||||
size_t group = detectAnimGroup(node);
|
size_t blendMask = detectBlendMask(node);
|
||||||
|
|
||||||
// clone the controller, because each Animation needs its own ControllerSource
|
// clone the controller, because each Animation needs its own ControllerSource
|
||||||
osg::ref_ptr<NifOsg::KeyframeController> cloned = osg::clone(it->second.get(), osg::CopyOp::DEEP_COPY_ALL);
|
osg::ref_ptr<NifOsg::KeyframeController> cloned = osg::clone(it->second.get(), osg::CopyOp::DEEP_COPY_ALL);
|
||||||
cloned->setSource(mAnimationTimePtr[group]);
|
cloned->setSource(mAnimationTimePtr[blendMask]);
|
||||||
|
|
||||||
animsrc->mControllerMap[group].insert(std::make_pair(bonename, cloned));
|
animsrc->mControllerMap[blendMask].insert(std::make_pair(bonename, cloned));
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimSources.push_back(animsrc);
|
mAnimSources.push_back(animsrc);
|
||||||
|
@ -390,7 +390,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
mStates.clear();
|
mStates.clear();
|
||||||
|
|
||||||
for(size_t i = 0;i < sNumGroups;i++)
|
for(size_t i = 0;i < sNumBlendMasks;i++)
|
||||||
mAnimationTimePtr[i]->setTimePtr(boost::shared_ptr<float>());
|
mAnimationTimePtr[i]->setTimePtr(boost::shared_ptr<float>());
|
||||||
|
|
||||||
mAccumCtrl = NULL;
|
mAccumCtrl = NULL;
|
||||||
|
@ -461,7 +461,7 @@ namespace MWRender
|
||||||
mTextKeyListener->handleTextKey(groupname, key, map);
|
mTextKeyListener->handleTextKey(groupname, key, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult,
|
void Animation::play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
|
||||||
const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback)
|
const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback)
|
||||||
{
|
{
|
||||||
if(!mObjectRoot || mAnimSources.empty())
|
if(!mObjectRoot || mAnimSources.empty())
|
||||||
|
@ -473,8 +473,6 @@ namespace MWRender
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
priority = std::max(0, priority);
|
|
||||||
|
|
||||||
AnimStateMap::iterator stateiter = mStates.begin();
|
AnimStateMap::iterator stateiter = mStates.begin();
|
||||||
while(stateiter != mStates.end())
|
while(stateiter != mStates.end())
|
||||||
{
|
{
|
||||||
|
@ -505,7 +503,7 @@ namespace MWRender
|
||||||
state.mLoopCount = loops;
|
state.mLoopCount = loops;
|
||||||
state.mPlaying = (state.getTime() < state.mStopTime);
|
state.mPlaying = (state.getTime() < state.mStopTime);
|
||||||
state.mPriority = priority;
|
state.mPriority = priority;
|
||||||
state.mGroups = groups;
|
state.mBlendMask = blendMask;
|
||||||
state.mAutoDisable = autodisable;
|
state.mAutoDisable = autodisable;
|
||||||
mStates[groupname] = state;
|
mStates[groupname] = state;
|
||||||
|
|
||||||
|
@ -600,9 +598,7 @@ namespace MWRender
|
||||||
state.setTime(state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint));
|
state.setTime(state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint));
|
||||||
|
|
||||||
// mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation
|
// mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation
|
||||||
// (see handleTextKey). But if startpoint is already past these keys, we need to assign them now.
|
// (see handleTextKey). But if startpoint is already past these keys, or start time is == stop time, we need to assign them now.
|
||||||
if(state.getTime() > state.mStartTime)
|
|
||||||
{
|
|
||||||
const std::string loopstarttag = groupname+": loop start";
|
const std::string loopstarttag = groupname+": loop start";
|
||||||
const std::string loopstoptag = groupname+": loop stop";
|
const std::string loopstoptag = groupname+": loop stop";
|
||||||
|
|
||||||
|
@ -617,7 +613,6 @@ namespace MWRender
|
||||||
else if (key->second == loopstoptag)
|
else if (key->second == loopstoptag)
|
||||||
state.mLoopStopTime = key->first;
|
state.mLoopStopTime = key->first;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -643,35 +638,35 @@ namespace MWRender
|
||||||
|
|
||||||
mAccumCtrl = NULL;
|
mAccumCtrl = NULL;
|
||||||
|
|
||||||
for(size_t grp = 0;grp < sNumGroups;grp++)
|
for(size_t blendMask = 0;blendMask < sNumBlendMasks;blendMask++)
|
||||||
{
|
{
|
||||||
AnimStateMap::const_iterator active = mStates.end();
|
AnimStateMap::const_iterator active = mStates.end();
|
||||||
|
|
||||||
AnimStateMap::const_iterator state = mStates.begin();
|
AnimStateMap::const_iterator state = mStates.begin();
|
||||||
for(;state != mStates.end();++state)
|
for(;state != mStates.end();++state)
|
||||||
{
|
{
|
||||||
if(!(state->second.mGroups&(1<<grp)))
|
if(!(state->second.mBlendMask&(1<<blendMask)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(active == mStates.end() || active->second.mPriority < state->second.mPriority)
|
if(active == mStates.end() || active->second.mPriority.mPriority[blendMask] < state->second.mPriority.mPriority[blendMask])
|
||||||
active = state;
|
active = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
mAnimationTimePtr[grp]->setTimePtr(active == mStates.end() ? boost::shared_ptr<float>() : active->second.mTime);
|
mAnimationTimePtr[blendMask]->setTimePtr(active == mStates.end() ? boost::shared_ptr<float>() : active->second.mTime);
|
||||||
|
|
||||||
// add external controllers for the AnimSource active in this group
|
// add external controllers for the AnimSource active in this blend mask
|
||||||
if (active != mStates.end())
|
if (active != mStates.end())
|
||||||
{
|
{
|
||||||
boost::shared_ptr<AnimSource> animsrc = active->second.mSource;
|
boost::shared_ptr<AnimSource> animsrc = active->second.mSource;
|
||||||
|
|
||||||
for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[grp].begin(); it != animsrc->mControllerMap[grp].end(); ++it)
|
for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[blendMask].begin(); it != animsrc->mControllerMap[blendMask].end(); ++it)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Node> node = mNodeMap.at(it->first); // this should not throw, we already checked for the node existing in addAnimSource
|
osg::ref_ptr<osg::Node> node = mNodeMap.at(it->first); // this should not throw, we already checked for the node existing in addAnimSource
|
||||||
|
|
||||||
node->addUpdateCallback(it->second);
|
node->addUpdateCallback(it->second);
|
||||||
mActiveControllers.insert(std::make_pair(node, it->second));
|
mActiveControllers.insert(std::make_pair(node, it->second));
|
||||||
|
|
||||||
if (grp == 0 && node == mAccumRoot)
|
if (blendMask == 0 && node == mAccumRoot)
|
||||||
{
|
{
|
||||||
mAccumCtrl = it->second;
|
mAccumCtrl = it->second;
|
||||||
|
|
||||||
|
@ -690,20 +685,6 @@ namespace MWRender
|
||||||
addControllers();
|
addControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::changeGroups(const std::string &groupname, int groups)
|
|
||||||
{
|
|
||||||
AnimStateMap::iterator stateiter = mStates.find(groupname);
|
|
||||||
if(stateiter != mStates.end())
|
|
||||||
{
|
|
||||||
if(stateiter->second.mGroups != groups)
|
|
||||||
{
|
|
||||||
stateiter->second.mGroups = groups;
|
|
||||||
resetActiveGroups();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Animation::stopLooping(const std::string& groupname)
|
void Animation::stopLooping(const std::string& groupname)
|
||||||
{
|
{
|
||||||
AnimStateMap::iterator stateiter = mStates.find(groupname);
|
AnimStateMap::iterator stateiter = mStates.find(groupname);
|
||||||
|
@ -1208,9 +1189,10 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter)
|
for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter)
|
||||||
{
|
{
|
||||||
if((stateiter->second.mPriority > MWMechanics::Priority_Movement
|
if (stateiter->second.mPriority.contains(int(MWMechanics::Priority_Hit))
|
||||||
&& stateiter->second.mPriority < MWMechanics::Priority_Torch)
|
|| stateiter->second.mPriority.contains(int(MWMechanics::Priority_Weapon))
|
||||||
|| stateiter->second.mPriority == MWMechanics::Priority_Death)
|
|| stateiter->second.mPriority.contains(int(MWMechanics::Priority_Knockdown))
|
||||||
|
|| stateiter->second.mPriority.contains(int(MWMechanics::Priority_Death)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -67,16 +67,53 @@ typedef boost::shared_ptr<PartHolder> PartHolderPtr;
|
||||||
class Animation
|
class Animation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Group {
|
enum BoneGroup {
|
||||||
Group_LowerBody = 1<<0,
|
BoneGroup_LowerBody = 0,
|
||||||
|
BoneGroup_Torso,
|
||||||
|
BoneGroup_LeftArm,
|
||||||
|
BoneGroup_RightArm
|
||||||
|
};
|
||||||
|
|
||||||
Group_Torso = 1<<1,
|
enum BlendMask {
|
||||||
Group_LeftArm = 1<<2,
|
BlendMask_LowerBody = 1<<0,
|
||||||
Group_RightArm = 1<<3,
|
BlendMask_Torso = 1<<1,
|
||||||
|
BlendMask_LeftArm = 1<<2,
|
||||||
|
BlendMask_RightArm = 1<<3,
|
||||||
|
|
||||||
Group_UpperBody = Group_Torso | Group_LeftArm | Group_RightArm,
|
BlendMask_UpperBody = BlendMask_Torso | BlendMask_LeftArm | BlendMask_RightArm,
|
||||||
|
|
||||||
Group_All = Group_LowerBody | Group_UpperBody
|
BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody
|
||||||
|
};
|
||||||
|
/* This is the number of *discrete* blend masks. */
|
||||||
|
static const size_t sNumBlendMasks = 4;
|
||||||
|
|
||||||
|
/// Holds an animation priority value for each BoneGroup.
|
||||||
|
struct AnimPriority
|
||||||
|
{
|
||||||
|
/// Convenience constructor, initialises all priorities to the same value.
|
||||||
|
AnimPriority(int priority)
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<sNumBlendMasks; ++i)
|
||||||
|
mPriority[i] = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const AnimPriority& other) const
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<sNumBlendMasks; ++i)
|
||||||
|
if (other.mPriority[i] != mPriority[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(int priority) const
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<sNumBlendMasks; ++i)
|
||||||
|
if (priority == mPriority[i])
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mPriority[sNumBlendMasks];
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextKeyListener
|
class TextKeyListener
|
||||||
|
@ -89,9 +126,6 @@ public:
|
||||||
void setTextKeyListener(TextKeyListener* listener);
|
void setTextKeyListener(TextKeyListener* listener);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* This is the number of *discrete* groups. */
|
|
||||||
static const size_t sNumGroups = 4;
|
|
||||||
|
|
||||||
class AnimationTime : public SceneUtil::ControllerSource
|
class AnimationTime : public SceneUtil::ControllerSource
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -132,13 +166,13 @@ protected:
|
||||||
bool mPlaying;
|
bool mPlaying;
|
||||||
size_t mLoopCount;
|
size_t mLoopCount;
|
||||||
|
|
||||||
int mPriority;
|
AnimPriority mPriority;
|
||||||
int mGroups;
|
int mBlendMask;
|
||||||
bool mAutoDisable;
|
bool mAutoDisable;
|
||||||
|
|
||||||
AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),
|
AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),
|
||||||
mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
|
mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
|
||||||
mPriority(0), mGroups(0), mAutoDisable(true)
|
mPriority(0), mBlendMask(0), mAutoDisable(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
~AnimState();
|
~AnimState();
|
||||||
|
@ -176,7 +210,7 @@ protected:
|
||||||
typedef std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> > ControllerMap;
|
typedef std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> > ControllerMap;
|
||||||
ControllerMap mActiveControllers;
|
ControllerMap mActiveControllers;
|
||||||
|
|
||||||
boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumGroups];
|
boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumBlendMasks];
|
||||||
|
|
||||||
// Stored in all lowercase for a case-insensitive lookup
|
// Stored in all lowercase for a case-insensitive lookup
|
||||||
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
|
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
|
||||||
|
@ -213,7 +247,7 @@ protected:
|
||||||
*/
|
*/
|
||||||
void resetActiveGroups();
|
void resetActiveGroups();
|
||||||
|
|
||||||
size_t detectAnimGroup(osg::Node* node);
|
size_t detectBlendMask(osg::Node* node);
|
||||||
|
|
||||||
/* Updates the position of the accum root node for the given time, and
|
/* Updates the position of the accum root node for the given time, and
|
||||||
* returns the wanted movement vector from the previous time. */
|
* returns the wanted movement vector from the previous time. */
|
||||||
|
@ -304,7 +338,7 @@ public:
|
||||||
* \param priority Priority of the animation. The animation will play on
|
* \param priority Priority of the animation. The animation will play on
|
||||||
* bone groups that don't have another animation set of a
|
* bone groups that don't have another animation set of a
|
||||||
* higher priority.
|
* higher priority.
|
||||||
* \param groups Bone groups to play the animation on.
|
* \param blendMask Bone groups to play the animation on.
|
||||||
* \param autodisable Automatically disable the animation when it stops
|
* \param autodisable Automatically disable the animation when it stops
|
||||||
* playing.
|
* playing.
|
||||||
* \param speedmult Speed multiplier for the animation.
|
* \param speedmult Speed multiplier for the animation.
|
||||||
|
@ -319,7 +353,7 @@ public:
|
||||||
* \param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use
|
* \param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use
|
||||||
* the "start" and "stop" keys for looping?
|
* the "start" and "stop" keys for looping?
|
||||||
*/
|
*/
|
||||||
void play(const std::string &groupname, int priority, int groups, bool autodisable,
|
void play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable,
|
||||||
float speedmult, const std::string &start, const std::string &stop,
|
float speedmult, const std::string &start, const std::string &stop,
|
||||||
float startpoint, size_t loops, bool loopfallback=false);
|
float startpoint, size_t loops, bool loopfallback=false);
|
||||||
|
|
||||||
|
@ -358,7 +392,6 @@ public:
|
||||||
* \param groupname Animation group to disable.
|
* \param groupname Animation group to disable.
|
||||||
*/
|
*/
|
||||||
void disable(const std::string &groupname);
|
void disable(const std::string &groupname);
|
||||||
void changeGroups(const std::string &groupname, int group);
|
|
||||||
|
|
||||||
/** Retrieves the velocity (in units per second) that the animation will move. */
|
/** Retrieves the velocity (in units per second) that the animation will move. */
|
||||||
float getVelocity(const std::string &groupname) const;
|
float getVelocity(const std::string &groupname) const;
|
||||||
|
|
|
@ -241,13 +241,13 @@ namespace MWRender
|
||||||
mAnimation->showCarriedLeft(showCarriedLeft);
|
mAnimation->showCarriedLeft(showCarriedLeft);
|
||||||
|
|
||||||
mCurrentAnimGroup = groupname;
|
mCurrentAnimGroup = groupname;
|
||||||
mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
mAnimation->play(mCurrentAnimGroup, 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
||||||
|
|
||||||
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||||
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && showCarriedLeft)
|
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && showCarriedLeft)
|
||||||
{
|
{
|
||||||
if(!mAnimation->getInfo("torch"))
|
if(!mAnimation->getInfo("torch"))
|
||||||
mAnimation->play("torch", 2, MWRender::Animation::Group_LeftArm, false,
|
mAnimation->play("torch", 2, Animation::BlendMask_LeftArm, false,
|
||||||
1.0f, "start", "stop", 0.0f, ~0ul, true);
|
1.0f, "start", "stop", 0.0f, ~0ul, true);
|
||||||
}
|
}
|
||||||
else if(mAnimation->getInfo("torch"))
|
else if(mAnimation->getInfo("torch"))
|
||||||
|
@ -357,7 +357,7 @@ namespace MWRender
|
||||||
|
|
||||||
void RaceSelectionPreview::onSetup ()
|
void RaceSelectionPreview::onSetup ()
|
||||||
{
|
{
|
||||||
mAnimation->play("idle", 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
mAnimation->play("idle", 1, Animation::BlendMask_All, false, 1.0f, "start", "stop", 0.0f, 0);
|
||||||
mAnimation->runAnimation(0.f);
|
mAnimation->runAnimation(0.f);
|
||||||
|
|
||||||
// attach camera to follow the head node
|
// attach camera to follow the head node
|
||||||
|
|
Loading…
Reference in a new issue