Files
RP412/MUNGA/JMOVER.cpp
T
CydandClaude Opus 5 6467a5f930 The teardown looks at the segments before it calls through them
The podium crash dies in SocketIterator::DeletePlugs, calling through a
segment whose vtable dword has been replaced by a small float. The three
dumps prove the segment is wrong BY teardown; nothing in them says when
it went wrong, and six configurations of the local repro rig - parked
pods, driven pods, light and full page heap, two, four and six pods -
reached the podium and tore down clean.

So the next real playtest becomes the instrument. RP412SEGCHECK walks
the segment table in ~JointedMover before the delete, guarded-reads each
segment's first dword, and if one does not match the vtable captured
from the very first segment ever built it writes the forensics into
rpl4-fail.log, which is closed on the way down and survives the abort -
rpl4.log does not. The report carries the entity and whether it was the
local pod, which index went bad and what is in it, the first two rows of
the object as hex and float, and the heap deltas to its neighbours on
either side.

Three bracket calls in the winners' circle answer the question the dumps
cannot: at podium entry, and either side of the second
MakeEntityRenderables on the own pod. Whichever fires first is recorded
and travels inside the teardown report, so the log says whether the race
broke the segment or the podium did.

It deliberately does not skip the delete or repair the pointer. The
ownership bug is unfixed and a guard would cost exactly the evidence
this is here to collect - it stops on the same object, one step earlier,
holding the forensics. On by default, a handful of pointer compares per
pod per race; RP412SEGCHECK=0 turns it off, and the environ.ini template
says so.

tools/podium-repro is the rig itself, banked with what the dumps already
established: page heap turned on through the PEB without gflags or
elevation, N sandboxed installs, and a feeder that drives full races
through them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 16:33:31 -05:00

