//===========================================================================// // File: AnimationState.cpp //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 02/23/999 JSE Inital coding //---------------------------------------------------------------------------// // Copyright (C) 1998, Microsoft Corp. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include "MW4Headers.hpp" #include "MWObject.hpp" #include "AnimationState.hpp" #include #include #if 1 #define RUNSTATES_LOGIC(string) LOGIC("Animation::RunAnimStates::" string) #else #define RUNSTATES_LOGIC(string) #endif using namespace MW4Animation; DEFINE_TIMER(MechWarrior4, RunAnimStates); DEFINE_TIMER(MechWarrior4, Iterate); DEFINE_TIMER(MechWarrior4, Blend); DEFINE_TIMER(MechWarrior4, Apply); DEFINE_TIMER(MechWarrior4, BlendAlloc); DEFINE_TIMER(MechWarrior4, blendDealloc); DEFINE_TIMER(MechWarrior4, BlendAnimCombine); DEFINE_TIMER(MechWarrior4, BlendCombine); DEFINE_TIMER(MechWarrior4, BlendAndApply); //############################################################################# //########################## AnimationStateEngine ######################## //############################################################################# HermiteSpline1D AnimCurve::BumpCurveMat, AnimCurve::EaseUpSplineCurveMat, AnimCurve::CurveUpCurveMat, AnimCurve::SpikeUpCurveMat, AnimCurve::BellCurveMat, AnimCurve::SpikeCurveMat, AnimCurve::LongBumpCurveMat, AnimCurve::BumpSpikeCurveMat, AnimCurve::SpikeBellCurveMat; AnimationStateEngine::ClassData* AnimationStateEngine::DefaultData = NULL; const StateEngine::StateEntry AnimationStateEngine::StateEntries[]= { STATE_ENTRY(AnimationStateEngine, NoAnimation), STATE_ENTRY(AnimationStateEngine, Test1), STATE_ENTRY(AnimationStateEngine, Test2), STATE_ENTRY(AnimationStateEngine, Test3), STATE_ENTRY(AnimationStateEngine, Test4), STATE_ENTRY(AnimationStateEngine, Test5), STATE_ENTRY(AnimationStateEngine, Test6), STATE_ENTRY(AnimationStateEngine, Test7), STATE_ENTRY(AnimationStateEngine, Test8) }; HGOSHEAP MechWarrior4::g_AnimScript = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::InitializeClass() { Check_Object(StateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( AnimationStateEngineClassID, "AnimationStateEngine", StateEngine::DefaultData, ELEMENTS(StateEntries), StateEntries, NULL, NULL ); Register_Object(DefaultData); AnimCurve::InitializeClass(); #if !defined(NO_TIMERS) StatisticFormat(""); StatisticFormat("MW4 ANIMATION"); StatisticFormat("============="); Initialize_Timer(RunAnimStates, "RunAnimStates"); Initialize_Timer(Iterate, "Iterate Anims"); Initialize_Timer(BlendAndApply, "BlendAndApply"); Initialize_Timer(Apply, "Apply Anims"); Initialize_Timer(BlendAlloc, "Alloc"); Initialize_Timer(blendDealloc, "DeAlloc"); Initialize_Timer(Blend, "Blend Anims"); Initialize_Timer(BlendAnimCombine, " Combine"); Initialize_Timer(BlendCombine, " AnimCombine"); #endif Verify(!g_AnimScript); g_AnimScript = gos_CreateMemoryHeap("Animation-Scripts", 0, g_AnimParentHeap); Check_Pointer(g_AnimScript); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; Check_Pointer(g_AnimScript); gos_DestroyMemoryHeap(g_AnimScript); g_AnimScript = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AnimationStateEngine* AnimationStateEngine::Make(MWObject *vehicle, const FactoryRequest *request) { Check_Object(request); Check_Object(vehicle); gos_PushCurrentHeap(g_AnimScript); AnimationStateEngine* engine = new AnimationStateEngine(vehicle, DefaultData, request); gos_PopCurrentHeap(); return engine; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::Save(FactoryRequest *request) { Check_Object(this); Check_Object(request); StateEngine::Save(request); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::Reuse(const FactoryRequest *request) { Check_Object(this); Check_Object(request); StateEngine::Reuse(request); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AnimationStateEngine::AnimationStateEngine( MWObject *vehicle, ClassData *class_data, const FactoryRequest *request ): StateEngine(class_data, request), statePlaying(NULL), transitionPlaying(NULL), expressionsPlaying(NULL), animStatesLoaded(NULL), transitionsLoaded(NULL), expressionsLoaded(NULL), newStatePlaying(NULL), animInstancesLoaded(NULL) { Check_Pointer(this); Check_Object(class_data); Check_Object(vehicle); Check_Object(request); alreadyLoaded = false; vehicleParent = vehicle; iteratorManager = new MW4Animation::AnimHierarchyIteratorManager( (MW4Animation::AnimHierarchyIteratorManager::UserDefinedApplyChannelFunction)MWObject::ApplyChannel, vehicle ); Register_Object(iteratorManager); animHolderArray = NULL; animCurves = NULL; transitionFlag = false; quedCurrentState = false; quedNewState = false; quedTransState = false; transitionTo = -1; lastCurrentStatePosition = 0.0f; #if 0 GUITextManager::Instance->MakeNewDebugTextObject( "OldBlend", &oldBlendValue, GUIDebugText::ScalarType ); GUITextManager::Instance->MakeNewDebugTextObject( "TrnsBlend", &transBlendValue, GUIDebugText::ScalarType ); GUITextManager::Instance->MakeNewDebugTextObject( "NewBlend", &newBlendValue, GUIDebugText::ScalarType ); #endif } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AnimationStateEngine::~AnimationStateEngine() { // delete everything Stuff::ChainIteratorOf iterator1(&animStatesLoaded); AnimationState *state; while ((state = iterator1.GetCurrent()) != NULL) { state->UnloadIterators(); iterator1.Remove(); Unregister_Object(state); delete state; } Stuff::ChainIteratorOf iterator2(&transitionsLoaded); while ((state = iterator2.GetCurrent()) != NULL) { state->UnloadIterators(); iterator2.Remove(); Unregister_Object(state); delete state; } Stuff::ChainIteratorOf iterator3(&expressionsLoaded); while ((state = iterator3.GetCurrent()) != NULL) { state->UnloadIterators(); iterator3.Remove(); Unregister_Object(state); delete state; } Stuff::Plug *instance; Stuff::ChainIteratorOf iterator4(&animInstancesLoaded); while ((instance = iterator4.GetCurrent()) != NULL) { iterator4.Remove(); Unregister_Object(instance); delete instance; } if (animHolderArray != NULL) { for (int i = 0; i < animHoldersCount; ++i) { Unregister_Pointer(animHolderArray[i]); delete animHolderArray[i]; } Unregister_Pointer(animHolderArray); delete[] animHolderArray; } if (animCurves != NULL) { Unregister_Pointer(animCurves); delete[] animCurves; } Unregister_Object(iteratorManager); delete iteratorManager; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::Reset() { // unload anything that may be running.. AnimationState *state = statePlaying.GetCurrent(); if (state != NULL) { //state->UnloadIterators(); statePlaying.Remove(); } state = newStatePlaying.GetCurrent(); if (state != NULL) { //state->UnloadIterators(); newStatePlaying.Remove(); } state = transitionPlaying.GetCurrent(); if (state != NULL) { //state->UnloadIterators(); transitionPlaying.Remove(); } Stuff::ChainIteratorOf state_iterator(&expressionsPlaying); while ((state = state_iterator.GetCurrent()) != NULL) { //state->UnloadIterators(); state_iterator.Remove(); } currentState = NoAnimationState; transitionFlag = false; quedCurrentState = false; quedNewState = false; quedTransState = false; transitionTo = -1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int AnimationStateEngine::GetTransitionState() { if (!transitionFlag) return NoAnimationState; return transitionTo; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Scalar AnimationStateEngine::GetCurrentStatePercentage() { Check_Pointer(statePlaying.GetCurrent()); return statePlaying.GetCurrent()->GetStatePercentage(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Scalar AnimationStateEngine::GetTranistionStatePercentage() { Check_Pointer(transitionPlaying.GetCurrent()); return transitionPlaying.GetCurrent()->GetStatePercentage(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Scalar AnimationStateEngine::GetNewStatePercentage() { Check_Pointer(newStatePlaying.GetCurrent()); return newStatePlaying.GetCurrent()->GetStatePercentage(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool AnimationStateEngine::CurrentStateLoopedThisFrame() { if (statePlaying.GetCurrent() == NULL) return true; return statePlaying.GetCurrent()->LoopedThisFrame(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AnimationState::AnimationState(AnimationStateEngine *anim_state_engine): Plug(DefaultData) { Check_Object(anim_state_engine); animStateEngine = anim_state_engine; myState = -1; startTime = 0.0f; elapsedTime = 0.0f; animCount = 0; firstAnim = 0; justLooped = false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // TransitionState::TransitionState(AnimationStateEngine *anim_state_engine): AnimationState(anim_state_engine) { transitionToState = -1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AnimationState * AnimationStateEngine::FindAnimationState(int state_number) { Stuff::ChainIteratorOf iterator(&animStatesLoaded); AnimationState *anim_state; while ((anim_state = iterator.ReadAndNext()) != NULL) { if (anim_state->myState == state_number) { return anim_state; } } return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // TransitionState * AnimationStateEngine::FindTransitionState(int state_number, int trans_state) { Stuff::ChainIteratorOf iterator(&transitionsLoaded); TransitionState *anim_state = NULL; TransitionState *best_match = NULL; while ((anim_state = iterator.ReadAndNext()) != NULL) { if (anim_state->myState == state_number && anim_state->transitionToState == trans_state) { return anim_state; } else if (anim_state->myState == -1 && anim_state->transitionToState == trans_state) { //this is the default so mark it but keep searching for the exact match best_match = anim_state; } } return best_match; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int AnimationStateEngine::RequestState( int new_state, bool run_minimal, void* data ) { Check_Object(this); Check_Object(GetClassData()); Verify(new_state < GetClassData()->GetStateCount()); //SPEW(("jerryeds","%d, request %d", vehicleParent->GetReplicatorID().localID, new_state)); if (new_state == currentState) { return currentState; } RUNSTATES_LOGIC("RequestState"); Start_Timer(RunAnimStates); // All iterator loading happens in here // put the new state on the que... if (!transitionFlag) { transitionFlag = true; quedCurrentState = false; quedNewState = false; quedTransState = false; transitionTo = new_state; Verify(transitionPlaying.GetCurrent() == NULL); Verify(newStatePlaying.GetCurrent() == NULL); // state playing keeps playing.... // load the transition and the new state... AnimationState *new_anim_state = FindAnimationState(new_state); if (new_anim_state == NULL) { //SPEW(("jerryeds"," NONE")); // Don't bother playing transitions to states we can't get to // just switch states and stop playing animations transitionFlag = false; AnimationState *playing_state = statePlaying.GetCurrent(); if (playing_state != NULL) { Verify(playing_state->animCount > 0); //playing_state->UnloadIterators(); statePlaying.Remove(); } NewState(transitionTo); Stop_Timer(RunAnimStates); return -1; } else { Verify(new_anim_state->animCount > 0); #if defined(_ARMOR) AnimationState *current_state = statePlaying.GetCurrent(); #endif if (statePlaying.GetCurrent() != NULL) { //SPEW(("jerryeds"," ADD")); Verify(current_state->animCount > 0); quedCurrentState = true; Check_Object(current_state); Verify(newStatePlaying.GetCurrent() == NULL); newStatePlaying.Add(new_anim_state); //new_anim_state->LoadIterators(); TransitionState *trans_state = FindTransitionState(currentState, new_state); if (trans_state != NULL) { Verify(trans_state->animCount > 0); Verify(transitionPlaying.GetCurrent() == NULL); transitionPlaying.Add(trans_state); //trans_state->LoadIterators(); } } else { //SPEW(("jerryeds"," FIRST")); // first pass and nothing is acutally playing on the stack transitionFlag = false; quedCurrentState = true; Verify(statePlaying.GetCurrent() == NULL); statePlaying.Add(new_anim_state); //new_anim_state->LoadIterators(); new_anim_state->Que(0.0f, -1.0f, run_minimal); NewState(transitionTo); } } } Stop_Timer(RunAnimStates); return -1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::GetStates( int ¤t_state, int &transition_state, bool &cued_current, Scalar ¤t_position, bool &cued_transition, Scalar &transition_position, bool &cued_new, Scalar &new_position) { current_state = AnimationStateEngine::NoAnimationState; transition_state = AnimationStateEngine::NoAnimationState; current_position = 0.0f; transition_position = 0.0f; new_position = 0.0f; cued_current = false; cued_transition = false; cued_new = false; // we should always have a current state // but it isn't always qued and playing current_state = GetState(); // If there is a currentstate is playing mark it and it's position if (quedCurrentState) { cued_current = true; current_position = GetCurrentStatePercentage(); Verify(current_state == statePlaying.GetCurrent()->myState); } else { cued_current = false; current_position = 0.0f; } // if we are transitioning if (transitionFlag) { // find out the state we are transitioning to transition_state = GetTransitionState(); if (transition_state == current_state) { current_state = transitionPlaying.GetCurrent()->myState; } // if the transition is qued mark it and the position cued_transition = quedTransState; // we always need the position of the transition even if the animation isn't qued // since we use that also transition_position = GetTranistionStatePercentage(); } else { cued_transition = false; transition_state = NoAnimationState; transition_position = 0.0f; } // if we have a new state if (quedNewState) { // mark it as being qued and the position cued_new = true; new_position = GetNewStatePercentage(); } else { cued_new = false; new_position = 0.0f; } Verify(current_state < GetClassData()->GetStateCount()); Verify(transition_state < GetClassData()->GetStateCount()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::SetStates( int current_state, int transition_state, bool cued_current, Scalar current_position, bool cued_transition, Scalar transition_position, bool cued_new, Scalar new_position, bool run_minimal) { // Wow, this routine is a hell of a lot more complicated // than I intended it to be! #ifdef LAB_ONLY if(current_state >= GetClassData()->GetStateCount()) { STOP(("CLIENT READING INVALID ANIMATION STATE")); } if(transition_state >= GetClassData()->GetStateCount()) { STOP(("CLIENT READING INVALID ANIMATION STATE")); } #endif transitionFlag = false; quedCurrentState = false; quedNewState = false; quedTransState = false; transitionTo = -1; //------------------------------------------------------------ // See if our current animation matches the incomming state // if it doesn't match or doesn't exist at all // find and add the current state // this saves lookup and que time when the network // is just echoing states we are already in. //------------------------------------------------------------ AnimationState *new_anim_state = statePlaying.GetCurrent(); if (new_anim_state != NULL) { if (new_anim_state->myState != current_state) { // We have a state but it doesn't match statePlaying.Remove(); new_anim_state = FindAnimationState(current_state); if (new_anim_state != NULL) { Check_Object(new_anim_state); Verify(statePlaying.GetCurrent() == NULL); statePlaying.Add(new_anim_state); NewState(current_state); } else { // we were givin a state we don't have // just set our state transitionFlag = false; statePlaying.Remove(); NewState(current_state); } } } else { // we don't have any state currently qued new_anim_state = FindAnimationState(current_state); Check_Object(new_anim_state); Verify(statePlaying.GetCurrent() == NULL); statePlaying.Add(new_anim_state); } //------------------------------------------------------------ // If the current state is qued, set it's position //------------------------------------------------------------ if (cued_current) { quedCurrentState = true; new_anim_state->Que(-1.0f, current_position, run_minimal); new_anim_state->CalculateStatePercentage(); NewState(current_state); lastCurrentStatePosition = current_position; } //------------------------------------------------------------ // Que the transition if we have one //------------------------------------------------------------ if (transition_state != AnimationStateEngine::NoAnimationState) { TransitionState *trans_new_anim_state = (TransitionState *)transitionPlaying.GetCurrent(); if (trans_new_anim_state != NULL) { if ((trans_new_anim_state->myState != current_state) && (trans_new_anim_state->transitionToState != transition_state)) { //------------------------------------------------------------ // We have a transition loaded but it doesn't match the request one // so we remove the current one and load the requested one //------------------------------------------------------------ transitionPlaying.Remove(); trans_new_anim_state = FindTransitionState(current_state, transition_state); Check_Object(trans_new_anim_state); Verify(transitionPlaying.GetCurrent() == NULL); transitionPlaying.Add(trans_new_anim_state); } } else { //------------------------------------------------------------ // We don't have a transition loaded so go ahead and load // the requested one //------------------------------------------------------------ trans_new_anim_state = FindTransitionState(current_state, transition_state); Check_Object(trans_new_anim_state); Verify(transitionPlaying.GetCurrent() == NULL); transitionPlaying.Add(trans_new_anim_state); } transitionFlag = true; transitionTo = transition_state; if (cued_transition) { //------------------------------------------------------------ // If the transition was qued go ahead and set the position //------------------------------------------------------------ quedTransState = true; trans_new_anim_state->Que(-1.0f, transition_position, run_minimal); trans_new_anim_state->CalculateStatePercentage(); } AnimationState *new_anim_state = newStatePlaying.GetCurrent(); if (new_anim_state != NULL) { if (new_anim_state->myState != transition_state) { //------------------------------------------------------------ // We have a new state loaded but it doesn't match the request one // so we remove the current one and load the requested one //------------------------------------------------------------ newStatePlaying.Remove(); new_anim_state = FindAnimationState(transition_state); Check_Object(new_anim_state); Verify(newStatePlaying.GetCurrent() == NULL); newStatePlaying.Add(new_anim_state); } } else { //------------------------------------------------------------ // We don't have a new state loaded so go ahead and load // the requested one //------------------------------------------------------------ new_anim_state = FindAnimationState(transition_state); Check_Object(new_anim_state); Verify(newStatePlaying.GetCurrent() == NULL); newStatePlaying.Add(new_anim_state); } if (cued_new) { //------------------------------------------------------------ // If the new state was qued go ahead and set the position //------------------------------------------------------------ quedNewState = true; new_anim_state->Que(-1.0f,new_position, run_minimal); new_anim_state->CalculateStatePercentage(); } } else if (transitionPlaying.GetCurrent() != NULL) { //------------------------------------------------------------ // We have a transition and one isn't qued so we remove it //------------------------------------------------------------ AnimationState *state = transitionPlaying.GetCurrent(); transitionPlaying.Remove(); state = newStatePlaying.GetCurrent(); if (state != NULL) { newStatePlaying.Remove(); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::RunStates(Stuff::Scalar time_slice, bool run_minimal) { Check_Object(this); iteratorManager->PushLayer(1.0f); RUNSTATES_LOGIC("RunStates"); Start_Timer(RunAnimStates); { RUNSTATES_LOGIC("RunStates::Iterate"); Start_Timer(Iterate); AnimHierarchyNode *transition_node = NULL; AnimHierarchyNode *new_state_node = NULL; AnimHierarchyNode *cur_state_node = NULL; transBlendValue = 0.0f; newBlendValue = 0.0f; oldBlendValue = 0.0f; bool advance_new_state = true; bool advance_current_state = true; if (transitionFlag) { if (transitionPlaying.GetCurrent() == NULL) { // No transition - pure cut! //if there is no transition on the stack than just stop the //current state and move the new state over there... transitionFlag = false; quedCurrentState = true; AnimationState *state_playing = statePlaying.GetCurrent(); Check_Object(state_playing); Stuff::Scalar old_position = state_playing->GetStatePercentage(); statePlaying.Remove(); //state_playing->UnloadIterators(); AnimationState *new_state = newStatePlaying.GetCurrent(); Check_Object(new_state); newStatePlaying.Remove(); new_state->Que(old_position, -1.0f, run_minimal); statePlaying.Add(new_state); NewState(transitionTo); } } if (quedTransState) { transition_node = iteratorManager->PushLayer(1.0f); Verify(transitionPlaying.GetCurrent() != NULL); //SPEW(("jerryeds", "T : - %f", time_slice)); transitionPlaying.GetCurrent()->Advance(time_slice, 0, run_minimal); iteratorManager->PopLayer(); transBlendValue = 1.0f; TransitionState *trans_state = (TransitionState*)transitionPlaying.GetCurrent(); Check_Object(trans_state); // see if we need to stop the old state if(trans_state->GetStatePercentage() >= trans_state->playOldStateTill && quedCurrentState) { quedCurrentState = false; } // see if we need to load the new state. if(trans_state->GetStatePercentage() >= trans_state->startNewState && !quedNewState) { quedNewState = true; advance_new_state = false; AnimationState *new_state = newStatePlaying.GetCurrent(); Check_Object(new_state); //SPEW(("jerryeds", "QUE NEW STATE: %d %f", trans_state->myState, trans_state->GetStatePercentage())); new_state->Que(trans_state->GetStatePercentage(), trans_state->overrideNewStatePosition, run_minimal); } // see if we can stop the transition if(trans_state->GetStatePercentage() >= 1.0f) { Verify(quedCurrentState == false); Verify(quedNewState == true); transitionFlag = false; quedCurrentState = true; quedTransState = false; quedNewState = false; advance_current_state = false; //trans_state->UnloadIterators(); transitionPlaying.Remove(); //current_state->UnloadIterators(); statePlaying.Remove(); AnimationState *new_state = newStatePlaying.GetCurrent(); Check_Object(new_state); newStatePlaying.Remove(); statePlaying.Add(new_state); NewState(transitionTo); } } if (quedCurrentState) { cur_state_node = iteratorManager->PushLayer(1.0f); Verify(statePlaying.GetCurrent() != NULL); lastCurrentStatePosition = statePlaying.GetCurrent()->GetStatePercentage(); if (advance_current_state) { //SPEW(("jerryeds", "C : - %f", time_slice)); statePlaying.GetCurrent()->Advance(time_slice, 0, run_minimal); } else { //SPEW(("jerryeds", "C : - %f", 0.0f)); statePlaying.GetCurrent()->NoAdvance(0.0f, 0, run_minimal); } iteratorManager->PopLayer(); oldBlendValue = 1.0f; if (transitionFlag) { #if defined (_ARMOR) AnimationState *new_state = newStatePlaying.GetCurrent(); Check_Object(new_state); #endif if(!quedTransState) { TransitionState *trans_state = (TransitionState*)transitionPlaying.GetCurrent(); Check_Object(trans_state); if (trans_state->transitionStart == TransitionState::StartAnyWhereToken) { AnimationState *current_state = statePlaying.GetCurrent(); quedTransState = true; trans_state->Que(current_state->GetStatePercentage(), -1.0f, run_minimal); } else { AnimationState *current_state = statePlaying.GetCurrent(); // we are waiting for the old state to get to a specific place // so that we can start the transition if (lastCurrentStatePosition != -1) { if(lastCurrentStatePosition <= trans_state->transitionStart && current_state->GetStatePercentage() >= trans_state->transitionStart) { // if it passed the trigger location //SPEW(("jerryeds", "1- %f:%f", lastCurrentStatePosition, current_state->GetStatePercentage())); quedTransState = true; trans_state->Que(current_state->GetStatePercentage(), -1.0f, run_minimal); } else if (lastCurrentStatePosition <= trans_state->transitionStart && current_state->LoopedThisFrame()) { // if it was lower than the trigger spot and then looped quedTransState = true; trans_state->Que(current_state->GetStatePercentage(), -1.0f, run_minimal); //SPEW(("jerryeds", "2- %f:%f", lastCurrentStatePosition, current_state->GetStatePercentage())); } else if (current_state->GetStatePercentage() >= trans_state->transitionStart && current_state->LoopedThisFrame()) { // if it is higher than the trigger location and it looped quedTransState = true; trans_state->Que(current_state->GetStatePercentage(), -1.0f, run_minimal); //SPEW(("jerryeds", "3- %f:%f", lastCurrentStatePosition, current_state->GetStatePercentage())); } } } if (quedTransState) { transition_node = iteratorManager->PushLayer(1.0f); Verify(transitionPlaying.GetCurrent() != NULL); //SPEW(("jerryeds", "T2 : - %f", 0.0f)); transitionPlaying.GetCurrent()->Advance(0.0f, 0, run_minimal); iteratorManager->PopLayer(); transBlendValue = 1.0f; } } } } if (quedNewState) { new_state_node = iteratorManager->PushLayer(1.0f); Verify(newStatePlaying.GetCurrent() != NULL); if (advance_new_state) { //SPEW(("jerryeds", "N : - %f", time_slice)); newStatePlaying.GetCurrent()->Advance(time_slice, 0, run_minimal); } else { //SPEW(("jerryeds", "N : - %f", 0.0f)); newStatePlaying.GetCurrent()->NoAdvance(0.0f, 0, run_minimal); } iteratorManager->PopLayer(); newBlendValue = 1.0f; } iteratorManager->PopLayer(); // set up the curves now if (quedTransState) { TransitionState *trans_state = (TransitionState *)transitionPlaying.GetCurrent(); Check_Pointer(trans_state); if (quedCurrentState) { //where in the transition are we? Stuff::Scalar curve_percent; if (trans_state->playOldStateTill != 0.0f) { curve_percent = trans_state->GetStatePercentage() / trans_state->playOldStateTill; Clamp(curve_percent, 0.0f, 1.0f); } else { curve_percent = 0.0f; } Check_Pointer(cur_state_node); Check_Pointer(transition_node); Stuff::Scalar curve_position = animCurves[trans_state->startOverlapCurve].Play(curve_percent); cur_state_node->SetBlendValue(1.0f - curve_position); transition_node->SetBlendValue(curve_position); transBlendValue = curve_position; oldBlendValue = 1.0f - curve_position; newBlendValue = 0.0f; } else if(quedNewState) { Check_Pointer(new_state_node); Check_Pointer(transition_node); Stuff::Scalar curve_percent; if (trans_state->startNewState != 1.0f) { curve_percent = (trans_state->GetStatePercentage() - trans_state->startNewState) / (1.0f - trans_state->startNewState); Clamp(curve_percent, 0.0f, 1.0f); } else { curve_percent = 0.0f; } Stuff::Scalar curve_position = animCurves[trans_state->endOverlapCurve].Play(curve_percent); new_state_node->SetBlendValue(1.0f - curve_position); transition_node->SetBlendValue(curve_position); transBlendValue = curve_position; newBlendValue = 1.0f - curve_position; oldBlendValue = 0.0f; } } Stop_Timer(Iterate); } { RUNSTATES_LOGIC("RunStates::BlendAndApply"); iteratorManager->BlendAndApply(vehicleParent->animationArrayCount); } Stop_Timer(RunAnimStates); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::Advance(Stuff::Scalar time_slice, int depth_count, bool run_minimal) { // play the internal state if (time_slice <= 0.0f) { return; } elapsedTime += time_slice; if (depth_count == 0) { justLooped = false; } else if (depth_count > 4) { CalculateStatePercentage(); return; } bool any_playing = false; Scalar min_carryover = 99999999.0f; for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); // if not in a chain or the current animation in a chain if (anim_holder->currentStateFlag == AnimHolder::NowPlaying) { if (anim_holder->Advance(animStateEngine, time_slice, run_minimal)) { // one of the anims just ended... Scalar local_carryover = anim_holder->GetCarryOver(); // look for the closest one to the edge if (local_carryover < min_carryover) { min_carryover = local_carryover; } anim_holder->currentStateFlag = AnimHolder::DonePlaying; } else { any_playing = true; } } else if (anim_holder->currentStateFlag == AnimHolder::BeforePlaying) { if (anim_holder->waitForAnim != AnimHolder::NoStartAfterToken) { // check to see if this guy is ready to go... AnimHolder *wait_on_holder = animStateEngine->animHolderArray[anim_holder->waitForAnim + firstAnim]; Check_Pointer(wait_on_holder); if (wait_on_holder->currentStateFlag == AnimHolder::DonePlaying) { anim_holder->currentStateFlag = AnimHolder::NowPlaying; anim_holder->Que(0.0f, run_minimal); if (anim_holder->Advance(animStateEngine, wait_on_holder->GetCarryOver(), run_minimal)) { // one of the anims just ended... Scalar local_carryover = anim_holder->GetCarryOver(); // look for the closest one to the edge if (local_carryover < min_carryover) { min_carryover = local_carryover; } anim_holder->currentStateFlag = AnimHolder::DonePlaying; } else { any_playing = true; } } } } } if (!any_playing) { // reset we are done Que(0.0f, -1.0f, run_minimal); Verify(min_carryover != 99999999.0f); if (carryOver) { Advance(min_carryover, depth_count + 1, run_minimal); } justLooped = true; } CalculateStatePercentage(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::NoAdvance(Stuff::Scalar time_slice, int depth_count, bool run_minimal) { // play the internal state if (depth_count == 0) { justLooped = false; } bool any_playing = false; Scalar min_carryover = 99999999.0f; for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); // if not in a chain or the current animation in a chain if (anim_holder->currentStateFlag == AnimHolder::NowPlaying) { if (anim_holder->Advance(animStateEngine, time_slice, run_minimal)) { // one of the anims just ended... Scalar local_carryover = anim_holder->GetCarryOver(); // look for the closest one to the edge if (local_carryover < min_carryover) { min_carryover = local_carryover; } anim_holder->currentStateFlag = AnimHolder::DonePlaying; } else { any_playing = true; } } else if (anim_holder->currentStateFlag == AnimHolder::BeforePlaying) { if (anim_holder->waitForAnim != AnimHolder::NoStartAfterToken) { // check to see if this guy is ready to go... AnimHolder *wait_on_holder = animStateEngine->animHolderArray[anim_holder->waitForAnim + firstAnim]; Check_Pointer(wait_on_holder); if (wait_on_holder->currentStateFlag == AnimHolder::DonePlaying) { anim_holder->currentStateFlag = AnimHolder::NowPlaying; anim_holder->Que(0.0f, run_minimal); if (anim_holder->Advance(animStateEngine, wait_on_holder->GetCarryOver(), run_minimal)) { // one of the anims just ended... Scalar local_carryover = anim_holder->GetCarryOver(); // look for the closest one to the edge if (local_carryover < min_carryover) { min_carryover = local_carryover; } anim_holder->currentStateFlag = AnimHolder::DonePlaying; } else { any_playing = true; } } } } } if (!any_playing) { // reset we are done Que(0.0f, -1.0f, run_minimal); } CalculateStatePercentage(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void TransitionState::Advance(Stuff::Scalar time_slice, int depth_count, bool run_minimal) { elapsedTime += time_slice; // play the internal state // we transition and that is IT Verify (depth_count == 0); Scalar min_carryover = 99999999.0f; for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); // if not in a chain or the current animation in a chain if (anim_holder->currentStateFlag == AnimHolder::NowPlaying) { if (anim_holder->Advance(animStateEngine, time_slice, run_minimal)) { //SPEWALWAYS(("jerryeds","TRANS DONE - NO QUE!!!!")); // one of the anims just ended... Scalar local_carryover = anim_holder->GetCarryOver(); // look for the closest one to the edge if (local_carryover < min_carryover) { min_carryover = local_carryover; } anim_holder->currentStateFlag = AnimHolder::DonePlaying; } } if (anim_holder->currentStateFlag == AnimHolder::BeforePlaying) { if (anim_holder->waitForAnim != AnimHolder::NoStartAfterToken) { // check to see if this guy is ready to go... AnimHolder *wait_on_holder = animStateEngine->animHolderArray[anim_holder->waitForAnim + firstAnim]; Check_Pointer(wait_on_holder); if (wait_on_holder->currentStateFlag == AnimHolder::DonePlaying) { anim_holder->currentStateFlag = AnimHolder::NowPlaying; anim_holder->Que(0.0f, run_minimal); if (anim_holder->Advance(animStateEngine, wait_on_holder->GetCarryOver(), run_minimal)) { // one of the anims just ended... Scalar local_carryover = anim_holder->GetCarryOver(); // look for the closest one to the edge if (local_carryover < min_carryover) { min_carryover = local_carryover; } anim_holder->currentStateFlag = AnimHolder::DonePlaying; } } } } } CalculateStatePercentage(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::CalculateStatePercentage(void) { // complications - THIS IS NOT EASY PAY ATTENTION // // First rule - Anim with the most time left right now sets the percentage // // Second rule - animation Delays are handled internaly to the animation // so no consideration is made for that // // third rule - animations that are chained are treated as one animation // but they do so internaly, and all should return the // same percantage of completion // // forth rule - we project time to completion by a simple deadreckon // time_left = percent_left_to_play * (percent_played / delta_time) Scalar max_time_left = 0.0f; Scalar max_timed_percentage = 0.0f; Scalar delta_time = static_cast(elapsedTime - startTime); if (Close_Enough(delta_time, 0.0f)) { statePercentage = 0.0f; return; } Verify(delta_time != 0.0f); bool all_done = true; for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); // if not in a chain or the current animation in a chain if (anim_holder->currentStateFlag == AnimHolder::NowPlaying) { Scalar current_pos = anim_holder->GetCurrentPercentage(); Verify(current_pos <= 1.0f); Verify(current_pos >= 0.0f); // don't realy have to do the subtraction // but for debuging it helps to see actual time // left // if not it is zero anyway and we are in the same boat! if (current_pos != 0.0f) { Scalar time_left = (delta_time/current_pos) - delta_time; if (time_left >= max_time_left) { max_timed_percentage = current_pos; max_time_left = time_left; } } all_done = false; } } if (all_done) { statePercentage = 1.0f; return; } statePercentage = max_timed_percentage; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::Que(Stuff::Scalar old_state_position, Stuff::Scalar over_ride_position, bool run_minimal) { // this is 0.0f //startTime = gos_GetElapsedTime(); #if defined(_ARMOR) bool verify_qued_animation = false; #endif justLooped = false; for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); // if we are not in a chain or are the first node in the chain // go ahead and start the animation Scalar set_position = -1; if (over_ride_position != -1) { set_position = over_ride_position; } //else if (old_state_position != -1) //{ //set_position = old_state_position; //} if (set_position != -1) { if (set_position >= anim_holder->startPercentage && set_position <= (anim_holder->percentageOfChain + anim_holder->startPercentage)) { // que this bad boy up anim_holder->currentStateFlag = AnimHolder::NowPlaying; #if defined(_ARMOR) verify_qued_animation = true; #endif // scale the position accordingly set_position = (set_position - anim_holder->startPercentage)/anim_holder->percentageOfChain; anim_holder->Que(set_position, run_minimal); elapsedTime = anim_holder->currentTime; CalculateStatePercentage(); } else { anim_holder->currentStateFlag = AnimHolder::BeforePlaying; } } else { if (anim_holder->waitForAnim == AnimHolder::NoStartAfterToken) { #if defined(_ARMOR) verify_qued_animation = true; #endif anim_holder->currentStateFlag = AnimHolder::NowPlaying; anim_holder->Que(set_position, run_minimal); elapsedTime = anim_holder->currentTime; CalculateStatePercentage(); } else { anim_holder->currentStateFlag = AnimHolder::BeforePlaying; } } } Verify(verify_qued_animation); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::LoadIterators(bool run_minimal) { for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); // if we are not in a chain or are the first node in the chain // go ahead and start the animation anim_holder->LoadIterators(run_minimal); } statePercentage = 0.0f; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::UnloadIterators(void) { // shut em down... for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); //if (anim_holder->inAnimIteratorManager) //{ // anim_holder->Stop(animStateEngine); //} anim_holder->UnloadIterators(); } statePercentage = -1.0f; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void HermiteSpline1D::Init( Stuff::Scalar start_point, Stuff::Scalar out_tan, Stuff::Scalar in_tan, Stuff::Scalar stop_point ) { point1 = start_point; tangent1 = out_tan; tangent2 = in_tan; point2 = stop_point; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Scalar HermiteSpline1D::Evaluate(Stuff::Scalar time_of_curve) { Verify(time_of_curve <= 1.0f); Verify(time_of_curve >= 0.0f); Stuff::Scalar time_squared = time_of_curve * time_of_curve; Stuff::Scalar time_cubed = time_of_curve * time_of_curve * time_of_curve; Stuff::Scalar h1 = 2*time_cubed - 3*time_squared + 1; Stuff::Scalar h2 = -2*time_cubed + 3*time_squared; Stuff::Scalar h3 = time_cubed - 2 * time_squared + time_of_curve; Stuff::Scalar h4 = time_cubed - time_squared; return h1*point1 + h2*point2 + h3 * tangent1 + h4 * tangent2; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimCurve::InitializeClass(void) { BumpCurveMat.Init( 0.0f, 4.0f, -4.0f, 0.0f); EaseUpSplineCurveMat.Init( 0.0f, 0.0f, 0.0f, 1.0f); CurveUpCurveMat.Init( 0.0f, 0.0f, 2.0f, 1.0f); SpikeUpCurveMat.Init( 0.0f, 0.8f, 4.5f, 1.0f); BellCurveMat.Init( 0.0f,-0.25f, 0.0f, 1.0f); SpikeCurveMat.Init( 0.0f, 0.0f, 3.0f, 1.0f); LongBumpCurveMat.Init( 0.0f, 3.2f, 0.0f, 1.0f); BumpSpikeCurveMat.Init( 0.0f, 2.0f, 1.0f, 1.0f); SpikeBellCurveMat.Init( 0.0f, 0.0f, 1.0f, 1.0f); } Stuff::Scalar AnimCurve::Play(Stuff::Scalar percent_of_curve) { Verify(percent_of_curve >= 0.0f && percent_of_curve <= 1.0f); Verify(curveType >= 0 && curveType < CurveTypeCount); Verify(curveMin >= 0.0f && curveMin <= 1.0f); Verify(curveMax >= 0.0f && curveMax <= 1.0f); Verify(timeStart >= 0.0f && timeStart <= 1.0f); Verify(timeEnd >= 0.0f && timeEnd <= 1.0f); Stuff::Scalar curve_value = 0.0f; if (percent_of_curve < timeStart) { percent_of_curve = 0.0f; } else if (percent_of_curve > timeEnd) { percent_of_curve = 1.0f; } else { Verify(timeEnd - timeStart > 0.0f); Scalar position = percent_of_curve - timeStart; percent_of_curve = position / timeEnd - timeStart; } if (invertTime) { percent_of_curve = 1.0f - percent_of_curve; } switch(curveType) { case LinearUpCurveType: curve_value = percent_of_curve; break; case CubedCurveType: curve_value = percent_of_curve * percent_of_curve * percent_of_curve; break; case EaseUpCurveType: curve_value = (-2.0f*percent_of_curve*percent_of_curve*percent_of_curve) + (3.0f*percent_of_curve*percent_of_curve); break; case EdgeCurveType: curve_value = 1.0f-(Scalar)fabs((percent_of_curve-0.5f)*2.0f); break; case BumpCurveType: curve_value = BumpCurveMat.Evaluate(percent_of_curve); break; case EaseUpSplineCurveType: curve_value = EaseUpSplineCurveMat.Evaluate(percent_of_curve); break; case CurveUpCurveType: curve_value = CurveUpCurveMat.Evaluate(percent_of_curve); break; case SpikeUpCurveType: curve_value = SpikeUpCurveMat.Evaluate(percent_of_curve); break; case BellCurveType: { if(percent_of_curve >= 0.5f) { Stuff::Scalar reversed_double_spline; reversed_double_spline = 1.0f-((percent_of_curve - 0.5f)*2.0f); curve_value = BellCurveMat.Evaluate( reversed_double_spline); } else { Stuff::Scalar double_spline = percent_of_curve * 2.0f; curve_value = BellCurveMat.Evaluate(double_spline); } } break; case SpikeCurveType: { if(percent_of_curve >= 0.5f) { Stuff::Scalar reversed_double_spline; reversed_double_spline = 1.0f-((percent_of_curve - 0.5f)*2.0f); curve_value = SpikeCurveMat.Evaluate( reversed_double_spline); } else { Stuff::Scalar double_spline = percent_of_curve * 2.0f; curve_value = SpikeCurveMat.Evaluate(double_spline); } } break; case LongBumpCurveType: { if(percent_of_curve >= 0.5f) { Stuff::Scalar reversed_double_spline; reversed_double_spline = 1.0f-((percent_of_curve - 0.5f)*2.0f); curve_value = LongBumpCurveMat.Evaluate( reversed_double_spline); } else { Stuff::Scalar double_spline = percent_of_curve * 2.0f; curve_value = LongBumpCurveMat.Evaluate(double_spline); } } break; case BumpSpikeCurveType: { if(percent_of_curve >= 0.5f) { Stuff::Scalar reversed_double_spline; reversed_double_spline = 1.0f-((percent_of_curve - 0.5f)*2.0f); curve_value = BumpSpikeCurveMat.Evaluate( reversed_double_spline); } else { Stuff::Scalar double_spline = percent_of_curve * 2.0f; curve_value = BumpSpikeCurveMat.Evaluate(double_spline); } } break; case SpikeBellCurveType: { if(percent_of_curve >= 0.5f) { Stuff::Scalar reversed_double_spline; reversed_double_spline = 1.0f-((percent_of_curve - 0.5f)*2.0f); curve_value = SpikeBellCurveMat.Evaluate( reversed_double_spline); } else { Stuff::Scalar double_spline = percent_of_curve * 2.0f; curve_value = SpikeBellCurveMat.Evaluate(double_spline); } } break; default: STOP(("AnimCurve::Play : BAD CURVE TYPE!")); break; } Scalar spread = curveMax - curveMin; curve_value *= spread; curve_value += curveMin; if (invertScale) { curve_value = 1.0f - curve_value; } return curve_value; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::TestClass() { Verify(IsDerivedFrom(DefaultData)); }