#include "munga.h" #pragma hdrstop #include "door.h" #include "fileutil.h" #include "boxsolid.h" #include "doorfram.h" #include "app.h" #include "notation.h" //############################################################################# // Shared Data Support // Door::SharedData Door::DefaultData( Door::GetClassDerivations(), Door::GetMessageHandlers(), Door::GetAttributeIndex(), Door::StateCount ); Derivation* Door::GetClassDerivations() { static Derivation classDerivations(Subsystem::GetClassDerivations(), "Door"); return &classDerivations; } //############################################################################# // Messaging Support // //############################################################################# // Attribute Support // const Door::IndexEntry Door::AttributePointers[]= { { Door::CurrentPositionAttributeID, "CurrentPosition", (Simulation::AttributePointer)&Door::currentPosition }, { Door::VideoResourceAttributeID, "VideoResource", (Simulation::AttributePointer)&Door::videoResource }, { Door::PercentOpenAttributeID, "PercentOpen", (Simulation::AttributePointer)&Door::percentOpen }, { Door::CurrentVelocityAttributeID, "CurrentVelocity", (Simulation::AttributePointer)&Door::currentVelocity } }; Door::AttributeIndexSet& Door::GetAttributeIndex() { static Door::AttributeIndexSet attributeIndex(ELEMENTS(Door::AttributePointers), Door::AttributePointers, Subsystem::GetAttributeIndex() ); return attributeIndex; } //############################################################################# // Model Support // // There is no ReadUpdateRecord/WriteUpdateRecord pair here on purpose. Doors // are Hermit instances built independently on every host, so no door state is // ever sent or received - the phase function below is the only thing that // decides where a door is, and it reaches the same answer everywhere. // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Door::SlideDoor(Scalar) { Check(this); // //-------------------------------------------------------------------------- // The door is clockwork. Its position is a function of how long the race // has been running, not of a countdown integrated frame by frame, so: // // - every machine puts this door in the same place from the same mission // clock, without a byte crossing the wire, // - a frame hitch of any length costs nothing, because there is no // accumulated state left to fall behind (the old code dropped any slice // over a second outright and never got that time back). // // Phase zero is the instant the door starts to close, fully open, which is // where the original state machine began from DefaultState: // // [0, travel) Closing 1 -> 0 // [travel, travel+dead) Closed 0 // [travel+dead, 2travel+dead) Opening 0 -> 1 // [2travel+dead, cycle) Opened 1 //-------------------------------------------------------------------------- // if (cycleTime <= 0.0f) { MoveCollisionVolume(0.0f); SetSimulationState(Closed); Check_Fpu(); return; } Check(application); Scalar phase = fmod(application->GetMissionElapsed() - phaseOffset, cycleTime); if (phase < 0.0f) { phase += cycleTime; } // //-------------------------------------------------------------------------- // Pick the band. Each division below is guarded by the comparison that // selected the branch, so a door with a zero travelTime or deadTime simply // loses that band rather than dividing by zero. //-------------------------------------------------------------------------- // Scalar open_start = travelTime + deadTime; int new_state; Scalar percent_open; if (phase < travelTime) { new_state = Closing; percent_open = 1.0f - phase/travelTime; currentVelocity.Subtract( GetEntity()->localOrigin.linearPosition, worldExtent ); currentVelocity /= travelTime; } else if (phase < open_start) { new_state = Closed; percent_open = 0.0f; currentVelocity = Vector3D::Identity; } else if (phase < open_start + travelTime) { new_state = Opening; percent_open = (phase - open_start)/travelTime; currentVelocity.Subtract( worldExtent, GetEntity()->localOrigin.linearPosition ); currentVelocity /= travelTime; } else { new_state = Opened; percent_open = 1.0f; currentVelocity = Vector3D::Identity; } // //------------------------------------------------- // Move the collision volumes and set the new state //------------------------------------------------- // MoveCollisionVolume(percent_open); SetSimulationState(new_state); Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Door::MoveCollisionVolume(Scalar percent_open) { Check(this); // //--------------------------------------------------------- // If the door hasn't moved, don't bother updating anything //--------------------------------------------------------- // Entity *entity = GetEntity(); Check(entity); if ( GetSimulationState() != simulationState.GetOldState() && entity->GetInstance() == Entity::MasterInstance && entity->IsDynamic() ) { ForceUpdate(); } if (percent_open == percentOpen) { return; } // //----------------------------------- // Otherwise, lerp the local position //----------------------------------- // percentOpen = percent_open; currentPosition.Lerp(Point3D::Identity, localExtent, percentOpen); // //----------------------------------------------------------- // Now, lerp the world position and set the collision volumes //----------------------------------------------------------- // Point3D world; world.Lerp( GetEntity()->localOrigin.linearPosition, worldExtent, percentOpen ); BoxedSolid* temp = collisionTemplates; BoxedSolid* vol = collisionVolumes; while (temp) { Check(temp); Check(vol); vol->minX = temp->minX + world.x; vol->maxX = temp->maxX + world.x; vol->minY = temp->minY + world.y; vol->maxY = temp->maxY + world.y; vol->minZ = temp->minZ + world.z; vol->maxZ = temp->maxZ + world.z; vol = vol->GetNextSolid(); temp = temp->GetNextSolid(); } Verify(!vol); Check_Fpu(); } //############################################################################# // Constructer/Destructor Support // Door::Door( DoorFrame *entity, int subsystem_ID, SubsystemResource *subsystem_resource ): Subsystem(entity, subsystem_ID, subsystem_resource, DefaultData) { Check_Pointer(this); Check(entity); Check_Pointer(subsystem_resource); // // GetStream data NOTE::This must be in the same order as below!!! // localExtent = subsystem_resource->motionExtent; travelTime = subsystem_resource->travelTime; deadTime = subsystem_resource->deadTime; // // Initialize variables // phaseOffset = 0.0f; cycleTime = 2.0f*(travelTime + deadTime); currentPosition = Point3D::Identity; SetPerformance(&Door::SlideDoor); SetSimulationState(DefaultState); collisionVolumeCount = 0; collisionTemplates = NULL; collisionVolumes = NULL; percentOpen = -1.0f; // //------------------------------------- // Establish the worldspace coordinates //------------------------------------- // worldExtent.Multiply(localExtent, entity->localToWorld); Origin local_origin = entity->localOrigin; local_origin.linearPosition = Point3D::Identity; // //------------------------------ // Read in the collision volumes //------------------------------ // Check(application); ResourceFile *res_file = application->GetResourceFile(); Check(res_file); ResourceDescription *res = res_file->FindResourceDescription(subsystem_resource->collisionID); Check(res); res->Lock(); BoxedSolidResource* box = (BoxedSolidResource*)res->resourceAddress; Check_Pointer(box); collisionVolumeCount = res->resourceSize / sizeof(BoxedSolidResource); for (int i=0; ilocalOrigin); local_box.Instance(*box,local_origin); collisionTemplates = BoxedSolid::MakeBoxedSolid(&local_box, this, collisionTemplates); Register_Object(collisionTemplates); collisionVolumes = BoxedSolid::MakeBoxedSolid(&world_box, this, collisionVolumes); Register_Object(collisionVolumes); ++box; } res->Unlock(); MoveCollisionVolume(0.0f); Check(this); } Door::~Door() { BoxedSolid *box = collisionTemplates; while (box) { BoxedSolid *next_box = box->GetNextSolid(); Unregister_Object(box); delete box; box = next_box; } box = collisionVolumes; while (box) { BoxedSolid *next_box = box->GetNextSolid(); Unregister_Object(box); delete box; box = next_box; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CreateStreamedSubsystem // Logical Door::CreateStreamedSubsystem( NotationFile *model_file, const char *model_name, const char* subsystem_name, SubsystemResource *subsystem_resource, NotationFile *subsystem_file, const ResourceDirectories *directories, ResourceFile *res_file ) { if ( !Subsystem::CreateStreamedSubsystem( model_file, model_name, subsystem_name, subsystem_resource, subsystem_file, directories ) ) { return False; } subsystem_resource->subsystemModelSize = sizeof(*subsystem_resource); subsystem_resource->classID = RegisteredClass::DoorClassID; // // Get the motion Extent // const char *extent_data; if( !subsystem_file->GetEntry( subsystem_name, "MotionExtent", &extent_data ) ) { std::cerr << subsystem_name << " missing MotionExtent!!\n"; return -1; } sscanf( extent_data, "%f %f %f", &subsystem_resource->motionExtent.x, &subsystem_resource->motionExtent.y, &subsystem_resource->motionExtent.z ); // // Get the travelTime // if( !subsystem_file->GetEntry( subsystem_name, "TravelTime", &subsystem_resource->travelTime ) ) { std::cerr << subsystem_name << " missing TravelTime!\n"; return -1; } // // Read in the Deadtime // if( !subsystem_file->GetEntry( subsystem_name, "DeadTime", &subsystem_resource->deadTime ) ) { std::cerr << subsystem_name << " missing DeadTime!\n"; return False; } const char* collision_file; if ( !subsystem_file->GetEntry( subsystem_name, "Collision", &collision_file ) ) { std::cerr << subsystem_name << " missing Collision!\n"; return False; } subsystem_resource->collisionID = BoxedSolidResource::CreateBoxedSolidStream( collision_file, res_file, directories ); return True; } Logical Door::TestInstance() const { return IsDerivedFrom(*GetClassDerivations()); }