mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-14 16:14:38 +00:00
anim-clip -- sequence selection by afx effects
This commit is contained in:
parent
8c65467697
commit
ab88b8f489
6 changed files with 511 additions and 14 deletions
|
|
@ -20,6 +20,11 @@
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
||||||
|
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
|
||||||
|
// Copyright (C) 2015 Faust Logic, Inc.
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
||||||
|
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
#include "T3D/aiPlayer.h"
|
#include "T3D/aiPlayer.h"
|
||||||
|
|
||||||
|
|
@ -97,6 +102,9 @@ AIPlayer::AIPlayer()
|
||||||
mMoveSlowdown = true;
|
mMoveSlowdown = true;
|
||||||
mMoveState = ModeStop;
|
mMoveState = ModeStop;
|
||||||
|
|
||||||
|
// This new member saves the movement state of the AI so that
|
||||||
|
// it can be restored after a substituted animation is finished.
|
||||||
|
mMoveState_saved = -1;
|
||||||
mAimObject = 0;
|
mAimObject = 0;
|
||||||
mAimLocationSet = false;
|
mAimLocationSet = false;
|
||||||
mTargetInLOS = false;
|
mTargetInLOS = false;
|
||||||
|
|
@ -547,23 +555,27 @@ bool AIPlayer::getAIMove(Move *movePtr)
|
||||||
mMoveState = ModeMove;
|
mMoveState = ModeMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mMoveStuckTestCountdown > 0)
|
// Don't check for ai stuckness if animation during
|
||||||
--mMoveStuckTestCountdown;
|
// an anim-clip effect override.
|
||||||
else
|
if (mDamageState == Enabled && !(anim_clip_flags & ANIM_OVERRIDDEN) && !isAnimationLocked()) {
|
||||||
{
|
if (mMoveStuckTestCountdown > 0)
|
||||||
// We should check to see if we are stuck...
|
--mMoveStuckTestCountdown;
|
||||||
F32 locationDelta = (location - mLastLocation).len();
|
else
|
||||||
|
{
|
||||||
|
// We should check to see if we are stuck...
|
||||||
|
F32 locationDelta = (location - mLastLocation).len();
|
||||||
if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled)
|
if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled)
|
||||||
{
|
{
|
||||||
// If we are slowing down, then it's likely that our location delta will be less than
|
// If we are slowing down, then it's likely that our location delta will be less than
|
||||||
// our move stuck tolerance. Because we can be both slowing and stuck
|
// our move stuck tolerance. Because we can be both slowing and stuck
|
||||||
// we should TRY to check if we've moved. This could use better detection.
|
// we should TRY to check if we've moved. This could use better detection.
|
||||||
if ( mMoveState != ModeSlowing || locationDelta == 0 )
|
if ( mMoveState != ModeSlowing || locationDelta == 0 )
|
||||||
{
|
{
|
||||||
mMoveState = ModeStuck;
|
mMoveState = ModeStuck;
|
||||||
onStuck();
|
onStuck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -626,6 +638,7 @@ bool AIPlayer::getAIMove(Move *movePtr)
|
||||||
}
|
}
|
||||||
#endif // TORQUE_NAVIGATION_ENABLED
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
|
||||||
|
if (!(anim_clip_flags & ANIM_OVERRIDDEN) && !isAnimationLocked())
|
||||||
mLastLocation = location;
|
mLastLocation = location;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1415,6 +1428,47 @@ DefineEngineMethod( AIPlayer, clearMoveTriggers, void, ( ),,
|
||||||
object->clearMoveTriggers();
|
object->clearMoveTriggers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These changes coordinate with anim-clip mods to parent class, Player.
|
||||||
|
|
||||||
|
// New method, restartMove(), restores the AIPlayer to its normal move-state
|
||||||
|
// following animation overrides from AFX. The tag argument is used to match
|
||||||
|
// the latest override and prevents interruption of overlapping animation
|
||||||
|
// overrides. See related anim-clip changes in Player.[h,cc].
|
||||||
|
void AIPlayer::restartMove(U32 tag)
|
||||||
|
{
|
||||||
|
if (tag != 0 && tag == last_anim_tag)
|
||||||
|
{
|
||||||
|
if (mMoveState_saved != -1)
|
||||||
|
{
|
||||||
|
mMoveState = (MoveState) mMoveState_saved;
|
||||||
|
mMoveState_saved = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_death_anim = ((anim_clip_flags & IS_DEATH_ANIM) != 0);
|
||||||
|
|
||||||
|
last_anim_tag = 0;
|
||||||
|
anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
|
||||||
|
|
||||||
|
if (mDamageState != Enabled)
|
||||||
|
{
|
||||||
|
if (!is_death_anim)
|
||||||
|
{
|
||||||
|
// this is a bit hardwired and desperate,
|
||||||
|
// but if he's dead he needs to look like it.
|
||||||
|
setActionThread("death10", false, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New method, saveMoveState(), stores the current movement state
|
||||||
|
// so that it can be restored when restartMove() is called.
|
||||||
|
void AIPlayer::saveMoveState()
|
||||||
|
{
|
||||||
|
if (mMoveState_saved == -1)
|
||||||
|
mMoveState_saved = (S32) mMoveState;
|
||||||
|
}
|
||||||
|
|
||||||
F32 AIPlayer::getTargetDistance(GameBase* target, bool _checkEnabled)
|
F32 AIPlayer::getTargetDistance(GameBase* target, bool _checkEnabled)
|
||||||
{
|
{
|
||||||
if (!isServerObject()) return false;
|
if (!isServerObject()) return false;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
||||||
|
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
|
||||||
|
// Copyright (C) 2015 Faust Logic, Inc.
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
||||||
|
|
||||||
#ifndef _AIPLAYER_H_
|
#ifndef _AIPLAYER_H_
|
||||||
#define _AIPLAYER_H_
|
#define _AIPLAYER_H_
|
||||||
|
|
||||||
|
|
@ -225,6 +230,18 @@ public:
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
#endif // TORQUE_NAVIGATION_ENABLED
|
#endif // TORQUE_NAVIGATION_ENABLED
|
||||||
|
// New method, restartMove(), restores the AIPlayer to its normal move-state
|
||||||
|
// following animation overrides from AFX. The tag argument is used to match
|
||||||
|
// the latest override and prevents interruption of overlapping animation
|
||||||
|
// overrides.
|
||||||
|
// New method, saveMoveState(), stores the current movement state
|
||||||
|
// so that it can be restored when restartMove() is called.
|
||||||
|
// See related anim-clip changes in Player.[h,cc].
|
||||||
|
private:
|
||||||
|
S32 mMoveState_saved;
|
||||||
|
public:
|
||||||
|
void restartMove(U32 tag);
|
||||||
|
void saveMoveState();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2746,7 +2746,12 @@ void Player::updateMove(const Move* move)
|
||||||
// Desired move direction & speed
|
// Desired move direction & speed
|
||||||
VectorF moveVec;
|
VectorF moveVec;
|
||||||
F32 moveSpeed;
|
F32 moveSpeed;
|
||||||
if ((mState == MoveState || (mState == RecoverState && mDataBlock->recoverRunForceScale > 0.0f)) && mDamageState == Enabled)
|
// If BLOCK_USER_CONTROL is set in anim_clip_flags, the user won't be able to
|
||||||
|
// resume control over the player character. This generally happens for
|
||||||
|
// short periods of time synchronized with script driven animation at places
|
||||||
|
// where it makes sense that user motion is prohibited, such as when the
|
||||||
|
// player is lifted off the ground or knocked down.
|
||||||
|
if ((mState == MoveState || (mState == RecoverState && mDataBlock->recoverRunForceScale > 0.0f)) && mDamageState == Enabled && !isAnimationLocked())
|
||||||
{
|
{
|
||||||
zRot.getColumn(0,&moveVec);
|
zRot.getColumn(0,&moveVec);
|
||||||
moveVec *= (move->x * (mPose == SprintPose ? mDataBlock->sprintStrafeScale : 1.0f));
|
moveVec *= (move->x * (mPose == SprintPose ? mDataBlock->sprintStrafeScale : 1.0f));
|
||||||
|
|
@ -3031,7 +3036,9 @@ void Player::updateMove(const Move* move)
|
||||||
mContactTimer++;
|
mContactTimer++;
|
||||||
|
|
||||||
// Acceleration from Jumping
|
// Acceleration from Jumping
|
||||||
if (move->trigger[sJumpTrigger] && canJump())// !isMounted() &&
|
// While BLOCK_USER_CONTROL is set in anim_clip_flags, the user won't be able to
|
||||||
|
// make the player character jump.
|
||||||
|
if (move->trigger[sJumpTrigger] && canJump() && !isAnimationLocked())
|
||||||
{
|
{
|
||||||
// Scale the jump impulse base on maxJumpSpeed
|
// Scale the jump impulse base on maxJumpSpeed
|
||||||
F32 zSpeedScale = mVelocity.z;
|
F32 zSpeedScale = mVelocity.z;
|
||||||
|
|
@ -3539,6 +3546,8 @@ void Player::updateLookAnimation(F32 dt)
|
||||||
|
|
||||||
bool Player::inDeathAnim()
|
bool Player::inDeathAnim()
|
||||||
{
|
{
|
||||||
|
if ((anim_clip_flags & ANIM_OVERRIDDEN) != 0 && (anim_clip_flags & IS_DEATH_ANIM) == 0)
|
||||||
|
return false;
|
||||||
if (mActionAnimation.thread && mActionAnimation.action >= 0)
|
if (mActionAnimation.thread && mActionAnimation.action >= 0)
|
||||||
if (mActionAnimation.action < mDataBlock->actionCount)
|
if (mActionAnimation.action < mDataBlock->actionCount)
|
||||||
return mDataBlock->actionList[mActionAnimation.action].death;
|
return mDataBlock->actionList[mActionAnimation.action].death;
|
||||||
|
|
@ -3748,6 +3757,8 @@ bool Player::setArmThread(U32 action)
|
||||||
|
|
||||||
bool Player::setActionThread(const char* sequence,bool hold,bool wait,bool fsp)
|
bool Player::setActionThread(const char* sequence,bool hold,bool wait,bool fsp)
|
||||||
{
|
{
|
||||||
|
if (anim_clip_flags & ANIM_OVERRIDDEN)
|
||||||
|
return false;
|
||||||
for (U32 i = 1; i < mDataBlock->actionCount; i++)
|
for (U32 i = 1; i < mDataBlock->actionCount; i++)
|
||||||
{
|
{
|
||||||
PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
|
PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
|
||||||
|
|
@ -3947,8 +3958,10 @@ void Player::updateActionThread()
|
||||||
pickActionAnimation();
|
pickActionAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prevent scaling of AFX picked actions
|
||||||
if ( (mActionAnimation.action != PlayerData::LandAnim) &&
|
if ( (mActionAnimation.action != PlayerData::LandAnim) &&
|
||||||
(mActionAnimation.action != PlayerData::NullAnimation) )
|
(mActionAnimation.action != PlayerData::NullAnimation) &&
|
||||||
|
!(anim_clip_flags & ANIM_OVERRIDDEN))
|
||||||
{
|
{
|
||||||
// Update action animation time scale to match ground velocity
|
// Update action animation time scale to match ground velocity
|
||||||
PlayerData::ActionAnimation &anim =
|
PlayerData::ActionAnimation &anim =
|
||||||
|
|
@ -4566,6 +4579,10 @@ void Player::updateAnimation(F32 dt)
|
||||||
if (mImageStateThread)
|
if (mImageStateThread)
|
||||||
mShapeInstance->advanceTime(dt,mImageStateThread);
|
mShapeInstance->advanceTime(dt,mImageStateThread);
|
||||||
|
|
||||||
|
// update any active blend clips
|
||||||
|
if (isGhost())
|
||||||
|
for (S32 i = 0; i < blend_clips.size(); i++)
|
||||||
|
mShapeInstance->advanceTime(dt, blend_clips[i].thread);
|
||||||
// If we are the client's player on this machine, then we need
|
// If we are the client's player on this machine, then we need
|
||||||
// to make sure the transforms are up to date as they are used
|
// to make sure the transforms are up to date as they are used
|
||||||
// to setup the camera.
|
// to setup the camera.
|
||||||
|
|
@ -4579,6 +4596,11 @@ void Player::updateAnimation(F32 dt)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
updateAnimationTree(false);
|
updateAnimationTree(false);
|
||||||
|
// This addition forces recently visible players to animate their
|
||||||
|
// skeleton now rather than in pre-render so that constrained effects
|
||||||
|
// get up-to-date node transforms.
|
||||||
|
if (didRenderLastRender())
|
||||||
|
mShapeInstance->animate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7249,6 +7271,165 @@ void Player::afx_unpackUpdate(NetConnection* con, BitStream* stream)
|
||||||
mark_fx_c_triggers = mask;
|
mark_fx_c_triggers = mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Code for overriding player's animation with sequences selected by the
|
||||||
|
// anim-clip component effect.
|
||||||
|
|
||||||
|
void Player::restoreAnimation(U32 tag)
|
||||||
|
{
|
||||||
|
// check if this is a blended clip
|
||||||
|
if ((tag & BLENDED_CLIP) != 0)
|
||||||
|
{
|
||||||
|
restoreBlendAnimation(tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag != 0 && tag == last_anim_tag)
|
||||||
|
{
|
||||||
|
bool is_death_anim = ((anim_clip_flags & IS_DEATH_ANIM) != 0);
|
||||||
|
|
||||||
|
anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
|
||||||
|
|
||||||
|
if (isClientObject())
|
||||||
|
{
|
||||||
|
if (mDamageState != Enabled)
|
||||||
|
{
|
||||||
|
if (!is_death_anim)
|
||||||
|
{
|
||||||
|
// this is a bit hardwired and desperate,
|
||||||
|
// but if he's dead he needs to look like it.
|
||||||
|
setActionThread("death10", false, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mState != MoveState)
|
||||||
|
{
|
||||||
|
// not sure what happens here
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pickActionAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_anim_tag = 0;
|
||||||
|
last_anim_id = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 Player::getAnimationID(const char* name)
|
||||||
|
{
|
||||||
|
for (U32 i = 0; i < mDataBlock->actionCount; i++)
|
||||||
|
{
|
||||||
|
PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
|
||||||
|
if (dStricmp(anim.name, name) == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Con::errorf("Player::getAnimationID() -- Player does not contain a sequence that matches the name, %s.", name);
|
||||||
|
return BAD_ANIM_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 Player::playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
|
||||||
|
{
|
||||||
|
if (anim_id == BAD_ANIM_ID)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
S32 seq_id = mDataBlock->actionList[anim_id].sequence;
|
||||||
|
if (seq_id == -1)
|
||||||
|
{
|
||||||
|
Con::errorf("Player::playAnimation() problem. BAD_SEQ_ID");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mShapeInstance->getShape()->sequences[seq_id].isBlend())
|
||||||
|
return playBlendAnimation(seq_id, pos, rate);
|
||||||
|
|
||||||
|
if (isClientObject())
|
||||||
|
{
|
||||||
|
PlayerData::ActionAnimation &anim = mDataBlock->actionList[anim_id];
|
||||||
|
if (anim.sequence != -1)
|
||||||
|
{
|
||||||
|
mActionAnimation.action = anim_id;
|
||||||
|
mActionAnimation.forward = (rate >= 0);
|
||||||
|
mActionAnimation.firstPerson = false;
|
||||||
|
mActionAnimation.holdAtEnd = hold;
|
||||||
|
mActionAnimation.waitForEnd = hold? true: wait;
|
||||||
|
mActionAnimation.animateOnServer = false;
|
||||||
|
mActionAnimation.atEnd = false;
|
||||||
|
mActionAnimation.delayTicks = (S32)sNewAnimationTickTime;
|
||||||
|
|
||||||
|
F32 transTime = (trans < 0) ? sAnimationTransitionTime : trans;
|
||||||
|
|
||||||
|
mShapeInstance->setTimeScale(mActionAnimation.thread, rate);
|
||||||
|
mShapeInstance->transitionToSequence(mActionAnimation.thread,anim.sequence,
|
||||||
|
pos, transTime, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_death_anim)
|
||||||
|
anim_clip_flags |= IS_DEATH_ANIM;
|
||||||
|
else
|
||||||
|
anim_clip_flags &= ~IS_DEATH_ANIM;
|
||||||
|
|
||||||
|
anim_clip_flags |= ANIM_OVERRIDDEN;
|
||||||
|
last_anim_tag = unique_anim_tag_counter++;
|
||||||
|
last_anim_id = anim_id;
|
||||||
|
|
||||||
|
return last_anim_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 Player::getAnimationDurationByID(U32 anim_id)
|
||||||
|
{
|
||||||
|
if (anim_id == BAD_ANIM_ID)
|
||||||
|
return 0.0f;
|
||||||
|
S32 seq_id = mDataBlock->actionList[anim_id].sequence;
|
||||||
|
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
|
||||||
|
return mDataBlock->mShape->sequences[seq_id].duration;
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::isBlendAnimation(const char* name)
|
||||||
|
{
|
||||||
|
U32 anim_id = getAnimationID(name);
|
||||||
|
if (anim_id == BAD_ANIM_ID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
S32 seq_id = mDataBlock->actionList[anim_id].sequence;
|
||||||
|
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
|
||||||
|
return mDataBlock->mShape->sequences[seq_id].isBlend();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Player::getLastClipName(U32 clip_tag)
|
||||||
|
{
|
||||||
|
if (clip_tag != last_anim_tag || last_anim_id >= PlayerData::NumActionAnims)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return mDataBlock->actionList[last_anim_id].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::unlockAnimation(U32 tag, bool force)
|
||||||
|
{
|
||||||
|
if ((tag != 0 && tag == last_anim_lock_tag) || force)
|
||||||
|
anim_clip_flags &= ~BLOCK_USER_CONTROL;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 Player::lockAnimation()
|
||||||
|
{
|
||||||
|
anim_clip_flags |= BLOCK_USER_CONTROL;
|
||||||
|
last_anim_lock_tag = unique_anim_tag_counter++;
|
||||||
|
|
||||||
|
return last_anim_lock_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleMethod(Player, isAnimationLocked, bool, 2, 2, "isAnimationLocked()")
|
||||||
|
{
|
||||||
|
return object->isAnimationLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef TORQUE_OPENVR
|
#ifdef TORQUE_OPENVR
|
||||||
void Player::setControllers(Vector<OpenVRTrackedObject*> controllerList)
|
void Player::setControllers(Vector<OpenVRTrackedObject*> controllerList)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -793,6 +793,17 @@ private:
|
||||||
private:
|
private:
|
||||||
static bool sCorpsesHiddenFromRayCast;
|
static bool sCorpsesHiddenFromRayCast;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void restoreAnimation(U32 tag);
|
||||||
|
virtual U32 getAnimationID(const char* name);
|
||||||
|
virtual U32 playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim);
|
||||||
|
virtual F32 getAnimationDurationByID(U32 anim_id);
|
||||||
|
virtual bool isBlendAnimation(const char* name);
|
||||||
|
virtual const char* getLastClipName(U32 clip_tag);
|
||||||
|
virtual void unlockAnimation(U32 tag, bool force=false);
|
||||||
|
virtual U32 lockAnimation();
|
||||||
|
virtual bool isAnimationLocked() const { return ((anim_clip_flags & BLOCK_USER_CONTROL) != 0); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Player::Pose PlayerPose;
|
typedef Player::Pose PlayerPose;
|
||||||
|
|
|
||||||
|
|
@ -1045,6 +1045,13 @@ ShapeBase::ShapeBase()
|
||||||
|
|
||||||
for (i = 0; i < MaxTriggerKeys; i++)
|
for (i = 0; i < MaxTriggerKeys; i++)
|
||||||
mTrigger[i] = false;
|
mTrigger[i] = false;
|
||||||
|
anim_clip_flags = 0;
|
||||||
|
last_anim_id = -1;
|
||||||
|
last_anim_tag = 0;
|
||||||
|
last_anim_lock_tag = 0;
|
||||||
|
saved_seq_id = -1;
|
||||||
|
saved_pos = 0.0f;
|
||||||
|
saved_rate = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1183,6 +1190,16 @@ void ShapeBase::onSceneRemove()
|
||||||
|
|
||||||
bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
|
bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
|
||||||
{
|
{
|
||||||
|
// need to destroy blend-clips or we crash
|
||||||
|
if (isGhost())
|
||||||
|
{
|
||||||
|
for (S32 i = 0; i < blend_clips.size(); i++)
|
||||||
|
{
|
||||||
|
if (blend_clips[i].thread)
|
||||||
|
mShapeInstance->destroyThread(blend_clips[i].thread);
|
||||||
|
blend_clips.erase_fast(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
ShapeBaseData *prevDB = dynamic_cast<ShapeBaseData*>( mDataBlock );
|
ShapeBaseData *prevDB = dynamic_cast<ShapeBaseData*>( mDataBlock );
|
||||||
|
|
||||||
bool isInitialDataBlock = ( mDataBlock == 0 );
|
bool isInitialDataBlock = ( mDataBlock == 0 );
|
||||||
|
|
@ -5101,6 +5118,186 @@ DefineEngineMethod( ShapeBase, getModelFile, const char *, (),,
|
||||||
return datablock->getDataField( fieldName, NULL );
|
return datablock->getDataField( fieldName, NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
U32 ShapeBase::unique_anim_tag_counter = 1;
|
||||||
|
|
||||||
|
U32 ShapeBase::playBlendAnimation(S32 seq_id, F32 pos, F32 rate)
|
||||||
|
{
|
||||||
|
BlendThread blend_clip;
|
||||||
|
blend_clip.tag = ((unique_anim_tag_counter++) | BLENDED_CLIP);
|
||||||
|
blend_clip.thread = 0;
|
||||||
|
|
||||||
|
if (isClientObject())
|
||||||
|
{
|
||||||
|
blend_clip.thread = mShapeInstance->addThread();
|
||||||
|
mShapeInstance->setSequence(blend_clip.thread, seq_id, pos);
|
||||||
|
mShapeInstance->setTimeScale(blend_clip.thread, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
blend_clips.push_back(blend_clip);
|
||||||
|
|
||||||
|
return blend_clip.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeBase::restoreBlendAnimation(U32 tag)
|
||||||
|
{
|
||||||
|
for (S32 i = 0; i < blend_clips.size(); i++)
|
||||||
|
{
|
||||||
|
if (blend_clips[i].tag == tag)
|
||||||
|
{
|
||||||
|
if (blend_clips[i].thread)
|
||||||
|
{
|
||||||
|
mShapeInstance->destroyThread(blend_clips[i].thread);
|
||||||
|
}
|
||||||
|
blend_clips.erase_fast(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
void ShapeBase::restoreAnimation(U32 tag)
|
||||||
|
{
|
||||||
|
if (!isClientObject())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check if this is a blended clip
|
||||||
|
if ((tag & BLENDED_CLIP) != 0)
|
||||||
|
{
|
||||||
|
restoreBlendAnimation(tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag != 0 && tag == last_anim_tag)
|
||||||
|
{
|
||||||
|
anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
|
||||||
|
|
||||||
|
stopThread(0);
|
||||||
|
|
||||||
|
if (saved_seq_id != -1)
|
||||||
|
{
|
||||||
|
setThreadSequence(0, saved_seq_id);
|
||||||
|
setThreadPosition(0, saved_pos);
|
||||||
|
setThreadTimeScale(0, saved_rate);
|
||||||
|
setThreadDir(0, (saved_rate >= 0));
|
||||||
|
playThread(0);
|
||||||
|
|
||||||
|
saved_seq_id = -1;
|
||||||
|
saved_pos = 0.0f;
|
||||||
|
saved_rate = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_anim_tag = 0;
|
||||||
|
last_anim_id = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 ShapeBase::getAnimationID(const char* name)
|
||||||
|
{
|
||||||
|
const TSShape* ts_shape = getShape();
|
||||||
|
S32 seq_id = (ts_shape) ? ts_shape->findSequence(name) : -1;
|
||||||
|
return (seq_id >= 0) ? (U32) seq_id : BAD_ANIM_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
U32 ShapeBase::playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
|
||||||
|
{
|
||||||
|
if (!isClientObject())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (anim_id == BAD_ANIM_ID)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const TSShape* ts_shape = getShape();
|
||||||
|
if (!ts_shape)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
S32 seq_id = (S32) anim_id;
|
||||||
|
if (mShapeInstance->getShape()->sequences[seq_id].isBlend())
|
||||||
|
return playBlendAnimation(seq_id, pos, rate);
|
||||||
|
|
||||||
|
if (last_anim_tag == 0)
|
||||||
|
{
|
||||||
|
// try to save state of playing animation
|
||||||
|
Thread& st = mScriptThread[0];
|
||||||
|
if (st.sequence != -1)
|
||||||
|
{
|
||||||
|
saved_seq_id = st.sequence;
|
||||||
|
saved_pos = st.position;
|
||||||
|
saved_rate = st.timescale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// START OR TRANSITION TO SEQUENCE HERE
|
||||||
|
setThreadSequence(0, seq_id);
|
||||||
|
setThreadPosition(0, pos);
|
||||||
|
setThreadTimeScale(0, rate);
|
||||||
|
setThreadDir(0, (rate >= 0));
|
||||||
|
playThread(0);
|
||||||
|
|
||||||
|
if (is_death_anim)
|
||||||
|
anim_clip_flags |= IS_DEATH_ANIM;
|
||||||
|
else
|
||||||
|
anim_clip_flags &= ~IS_DEATH_ANIM;
|
||||||
|
|
||||||
|
anim_clip_flags |= ANIM_OVERRIDDEN;
|
||||||
|
last_anim_tag = unique_anim_tag_counter++;
|
||||||
|
last_anim_id = anim_id;
|
||||||
|
|
||||||
|
return last_anim_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 ShapeBase::getAnimationDurationByID(U32 anim_id)
|
||||||
|
{
|
||||||
|
if (anim_id == BAD_ANIM_ID)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
S32 seq_id = (S32) anim_id;
|
||||||
|
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
|
||||||
|
return mDataBlock->mShape->sequences[seq_id].duration;
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeBase::isBlendAnimation(const char* name)
|
||||||
|
{
|
||||||
|
U32 anim_id = getAnimationID(name);
|
||||||
|
if (anim_id == BAD_ANIM_ID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
S32 seq_id = (S32) anim_id;
|
||||||
|
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
|
||||||
|
return mDataBlock->mShape->sequences[seq_id].isBlend();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ShapeBase::getLastClipName(U32 clip_tag)
|
||||||
|
{
|
||||||
|
if (clip_tag != last_anim_tag)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
S32 seq_id = (S32) last_anim_id;
|
||||||
|
|
||||||
|
S32 idx = mDataBlock->mShape->sequences[seq_id].nameIndex;
|
||||||
|
if (idx < 0 || idx >= mDataBlock->mShape->names.size())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mDataBlock->mShape->names[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
U32 ShapeBase::playAnimation(const char* name, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
|
||||||
|
{
|
||||||
|
return playAnimationByID(getAnimationID(name), pos, rate, trans, hold, wait, is_death_anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
F32 ShapeBase::getAnimationDuration(const char* name)
|
||||||
|
{
|
||||||
|
return getAnimationDurationByID(getAnimationID(name));
|
||||||
|
}
|
||||||
|
|
||||||
void ShapeBase::setSelectionFlags(U8 flags)
|
void ShapeBase::setSelectionFlags(U8 flags)
|
||||||
{
|
{
|
||||||
Parent::setSelectionFlags(flags);
|
Parent::setSelectionFlags(flags);
|
||||||
|
|
|
||||||
|
|
@ -1861,6 +1861,43 @@ public:
|
||||||
protected:
|
protected:
|
||||||
DECLARE_CALLBACK( F32, validateCameraFov, (F32 fov) );
|
DECLARE_CALLBACK( F32, validateCameraFov, (F32 fov) );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum {
|
||||||
|
ANIM_OVERRIDDEN = BIT(0),
|
||||||
|
BLOCK_USER_CONTROL = BIT(1),
|
||||||
|
IS_DEATH_ANIM = BIT(2),
|
||||||
|
BAD_ANIM_ID = 999999999,
|
||||||
|
BLENDED_CLIP = 0x80000000,
|
||||||
|
};
|
||||||
|
struct BlendThread
|
||||||
|
{
|
||||||
|
TSThread* thread;
|
||||||
|
U32 tag;
|
||||||
|
};
|
||||||
|
Vector<BlendThread> blend_clips;
|
||||||
|
static U32 unique_anim_tag_counter;
|
||||||
|
U8 anim_clip_flags;
|
||||||
|
S32 last_anim_id;
|
||||||
|
U32 last_anim_tag;
|
||||||
|
U32 last_anim_lock_tag;
|
||||||
|
S32 saved_seq_id;
|
||||||
|
F32 saved_pos;
|
||||||
|
F32 saved_rate;
|
||||||
|
U32 playBlendAnimation(S32 seq_id, F32 pos, F32 rate);
|
||||||
|
void restoreBlendAnimation(U32 tag);
|
||||||
|
public:
|
||||||
|
U32 playAnimation(const char* name, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim);
|
||||||
|
F32 getAnimationDuration(const char* name);
|
||||||
|
|
||||||
|
virtual void restoreAnimation(U32 tag);
|
||||||
|
virtual U32 getAnimationID(const char* name);
|
||||||
|
virtual U32 playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim);
|
||||||
|
virtual F32 getAnimationDurationByID(U32 anim_id);
|
||||||
|
virtual bool isBlendAnimation(const char* name);
|
||||||
|
virtual const char* getLastClipName(U32 clip_tag);
|
||||||
|
virtual void unlockAnimation(U32 tag, bool force=false) { }
|
||||||
|
virtual U32 lockAnimation() { return 0; }
|
||||||
|
virtual bool isAnimationLocked() const { return false; }
|
||||||
|
|
||||||
virtual void setSelectionFlags(U8 flags);
|
virtual void setSelectionFlags(U8 flags);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue