Files
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

886 lines
22 KiB
C++

//===========================================================================//
// 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<MW4Animation::AnimInstance*> 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<AnimationState*> s_iterator(&animStatesLoaded);
Stuff::ChainIteratorOf<TransitionState*> 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 &current_holder, int &current_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(&note_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 &current_holder, int &current_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 &current_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<Note*> *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<Note*> *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)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//