//===========================================================================// // 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 "Vehicle.hpp" #include "AnimationState.hpp" //############################################################################# //########################## AnimationStateEngine ######################## //############################################################################# //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int AnimationStateEngine::GetAnimHolderType(const char *type_name) { Check_Pointer(type_name); if (!_stricmp(type_name, "PoseType")) { return PoseType; } else if (!_stricmp(type_name, "CycleType")) { return CycleType; } else if (!_stricmp(type_name, "SpeedCycleType")) { return SpeedCycleType; } else if (!_stricmp(type_name, "LerpCycleType")) { return LerpCycleType; } else if (!_stricmp(type_name, "FullHeightSpeedBlenderCycleType")) { return FullHeightSpeedBlenderCycleType; } else if (!_stricmp(type_name, "FullHeightBlenderCycleType")) { return FullHeightBlenderCycleType; } else if (!_stricmp(type_name, "SpeedBlenderCycleType")) { return SpeedBlenderCycleType; } else if (!_stricmp(type_name, "FullHeightPoseHolderType")) { return FullHeightPoseHolderType; } else { STOP(("AnimationStateEngine::GetAnimHolderType : %s Not a valid type", type_name)); } return -1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // size_t AnimationStateEngine::GetAnimHolderSize(int type) { Verify(type < AnimTypeCount); Verify(type > 0); switch (type) { case PoseType: return sizeof(PoseHolder); case CycleType: return sizeof(CycleHolder); case SpeedCycleType: return sizeof(SpeedCycleHolder); case LerpCycleType: return sizeof(LerpCycleHolder); case FullHeightSpeedBlenderCycleType: return sizeof(FullHeightSpeedBlenderCycleHolder); case FullHeightBlenderCycleType: return sizeof(FullHeightBlenderCycleHolder); case SpeedBlenderCycleType: return sizeof(SpeedBlenderCycleHolder); case FullHeightPoseHolderType: return sizeof(FullHeightPoseHolder); default: STOP(("AnimationStateEngine::GetAnimHolderSize - All types not defined!")); } return 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AnimHolder *AnimationStateEngine::MakeAnimHolder(int type) { Verify(type < AnimTypeCount); Verify(type > 0); switch (type) { case PoseType: return new PoseHolder; case CycleType: return new CycleHolder; case SpeedCycleType: return new SpeedCycleHolder(); case LerpCycleType: return new LerpCycleHolder(); case FullHeightSpeedBlenderCycleType: return new FullHeightSpeedBlenderCycleHolder(); case FullHeightBlenderCycleType: return new FullHeightBlenderCycleHolder(); case SpeedBlenderCycleType: return new SpeedBlenderCycleHolder(); case FullHeightPoseHolderType: return new FullHeightPoseHolder(); default: STOP(("AnimationStateEngine::MakeAnimHolder - All types not defined!")); } return 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // MW4Animation::AnimInstance *AnimationStateEngine::LoadOrMakeAnimInstance(const char *animation_name) { Stuff::ChainIteratorOf iterator(&animInstancesLoaded); MW4Animation::AnimInstance *anim_instance; while ((anim_instance = iterator.ReadAndNext()) != NULL) { if(!_stricmp(anim_instance->animData->animHeaderBlock->animationName, animation_name)) { return anim_instance; } } // make anim_instance anim_instance = MW4Animation::AnimInstanceManager::Anim_Instance_Manager->MakeAnimInstance( animation_name, (MW4Animation::AnimInstanceManager::UserDefinedSearchFunction)Vehicle::FindChildMoverIndexUserData, (void *)vehicleParent ); Check_Object(anim_instance); Check_Object(anim_instance); animInstancesLoaded.Add(anim_instance); return anim_instance; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationStateEngine::LoadScript(const char *filename, bool run_minimal) { //SPEW(("jerryeds", "LOAD SCRIPT : %s", filename)); Verify(!alreadyLoaded); alreadyLoaded = true; // open the notation file Check_Pointer(filename); gos_PushCurrentHeap(g_AnimScript); Adept::Resource anim_res(filename); if(!anim_res.DoesResourceExist()) { SPEW((0,"AnimationScript not in props.build : %s", filename)); return; } anim_res.LoadData(); NotationFile script_notefile(&anim_res); // spin thru and load appropriate type of anim holder NotationFile::PageIterator *pages = script_notefile.MakePageIterator(); Check_Object(pages); int anim_holder_count = 0; int total_curve_count = 0; Page *page; while ((page = pages->ReadAndNext()) != NULL) { Check_Object(page); const char* anim_state_name = page->GetName(); Check_Pointer(anim_state_name); //SPEW(("jerryeds", "CREATE : %s", anim_state_name)); // load the states if (strstr(anim_state_name, "::") == NULL) { // load the state based off of type (anim, transition or expression) AnimationState *new_anim_state = new AnimationState(this); Check_Object(new_anim_state); new_anim_state->PreLoad(page); anim_holder_count += new_anim_state->GetAnimHolderCount(page); total_curve_count += new_anim_state->GetCurveCount(page); animStatesLoaded.Add(new_anim_state); } else if (strstr(anim_state_name, "::") != NULL) // load the transitions { // load the transition based off of type (anim, transition or expression) TransitionState *new_anim_state = new TransitionState(this); Check_Object(new_anim_state); new_anim_state->PreLoad(page); anim_holder_count += new_anim_state->GetAnimHolderCount(page); total_curve_count += new_anim_state->GetCurveCount(page); transitionsLoaded.Add(new_anim_state); } else { STOP(("AnimationStateEngine::LoadScript : INVALID CODE - SHOULD NEVER REACH HERE")); } } //allocate curves and animholders animHoldersCount = anim_holder_count; Verify(animHoldersCount > 0); animHolderArray = new AnimHolder*[animHoldersCount]; Register_Pointer(animHolderArray); animCurvesCount = total_curve_count; if (animCurvesCount != 0) { animCurves = new AnimCurve[animCurvesCount]; Register_Pointer(animCurves); } // Now load each animHolder which will: // load each animinstances // load the curves int current_curve = 0; int current_holder = 0; Stuff::ChainIteratorOf s_iterator(&animStatesLoaded); Stuff::ChainIteratorOf t_iterator(&transitionsLoaded); AnimationState *current_state; AnimationState *current_transition; pages->First(); while ((page = pages->ReadAndNext()) != NULL) { const char* anim_state_name = page->GetName(); Check_Pointer(anim_state_name); //SPEW(("jerryeds", "LOAD : %s", anim_state_name)); if (strstr(anim_state_name, "::") == NULL) { current_state = s_iterator.ReadAndNext(); Check_Object(current_state); current_state->LoadScriptEntry(page, current_holder, current_curve, run_minimal); current_state->CalculateChainTimes(); current_state->LoadIterators(run_minimal); } else if (strstr(anim_state_name, "::") != NULL) { current_transition = t_iterator.ReadAndNext(); Check_Object(current_transition); current_transition->LoadScriptEntry(page, current_holder, current_curve, run_minimal); current_transition->CalculateChainTimes(); current_transition->LoadIterators(run_minimal); } else { STOP(("AnimationStateEngine::LoadScript : INVALID CODE - SHOULD NEVER REACH HERE")); } } Verify(s_iterator.GetCurrent() == NULL); Verify(t_iterator.GetCurrent() == NULL); Check_Object(pages); delete pages; anim_res.UnloadData(); gos_PopCurrentHeap(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::PreLoad(Page *page) { Check_Object(page); Check_Object(animStateEngine); // look up state StateEngine::ClassData *state_class_data; state_class_data = animStateEngine->GetClassData(); Check_Object(state_class_data); const StateEngine::StateEntry *state_entry = state_class_data->FindStateEntry(page->GetName()); if(state_entry != NULL) { Check_Object(state_entry); myState = state_entry->stateNumber; } else { myState = -1; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void TransitionState::PreLoad(Page *page) { Check_Object(page); char *copy_name; char *first_name; char *second_name; const char* page_name = page->GetName(); Check_Pointer(page_name); copy_name = new char [strlen(page_name)+1]; Register_Pointer(copy_name); Str_Copy(copy_name, page_name, strlen(page_name)+1); first_name = copy_name; second_name = strstr(copy_name, "::"); *second_name = '\0'; second_name++; *second_name = '\0'; second_name++; // look up state StateEngine::ClassData *state_class_data; state_class_data = animStateEngine->GetClassData(); Check_Object(state_class_data); const StateEngine::StateEntry *state_entry = state_class_data->FindStateEntry(first_name); if(state_entry != NULL) { Check_Object(state_entry); myState = state_entry->stateNumber; } else { myState = -1; } state_entry = state_class_data->FindStateEntry(second_name); if(state_entry != NULL) { Check_Object(state_entry); transitionToState = state_entry->stateNumber; } else { transitionToState = -1; } const char *entry_data; page->GetEntry("TransitionStart", &entry_data, true); if(!_strnicmp(entry_data, "any", 3)) { transitionStart = StartAnyWhereToken; } else { transitionStart = AtoF(entry_data); Verify(transitionStart >= 0.0f); Verify(transitionStart <= 1.0f); } page->GetEntry("PlayOldStateTill", &playOldStateTill, true); page->GetEntry("StartNewState", &startNewState, true); const char *override_text; page->GetEntry("OverrideNewStatePosition", &override_text, true); if (!_stricmp(override_text, "default")) { overrideNewStatePosition = -1; } else { overrideNewStatePosition = AtoF(override_text); Verify(overrideNewStatePosition >= 0.0f); Verify(overrideNewStatePosition <= 1.0f); } Verify(playOldStateTill >= 0.0f); Verify(playOldStateTill <= 1.0f); Verify(startNewState >= 0.0f); Verify(startNewState <= 1.0f); Verify(startNewState >= playOldStateTill); // these are only allowed to *touch* not overlap Unregister_Pointer(copy_name); delete[] copy_name; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::LoadScriptEntry(Page *page, int ¤t_holder, int ¤t_curve, bool run_minimal) { Check_Object(page); Check_Object(animStateEngine); // load all my children Page::NoteIterator *notes = page->MakeNoteIterator(); Check_Object(notes); firstAnim = current_holder; animCount = 0; NameList *anim_param_list = NULL; if (!page->GetEntry("CarryOver", &carryOver)) { carryOver = true; } Note *note; while ((note = notes->ReadAndNext()) != NULL) { Check_Object(note); const char* anim_type_name = note->GetName(); Check_Pointer(anim_type_name); const char* note_data; note->GetEntry(¬e_data); if (!_stricmp(anim_type_name, "AnimType")) { // load based off of list if (anim_param_list != NULL) { LoadAnimHolder(anim_param_list, firstAnim + animCount, current_curve, run_minimal); Check_Object(anim_param_list); delete anim_param_list; anim_param_list = NULL; ++animCount; } Verify(anim_param_list == NULL); anim_param_list = new NameList(); Check_Object(anim_param_list); anim_param_list->AddEntry(note->GetName(), (void*)note_data); } else { // in effect this spins till it gets the first animtype if (anim_param_list != NULL) { anim_param_list->AddEntry(note->GetName(), (void*)note_data); } } } if (anim_param_list != NULL) { LoadAnimHolder(anim_param_list, firstAnim + animCount, current_curve, run_minimal); Check_Object(anim_param_list); delete anim_param_list; anim_param_list = NULL; ++animCount; } Verify(animCount > 0); //SPEW(("jerryeds", "LOAD %s : %d", page->GetName(), animCount)); Check_Object(notes); delete notes; current_holder += animCount; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void TransitionState::LoadScriptEntry(Page *page, int ¤t_holder, int ¤t_curve, bool run_minimal) { Check_Object(page); Check_Object(animStateEngine); AnimationState::LoadScriptEntry(page, current_holder, current_curve, run_minimal); //now load my curves... const char *curve_param; page->GetEntry("StartOverlapCurve", &curve_param, true); startOverlapCurve = current_curve; animStateEngine->animCurves[current_curve].LoadCurve(curve_param); current_curve++; Verify(current_curve <= animStateEngine->animCurvesCount); page->GetEntry("EndOverlapCurve", &curve_param, true); endOverlapCurve = current_curve; animStateEngine->animCurves[current_curve].LoadCurve(curve_param); current_curve++; Verify(current_curve <= animStateEngine->animCurvesCount); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::LoadAnimHolder(NameList *param_list, int anim_holder_count, int ¤t_curve, bool run_minimal) { // This name list has only the animation parameters and nothing else. // Now we can use the search interface to make it work pretty // figure out the type NameList::Entry *page = param_list->FindEntry("AnimType"); Check_Object(page); int type = animStateEngine->GetAnimHolderType(page->GetChar()); // load generic data Verify(anim_holder_count < animStateEngine->animHoldersCount); AnimHolder *anim_holder = animStateEngine->MakeAnimHolder(type); animStateEngine->animHolderArray[anim_holder_count] = anim_holder; Check_Pointer(animStateEngine->animHolderArray[anim_holder_count]); Register_Pointer(animStateEngine->animHolderArray[anim_holder_count]); anim_holder->animType = type; page = param_list->FindEntry("Start"); Check_Object(page); const char *contents = page->GetChar(); if (!_strnicmp(contents, "after", 5)) { anim_holder->startDelay = 0.0f; sscanf(contents, "after %d", &anim_holder->waitForAnim); } else { anim_holder->waitForAnim = AnimHolder::NoStartAfterToken; anim_holder->startDelay = page->GetAtof(); } animStateEngine->animHolderArray[anim_holder_count]->Load(animStateEngine, param_list, run_minimal); anim_holder->firstCurve = current_curve; anim_holder->curveCount = 0; //load the curves page = param_list->GetFirstEntry(); while(page != NULL) { if (!_stricmp(page->GetName(), "Curve")) { //load er up animStateEngine->animCurves[anim_holder->firstCurve+anim_holder->curveCount].LoadCurve(page->GetChar()); anim_holder->curveCount++; } else if(!_stricmp(page->GetName(), "CurveMask")) { Verify(anim_holder->curveCount != 0); // verify we already have a curve... animStateEngine->animCurves[anim_holder->firstCurve+anim_holder->curveCount].LoadMasks(page->GetChar()); } page = page->GetNextEntry(); } current_curve += anim_holder->curveCount; Verify(current_curve <= animStateEngine->animCurvesCount); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // size_t AnimationState::GetAnimHolderCount(Page *page) { Check_Object(page); // spin thru and load appropriate type of anim holder ChainOf *type_chain = page->MakeNoteChain("AnimType"); Check_Object(type_chain); Page::NoteIterator pages(type_chain); int anim_holder_count = pages.GetSize(); Check_Object(type_chain); delete type_chain; return anim_holder_count; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int AnimationState::GetCurveCount(Page *page) { Check_Object(page); // spin thru and load appropriate type of anim holder ChainOf *curve_chain = page->MakeNoteChain("Curve"); Check_Object(curve_chain); Page::NoteIterator pages(curve_chain); int curve_count = pages.GetSize(); Check_Object(curve_chain); delete curve_chain; return curve_count; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int TransitionState::GetCurveCount(Page *page) { return AnimationState::GetCurveCount(page) + 2; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //#################################### BIG NOTE ###################################### // This is kind of tricky. // The problem lies in the fact that these guys can dynamicly scale // themselevs. Which makes calculating this part a wee bit hard. // It seems as if the solution is to treat each // animation or chain of animations independantly // when calculating the state position we figure // out how much time has gone by since the state // started. Based off of that we dead reckon each // chain of animations from current position to finish. // based off of that we will have the "longest" animation // chain. Then we simply see where it is percentage wise. // in order to do this we must have each animation know if it in // a chain and if so how much percentage of it is used for the anim // This is will be a slow function if there are a lot of // chained animations void AnimationState::CalculateChainTimes() { // we are measuring total time of chained animations Verify(animCount > 0); for (int i = 0; i < animCount; ++i) { AnimHolder *anim_holder = animStateEngine->animHolderArray[i+firstAnim]; Check_Pointer(anim_holder); if (anim_holder->waitForAnim != AnimHolder::NoStartAfterToken) { // we have a chain here... // recurse the chain and adjust... // this algorithim depends on order so... Verify(anim_holder->waitForAnim < i); Verify(anim_holder->waitForAnim >= 0); Verify(anim_holder->waitForAnim < animCount); Stuff::Scalar total_time = 0.0f; RecurseAndCalculateChainTime(total_time, i); } else { anim_holder->startPercentage = 0.0f; anim_holder->percentageOfChain = 1.0f; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimationState::RecurseAndCalculateChainTime(Stuff::Scalar &total_time, int current_anim) { AnimHolder *anim_holder = animStateEngine->animHolderArray[current_anim+firstAnim]; Check_Pointer(anim_holder); total_time += anim_holder->GetTimeTotal(); if (anim_holder->waitForAnim != AnimHolder::NoStartAfterToken) { Verify(anim_holder->waitForAnim < current_anim); Verify(anim_holder->waitForAnim >= 0); Verify(anim_holder->waitForAnim < animCount); RecurseAndCalculateChainTime(total_time, anim_holder->waitForAnim); } // depth first iteratation // so we start at the bottom of the list // calculate our percentage of the total time.. if (!Small_Enough(total_time)) { if (!Small_Enough(anim_holder->GetTimeTotal())) { anim_holder->percentageOfChain = anim_holder->GetTimeTotal() / total_time; } else { anim_holder->percentageOfChain = 0; } } else { anim_holder->percentageOfChain = 0; } // calculate our start position if (anim_holder->waitForAnim == AnimHolder::NoStartAfterToken) { // first one starts at zero (internal delay is handled by the holder) anim_holder->startPercentage = 0.0f; } else { // our start is the waiting states start plus his percentage AnimHolder *wait_anim_holder = animStateEngine->animHolderArray[anim_holder->waitForAnim+firstAnim]; Check_Pointer(wait_anim_holder); anim_holder->startPercentage = wait_anim_holder->startPercentage + wait_anim_holder->percentageOfChain; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // AnimCurve::AnimCurve() { curveType = NULLAnimType; curveMin = -1.0f; curveMax = -1.0f; timeStart = -1.0f; timeEnd = -1.0f; invertTime = false; invertScale = false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimCurve::LoadCurve(const char *curve_text) { int invert_time_test = -1; int invert_scale_test = -1; sscanf(curve_text, "%d %f %f %f %f %d %d", &curveType, &curveMin, &curveMax, &timeStart, &timeEnd, &invert_time_test, &invert_scale_test); // sscanf(curve_text, "%f", &curveMin); // sscanf(curve_text, "%f", &curveMax); // sscanf(curve_text, "%f", &timeStart); // sscanf(curve_text, "%f", &timeEnd); // sscanf(curve_text, "%d", &invert_time_test); // sscanf(curve_text, "%d", &invert_scale_test); 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); Verify(invert_time_test == 0 || invert_time_test == 1); Verify(invert_scale_test == 0 || invert_scale_test == 1); invertTime = (invert_time_test)?true:false; invertScale = (invert_scale_test)?true:false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void AnimCurve::LoadMasks(const char *mask_text) { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //