//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2001. // ------------------------------------------------------------------------- // File name: entitynode.cpp // Version: v1.00 // Created: 23/4/2002 by Timur. // Compilers: Visual C++ 7.0 // Description: // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "EntityNode.h" #include "EventTrack.h" #include "CharacterTrack.h" #include "AnimSplineTrack.h" #include "ExprTrack.h" #include "BoolTrack.h" #include "isystem.h" #include "ilog.h" #include "Movie.h" #include #include #include #include #include ////////////////////////////////////////////////////////////////////////// namespace { ////////////////////////////////////////////////////////////////////////// Matrix44 Vector2Matrix( const Vec3 &dir,const Vec3 &up,float rollAngle=0 ) { Matrix44 M; // LookAt transform. Vec3d xAxis,yAxis,zAxis; Vec3d upVector = up; yAxis = GetNormalized(-dir); //if (zAxis.x == 0.0 && zAxis.z == 0) up.Set( -zAxis.y,0,0 ); else up.Set( 0,1.0f,0 ); xAxis = GetNormalized((upVector.Cross(yAxis))); zAxis = GetNormalized(xAxis.Cross(yAxis)); // OpenGL kind of matrix. M[0][0] = xAxis.x; M[1][0] = yAxis.x; M[2][0] = zAxis.x; M[3][0] = 0; M[0][1] = xAxis.y; M[1][1] = yAxis.y; M[2][1] = zAxis.y; M[3][1] = 0; M[0][2] = xAxis.z; M[1][2] = yAxis.z; M[2][2] = zAxis.z; M[3][2] = 0; M[0][3] = 0; M[1][3] = 0; M[2][3] = 0; M[3][3] = 1; if (rollAngle != 0) { Matrix44 RollMtx; RollMtx.SetIdentity(); float s = cry_sinf(rollAngle); float c = cry_cosf(rollAngle); RollMtx[0][0] = c; RollMtx[2][0] = -s;; RollMtx[0][2] = s; RollMtx[2][2] = c; // Matrix multiply. M = RollMtx * M; } return M; } bool s_nodeParamsInitialized = false; std::vector s_nodeParams; void AddSupportedParam( std::vector &nodeParams,const char *sName,int paramId,EAnimValue valueType ) { IAnimNode::SParamInfo param; param.name = sName; param.paramId = paramId; param.valueType = valueType; nodeParams.push_back( param ); } }; ////////////////////////////////////////////////////////////////////////// CAnimEntityNode::CAnimEntityNode( IMovieSystem *sys ) : CAnimNode(sys) { m_dwSupportedTracks = PARAM_BIT(APARAM_POS)|PARAM_BIT(APARAM_ROT)|PARAM_BIT(APARAM_SCL)| PARAM_BIT(APARAM_EVENT)|PARAM_BIT(APARAM_VISIBLE)| PARAM_BIT(APARAM_SOUND1)|PARAM_BIT(APARAM_SOUND2)|PARAM_BIT(APARAM_SOUND3)| PARAM_BIT(APARAM_CHARACTER1)|PARAM_BIT(APARAM_CHARACTER2)|PARAM_BIT(APARAM_CHARACTER3)| PARAM_BIT(APARAM_EXPRESSION1)|PARAM_BIT(APARAM_EXPRESSION2)|PARAM_BIT(APARAM_EXPRESSION3); m_entity = 0; m_EntityId = 0; m_target=NULL; m_pMovie=sys; m_bMatrixValid = 0; m_bMatrixInWorldSpace = 0; m_worldTM.SetIdentity(); m_pos(0,0,0); m_scale(1,1,1); m_visible = true; m_lastEntityKey = -1; m_lastCharacterKey[0] = -1; m_lastCharacterKey[1] = -1; m_lastCharacterKey[2] = -1; for (int i=0;i &nodeParams ) { AddSupportedParam( nodeParams,"Position",APARAM_POS,AVALUE_VECTOR ); AddSupportedParam( nodeParams,"Rotation",APARAM_ROT,AVALUE_QUAT ); AddSupportedParam( nodeParams,"Scale",APARAM_SCL,AVALUE_VECTOR ); AddSupportedParam( nodeParams,"Visibility",APARAM_VISIBLE,AVALUE_BOOL ); AddSupportedParam( nodeParams,"Event",APARAM_EVENT,AVALUE_EVENT ); AddSupportedParam( nodeParams,"Sound1",APARAM_SOUND1,AVALUE_SOUND ); AddSupportedParam( nodeParams,"Sound2",APARAM_SOUND2,AVALUE_SOUND ); AddSupportedParam( nodeParams,"Sound3",APARAM_SOUND3,AVALUE_SOUND ); AddSupportedParam( nodeParams,"Animation1",APARAM_CHARACTER1,AVALUE_CHARACTER ); AddSupportedParam( nodeParams,"Animation2",APARAM_CHARACTER2,AVALUE_CHARACTER ); AddSupportedParam( nodeParams,"Animation3",APARAM_CHARACTER3,AVALUE_CHARACTER ); AddSupportedParam( nodeParams,"Expression1",APARAM_EXPRESSION1,AVALUE_EXPRESSION ); AddSupportedParam( nodeParams,"Expression2",APARAM_EXPRESSION2,AVALUE_EXPRESSION ); AddSupportedParam( nodeParams,"Expression3",APARAM_EXPRESSION3,AVALUE_EXPRESSION ); } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::CreateDefaultTracks() { CreateTrack(APARAM_POS); CreateTrack(APARAM_ROT); }; ////////////////////////////////////////////////////////////////////////// CAnimEntityNode::~CAnimEntityNode() { ReleaseSounds(); } ////////////////////////////////////////////////////////////////////////// int CAnimEntityNode::GetParamCount() const { return s_nodeParams.size(); } ////////////////////////////////////////////////////////////////////////// bool CAnimEntityNode::GetParamInfo( int nIndex, SParamInfo &info ) const { if (nIndex >= 0 && nIndex < s_nodeParams.size()) { info = s_nodeParams[nIndex]; return true; } return false; } ////////////////////////////////////////////////////////////////////////// bool CAnimEntityNode::GetParamInfoFromId( int paramId, SParamInfo &info ) const { for (int i = 0; i < s_nodeParams.size(); i++) { if (s_nodeParams[i].paramId == paramId) { info = s_nodeParams[i]; return true; } } return false; } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::SetEntity( IEntity *entity ) { m_entity = entity; if (m_entity) m_EntityId = m_entity->GetId(); else m_EntityId = 0; } ////////////////////////////////////////////////////////////////////////// IEntity* CAnimEntityNode::GetEntity() { if (!m_entity) ResolveEntity(); return m_entity; } /* ////////////////////////////////////////////////////////////////////////// IAnimBlock* CAnimEntityNode::CreateAnimBlock() { // Assign entity tracks to this anim node. IAnimBlock *block = new CAnimBlock; CreateTrack(block, APARAM_POS); CreateTrack(block, APARAM_ROT); return block; };*/ ////////////////////////////////////////////////////////////////////////// bool CAnimEntityNode::ResolveEntity() { IEntitySystem *pEntitySystem=GetMovieSystem()->GetSystem()->GetIEntitySystem(); if (!pEntitySystem) return false; m_entity = pEntitySystem->GetEntity(m_EntityId); return (m_entity != NULL); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::Animate( SAnimContext &ec ) { CAnimBlock *anim = (CAnimBlock*)GetAnimBlock(); if (!anim) return; if (!m_entity) { if (!ResolveEntity()) return; } Vec3 pos = m_pos; Quat rotate = m_rotate; Vec3 scale = m_scale; IAnimTrack *posTrack = NULL; IAnimTrack *rotTrack = NULL; IAnimTrack *sclTrack = NULL; if (!ec.bResetting) { StopExpressions(); } int paramCount = anim->GetTrackCount(); for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { int trackType; IAnimTrack *pTrack; if (!anim->GetTrackInfo( paramIndex,trackType,&pTrack )) continue; switch (trackType) { case APARAM_POS: posTrack = pTrack; posTrack->GetValue( ec.time,pos ); break; case APARAM_ROT: rotTrack = pTrack; rotTrack->GetValue( ec.time,rotate ); break; case APARAM_SCL: sclTrack = pTrack; sclTrack->GetValue( ec.time,scale ); break; case APARAM_EVENT: if (!ec.bResetting) { CEventTrack *entityTrack = (CEventTrack*)pTrack; IEventKey key; int entityKey = entityTrack->GetActiveKey(ec.time,&key); // If key is different or if time is standing exactly on key time. //if ((entityKey != m_lastEntityKey || key.time == ec.time) && (!ec.bSingleFrame)) if (entityKey != m_lastEntityKey || key.time == ec.time) { m_lastEntityKey = entityKey; if (entityKey >= 0) { if (!ec.bSingleFrame || key.time == ec.time) // If Single frame update key time must match current time. ApplyEventKey( entityTrack,entityKey,key ); } } } break; case APARAM_VISIBLE: if (!ec.bResetting) { IAnimTrack *visTrack = pTrack; bool visible = m_visible; visTrack->GetValue( ec.time,visible ); m_entity->Hide(!visible); m_visible = visible; } break; ////////////////////////////////////////////////////////////////////////// case APARAM_SOUND1: case APARAM_SOUND2: case APARAM_SOUND3: if (!ec.bResetting) { int nSoundIndex = trackType - APARAM_SOUND1; CSoundTrack *pSoundTrack = (CSoundTrack*)pTrack; ISoundKey key; int nSoundKey = pSoundTrack->GetActiveKey(ec.time, &key); if (nSoundKey!=m_SoundInfo[nSoundIndex].nLastKey || key.time==ec.time || nSoundKey==-1) { if (!ec.bSingleFrame || key.time == ec.time) // If Single frame update key time must match current time. { m_SoundInfo[nSoundIndex].nLastKey=nSoundKey; ApplySoundKey( pSoundTrack,nSoundKey,nSoundIndex, key, ec); } } } break; ////////////////////////////////////////////////////////////////////////// case APARAM_CHARACTER1: case APARAM_CHARACTER2: case APARAM_CHARACTER3: if (!ec.bResetting) { int index = trackType - APARAM_CHARACTER1; CCharacterTrack *pCharTrack = (CCharacterTrack*)pTrack; AnimateCharacterTrack( pCharTrack,ec,index ); } break; ////////////////////////////////////////////////////////////////////////// case APARAM_EXPRESSION1: case APARAM_EXPRESSION2: case APARAM_EXPRESSION3: if (!ec.bResetting) { CExprTrack *pExpTrack = (CExprTrack*)pTrack; AnimateExpressionTrack( pExpTrack,ec ); } break; }; } if (!ec.bResetting) { ////////////////////////////////////////////////////////////////////////// // If not restting animation sequence. ////////////////////////////////////////////////////////////////////////// if (!ec.bSingleFrame) { IPhysicalEntity *pEntityPhysics = m_entity->GetPhysics(); if (pEntityPhysics) { if (ec.time - m_time < 0.1f) { float timeStep = ec.time - m_time; float rtimeStep = timeStep>1E-5f ? 1.0f/timeStep : 0.0f; pe_action_set_velocity asv; asv.v = (pos - m_pos)*rtimeStep; asv.w = (rotate*!m_rotate).v*(rtimeStep*2); pEntityPhysics->Action(&asv); } } } } // not single frame bool bMatrixModified = false; bool bPosModified = (m_pos != pos); bool bAnglesModified = (m_rotate.v != rotate.v) || (m_rotate.w != rotate.w); if (bPosModified || bAnglesModified || (m_scale != scale) || (m_target!=NULL)) { InvalidateTM(); bMatrixModified = true; m_pos = pos; m_scale = scale; m_rotate = rotate; } m_time = ec.time; if (bMatrixModified) { /* if (m_target) { Matrix Mat; GetWorldTM(Mat); Quat q(Mat); Vec3 angles = q.GetEulerAngles(); angles = RAD2DEG(angles); angles = angles; } */ if (m_callback) { if (m_target) { // To Update m_rotate member for Editor. Matrix44 Mat; GetWorldTM(Mat); } m_callback->OnNodeAnimated(); } else // no callback specified, so lets move the entity directly { if (bPosModified && posTrack) m_entity->SetPos(m_pos,false); if (m_target) { Matrix44 Mat; GetWorldTM(Mat); //CHAMGED_BY_IVO //Quat q(Mat); //Quat q = CovertMatToQuat( GetTransposed44(Mat) ); Quat q = Quat( GetTransposed44(Mat) ); Vec3 angles = Ang3::GetAnglesXYZ(Matrix33(q)); angles = RAD2DEG(angles); m_entity->SetAngles(angles); }else { if (bAnglesModified && rotTrack) m_entity->SetAngles( RAD2DEG(Ang3::GetAnglesXYZ(Matrix33(m_rotate))) ); } if (sclTrack) m_entity->SetScale(m_scale.x); } } } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::ReleaseSounds() { // stop all sounds for (int i=0;iStop(); m_SoundInfo[i].pSound = NULL; m_SoundInfo[i].nLastKey=-1; if (!m_SoundInfo[i].sLastFilename.empty()) m_SoundInfo[i].sLastFilename = ""; } } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::Reset() { m_lastEntityKey = -1; m_lastCharacterKey[0] = -1; m_lastCharacterKey[1] = -1; m_lastCharacterKey[2] = -1; ReleaseSounds(); ReleaseAllAnims(); m_entity = NULL; } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::Pause() { ReleaseSounds(); } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::CalcLocalMatrix() { if (m_target) { // Calculate loookat matrix. float fRollAngle = 0; Matrix44 trgTM; m_target->GetWorldTM(trgTM); Vec3 pos = m_pos; IAnimNode *pParent = GetParent(); if (pParent) { // Get our world position. Matrix44 parentTM; pParent->GetWorldTM(parentTM); pos = parentTM.TransformPointOLD(pos); } Vec3 camDirection = trgTM.GetTranslationOLD() - pos; m_worldTM = Vector2Matrix( camDirection,Vec3(0,0,-1),fRollAngle ); // Rotate.. Assign to quaternion actual rotation. //QUAT_CHANGED_BY_IVO //m_rotate = Quat(m_worldTM); //m_rotate = CovertMatToQuat( GetTransposed44(m_worldTM) ); m_rotate = Quat( GetTransposed44(m_worldTM) ); // Translate matrix. m_worldTM[3][0] = m_pos.x; m_worldTM[3][1] = m_pos.y; m_worldTM[3][2] = m_pos.z; m_worldTM[3][3] = 1.0f; m_bMatrixInWorldSpace = true; m_bMatrixValid = true; } else { // Calculate PRS (Pos-Rotation-Scale) Transform. //Q2M_CHANGED_BY_IVO //m_rotate.GetMatrix( m_worldTM ); m_worldTM = GetTransposed44( Matrix44(m_rotate) ); //SCALE_CHANGED_BY_IVO //m_worldTM.ScaleMatrix( m_scale.x,m_scale.y,m_scale.z ); m_worldTM=Matrix33::CreateScale( Vec3(m_scale.x,m_scale.y,m_scale.z) ) * m_worldTM; // Translate matrix. m_worldTM[3][0] = m_pos.x; m_worldTM[3][1] = m_pos.y; m_worldTM[3][2] = m_pos.z; m_worldTM[3][3] = 1.0f; m_bMatrixInWorldSpace = false; m_bMatrixValid = true; } } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::GetWorldTM( Matrix44 &tm ) { if (!m_bMatrixValid) { CalcLocalMatrix(); } if (!m_bMatrixInWorldSpace) { IAnimNode *parent = GetParent(); if (parent) { Matrix44 parentTM; parent->GetWorldTM(parentTM); m_worldTM = m_worldTM * parentTM; } m_bMatrixInWorldSpace = true; } tm = m_worldTM; } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::InvalidateTM() { m_bMatrixInWorldSpace = false; m_bMatrixValid = false; } /* ////////////////////////////////////////////////////////////////////////// Vec3 CAnimEntityNode::GetPos( float time ) { Vec3 pos(0,0,0); IAnimBlock *anim = GetAnimBlock(); if (!anim) return pos; IAnimTrack *posTrack = anim->GetTrack(APARAM_POS); if (posTrack) posTrack->GetValue( time,pos ); return pos; } ////////////////////////////////////////////////////////////////////////// Quat CAnimEntityNode::GetRotate( float time ) { Quat q; IAnimBlock *anim = GetAnimBlock(); if (!anim) return q; IAnimTrack *rotTrack = anim->GetTrack(APARAM_ROT); if (rotTrack) rotTrack->GetValue( time,q ); return q; } ////////////////////////////////////////////////////////////////////////// Vec3 CAnimEntityNode::GetScale( float time ) { Vec3 scl(1,1,1); IAnimBlock *anim = GetAnimBlock(); if (!anim) return scl; IAnimTrack *sclTrack = anim->GetTrack(APARAM_SCL); if (sclTrack) sclTrack->GetValue( time,scl ); return scl; } */ ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::SetPos( float time,const Vec3 &pos ) { bool bDefault = !(m_pMovieSystem->IsRecording() && (m_flags&ANODE_FLAG_SELECTED)); // Only selected nodes can be recorded IAnimBlock *anim = GetAnimBlock(); if (anim) { IAnimTrack *posTrack = anim->GetTrack(APARAM_POS); if (posTrack) { posTrack->SetValue( time,pos,bDefault ); if (bDefault && posTrack->GetNumKeys() > 0) { // Track is not empty. offset all keys by move ammount. Vec3 offset = pos - m_pos; // Iterator over all keys. ITcbKey key; for (int i = 0; i < posTrack->GetNumKeys(); i++) { // Offset each key. posTrack->GetKey(i,&key); key.SetVec3( key.GetVec3()+offset ); posTrack->SetKey(i,&key); } } } } m_pos = pos; InvalidateTM(); } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::SetRotate( float time,const Quat &quat ) { m_rotate = quat; InvalidateTM(); bool bDefault = !(m_pMovieSystem->IsRecording() && (m_flags&ANODE_FLAG_SELECTED)); // Only selected nodes can be recorded IAnimBlock *anim = GetAnimBlock(); if (anim) { IAnimTrack *rotTrack = anim->GetTrack(APARAM_ROT); if (rotTrack) rotTrack->SetValue( time,m_rotate,bDefault ); } } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::SetScale( float time,const Vec3 &scale ) { m_scale = scale; InvalidateTM(); bool bDefault = !(m_pMovieSystem->IsRecording() && (m_flags&ANODE_FLAG_SELECTED)); // Only selected nodes can be recorded IAnimBlock *anim = GetAnimBlock(); if (anim) { IAnimTrack *sclTrack = anim->GetTrack(APARAM_SCL); if (sclTrack) sclTrack->SetValue( time,scale,bDefault ); } } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::ApplyEventKey( CEventTrack *track,int keyIndex,IEventKey &key ) { if (!m_entity) return; if (*key.animation) // if there is an animation { // Start playing animation. m_entity->StartAnimation( 0,key.animation,0,0 ); ICryCharInstance *pCharacter = m_entity->GetCharInterface()->GetCharacter(0); if (pCharacter) { IAnimationSet* pAnimations = pCharacter->GetModel()->GetAnimationSet(); assert (pAnimations); float duration = pAnimations->GetLength( key.animation ); if (duration != key.duration) { key.duration = duration; track->SetKey( keyIndex,&key ); } } //char str[1024]; //sprintf( str,"StartAnim: %s",key.animation ); //GetMovieSystem()->GetSystem()->GetILog()->LogToConsole( str ); } if (*key.event) // if there's an event { // Fire event on Entity. m_entity->CallEventHandler(key.event); } //m_entity->Hide( key.hidden ); } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::ApplySoundKey( IAnimTrack *pTrack,int nCurrKey,int nLayer, ISoundKey &key, SAnimContext &ec ) { if (m_SoundInfo[nLayer].nLastKey==-1) { if (m_SoundInfo[nLayer].pSound) m_SoundInfo[nLayer].pSound->Stop(); m_SoundInfo[nLayer].pSound=NULL; return; } if (((strcmp(m_SoundInfo[nLayer].sLastFilename.c_str(), key.pszFilename)) || (!m_SoundInfo[nLayer].pSound))) { int flags = 0; //if (key.bStream) //flags |= FLAG_SOUND_STREAM; //else flags |= FLAG_SOUND_LOAD_SYNCHRONOUSLY; // Allways synchronously for now. if (key.bLoop) flags |= FLAG_SOUND_LOOP; if (key.b3DSound) { // 3D sound. flags |= FLAG_SOUND_3D|FLAG_SOUND_RADIUS; } else { // 2D sound. flags |= FLAG_SOUND_2D|FLAG_SOUND_STEREO|FLAG_SOUND_16BITS; } // we have a different sound now if (m_SoundInfo[nLayer].pSound) m_SoundInfo[nLayer].pSound->Stop(); m_SoundInfo[nLayer].pSound=m_pMovie->GetSystem()->GetISoundSystem()->LoadSound(key.pszFilename, flags | FLAG_SOUND_UNSCALABLE ); m_SoundInfo[nLayer].sLastFilename=key.pszFilename; if (m_SoundInfo[nLayer].pSound) { m_SoundInfo[nLayer].nLength=m_SoundInfo[nLayer].pSound->GetLengthMs(); key.fDuration = ((float)m_SoundInfo[nLayer].nLength) / 1000.0f; pTrack->SetKey( nCurrKey,&key ); // Update key duration. } }else { if (m_SoundInfo[nLayer].pSound) m_SoundInfo[nLayer].pSound->Stop(); } if (!m_SoundInfo[nLayer].pSound) return; m_SoundInfo[nLayer].pSound->SetSoundPriority( MOVIE_SOUND_PRIORITY ); m_SoundInfo[nLayer].pSound->SetVolume(key.nVolume); if (key.b3DSound) { // 3D sound. m_SoundInfo[nLayer].pSound->SetPosition( m_entity->GetPos() ); m_SoundInfo[nLayer].pSound->SetMinMaxDistance( key.inRadius,key.outRadius ); } else { // 2D sound. m_SoundInfo[nLayer].pSound->SetPan(key.nPan); } int nOffset=(int)((ec.time-key.time)*1000.0f); if (nOffset < m_SoundInfo[nLayer].nLength) { //return; m_SoundInfo[nLayer].pSound->SetCurrentSamplePos(nOffset, true); } ((CMovieSystem*)m_pMovie)->OnPlaySound( m_SoundInfo[nLayer].pSound ); if (!m_SoundInfo[nLayer].pSound->IsPlaying()) m_SoundInfo[nLayer].pSound->Play(); } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::ReleaseAllAnims() { ResolveEntity(); // Resolve entity here as it may get deleted already. if (!m_entity) return; ICryCharInstance *pCharacter = m_entity->GetCharInterface()->GetCharacter(0); if (!pCharacter) return; ICryAnimationSet* pAnimations = pCharacter->GetModel()->GetAnimationSet(); assert(pAnimations); for (TStringSetIt It=m_setAnimationSinks.begin();It!=m_setAnimationSinks.end();++It) { const char *pszName=(*It).c_str(); pCharacter->RemoveAnimationEventSink(pszName, this); pAnimations->UnloadAnimation(pszName); } m_setAnimationSinks.clear(); } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::OnEndAnimation(const char *sAnimation) { if (!m_entity) return; TStringSetIt It=m_setAnimationSinks.find(sAnimation); if (It==m_setAnimationSinks.end()) return; // this anim was not started by us... m_setAnimationSinks.erase(It); ICryCharInstance *pCharacter = m_entity->GetCharInterface()->GetCharacter(0); if (!pCharacter) return; ICryAnimationSet* pAnimations = pCharacter->GetModel()->GetAnimationSet(); assert(pAnimations); pCharacter->RemoveAnimationEventSink(sAnimation, this); pAnimations->UnloadAnimation(sAnimation); } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::AnimateCharacterTrack( class CCharacterTrack* track,SAnimContext &ec,int layer ) { ICryCharInstance *pCharacter = m_entity->GetCharInterface()->GetCharacter(0); if (!pCharacter) return; ICharacterKey key; int currKey = track->GetActiveKey(ec.time,&key); // If key is different or if time is standing exactly on key time. if (currKey != m_lastCharacterKey[layer] || key.time == ec.time || ec.time < m_time) { m_lastCharacterKey[layer] = currKey; if (strlen(key.animation) > 0) { // retrieve the animation collection for the model ICryAnimationSet* pAnimations = pCharacter->GetModel()->GetAnimationSet(); assert (pAnimations); if (key.bUnload) { m_setAnimationSinks.insert(TStringSetIt::value_type(key.animation)); pCharacter->AddAnimationEventSink(key.animation, this); } // Start playing animation. m_entity->StartAnimation( 0,key.animation,layer,key.blendTime ); int animId = pAnimations->Find( key.animation ); // pCharacter->EnableTimeUpdate(false); pCharacter->SetAnimationSpeed(layer, 0.0f); // stop anim this way, so we dont block all other running anims float duration = pAnimations->GetLength(animId) + pAnimations->GetStart(animId); if (duration != key.duration) { key.duration = duration; track->SetKey( currKey,&key ); } /* if (key.bLoop) { if (animId >= 0) pAnimations->SetLoop( animId,key.bLoop ); } */ } } // Animate. if (currKey >= 0) { float t = ec.time - key.time; t = key.startTime + t*key.speed; if (t < key.duration || key.bLoop) { if (key.bLoop) { t = fmod(t,key.duration); } // Start playing animation. //m_character->EnableTimeUpdate(false); pCharacter->SetLayerTime(layer,t); } } } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::StopExpressions() { if (m_setExpressions.empty()) return; IEntityCharacter *pEntChar=m_entity->GetCharInterface(); if (!pEntChar) return; ILipSync *pLipSync=pEntChar->GetLipSyncInterface(); if (!pLipSync) return; for (TStringSetIt It=m_setExpressions.begin();It!=m_setExpressions.end();++It) { pLipSync->StopExpression((*It).c_str()); } m_setExpressions.clear(); } void CAnimEntityNode::AnimateExpressionTrack(CExprTrack *pTrack, SAnimContext &ec) { IExprKey Key; int nKeys=pTrack->GetNumKeys(); // we go through all the keys, since expressions may overlap for (int nKey=0;nKeyGetKey(nKey, &Key); if ((!Key.pszName) || (!strlen(Key.pszName))) return; IEntityCharacter *pEntChar=m_entity->GetCharInterface(); if (!pEntChar) return; ILipSync *pLipSync=pEntChar->GetLipSyncInterface(); if (pLipSync) { float fKeyLentgh=Key.fBlendIn+Key.fHold+Key.fBlendOut; float fOffset=ec.time-Key.time; if ((Key.time>ec.time) || (fOffset>=fKeyLentgh)) { // pLipSync->StopExpression(Key.pszName); continue; } CryCharMorphParams MorphParams; MorphParams.fAmplitude=Key.fAmp; MorphParams.fBlendIn=Key.fBlendIn; MorphParams.fBlendOut=Key.fBlendOut; MorphParams.fLength=Key.fHold; MorphParams.fStartTime=fOffset; if (pLipSync->DoExpression(Key.pszName, MorphParams, false)) m_setExpressions.insert(Key.pszName); } } } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::SetTarget( IAnimNode *node ) { IAnimNode *prevTarget = m_target; m_target = node; if (prevTarget != m_target) InvalidateTM(); } ////////////////////////////////////////////////////////////////////////// IAnimNode* CAnimEntityNode::GetTarget() const { return m_target; } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::SetAnimBlock( IAnimBlock *block ) { CAnimNode::SetAnimBlock(block); if (block) { // Initialize new block with default track values. // Used exlusively in Editor. IAnimTrack *posTrack = block->GetTrack(APARAM_POS); IAnimTrack *rotTrack = block->GetTrack(APARAM_ROT); IAnimTrack *sclTrack = block->GetTrack(APARAM_SCL); if (posTrack) posTrack->SetValue( 0,m_pos,true ); if (rotTrack) rotTrack->SetValue( 0,m_rotate,true ); if (sclTrack) sclTrack->SetValue( 0,m_scale,true ); } } ////////////////////////////////////////////////////////////////////////// void CAnimEntityNode::Serialize( XmlNodeRef &xmlNode,bool bLoading ) { CAnimNode::Serialize( xmlNode,bLoading ); if (bLoading) { xmlNode->getAttr( "Pos",m_pos ); xmlNode->getAttr( "Rotate",m_rotate ); xmlNode->getAttr( "Scale",m_scale ); xmlNode->getAttr( "EntityId",m_EntityId ); } else { xmlNode->setAttr( "Pos",m_pos ); xmlNode->setAttr( "Rotate",m_rotate ); xmlNode->setAttr( "Scale",m_scale ); xmlNode->setAttr( "EntityId",m_EntityId ); } }