2368 lines
55 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "jmover.h"
#include "fileutil.h"
#include "app.h"
#include "notation.h"
#include "namelist.h"
// EXCEPTION_EXECUTE_HANDLER, for the guarded read in RPReadDword below.
// excpt.h rather than windows.h: this layer does not otherwise want it.
#include <excpt.h>
//#############################################################################
//############################ JointedMover #############################
//#############################################################################
//#############################################################################
// RP412SEGCHECK - segment validity at teardown
//#############################################################################
//
// The podium crash (three identical dumps, 2026-08-11) dies inside
// SocketIterator::DeletePlugs, called from ~JointedMover, calling through a
// segment whose FIRST DWORD - the vtable pointer - has been replaced by a
// small float (9.75 / 12.80 / 7.52 across the three machines). The table
// itself was untouched: numItems still 15, every TableEntry hooked up, which
// means nothing ever ran delete against that segment. Something wrote over
// it, or freed it behind the collection's back.
//
// Six configurations of the local repro rig - parked pods, driven pods,
// light page heap, full page heap, two, four and six pods - reached the
// podium and tore down clean. So this exists to make the next REAL playtest
// the instrument: look at each segment before calling through it, and if one
// is wrong, write what it looks like to rpl4-fail.log, which is fclose'd and
// survives the abort. Text written only to rpl4.log does not.
//
// This deliberately does NOT skip the delete, repair the pointer, or
// otherwise make the teardown survivable. The ownership bug is still
// unfixed, and a guard that hid it would cost exactly the evidence this is
// here to collect. It stops on the same object the crash would have died on,
// one step earlier, holding the forensics.
//
// RP412SEGCHECK=0 turns it off.
//
//
// The vtable every EntitySegment should carry, taken from the first one ever
// built - i.e. from fresh memory, before any race has run. Comparing against
// a captured value rather than a computed one keeps this independent of how
// the linker laid the image out.
//
static void
*gGoodSegmentVtable = NULL;
static int
RPSegCheckEnabled()
{
static int
enabled = -1;
if (enabled < 0)
{
const char
*setting = getenv("RP412SEGCHECK");
enabled = (setting != NULL && atoi(setting) == 0) ? 0 : 1;
}
return enabled;
}
//
// Read a dword that may not be there any more. If the block has been freed
// under a page-heap build the pages are decommitted, and the plain read
// would fault inside the checker - losing the report we came for.
//
static int
RPReadDword(const void *address, unsigned long *out)
{
__try
{
*out = *(const unsigned long *) address;
return 1;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
}
//
// WHERE the corruption was first seen. Recorded in a static rather than
// simply logged, because rpl4.log does not reliably survive the abort that
// follows at teardown - so the answer has to travel inside the Fail report.
//
// This is the question the dumps cannot answer: the crash proves a segment
// is wrong by teardown, not whether it was already wrong when the race
// ended. Bracketing the podium separates "something during the race broke
// it" from "the podium's second MakeEntityRenderables broke it".
//
static char
gFirstBadWhen[64] = "";
//
// Public entry point for the bracket call sites (see RPL4APP.cpp). Counts
// corrupted segments on one entity and remembers the first moment any were
// seen. Reports; never aborts - the teardown check does that, with this
// answer folded in.
//
int
RPCheckJointedMoverSegments(Entity *entity, const char *when)
{
if (!RPSegCheckEnabled() || gGoodSegmentVtable == NULL || entity == NULL)
{
return 0;
}
if (!entity->IsDerivedFrom(*JointedMover::GetClassDerivations()))
{
return 0;
}
JointedMover
*mover = (JointedMover *) entity;
EntitySegment::SegmentTableIterator
iterator(mover->segmentTable);
EntitySegment
*segment;
int
bad = 0;
iterator.First();
while ((segment = iterator.ReadAndNext()) != NULL)
{
unsigned long
vtable = 0;
if (!RPReadDword(segment, &vtable)
|| (void *) vtable != gGoodSegmentVtable)
{
++bad;
}
}
if (bad > 0 && gFirstBadWhen[0] == '\0')
{
strncpy(gFirstBadWhen, when, sizeof(gFirstBadWhen) - 1);
gFirstBadWhen[sizeof(gFirstBadWhen) - 1] = '\0';
DEBUG_STREAM << "SegCheck: " << bad
<< " segment(s) already corrupt at " << when << "\n" << std::flush;
}
return bad;
}
static void
RPCheckSegmentsBeforeTeardown(
Entity *mover,
EntitySegment::SegmentTable &segment_table
)
{
if (!RPSegCheckEnabled() || gGoodSegmentVtable == NULL)
{
return;
}
//
// Walk the table WITHOUT dereferencing anything: the iterator reads the
// TableEntry array, and GetPlug hands back the pointer without touching
// the object. The only read of segment memory is the guarded one below.
//
EntitySegment::SegmentTableIterator
iterator(segment_table);
EntitySegment
*segment,
*bad_segment = NULL,
*before_bad = NULL,
*after_bad = NULL;
unsigned long
bad_value = 0;
int
index = 0,
total = 0,
bad_count = 0,
bad_index = -1,
bad_readable = 0;
iterator.First();
while ((segment = iterator.ReadAndNext()) != NULL)
{
unsigned long
vtable = 0;
int
readable = RPReadDword(segment, &vtable);
if (!readable || (void *) vtable != gGoodSegmentVtable)
{
++bad_count;
if (bad_segment == NULL)
{
bad_segment = segment;
bad_index = index;
bad_value = vtable;
bad_readable = readable;
}
}
else if (bad_segment == NULL)
{
before_bad = segment; // last good one before the first bad
}
else if (after_bad == NULL)
{
after_bad = segment; // first one after it
}
++index;
++total;
}
if (bad_count == 0)
{
return;
}
//
// One buffer, one message: Fail_With_Message fprintf's the string it is
// given into rpl4-fail.log and closes the file, so everything worth
// having has to be inside it. Newlines are fine.
//
static char
report[1400];
char
*out = report;
Entity
*viewpoint = (application != NULL)
? application->GetViewpointEntity()
: NULL;
out += sprintf(out,
"segment plug corrupted BEFORE DeletePlugs\n"
" first seen: %s\n"
" entity %d:%d class%d, %s pod, owner %d\n"
" segments %d, corrupted %d, first bad index %d at 0x%08lX\n",
(gFirstBadWhen[0] != '\0') ? gFirstBadWhen
: "teardown (clean at every earlier check)",
(int) mover->entityID.GetHostID(), (int) mover->entityID,
(int) mover->GetClassID(),
(mover == viewpoint) ? "VIEWPOINT (local)" : "replicant",
(int) mover->GetOwnerID(),
total, bad_count, bad_index, (unsigned long) bad_segment);
if (!bad_readable)
{
out += sprintf(out,
" segment memory is NOT READABLE - freed and decommitted\n");
}
else
{
float
as_float;
memcpy(&as_float, &bad_value, sizeof(as_float));
out += sprintf(out,
" vtable expected 0x%08lX found 0x%08lX (as float %.6g)\n",
(unsigned long) gGoodSegmentVtable, bad_value, (double) as_float);
//
// The first 8 dwords, hex and float. The dumps showed a float where
// the vtable belongs; how FAR the float data runs says whether this
// was a stray single write or something walking through the object.
//
for (int row = 0; row < 2; ++row)
{
char
hex[80],
flt[80];
int
hex_used = 0,
flt_used = 0;
for (int col = 0; col < 4; ++col)
{
const unsigned char
*at = (const unsigned char *) bad_segment
+ (row * 16) + (col * 4);
unsigned long
word = 0;
if (!RPReadDword(at, &word))
{
hex_used += sprintf(hex + hex_used, " ????????");
flt_used += sprintf(flt + flt_used, " ????????");
continue;
}
float
word_float;
memcpy(&word_float, &word, sizeof(word_float));
hex_used += sprintf(hex + hex_used, " %08lX", word);
flt_used += sprintf(flt + flt_used, " %9.4g", (double) word_float);
}
out += sprintf(out, " +%02X:%s |%s\n", row * 16, hex, flt);
}
}
//
// Heap adjacency: if the corruption came from a neighbour running past
// its end, the deltas say which side it came from and how far it reached.
//
out += sprintf(out,
" neighbours: prev good 0x%08lX (delta %ld), next 0x%08lX (delta %ld)\n",
(unsigned long) before_bad,
(before_bad != NULL)
? (long) ((char *) bad_segment - (char *) before_bad) : 0L,
(unsigned long) after_bad,
(after_bad != NULL)
? (long) ((char *) after_bad - (char *) bad_segment) : 0L);
Fail(report);
}
//#############################################################################
// Shared Data Support
//
Derivation* JointedMover::GetClassDerivations()
{ static Derivation classDerivations(Mover::GetClassDerivations(), "JointedMover");
return &classDerivations;
}
JointedMover::SharedData
JointedMover::DefaultData(
JointedMover::GetClassDerivations(),
JointedMover::GetMessageHandlers(),
JointedMover::GetAttributeIndex(),
JointedMover::StateCount,
(Entity::MakeHandler)JointedMover::Make
);
//#############################################################################
// Attribute Support
//
const JointedMover::IndexEntry
JointedMover::AttributePointers[]=
{
{
JointedMover::SegmentCountAttributeID,
"SegmentCount",
(Simulation::AttributePointer)&JointedMover::segmentCount
},
{
JointedMover::SegmentTableAttributeID,
"SegmentTable",
(Simulation::AttributePointer)&JointedMover::segmentTable
},
{
JointedMover::JointSubsystemAttributeID,
"JointSubsystem",
(Simulation::AttributePointer)&JointedMover::jointSubsystem
}
};
JointedMover::AttributeIndexSet& JointedMover::GetAttributeIndex()
{
static JointedMover::AttributeIndexSet attributeIndex(ELEMENTS(JointedMover::AttributePointers),
JointedMover::AttributePointers,
Mover::GetAttributeIndex()
);
return attributeIndex;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntitySegment*
JointedMover::GetSegment(CString segment_name)
{
Check(this);
Check_Pointer(segment_name);
EntitySegment::SegmentTableIterator iterator(segmentTable);
EntitySegment *current_segment;
while( (current_segment = iterator.ReadAndNext() ) != NULL)
{
if(segment_name.Compare(current_segment->GetName()) == 0)
{
return current_segment;
}
}
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntitySegment*
JointedMover::GetSegmentFromDamageZone(CString damage_zone)
{
EntitySegment::SegmentTableIterator iterator(segmentTable);
EntitySegment *current_segment;
while ((current_segment = iterator.ReadAndNext() ) != NULL)
{
EntitySegment::IntegerTableIterator *damage_iterator;
damage_iterator = current_segment->MakeDamageZoneIndexTable();
Register_Object(damage_iterator);
EntitySegment::IntegerPlug *index_plug;
while ((index_plug = damage_iterator->ReadAndNext() ) != NULL)
{
CString current_name;
Verify(index_plug->GetItem() >= 0);
Verify(index_plug->GetItem() <= damageZoneCount);
current_name = damageZones[index_plug->GetItem()]->damageZoneName;
Check_Pointer(&current_name);
if(current_name.Compare(damage_zone) == 0)
{
Unregister_Object(damage_iterator);
delete damage_iterator;
return current_segment;
}
}
Unregister_Object(damage_iterator);
delete damage_iterator;
}
return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JointedMover::GetSegmentToWorld(
EntitySegment &my_segment,
LinearMatrix *transform
)
{
Check(this);
JointSubsystem *joints = GetJointSubsystem();
Check(joints);
//
//---------------------------------------------------------------
// Check to see if the segment transforms need to be recalculated
//---------------------------------------------------------------
//
if (joints->AreJointsModified())
{
EntitySegment::SegmentTableIterator iterator(segmentTable);
EntitySegment *current_segment;
while( (current_segment = iterator.ReadAndNext() ) != NULL)
{
current_segment->ModifySegment();
}
joints->ModifyJoints(False);
}
//
//-----------------------------
// Compute the actual transform
//-----------------------------
//
transform->Multiply(
my_segment.GetSegmentToEntity(),
localToWorld
);
Check_Fpu();
}
//#############################################################################
// Construction and Destruction
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointedMover::JointedMover(
JointedMover::MakeMessage *creation_message,
JointedMover::SharedData &virtual_data
):
Mover(creation_message, virtual_data),
jointSubsystem(NULL),
segmentTable(this, True)
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create the JointSubsystem
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointSubsystem *joint_subsystem = new JointSubsystem(
(Entity*) this,
Entity::EntitySubsystemID
);
Register_Object(joint_subsystem);
jointSubsystem.Add(joint_subsystem);
Check(joint_subsystem);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get Resource Description
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription *skl_resource =
application->GetResourceFile()->SearchList(
creation_message->resourceID,
ResourceDescription::SkeletonStreamResourceType
);
Check(skl_resource);
skl_resource->Lock();
DynamicMemoryStream segment_stream(
skl_resource->resourceAddress,
skl_resource->resourceSize);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// read in segmentCount and jointCount
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int joint_count;
segment_stream >> segmentCount >> joint_count;
int current_joint_type;
int current_joint_index = 0;
CString segment_name;
EntitySegment *parent_segment;
EntitySegment *current_segment;
int segment_joint_index;
Joint *segment_joint_pointer;
int parent_index;
CString video_object_name;
LinearMatrix segment_offset(LinearMatrix::Identity);
Logical site_segment;
int ii;
for(ii=0;ii<segmentCount;ii++)
{
//
//~~~~~~~~~~~~~~~~~~
// Read segment name
//~~~~~~~~~~~~~~~~~~
//
segment_stream >> segment_name >> parent_index;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// read parent index & find parentSegment ptr
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if(parent_index == -1)
{
parent_segment = NULL;
}
else
{
EntitySegment::SegmentTableIterator iterator(segmentTable);
parent_segment = iterator.GetNth(parent_index);
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Determine if this segment is a site
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
segment_stream >> site_segment >> current_joint_type;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get the joint type & init the joint
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if(current_joint_type != Joint::NULLJointType)
{
segment_joint_index = current_joint_index;
segment_joint_pointer =
new Joint(
joint_subsystem,
(Joint::JointType)current_joint_type
);
Register_Object(segment_joint_pointer);
joint_subsystem->AddJoint(current_joint_index, segment_joint_pointer);
++current_joint_index;
}
else
{
segment_joint_pointer = NULL;
segment_joint_index = -1;
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in the segment offset matrix
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
segment_stream >> segment_offset;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get The Primary Damage Zone
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int primary_dzone_index;
segment_stream >> primary_dzone_index;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create this segment and add
// add it to the segment table
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
current_segment = new EntitySegment(
segment_name,
ii, // my_index
parent_index,
segment_joint_index,
segment_joint_pointer,
segment_offset,
parent_segment,
site_segment,
primary_dzone_index
);
Register_Object(current_segment);
//
// The reference vtable for RPCheckSegmentsBeforeTeardown, taken once
// from the very first segment ever built - fresh memory, before any
// race has run. See the RP412SEGCHECK block at the top of this file.
//
if (gGoodSegmentVtable == NULL)
{
gGoodSegmentVtable = *(void **) current_segment;
}
segmentTable.AddValue(current_segment, ii);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in ALL VideoObjectNames
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int video_object_count;
EntitySegment::SkeletonType skl_type;
segment_stream >> video_object_count;
Verify(video_object_count);
int jj;
for(jj=0;jj<video_object_count;++jj)
{
segment_stream >> skl_type >> video_object_name;
if(video_object_name.Compare("None"))
{
current_segment->AddVideoObjectName(
video_object_name,
skl_type,
DamageZone::ExistsGraphicState
);
}
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in ALL DESTROYED VideoObjectNames
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int destroyed_video_object_count;
segment_stream >> destroyed_video_object_count;
for(jj=0;jj<destroyed_video_object_count;++jj)
{
segment_stream >> skl_type >> video_object_name;
if(video_object_name.Compare("None"))
{
current_segment->AddVideoObjectName(
video_object_name,
skl_type,
DamageZone::DestroyedGraphicState
);
}
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in DamageZone information
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int damage_zone_count;
int damage_zone_index;
segment_stream >> damage_zone_count;
if(damage_zone_count)
{
for(int dd=0;dd<damage_zone_count;++dd)
{
segment_stream >> damage_zone_index;
current_segment->AddDamageZone(damage_zone_index, dd);
}
}
//
// Read in the Children Information
//
int children_count;
int child_index;
segment_stream >> children_count;
if(children_count)
{
for(int jj=0;jj<children_count;++jj)
{
segment_stream >> child_index;
current_segment->AddChildIndex(child_index, jj);
}
}
}
skl_resource->Unlock();
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Initialize all the child Pointers for each segment
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
for(ii=0;ii<segmentCount;++ii)
{
EntitySegment::SegmentTableIterator iterator(segmentTable);
InitializeChildPointers(iterator.GetNth(ii));
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
JointedMover::InitializeChildPointers(EntitySegment *current_segment)
{
EntitySegment::IntegerTableIterator *iterator;
EntitySegment::SegmentTableIterator segment_iterator(segmentTable);
iterator = current_segment->MakeChildIndexTable();
Register_Object(iterator);
EntitySegment::IntegerPlug *current_index;
int child_count=0;
while((current_index = iterator->ReadAndNext()) != NULL)
{
EntitySegment *child_segment;
child_segment = segment_iterator.GetNth(current_index->GetItem());
Check(child_segment);
current_segment->AddChildPointer(child_segment, child_count);
++child_count;
}
Unregister_Object(iterator);
delete iterator;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointedMover::~JointedMover()
{
//
// Look at the segments before calling through them. This is where the
// podium crash lands; see the RP412SEGCHECK block at the top of this
// file for what it records and why it does not try to survive.
//
RPCheckSegmentsBeforeTeardown((Entity *) this, segmentTable);
EntitySegment::SegmentTableIterator iterator(segmentTable);
iterator.DeletePlugs();
//
//---------------------------------------------------------------------
// This should be deleted by the entity subsystemArray[jointSubsystem]
//---------------------------------------------------------------------
//
#if 0
JointSubsystem *joint_subsystem = jointSubsystem.GetCurrent();
Unregister_Object(joint_subsystem);
delete joint_subsystem;
#endif
}
//########################################################################
// DamageZone Support
//
ResourceDescription::ResourceID
JointedMover::CreateDamageZoneStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
)
{
Check(resource_file);
Check_Pointer(model_name);
Check(model_file);
Check_Pointer(directories);
//
//-------------------
// Find the .skl file
//-------------------
//
const char* skl_entry;
if (
!model_file->GetEntry(
"video",
"skeleton",
&skl_entry
)
)
{
DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush;
return -1;
}
char *skl_filename =
MakePathedFilename(directories->videoDirectory, skl_entry);
Register_Pointer(skl_filename);
NotationFile *skl_file = new NotationFile(skl_filename);
Register_Object(skl_file);
if (!skl_file->PageCount())
{
DEBUG_STREAM << skl_filename << " is empty or missing!\n" << std::flush;
Dump_And_Die_1:
Unregister_Pointer(skl_filename);
delete[] skl_filename;
Unregister_Object(skl_file);
delete skl_file;
return ResourceDescription::NullResourceID;
}
//
//-------------------
// Find the .dmg file
//-------------------
//
const char* dmg_entry;
if (
!model_file->GetEntry(
"gamedata",
"DamageZones",
&dmg_entry
)
)
{
DEBUG_STREAM << model_name << " is missing .dmg file specification!\n" << std::flush;
goto Dump_And_Die_1;
}
char *dmg_filename =
MakePathedFilename(directories->modelDirectory, dmg_entry);
Register_Pointer(dmg_filename);
NotationFile *dmg_file = new NotationFile(dmg_filename);
Register_Object(dmg_file);
if (!dmg_file->PageCount())
{
DEBUG_STREAM << dmg_filename << " is empty or missing!\n" << std::flush;
Dump_And_Die_2:
Unregister_Pointer(dmg_filename);
delete[] dmg_filename;
Unregister_Object(dmg_file);
delete dmg_file;
goto Dump_And_Die_1;
}
//
// Get the number of Damage zones
//
int dzone_count;
if (
!skl_file->GetEntry(
"ROOT",
"DZoneCount",
&dzone_count
)
)
{
DEBUG_STREAM << model_name << " is missing DZoneCount \n" << std::flush;
goto Dump_And_Die_2;
}
DynamicMemoryStream damage_zone_stream;
//
// write the Damage Zone Count Info from the
// .dmg file to the stream
//
damage_zone_stream << dzone_count;
//
// Make an entry list of all the entries in the damage zone page
//
NameList *dzone_namelist =
skl_file->MakeEntryList("DamageZones","dz_");
Register_Object(dzone_namelist);
if (dzone_namelist->EntryCount() == 0)
{
DEBUG_STREAM << "No dZones listed in DamageZones Page"<<std::endl << std::flush;
Dump_And_Die:
Unregister_Pointer(skl_filename);
delete skl_filename;
Unregister_Object(skl_file);
delete skl_file;
Unregister_Pointer(dmg_filename);
delete dmg_filename;
Unregister_Object(dmg_file);
delete dmg_file;
return -1;
}
NameList::Entry *dzone_entry;
char current_dzone_name[32];
int num_dzones_found = 0;
dzone_entry = dzone_namelist->GetFirstEntry();
while (dzone_entry)
{
++num_dzones_found;
//
// Get dzone name in .skl file
//
Str_Copy(
current_dzone_name,
dzone_entry->GetName(),
sizeof(current_dzone_name)
);
//
// Create the stream for this damage zone
//
DamageZone::CreateStreamedDamageZone(
model_file,
model_name,
skl_file,
current_dzone_name,
&damage_zone_stream,
dmg_file,
directories
);
//
// Get next dzone entry in this segment page
//
dzone_entry = dzone_entry->GetNextEntry();
} // End while more dzone_entries
if(dzone_count != num_dzones_found)
{
DEBUG_STREAM <<"DZoneCount != damage zones found in Page DamageZones"<<std::endl << std::flush;
goto Dump_And_Die;
}
//
//--------------------------------------------------------------------
// Write the stream out to disk. Size is equal to the byte difference
// between stream and damage zone buffer
//--------------------------------------------------------------------
//
ResourceDescription *new_res =
resource_file->AddResourceMemoryStream(
model_name,
ResourceDescription::DamageZoneStreamResourceType,
1,
ResourceDescription::Preload,
&damage_zone_stream
);
Check(new_res);
//
// Free mem
//
Unregister_Pointer(skl_filename);
delete skl_filename;
Unregister_Object(skl_file);
delete skl_file;
Unregister_Pointer(dmg_filename);
delete dmg_filename;
Unregister_Object(dmg_file);
delete dmg_file;
Unregister_Object(dzone_namelist);
delete dzone_namelist;
return new_res->resourceID;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
JointedMover::CreateSkeletonStream(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories *directories
)
{
Check_Pointer(model_name);
Check(model_file);
Check_Pointer(directories);
//
//-------------------
// Find the .skl file
//-------------------
//
const char *entry_data;
if (
!model_file->GetEntry(
"video",
"skeleton",
&entry_data
)
)
{
DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush;
return -1;
}
//
// Get the name of the SKL File
//
char *filename = MakePathedFilename(directories->videoDirectory, entry_data);
Register_Pointer(filename);
//
// Create a Notation file from the skl ASCII File
//
NotationFile *skl_file = new NotationFile(filename);
Register_Object(skl_file);
//
// Get number of Segments(or Pages)
//
int segment_count = skl_file->PageCount();
//
// Do not count Lab_Only and DamageZones they are not included
//
if (skl_file->PageExists("LAB_ONLY"))
--segment_count;
if(skl_file->PageExists("DamageZones"))
--segment_count;
if (!segment_count)
{
DEBUG_STREAM << model_name << " has no Segments!\n" << std::flush;
Dump_And_Die:
Unregister_Pointer(filename);
delete filename;
Unregister_Object(skl_file);
delete skl_file;
return -1;
}
int joint_count;
if (
!skl_file->GetEntry(
"ROOT",
"JointCount",
&joint_count
)
)
{
DEBUG_STREAM << model_name << " is missing JointCount \n" << std::flush;
goto Dump_And_Die;
}
DynamicMemoryStream segment_stream;
//
// Write Segment Count to the stream
//
segment_stream << segment_count << joint_count;
//
// Create a list of the pages and get the first entry
//
NameList *segment_namelist = skl_file->MakePageList();
Register_Object(segment_namelist);
NameList::Entry *segment_entry = segment_namelist->GetFirstEntry();
int
parent_index = -1,
children_count;
CString
segment_page_name;
EulerAngles
rotation;
Point3D
translation;
LinearMatrix
base_offset(LinearMatrix::Identity);
while(segment_entry)
{
segment_page_name = segment_entry->GetName();
if(
(strcmp(segment_page_name, "LAB_ONLY") == 0) ||
(strcmp(segment_page_name,"DamageZones") ==0)
)
{
segment_entry = segment_entry->GetNextEntry();
continue;
}
segment_stream << segment_page_name;
//
// Get the information about the parent
//
const char *parent_name;
if(
skl_file->GetEntry(
segment_page_name,
"parent",
&parent_name
)
)
{
parent_index = GetSegmentIndex(parent_name, skl_file);
if(parent_index == -1)
{
DEBUG_STREAM<<model_file<<":"<<parent_name<<" Not Found!"<<std::endl << std::flush;
goto Dump_And_Die;
}
}
segment_stream << parent_index;
//
// Get the joint Type, NULL Joint Type (-1) if no joint
//
const char *joint_name;
int joint_type = Joint::NULLJointType;
Logical is_site(False);
if(!skl_file->GetEntry(
segment_page_name,
"Type",
&joint_name)
)
{
if( strcmp("ROOT", segment_page_name) != 0)
{
is_site = True;
}
}
else
{
if(strcmp(joint_name,"static")==0)
{
joint_type = Joint::StaticJointType;
}
else
if(strcmp(joint_name, "hingex")==0)
{
joint_type = Joint::HingeXJointType;
}
else
if(strcmp(joint_name,"hingey")==0)
{
joint_type = Joint::HingeYJointType;
}
else
if(strcmp(joint_name,"hingez")==0)
{
joint_type = Joint::HingeZJointType;
}
else
if(strcmp(joint_name,"ball")==0)
{
joint_type = Joint::BallJointType;
}
else
if(strcmp(joint_name,"balltranslate")==0)
{
joint_type = Joint::BallTranslationJointType;
}
}
segment_stream << is_site << joint_type;
//
// Get offsets
//
skl_file->GetEntry(segment_page_name, "tranx" ,&translation.x);
skl_file->GetEntry(segment_page_name, "trany" ,&translation.y);
skl_file->GetEntry(segment_page_name, "tranz" ,&translation.z);
skl_file->GetEntry(segment_page_name, "pitch" ,&rotation.pitch.angle);
skl_file->GetEntry(segment_page_name, "yaw" ,&rotation.yaw.angle);
skl_file->GetEntry(segment_page_name, "roll" ,&rotation.roll.angle);
base_offset = translation;
base_offset = rotation;
//
// Write out the base offset
//
segment_stream << base_offset;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get the Primary Damage Zone
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char* skl_entry;
if (!model_file->GetEntry(
"video",
"skeleton",
&skl_entry
)
)
{
goto Dump_And_Die;
}
else
{
CString skl_filename =
MakePathedFilename(directories->videoDirectory, skl_entry);
NotationFile *skl_file = new NotationFile(skl_filename);
Register_Object(skl_file);
if (!skl_file->PageCount())
{
DEBUG_STREAM << skl_filename << " is empty or missing!"<<std::endl << std::flush;
Unregister_Object(skl_file);
delete skl_file;
goto Dump_And_Die;
}
int primary_damage_zone;
const char *dzone_name;
if (skl_file->GetEntry(
segment_page_name,
"dzone",
&dzone_name
)
)
{
primary_damage_zone = GetDamageZoneIndex(dzone_name, skl_file);
}
else
{
primary_damage_zone = -1;
}
segment_stream << primary_damage_zone;
Unregister_Object(skl_file);
delete skl_file;
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in ALL VideoObjectNames
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (!
InitVideoObjectNames(
&segment_stream,
model_file,
segment_page_name,
directories
)
)
{
goto Dump_And_Die;
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read in ALL DESTROYED VideoObjectNames
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (!
InitDestroyedVideoObjectNames(
&segment_stream,
model_file,
segment_page_name,
directories
)
)
{
goto Dump_And_Die;
}
//
// Get the damageZones associated with this segment
//
NameList *damage_zone_list =
skl_file->MakeEntryList(segment_entry->GetName(), "dzone");
Register_Object(damage_zone_list);
NameList::Entry *damage_zone_entry = damage_zone_list->GetFirstEntry();
int damage_zone_count = damage_zone_list->EntryCount();
segment_stream << damage_zone_count;
if (damage_zone_count)
{
for (int dd=0;dd<damage_zone_count;++dd)
{
CString damage_zone_name;
damage_zone_name = damage_zone_entry->GetChar();
int damage_zone_index;
damage_zone_index =
GetDamageZoneIndex(damage_zone_name, skl_file);
if(damage_zone_index == -1)
{
DEBUG_STREAM<<model_file<<":"<<damage_zone_name<<" Not Found!"<<std::endl << std::flush;
goto Dump_And_Die;
}
segment_stream << damage_zone_index;
damage_zone_entry = damage_zone_entry->GetNextEntry();
}
}
Unregister_Object(damage_zone_list);
delete damage_zone_list;
//
// Get the number of children, 0 if no children
//
NameList *children_list =
skl_file->MakeEntryList(segment_entry->GetName(),"joint");
Register_Object(children_list);
NameList::Entry *child_entry = children_list->GetFirstEntry();
children_count = 0;
while(child_entry)
{
++children_count;
child_entry = child_entry->GetNextEntry();
}
//
// Write how many children
//
segment_stream << children_count;
if(children_count)
{
child_entry = children_list->GetFirstEntry();
//
// Write out the segment index for every child
//
for(int cc=0;cc<children_count;++cc)
{
int child_index = GetSegmentIndex(child_entry->GetChar(), skl_file);
if(child_index == -1)
{
DEBUG_STREAM<<model_name<<":"<<child_entry->GetName()<<" Not Found!!"<<std::endl << std::flush;
goto Dump_And_Die;
}
segment_stream << child_index;
child_entry = child_entry->GetNextEntry();
}
}
Unregister_Object(children_list);
delete children_list;
segment_entry = segment_entry->GetNextEntry();
}
//
//--------------------------------------------------------------------
// Write the stream out to disk.
//--------------------------------------------------------------------
//
ResourceDescription *new_res =
resource_file->AddResourceMemoryStream(
model_name,
ResourceDescription::SkeletonStreamResourceType,
1,
ResourceDescription::Preload,
&segment_stream
);
Check(new_res);
//
// Fre Mem
//
Unregister_Pointer(filename);
delete filename;
Unregister_Object(skl_file);
delete skl_file;
Unregister_Object(segment_namelist);
delete segment_namelist;
return new_res->resourceID;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
JointedMover::InitDestroyedVideoObjectNames(
MemoryStream *mem_stream,
NotationFile *model_file,
CString page_name,
const ResourceDirectories *directories
)
{
Check(model_file);
Check(mem_stream);
NameList *skl_namelist = model_file->MakeEntryList("video","destroyed");
Register_Object(skl_namelist);
int entry_count = skl_namelist->EntryCount();
*mem_stream << entry_count;
if (!entry_count)
{
Unregister_Object(skl_namelist);
delete skl_namelist;
return True;
}
Verify(entry_count);
NameList::Entry *skl_entry = skl_namelist->GetFirstEntry();
while(skl_entry)
{
CString entry_name = skl_entry->GetName();
EntitySegment::SkeletonType skeleton_type;
if (! entry_name.Compare("destroyed") )
{
skeleton_type = EntitySegment::SkeletonType_N;
}
else if (! entry_name.Compare("destroyeds") )
{
skeleton_type = EntitySegment::SkeletonType_S;
}
else if (! entry_name.Compare("destroyedt") )
{
skeleton_type = EntitySegment::SkeletonType_T;
}
else if (! entry_name.Compare("destroyedo") )
{
skeleton_type = EntitySegment::SkeletonType_O;
}
else if (! entry_name.Compare("destroyeda") )
{
skeleton_type = EntitySegment::SkeletonType_A;
}
else if (! entry_name.Compare("destroyedb") )
{
skeleton_type = EntitySegment::SkeletonType_B;
}
else if (! entry_name.Compare("destroyedc") )
{
skeleton_type = EntitySegment::SkeletonType_C;
}
else if (! entry_name.Compare("destroyedd") )
{
skeleton_type = EntitySegment::SkeletonType_D;
}
*mem_stream << skeleton_type;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get the video object filename
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CString short_skl_filename = skl_entry->GetChar();
CString skl_filename =
MakePathedFilename(directories->videoDirectory, short_skl_filename);
NotationFile *skl_file = new NotationFile(skl_filename);
Register_Object(skl_file);
if (!skl_file->PageCount())
{
DEBUG_STREAM << skl_filename << " is empty or missing!"<<std::endl << std::flush;
Dump_And_Die:
Unregister_Object(skl_namelist);
delete skl_namelist;
Unregister_Object(skl_file);
delete skl_file;
return ResourceDescription::NullResourceID;
}
CString video_object_name;
const char *c_name;
if (skl_file->GetEntry(
page_name,
"Object",
&c_name
)
)
{
video_object_name = c_name;
}
else
{
video_object_name = "None";
}
*mem_stream << video_object_name;
Unregister_Object(skl_file);
delete skl_file;
skl_entry = skl_entry->GetNextEntry();
}
Unregister_Object(skl_namelist);
delete skl_namelist;
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
JointedMover::InitVideoObjectNames(
MemoryStream *mem_stream,
NotationFile *model_file,
CString page_name,
const ResourceDirectories *directories
)
{
Check(model_file);
Check(mem_stream);
NameList *skl_namelist = model_file->MakeEntryList("video","skeleton");
Register_Object(skl_namelist);
int entry_count = skl_namelist->EntryCount();
Verify(entry_count);
*mem_stream << entry_count;
NameList::Entry *skl_entry = skl_namelist->GetFirstEntry();
while(skl_entry)
{
CString entry_name = skl_entry->GetName();
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Write out the skeleton type
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntitySegment::SkeletonType skeleton_type;
if (! entry_name.Compare("skeleton") )
{
skeleton_type = EntitySegment::SkeletonType_N;
}
else if (! entry_name.Compare("skeletons") )
{
skeleton_type = EntitySegment::SkeletonType_S;
}
else if (! entry_name.Compare("skeletont") )
{
skeleton_type = EntitySegment::SkeletonType_T;
}
else if (! entry_name.Compare("skeletono") )
{
skeleton_type = EntitySegment::SkeletonType_O;
}
else if (! entry_name.Compare("skeletona") )
{
skeleton_type = EntitySegment::SkeletonType_A;
}
else if (! entry_name.Compare("skeletonb") )
{
skeleton_type = EntitySegment::SkeletonType_B;
}
else if (! entry_name.Compare("skeletonc") )
{
skeleton_type = EntitySegment::SkeletonType_C;
}
else if (! entry_name.Compare("skeletond") )
{
skeleton_type = EntitySegment::SkeletonType_D;
}
*mem_stream << skeleton_type;
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get the video object filename
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CString short_skl_filename = skl_entry->GetChar();
CString skl_filename =
MakePathedFilename(directories->videoDirectory, short_skl_filename);
NotationFile *skl_file = new NotationFile(skl_filename);
Register_Object(skl_file);
if (!skl_file->PageCount())
{
DEBUG_STREAM << skl_filename << " is empty or missing!"<<std::endl << std::flush;
Dump_And_Die:
Unregister_Object(skl_namelist);
delete skl_namelist;
Unregister_Object(skl_file);
delete skl_file;
return ResourceDescription::NullResourceID;
}
CString video_object_name;
const char *c_name;
if (skl_file->GetEntry(
page_name,
"Object",
&c_name
)
)
{
video_object_name = c_name;
}
else
{
video_object_name = "None";
}
*mem_stream << video_object_name;
Unregister_Object(skl_file);
delete skl_file;
skl_entry = skl_entry->GetNextEntry();
}
Unregister_Object(skl_namelist);
delete skl_namelist;
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
JointedMover::GetDamageZoneIndex(
CString damage_zone_name,
NotationFile *skl_file
)
{
int damage_zone_index = -1;
NameList *name_list = skl_file->MakeEntryList("DamageZones", "dz_");
Register_Object(name_list);
if (name_list->EntryCount() == 0)
{
DEBUG_STREAM << "No dZones listed in DamageZones Page"<<std::endl << std::flush;
Unregister_Object(name_list);
delete name_list;
return -1;
}
NameList::Entry *damage_zone = name_list->GetFirstEntry();
while(damage_zone)
{
if(strcmp(damage_zone->GetName(), damage_zone_name) == 0)
{
Convert_From_Ascii(damage_zone->GetChar(), &damage_zone_index);
Unregister_Object(name_list);
delete name_list;
return damage_zone_index;
}
damage_zone = damage_zone->GetNextEntry();
}
Unregister_Object(name_list);
delete name_list;
return damage_zone_index;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
JointedMover::GetSegmentIndex(
CString segment_name,
NotationFile *skl_file
)
{
int segment_index=-1;
char page_name[128];
NameList *page_list = skl_file->MakePageList();
Register_Object(page_list);
NameList::Entry *current_entry = page_list->GetFirstEntry();
while(current_entry)
{
Str_Copy(
page_name,
current_entry->GetName(),
sizeof(page_name)
);
if(
(strcmp(page_name, "LAB_ONLY") == 0) ||
(strcmp(page_name,"DamageZones") == 0)
)
{
current_entry = current_entry->GetNextEntry();
continue;
}
++segment_index;
if((strcmp(page_name, segment_name) == 0))
{
Unregister_Object(page_list);
delete page_list;
return segment_index;
}
current_entry = current_entry->GetNextEntry();
}
Unregister_Object(page_list);
delete page_list;
return -1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointedMover*
JointedMover::Make(JointedMover::MakeMessage *creation_message)
{
return new JointedMover(creation_message, DefaultData);
}
//##########################################################################
//########################## Animation ###############################
//##########################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AnimationInstance::AnimationInstance(JointedMover *jointed_mover)
{
Check_Pointer(this);
Check(jointed_mover);
lerpToZero = False;
moverToAnimate = jointed_mover;
jointSubToAnimate = jointed_mover->GetJointSubsystem();
animationResource = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
AnimationInstance::SetAnimation(
ResourceDescription::ResourceID animation_number,
JointedMover::AnimationCallback finished_callback
)
{
Check_Pointer(this);
finishedCallback = finished_callback;
currentTime = 0.0f;
currentFrame = 0;
if (animationResource)
{
Check(animationResource);
animationResource->Unlock();
}
animationResource =
application->GetResourceFile()->FindResourceDescription(
animation_number
);
Check(animationResource);
animationResource->Lock();
int *animation_data = (int*)animationResource->resourceAddress;
Check_Pointer(animation_data);
frameCount = animation_data[0];
jointCount = animation_data[1];
footStepThreshold = (float *)&animation_data[2];
jointIndices = &animation_data[3];
frameStart = (Scalar*)(jointIndices + jointCount);
keyFrames = frameStart + frameCount;
currentFrameTo = keyFrames;
void* to;
to = keyFrames;
//
//-----------------------------------
// find the end of the keyframes data
//-----------------------------------
//
for(int ii=0;ii<frameCount;++ii)
for(int jj=0;jj<jointCount;++jj)
{
switch(jointSubToAnimate->GetJoint(jointIndices[jj])->GetJointType())
{
case Joint::BallTranslationJointType:
{
to = (void *)((char *)to+sizeof(EulerAngles));
to = (void *)((char *)to+sizeof(Vector3D));
break;
}
case Joint::HingeXJointType:
case Joint::HingeYJointType:
case Joint::HingeZJointType:
{
to = (void *)((char *)to+sizeof(Hinge));
break;
}
case Joint::BallJointType:
to = (void *)((char *)to+sizeof(EulerAngles));
break;
}
}
//
//
// This should be contained in the keyframes
//
// keyJointPos = (Vector3D*)(keyFrames + jointCount*frameCount);
rootTranslations = (Vector3D*)(to);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AnimationInstance::~AnimationInstance()
{
if (animationResource)
{
Check(animationResource);
animationResource->Unlock();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
AnimationInstance::Animate(Scalar time_slice, Logical move_joints)
{
Check(this);
//
//--------------------------------------------------------------------------
// Find the frame to lerp towards. As each frame boundary is crossed, set
// the joints to the position of that boundary and adjust the remaining time
// to lerp
//--------------------------------------------------------------------------
//
int i;
//
//-----------------------------------------
// This void * can point to any joint type
//-----------------------------------------
//
void
*to;
Scalar frame_time = currentTime + time_slice;
Scalar movement = 0.0f;
//
//-------------------------------------------------------------
// to points to the place in the memory stream we left off at
// last time we were on this loop
//-------------------------------------------------------------
//
to = currentFrameTo;
Joint *current_joint;
while (currentFrame < frameCount)
{
if (frame_time < frameStart[currentFrame])
{
break;
}
for (i=0; i<jointCount; ++i)
{
current_joint = jointSubToAnimate->GetJoint(jointIndices[i]);
Check(current_joint);
switch(current_joint->GetJointType())
{
case Joint::BallTranslationJointType:
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Only modify the joint if the value has changed
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (current_joint->GetEulerAngles() != *(EulerAngles*)to)
{
if (move_joints)
{
current_joint->SetRotation(*(EulerAngles*)to);
}
}
to = (void *)((char *)to+sizeof(EulerAngles));
if (current_joint->GetTranslation() != *(Point3D*)to)
{
if (move_joints)
{
current_joint->SetTranslation(*(Point3D*)to);
}
}
to = (void *)((char *)to+sizeof(Vector3D));
break;
}
case Joint::HingeXJointType:
case Joint::HingeYJointType:
case Joint::HingeZJointType:
{
if (current_joint->GetHinge() != *(Hinge*) to)
{
if (move_joints)
{
current_joint->SetHinge(*(Hinge*) to);
}
}
to = (void *)((char *)to+sizeof(Hinge));
break;
}
case Joint::BallJointType:
if (current_joint->GetEulerAngles() != *(EulerAngles*)to)
{
if (move_joints)
{
current_joint->SetRotation(*(EulerAngles*)to);
}
}
to = (void *)((char *)to+sizeof(EulerAngles));
break;
case Joint::StaticJointType:
break;
}
}
movement +=
(frameStart[currentFrame] - currentTime)
* rootTranslations[currentFrame].z;
currentTime = frameStart[currentFrame];
++currentFrame;
}
//
//----------------------------------------------------------------------
// We we have run off the end of the animation, trigger the callback and
// return
//----------------------------------------------------------------------
//
if (currentFrame == frameCount)
{
movement +=
(moverToAnimate->*finishedCallback)(
animationResource->resourceID,
frame_time - frameStart[frameCount - 1],
move_joints
);
return movement;
}
currentFrameTo = to;
//
//-------------------------------------------------------
// Set up to lerp the rotations between to two key frames
//-------------------------------------------------------
//
Verify(!Small_Enough(frameStart[currentFrame] - currentTime));
Scalar delta =
(frame_time - currentTime)/(frameStart[currentFrame] - currentTime);
for (i=0; i<jointCount; ++i)
{
current_joint =
jointSubToAnimate->GetJoint(jointIndices[i]);
Check(current_joint);
switch(current_joint->GetJointType())
{
case Joint::BallTranslationJointType:
{
Point3D new_trans;
EulerAngles new_angles;
new_angles.Lerp(
current_joint->GetEulerAngles(),
*(EulerAngles*)to,
delta
);
if (current_joint->GetEulerAngles() != new_angles)
{
if (move_joints)
{
current_joint->SetRotation(new_angles);
}
}
to = (void *)((char *)to+sizeof(EulerAngles));
new_trans.Lerp(
current_joint->GetTranslation(),
*(Vector3D*)to,
delta
);
if (current_joint->GetTranslation() != new_trans)
{
if (move_joints)
{
current_joint->SetTranslation(new_trans);
}
}
to = (void *)((char *)to+sizeof(Vector3D));
break;
}
case Joint::HingeZJointType:
case Joint::HingeYJointType:
case Joint::HingeXJointType:
{
Hinge new_hinge;
new_hinge.Lerp(
current_joint->GetHinge(),
*(Hinge*)to,
delta
);
if (current_joint->GetRadians() != new_hinge.rotationAmount)
{
if (move_joints)
{
current_joint->SetRotation(new_hinge.rotationAmount);
}
}
to = (void *)((char *)to+sizeof(Hinge));
break;
}
case Joint::BallJointType:
{
EulerAngles new_angles;
new_angles.Lerp(
current_joint->GetEulerAngles(),
*(EulerAngles*)to,
delta
);
if (current_joint->GetEulerAngles() != new_angles)
{
if (move_joints)
{
current_joint->SetRotation(new_angles);
}
}
to = (void *)((char *)to+sizeof(EulerAngles));
break;
}
case Joint::StaticJointType:
break;
}
}
movement += (frame_time - currentTime) * rootTranslations[currentFrame].z;
currentTime = frame_time;
return movement;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
AnimationInstance::LerpToPose(Scalar time_slice, Scalar time_to_lerp, Logical move_joints)
{
Check(this);
int i;
//
//-----------------------------------------
// This void * can point to any joint type
//-----------------------------------------
//
void
*to;
if (!lerpToZero)
{
lerpToZero = True;
deltaTime = time_to_lerp;
}
Scalar delta = time_slice / deltaTime;
//
//-------------------------------------------------------------
// to points to the place in the memory stream we left off at
// last time we were on this loop
//-------------------------------------------------------------
//
to = currentFrameTo;
Joint *current_joint;
if (move_joints)
{
for (i=0; i<jointCount; ++i)
{
current_joint =
jointSubToAnimate->GetJoint(jointIndices[i]);
Check(current_joint);
switch(current_joint->GetJointType())
{
case Joint::BallTranslationJointType:
{
Point3D new_trans;
EulerAngles new_angles;
new_angles.Lerp(
current_joint->GetEulerAngles(),
*(EulerAngles*)to,
delta
);
if (current_joint->GetEulerAngles() != new_angles)
{
current_joint->SetRotation(new_angles);
}
to = (void *)((char *)to+sizeof(EulerAngles));
new_trans.Lerp(
current_joint->GetTranslation(),
*(Vector3D*)to,
delta
);
if (current_joint->GetTranslation() != new_trans)
{
current_joint->SetTranslation(new_trans);
}
to = (void *)((char *)to+sizeof(Vector3D));
break;
}
case Joint::HingeZJointType:
case Joint::HingeYJointType:
case Joint::HingeXJointType:
{
Hinge new_hinge;
new_hinge.Lerp(
current_joint->GetHinge(),
*(Hinge*)to,
delta
);
if (current_joint->GetRadians() != new_hinge.rotationAmount)
{
current_joint->SetRotation(new_hinge.rotationAmount);
}
to = (void *)((char *)to+sizeof(Hinge));
break;
}
case Joint::BallJointType:
{
EulerAngles new_angles;
new_angles.Lerp(
current_joint->GetEulerAngles(),
*(EulerAngles*)to,
delta
);
if (current_joint->GetEulerAngles() != new_angles)
{
current_joint->SetRotation(new_angles);
}
to = (void *)((char *)to+sizeof(EulerAngles));
break;
}
case Joint::StaticJointType:
break;
}
}
}
deltaTime -= time_slice;
if (deltaTime <= 0.0f)
{
lerpToZero = False;
return False;
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
AnimationInstance::LerpToRest(Scalar time_slice, Scalar time_to_lerp, Logical move_joints)
{
if (!lerpToZero)
{
lerpToZero = True;
deltaTime = time_to_lerp;
}
Scalar delta = time_slice / deltaTime;
if (move_joints)
{
EulerAngles zero_euler(0.0f, 0.0f, 0.0f);
Hinge zero_hingeX(0.0f, X_Axis);
Hinge zero_hingeY(0.0f, Y_Axis);
Hinge zero_hingeZ(0.0f, Z_Axis);
Vector3D zero_vector(0.0f, 0.0f, 0.0f);
for (int i=0; i<jointCount; ++i)
{
Joint *current_joint =
jointSubToAnimate->GetJoint(jointIndices[i]);
Check(current_joint);
switch(current_joint->GetJointType())
{
case Joint::BallTranslationJointType:
{
EulerAngles new_angles;
new_angles.Lerp(
current_joint->GetEulerAngles(),
zero_euler,
delta
);
if (current_joint->GetEulerAngles() != new_angles)
{
current_joint->SetRotation(new_angles);
}
Point3D new_trans;
new_trans.Lerp(
current_joint->GetTranslation(),
zero_vector,
delta
);
if (current_joint->GetTranslation() != new_trans)
{
current_joint->SetTranslation(new_trans);
}
break;
}
case Joint::HingeXJointType:
{
Hinge new_hinge;
new_hinge.Lerp(
current_joint->GetHinge(),
zero_hingeX,
delta
);
if(current_joint->GetRadians() != new_hinge.rotationAmount)
{
current_joint->SetRotation(new_hinge.rotationAmount);
}
break;
}
case Joint::HingeYJointType:
{
Hinge new_hinge;
new_hinge.Lerp(
current_joint->GetHinge(),
zero_hingeY,
delta
);
if(current_joint->GetRadians() != new_hinge.rotationAmount)
{
current_joint->SetRotation(new_hinge.rotationAmount);
}
break;
}
case Joint::HingeZJointType:
{
Hinge new_hinge;
new_hinge.Lerp(
current_joint->GetHinge(),
zero_hingeZ,
delta
);
if(current_joint->GetRadians() != new_hinge.rotationAmount)
{
current_joint->SetRotation(new_hinge.rotationAmount);
}
break;
}
case Joint::BallJointType:
{
EulerAngles new_angles;
new_angles.Lerp(
current_joint->GetEulerAngles(),
zero_euler,
delta
);
if (current_joint->GetEulerAngles() != new_angles)
{
current_joint->SetRotation(new_angles);
}
break;
}
case Joint::StaticJointType:
break;
}
}
}
deltaTime -= time_slice;
if (deltaTime <= 0.0f)
{
lerpToZero = False;
return False;
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
AnimationInstance::SnapToRest(Logical move_joints)
{
if (!move_joints)
{
return;
}
EulerAngles zero_euler(0.0f, 0.0f, 0.0f);
Hinge zero_hingeX(0.0f, X_Axis);
Hinge zero_hingeY(0.0f, Y_Axis);
Hinge zero_hingeZ(0.0f, Z_Axis);
Point3D zero_point(0.0f, 0.0f, 0.0f);
for (int i=0; i<jointCount; ++i)
{
Joint *current_joint =
jointSubToAnimate->GetJoint(jointIndices[i]);
Check(current_joint);
switch(current_joint->GetJointType())
{
case Joint::BallTranslationJointType:
{
current_joint->SetRotation(zero_euler);
current_joint->SetTranslation(zero_point);
break;
}
case Joint::HingeXJointType:
{
current_joint->SetHinge(zero_hingeX);
break;
}
case Joint::HingeYJointType:
{
current_joint->SetHinge(zero_hingeY);
break;
}
case Joint::HingeZJointType:
{
current_joint->SetHinge(zero_hingeZ);
break;
}
case Joint::BallJointType:
{
current_joint->SetRotation(zero_euler);
break;
}
case Joint::StaticJointType:
break;
}
}
}
//##########################################################################
//############## Sketon Notation file utilities ####################
//##########################################################################
int
Get_Active_Joint_Count(NotationFile *skl_file)
{
NameList *namelist = skl_file->MakePageList("joint");
Register_Object(namelist);
NameList::Entry *entry = namelist->GetFirstEntry();
int count = 0;
while (entry)
{
++count;
entry = entry->GetNextEntry();
}
Unregister_Object(namelist);
delete namelist;
return count;
}
Logical
JointedMover::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}