#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 //############################################################################# //############################ 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(¤t_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> 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> 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> 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_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> child_index; current_segment->AddChildIndex(child_index, jj); } } } skl_resource->Unlock(); // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Initialize all the child Pointers for each segment //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // for(ii=0;iiMakeChildIndexTable(); 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"<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"<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<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!"<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;ddGetChar(); int damage_zone_index; damage_zone_index = GetDamageZoneIndex(damage_zone_name, skl_file); if(damage_zone_index == -1) { DEBUG_STREAM<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;ccGetChar(), skl_file); if(child_index == -1) { DEBUG_STREAM<GetName()<<" Not Found!!"<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!"<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!"<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"<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;iiGetJoint(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; iGetJoint(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; iGetJoint(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; iGetJoint(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; iGetJoint(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; iGetJoint(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()); }