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/inventorystore.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -236,7 +237,7 @@ std::string CharacterController::chooseRandomGroup (const std::string& prefix, i
|
|||
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
|
||||
if(mPtr.getClass().isActor())
|
||||
|
@ -251,26 +252,28 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
{
|
||||
mHitState = CharState_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);
|
||||
}
|
||||
else if(knockdown)
|
||||
{
|
||||
mHitState = CharState_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)
|
||||
{
|
||||
mHitState = CharState_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)
|
||||
{
|
||||
mHitState = CharState_Block;
|
||||
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
|
||||
|
@ -303,7 +306,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
{
|
||||
mHitState = CharState_KnockDown;
|
||||
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))
|
||||
weap = sWeaponTypeListEnd;
|
||||
|
||||
if(force && mJumpState != JumpState_None)
|
||||
if(force || jump != mJumpState)
|
||||
{
|
||||
std::string jump;
|
||||
MWRender::Animation::Group jumpgroup = MWRender::Animation::Group_All;
|
||||
bool startAtLoop = (jump == mJumpState);
|
||||
mJumpState = jump;
|
||||
|
||||
std::string jumpAnimName;
|
||||
MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All;
|
||||
if(mJumpState != JumpState_None)
|
||||
{
|
||||
jump = "jump";
|
||||
jumpAnimName = "jump";
|
||||
if(weap != sWeaponTypeListEnd)
|
||||
{
|
||||
jump += weap->shortgroup;
|
||||
if(!mAnimation->hasAnimation(jump))
|
||||
jumpAnimName += weap->shortgroup;
|
||||
if(!mAnimation->hasAnimation(jumpAnimName))
|
||||
{
|
||||
jumpgroup = MWRender::Animation::Group_LowerBody;
|
||||
jump = "jump";
|
||||
jumpmask = MWRender::Animation::BlendMask_LowerBody;
|
||||
jumpAnimName = "jump";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mJumpState == JumpState_InAir)
|
||||
{
|
||||
int mode = ((jump == mCurrentJump) ? 2 : 1);
|
||||
|
||||
mAnimation->disable(mCurrentJump);
|
||||
mCurrentJump = jump;
|
||||
mCurrentJump = jumpAnimName;
|
||||
if (mAnimation->hasAnimation("jump"))
|
||||
mAnimation->play(mCurrentJump, Priority_Jump, jumpgroup, false,
|
||||
1.0f, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
|
||||
mAnimation->play(mCurrentJump, Priority_Jump, jumpmask, false,
|
||||
1.0f, (startAtLoop?"loop start":"start"), "stop", 0.0f, ~0ul);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAnimation->disable(mCurrentJump);
|
||||
mCurrentJump.clear();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -353,55 +357,55 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
{
|
||||
mMovementState = movement;
|
||||
|
||||
std::string movement;
|
||||
MWRender::Animation::Group movegroup = MWRender::Animation::Group_All;
|
||||
std::string movementAnimName;
|
||||
MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All;
|
||||
const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState));
|
||||
if(movestate != sMovementListEnd)
|
||||
{
|
||||
movement = movestate->groupname;
|
||||
if(weap != sWeaponTypeListEnd && movement.find("swim") == std::string::npos)
|
||||
movementAnimName = movestate->groupname;
|
||||
if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos)
|
||||
{
|
||||
movement += weap->shortgroup;
|
||||
if(!mAnimation->hasAnimation(movement))
|
||||
movementAnimName += weap->shortgroup;
|
||||
if(!mAnimation->hasAnimation(movementAnimName))
|
||||
{
|
||||
movegroup = MWRender::Animation::Group_LowerBody;
|
||||
movement = movestate->groupname;
|
||||
movemask = MWRender::Animation::BlendMask_LowerBody;
|
||||
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)
|
||||
{
|
||||
std::string::size_type runpos = movement.find("run");
|
||||
std::string::size_type runpos = movementAnimName.find("run");
|
||||
if (runpos != std::string::npos)
|
||||
{
|
||||
movement.replace(runpos, runpos+3, "walk");
|
||||
if (!mAnimation->hasAnimation(movement))
|
||||
movement.clear();
|
||||
movementAnimName.replace(runpos, runpos+3, "walk");
|
||||
if (!mAnimation->hasAnimation(movementAnimName))
|
||||
movementAnimName.clear();
|
||||
}
|
||||
else
|
||||
movement.clear();
|
||||
movementAnimName.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
movegroup = MWRender::Animation::Group_LowerBody;
|
||||
movement.erase(swimpos, 4);
|
||||
if(!mAnimation->hasAnimation(movement))
|
||||
movement.clear();
|
||||
movemask = MWRender::Animation::BlendMask_LowerBody;
|
||||
movementAnimName.erase(swimpos, 4);
|
||||
if(!mAnimation->hasAnimation(movementAnimName))
|
||||
movementAnimName.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're playing the same animation, restart from the loop start instead of the
|
||||
* beginning. */
|
||||
int mode = ((movement == mCurrentMovement) ? 2 : 1);
|
||||
int mode = ((movementAnimName == mCurrentMovement) ? 2 : 1);
|
||||
|
||||
mMovementAnimationControlled = true;
|
||||
|
||||
mAnimation->disable(mCurrentMovement);
|
||||
mCurrentMovement = movement;
|
||||
mCurrentMovement = movementAnimName;
|
||||
if(!mCurrentMovement.empty())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
// the idle animation should be displayed
|
||||
if ((mUpperBodyState != UpperCharState_Nothing
|
||||
|| mMovementState != CharState_None
|
||||
|| (mMovementState != CharState_None && mMovementState != CharState_TurnLeft && mMovementState != CharState_TurnRight)
|
||||
|| mHitState != CharState_None)
|
||||
&& !mPtr.getClass().isBipedal(mPtr))
|
||||
idle = CharState_None;
|
||||
|
@ -486,7 +500,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
mAnimation->disable(mCurrentIdle);
|
||||
mCurrentIdle = idle;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -602,7 +616,7 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
|
|||
mCurrentJump = "";
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -704,7 +718,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
|
||||
|
||||
if(mDeathState == CharState_None)
|
||||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
||||
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||
|
||||
mAnimation->runAnimation(0.f);
|
||||
}
|
||||
|
@ -868,10 +882,10 @@ void CharacterController::updateIdleStormState()
|
|||
mAnimation->getInfo("idlestorm", &complete);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
@ -882,7 +896,7 @@ void CharacterController::updateIdleStormState()
|
|||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -989,7 +1003,7 @@ bool CharacterController::updateCreatureState()
|
|||
if (!mCurrentWeapon.empty())
|
||||
{
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_All, true,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
1, startKey, stopKey,
|
||||
0.0f, 0);
|
||||
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;
|
||||
if(weaptype != mWeaponType && mHitState != CharState_KnockDown && mHitState != CharState_KnockOut
|
||||
&& mHitState != CharState_Hit)
|
||||
|
@ -1063,8 +1080,8 @@ bool CharacterController::updateWeaponState()
|
|||
if(weaptype == WeapType_None)
|
||||
{
|
||||
getWeaponGroup(mWeaponType, weapgroup);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
mAnimation->play(weapgroup, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||
}
|
||||
|
@ -1074,8 +1091,8 @@ bool CharacterController::updateWeaponState()
|
|||
mAnimation->showWeapons(false);
|
||||
mAnimation->setWeaponGroup(weapgroup);
|
||||
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
mAnimation->play(weapgroup, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
||||
|
||||
|
@ -1145,7 +1162,7 @@ bool CharacterController::updateWeaponState()
|
|||
bool animPlaying;
|
||||
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);
|
||||
mAttackType.clear();
|
||||
|
@ -1154,6 +1171,10 @@ bool CharacterController::updateWeaponState()
|
|||
// Unset casting flag, otherwise pressing the mouse button down would
|
||||
// continue casting every frame if there is no animation
|
||||
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();
|
||||
|
||||
|
@ -1191,8 +1212,8 @@ bool CharacterController::updateWeaponState()
|
|||
case 2: mAttackType = "target"; break;
|
||||
}
|
||||
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
weapSpeed, mAttackType+" start", mAttackType+" stop",
|
||||
0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_CastingSpell;
|
||||
|
@ -1223,8 +1244,8 @@ bool CharacterController::updateWeaponState()
|
|||
else if(item.getTypeName() == typeid(ESM::Probe).name())
|
||||
Security(mPtr).probeTrap(target, item, resultMessage, resultSound);
|
||||
}
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
1.0f, "start", "stop", 0.0, 0);
|
||||
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
|
||||
|
||||
|
@ -1250,8 +1271,8 @@ bool CharacterController::updateWeaponState()
|
|||
determineAttackType();
|
||||
}
|
||||
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
weapSpeed, mAttackType+" start", mAttackType+" min attack",
|
||||
0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_StartToMinAttack;
|
||||
|
@ -1301,8 +1322,8 @@ bool CharacterController::updateWeaponState()
|
|||
mAttackStrength = attackStrength;
|
||||
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
weapSpeed, mAttackType+" max attack", mAttackType+" min hit",
|
||||
1.0f-complete, 0);
|
||||
|
||||
|
@ -1371,15 +1392,6 @@ bool CharacterController::updateWeaponState()
|
|||
mAnimation->attachArrow();
|
||||
|
||||
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)
|
||||
mUpperBodyState = UpperCharState_Nothing;
|
||||
|
@ -1397,8 +1409,8 @@ bool CharacterController::updateWeaponState()
|
|||
case UpperCharState_MinAttackToMaxAttack:
|
||||
//hack to avoid body pos desync when jumping/sneaking in 'max attack' state
|
||||
if(!mAnimation->isPlaying(mCurrentWeapon))
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
0, mAttackType+" min attack", mAttackType+" max attack", 0.999f, 0);
|
||||
break;
|
||||
case UpperCharState_MaxAttackToMinHit:
|
||||
|
@ -1440,33 +1452,16 @@ bool CharacterController::updateWeaponState()
|
|||
{
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
if (mUpperBodyState == UpperCharState_FollowStartToFollowStop)
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, true,
|
||||
weapSpeed, start, stop, 0.0f, 0);
|
||||
else
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
mAnimation->play(mCurrentWeapon, priorityWeapon,
|
||||
MWRender::Animation::BlendMask_All, false,
|
||||
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))
|
||||
{
|
||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
||||
|
@ -1475,7 +1470,7 @@ bool CharacterController::updateWeaponState()
|
|||
&& 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);
|
||||
}
|
||||
else if (mAnimation->isPlaying("torch"))
|
||||
|
@ -1505,7 +1500,7 @@ void CharacterController::update(float duration)
|
|||
mAnimQueue.pop_front();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1562,6 +1557,8 @@ void CharacterController::update(float duration)
|
|||
|
||||
CharacterState movestate = CharState_None;
|
||||
CharacterState idlestate = CharState_SpecialIdle;
|
||||
JumpingState jumpstate = JumpState_None;
|
||||
|
||||
bool forcestateupdate = false;
|
||||
|
||||
mHasMovedInXY = std::abs(vec.x())+std::abs(vec.y()) > 0.0f;
|
||||
|
@ -1644,7 +1641,7 @@ void CharacterController::update(float duration)
|
|||
}
|
||||
|
||||
forcestateupdate = (mJumpState != JumpState_InAir);
|
||||
mJumpState = JumpState_InAir;
|
||||
jumpstate = JumpState_InAir;
|
||||
|
||||
static const float fJumpMoveBase = gmst.find("fJumpMoveBase")->getFloat();
|
||||
static const float fJumpMoveMult = gmst.find("fJumpMoveMult")->getFloat();
|
||||
|
@ -1689,7 +1686,7 @@ void CharacterController::update(float duration)
|
|||
else if(mJumpState == JumpState_InAir)
|
||||
{
|
||||
forcestateupdate = true;
|
||||
mJumpState = JumpState_Landing;
|
||||
jumpstate = JumpState_Landing;
|
||||
vec.z() = 0.0f;
|
||||
|
||||
float height = cls.getCreatureStats(mPtr).land();
|
||||
|
@ -1720,7 +1717,7 @@ void CharacterController::update(float duration)
|
|||
}
|
||||
else
|
||||
{
|
||||
mJumpState = JumpState_None;
|
||||
jumpstate = JumpState_None;
|
||||
vec.z() = 0.0f;
|
||||
|
||||
inJump = false;
|
||||
|
@ -1786,19 +1783,22 @@ void CharacterController::update(float duration)
|
|||
mAnimQueue.pop_front();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
forcestateupdate = updateWeaponState() || forcestateupdate;
|
||||
else
|
||||
forcestateupdate = updateCreatureState() || forcestateupdate;
|
||||
|
||||
if (!mSkipAnim)
|
||||
refreshCurrentAnims(idlestate, movestate, forcestateupdate);
|
||||
{
|
||||
// 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))
|
||||
forcestateupdate = updateWeaponState() || forcestateupdate;
|
||||
else
|
||||
forcestateupdate = updateCreatureState() || forcestateupdate;
|
||||
|
||||
refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate);
|
||||
}
|
||||
|
||||
if (inJump)
|
||||
mMovementAnimationControlled = false;
|
||||
|
||||
|
@ -1894,7 +1894,7 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int
|
|||
|
||||
mIdleState = CharState_SpecialIdle;
|
||||
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);
|
||||
}
|
||||
else if(mode == 0)
|
||||
|
@ -1934,7 +1934,7 @@ void CharacterController::forceStateUpdate()
|
|||
return;
|
||||
clearAnimQueue();
|
||||
|
||||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
||||
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||
if(mDeathState != CharState_None)
|
||||
{
|
||||
playRandomDeath();
|
||||
|
@ -2052,12 +2052,13 @@ void CharacterController::setAttackingOrSpell(bool attackingOrSpell)
|
|||
|
||||
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
|
||||
{
|
||||
if (mHitState != CharState_None)
|
||||
if (mHitState != CharState_None && mHitState != CharState_Block)
|
||||
return false;
|
||||
|
||||
if (mPtr.getClass().hasInventoryStore(mPtr) || mPtr.getClass().isBipedal(mPtr))
|
||||
|
|
|
@ -32,6 +32,7 @@ enum Priority {
|
|||
Priority_Movement,
|
||||
Priority_Hit,
|
||||
Priority_Weapon,
|
||||
Priority_Block,
|
||||
Priority_Knockdown,
|
||||
Priority_Torch,
|
||||
Priority_Storm,
|
||||
|
@ -185,7 +186,7 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
|||
|
||||
void determineAttackType();
|
||||
|
||||
void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false);
|
||||
void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false);
|
||||
|
||||
void clearAnimQueue();
|
||||
|
||||
|
|
|
@ -219,7 +219,7 @@ namespace MWRender
|
|||
|
||||
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();
|
||||
};
|
||||
|
@ -261,7 +261,7 @@ namespace MWRender
|
|||
, mHeadYawRadians(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);
|
||||
}
|
||||
|
||||
|
@ -299,9 +299,9 @@ namespace MWRender
|
|||
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 */
|
||||
"Bip01 Spine1", /* Torso */
|
||||
"Bip01 L Clavicle", /* Left arm */
|
||||
|
@ -311,9 +311,9 @@ namespace MWRender
|
|||
while(node != mObjectRoot)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -361,13 +361,13 @@ namespace MWRender
|
|||
|
||||
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
|
||||
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);
|
||||
|
@ -390,7 +390,7 @@ namespace MWRender
|
|||
{
|
||||
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>());
|
||||
|
||||
mAccumCtrl = NULL;
|
||||
|
@ -461,7 +461,7 @@ namespace MWRender
|
|||
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)
|
||||
{
|
||||
if(!mObjectRoot || mAnimSources.empty())
|
||||
|
@ -473,8 +473,6 @@ namespace MWRender
|
|||
return;
|
||||
}
|
||||
|
||||
priority = std::max(0, priority);
|
||||
|
||||
AnimStateMap::iterator stateiter = mStates.begin();
|
||||
while(stateiter != mStates.end())
|
||||
{
|
||||
|
@ -505,7 +503,7 @@ namespace MWRender
|
|||
state.mLoopCount = loops;
|
||||
state.mPlaying = (state.getTime() < state.mStopTime);
|
||||
state.mPriority = priority;
|
||||
state.mGroups = groups;
|
||||
state.mBlendMask = blendMask;
|
||||
state.mAutoDisable = autodisable;
|
||||
mStates[groupname] = state;
|
||||
|
||||
|
@ -600,23 +598,20 @@ namespace MWRender
|
|||
state.setTime(state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint));
|
||||
|
||||
// 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.
|
||||
if(state.getTime() > state.mStartTime)
|
||||
// (see handleTextKey). But if startpoint is already past these keys, or start time is == stop time, we need to assign them now.
|
||||
const std::string loopstarttag = groupname+": loop start";
|
||||
const std::string loopstoptag = groupname+": loop stop";
|
||||
|
||||
NifOsg::TextKeyMap::const_reverse_iterator key(groupend);
|
||||
for (; key != startkey && key != keys.rend(); ++key)
|
||||
{
|
||||
const std::string loopstarttag = groupname+": loop start";
|
||||
const std::string loopstoptag = groupname+": loop stop";
|
||||
if (key->first > state.getTime())
|
||||
continue;
|
||||
|
||||
NifOsg::TextKeyMap::const_reverse_iterator key(groupend);
|
||||
for (; key != startkey && key != keys.rend(); ++key)
|
||||
{
|
||||
if (key->first > state.getTime())
|
||||
continue;
|
||||
|
||||
if (key->second == loopstarttag)
|
||||
state.mLoopStartTime = key->first;
|
||||
else if (key->second == loopstoptag)
|
||||
state.mLoopStopTime = key->first;
|
||||
}
|
||||
if (key->second == loopstarttag)
|
||||
state.mLoopStartTime = key->first;
|
||||
else if (key->second == loopstoptag)
|
||||
state.mLoopStopTime = key->first;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -643,35 +638,35 @@ namespace MWRender
|
|||
|
||||
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 state = mStates.begin();
|
||||
for(;state != mStates.end();++state)
|
||||
{
|
||||
if(!(state->second.mGroups&(1<<grp)))
|
||||
if(!(state->second.mBlendMask&(1<<blendMask)))
|
||||
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;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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
|
||||
|
||||
node->addUpdateCallback(it->second);
|
||||
mActiveControllers.insert(std::make_pair(node, it->second));
|
||||
|
||||
if (grp == 0 && node == mAccumRoot)
|
||||
if (blendMask == 0 && node == mAccumRoot)
|
||||
{
|
||||
mAccumCtrl = it->second;
|
||||
|
||||
|
@ -690,20 +685,6 @@ namespace MWRender
|
|||
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)
|
||||
{
|
||||
AnimStateMap::iterator stateiter = mStates.find(groupname);
|
||||
|
@ -1208,9 +1189,10 @@ namespace MWRender
|
|||
{
|
||||
for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter)
|
||||
{
|
||||
if((stateiter->second.mPriority > MWMechanics::Priority_Movement
|
||||
&& stateiter->second.mPriority < MWMechanics::Priority_Torch)
|
||||
|| stateiter->second.mPriority == MWMechanics::Priority_Death)
|
||||
if (stateiter->second.mPriority.contains(int(MWMechanics::Priority_Hit))
|
||||
|| stateiter->second.mPriority.contains(int(MWMechanics::Priority_Weapon))
|
||||
|| stateiter->second.mPriority.contains(int(MWMechanics::Priority_Knockdown))
|
||||
|| stateiter->second.mPriority.contains(int(MWMechanics::Priority_Death)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -67,16 +67,53 @@ typedef boost::shared_ptr<PartHolder> PartHolderPtr;
|
|||
class Animation
|
||||
{
|
||||
public:
|
||||
enum Group {
|
||||
Group_LowerBody = 1<<0,
|
||||
enum BoneGroup {
|
||||
BoneGroup_LowerBody = 0,
|
||||
BoneGroup_Torso,
|
||||
BoneGroup_LeftArm,
|
||||
BoneGroup_RightArm
|
||||
};
|
||||
|
||||
Group_Torso = 1<<1,
|
||||
Group_LeftArm = 1<<2,
|
||||
Group_RightArm = 1<<3,
|
||||
enum BlendMask {
|
||||
BlendMask_LowerBody = 1<<0,
|
||||
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
|
||||
|
@ -89,9 +126,6 @@ public:
|
|||
void setTextKeyListener(TextKeyListener* listener);
|
||||
|
||||
protected:
|
||||
/* This is the number of *discrete* groups. */
|
||||
static const size_t sNumGroups = 4;
|
||||
|
||||
class AnimationTime : public SceneUtil::ControllerSource
|
||||
{
|
||||
private:
|
||||
|
@ -132,13 +166,13 @@ protected:
|
|||
bool mPlaying;
|
||||
size_t mLoopCount;
|
||||
|
||||
int mPriority;
|
||||
int mGroups;
|
||||
AnimPriority mPriority;
|
||||
int mBlendMask;
|
||||
bool mAutoDisable;
|
||||
|
||||
AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),
|
||||
mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
|
||||
mPriority(0), mGroups(0), mAutoDisable(true)
|
||||
mPriority(0), mBlendMask(0), mAutoDisable(true)
|
||||
{
|
||||
}
|
||||
~AnimState();
|
||||
|
@ -176,7 +210,7 @@ protected:
|
|||
typedef std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> > ControllerMap;
|
||||
ControllerMap mActiveControllers;
|
||||
|
||||
boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumGroups];
|
||||
boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumBlendMasks];
|
||||
|
||||
// Stored in all lowercase for a case-insensitive lookup
|
||||
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
|
||||
|
@ -213,7 +247,7 @@ protected:
|
|||
*/
|
||||
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
|
||||
* 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
|
||||
* bone groups that don't have another animation set of a
|
||||
* 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
|
||||
* playing.
|
||||
* \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
|
||||
* 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 startpoint, size_t loops, bool loopfallback=false);
|
||||
|
||||
|
@ -358,7 +392,6 @@ public:
|
|||
* \param groupname Animation group to disable.
|
||||
*/
|
||||
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. */
|
||||
float getVelocity(const std::string &groupname) const;
|
||||
|
|
|
@ -241,13 +241,13 @@ namespace MWRender
|
|||
mAnimation->showCarriedLeft(showCarriedLeft);
|
||||
|
||||
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);
|
||||
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name() && showCarriedLeft)
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if(mAnimation->getInfo("torch"))
|
||||
|
@ -357,7 +357,7 @@ namespace MWRender
|
|||
|
||||
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);
|
||||
|
||||
// attach camera to follow the head node
|
||||
|
|
Loading…
Reference in a new issue