Files
RP412/MUNGA/MOVER.cpp
T
CydandClaude Opus 5 adcb81e7fc Replicants interpolate too
The first cut hung the snapshot off Mover::BeginStep, which is inside
Entity::PerformAndWatch's fixed-step interleave - and that interleave sits
entirely inside "if (GetInstance() != ReplicantInstance)". A replicant
never runs it; it reaches the step loop through Simulation::PerformAndWatch
instead. Every remote pod is a replicant, so on a Live Cam the camera was
being interpolated while the car it was watching still stepped. Smoother,
and most of the way to nowhere - which is exactly what "still some
hitching" was.

So the hooks move to Simulation::PerformTo, where both paths meet:
SnapshotRenderOrigin before each Perform, SetRenderStepFraction after the
loop, two virtuals that do nothing by default and are overridden by Entity
because Entity owns the origin. Mover::BeginStep goes back to what it was,
so there is now one mechanism instead of two.

Taking the snapshot inside the step loop is also strictly better placed
than BeginStep was: it lands immediately before the integration, and still
after any BeginStep teleport, so a VTV's scheduled respawn stays a cut.

Entity::PerformAndWatch keeps computing the fraction itself after its
interleave, because there PerformTo is called once per step with a till
one step ahead and so sees no leftover at all - it needs the FRAME's till,
which only the interleave has.

Determinism re-proved, and more thoroughly than the first time. The
scripted lap at 240 fps, interpolation on and off, on both the old build
and this one: all four runs agree to the last decimal at the same
simulation time - pos -15.06739 3.01541 388.02603 at t=15.260. The one
"differing" sample in the raw comparison was the trace sampling at t=1.260
in one run and t=1.280 in the other and then realigning, not divergence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 23:36:44 -05:00

2124 lines
52 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "mover.h"
#include "player.h"
#include "boxsolid.h"
#include "interest.h"
#include "collasst.h"
#include "doorfram.h"
#include "door.h"
#include "line.h"
#include "app.h"
#include "notation.h"
//#############################################################################
//############################### Mover #################################
//#############################################################################
//#############################################################################
// Shared Data Support
//
Derivation* Mover::GetClassDerivations()
{
static Derivation classDerivations(Entity::GetClassDerivations(), "Mover");
return &classDerivations;
}
Mover::SharedData
Mover::DefaultData(
Mover::GetClassDerivations(),
Mover::GetMessageHandlers(),
Mover::GetAttributeIndex(),
Mover::StateCount,
(Entity::MakeHandler)Mover::Make
);
//#############################################################################
// Message Support
//
#if 0
const Receiver::HandlerEntry
Mover::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(Mover, Update)
};
Entity::MessageHandlerSet
Mover::MessageHandlers(
ELEMENTS(Mover::MessageHandlerEntries),
Mover::MessageHandlerEntries,
Entity::GetMessageHandlers()
);
#endif
//#############################################################################
// Attribute Support
//
const Mover::IndexEntry
Mover::AttributePointers[]=
{
ATTRIBUTE_ENTRY(Mover, LocalVelocity, localVelocity),
ATTRIBUTE_ENTRY(Mover, LocalAcceleration, localAcceleration),
ATTRIBUTE_ENTRY(Mover, WorldLinearVelocity, worldLinearVelocity),
ATTRIBUTE_ENTRY(Mover, WorldLinearAcceleration, worldLinearAcceleration),
ATTRIBUTE_ENTRY(Mover, MoverMass, moverMass),
ATTRIBUTE_ENTRY(Mover, MomentOfInertia, momentOfInertia),
ATTRIBUTE_ENTRY(
Mover,
PositiveLinearDragCoefficients,
positiveLinearDragCoefficients
),
ATTRIBUTE_ENTRY(
Mover,
NegativeLinearDragCoefficients,
negativeLinearDragCoefficients
),
ATTRIBUTE_ENTRY(Mover, AngularDragCoefficients, angularDragCoefficients),
ATTRIBUTE_ENTRY(Mover, FrictionCoefficient, frictionCoefficient),
ATTRIBUTE_ENTRY(Mover, ElasticityCoefficient, elasticityCoefficient),
ATTRIBUTE_ENTRY(Mover, MinimumBounceSpeed, minimumBounceSpeed)
};
Mover::AttributeIndexSet& Mover::GetAttributeIndex()
{
static Mover::AttributeIndexSet attributeIndex(ELEMENTS(Mover::AttributePointers),
Mover::AttributePointers,
Entity::GetAttributeIndex()
);
return attributeIndex;
}
//#############################################################################
// Model Support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::UpdateWorldMotion()
{
Check(this);
//
//---------------------------------------------------
// Move the accelerations back into world coordinates
//---------------------------------------------------
//
worldLinearAcceleration.Multiply(
localAcceleration.linearMotion,
localToWorld
);
worldLinearVelocity.Multiply(
localVelocity.linearMotion,
localToWorld
);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::UpdateLocalMotion()
{
Check(this);
localVelocity.linearMotion.MultiplyByInverse(
worldLinearVelocity,
localToWorld
);
localAcceleration.linearMotion.MultiplyByInverse(
worldLinearAcceleration,
localToWorld
);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::ApplyWorldAccelerations(Scalar time_slice)
{
Check(this);
Verify(time_slice > 0.0f);
//
//--------------------------------------------------
// Calculate the new position as p += v*t + a*.5*t*t
//--------------------------------------------------
//
Scalar
half_t_squared = 0.5f * time_slice * time_slice;
Vector3D
position_delta;
position_delta.Multiply(worldLinearAcceleration, half_t_squared);
Check_Fpu();
position_delta.AddScaled(
position_delta,
worldLinearVelocity,
time_slice
);
Check_Fpu();
localOrigin.linearPosition.Add(localOrigin.linearPosition, position_delta);
Check_Fpu();
position_delta.Multiply(localAcceleration.angularMotion, half_t_squared);
Check_Fpu();
position_delta.AddScaled(
position_delta,
localVelocity.angularMotion,
time_slice
);
Check_Fpu();
Quaternion
old_position = localOrigin.angularPosition;
localOrigin.angularPosition.Add(old_position, position_delta);
Check_Fpu();
//
//-----------------------------------
// Calculate our velocity as v += a*t
//-----------------------------------
//
worldLinearVelocity.AddScaled(
worldLinearVelocity,
worldLinearAcceleration,
time_slice
);
Check_Fpu();
localVelocity.angularMotion.AddScaled(
localVelocity.angularMotion,
localAcceleration.angularMotion,
time_slice
);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::CalculateDrag(
Vector3D *drag,
const Vector3D &velocity,
const Vector3D &positive_CODs,
const Vector3D &negative_CODs,
Scalar power
)
{
Environment *air = GetEnvironment();
Check(air);
Vector3D temp,temp2;
temp.MultiplyByInverse(air->GetWindVelocity(), localToWorld);
temp += velocity;
if (temp.x < 0.0f)
{
drag->x = negative_CODs.x;
temp2.x = Power(-temp.x, power);
Check_Fpu();
}
else
{
drag->x = -positive_CODs.x;
temp2.x = Power(temp.x, power);
Check_Fpu();
}
if (temp.y < 0.0f)
{
drag->y = negative_CODs.y;
temp2.y = Power(-temp.y, power);
Check_Fpu();
}
else
{
drag->y = -positive_CODs.y;
temp2.y = Power(temp.y, power);
Check_Fpu();
}
if (temp.z < 0.0f)
{
drag->z = negative_CODs.z;
temp2.z = Power(-temp.z, power);
Check_Fpu();
}
else
{
drag->z = -positive_CODs.z;
temp2.z = Power(temp.z, power);
Check_Fpu();
}
*drag *= air->airDensity;
drag->Multiply(*drag, temp2);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::ApplyAirResistanceAndGravity(Scalar power)
{
Check(this);
//
//-------------------------------------------------------------------------
// Apply drag to the system, allowing for different drag numbers based upon
// the direction of motion along the axis
//-------------------------------------------------------------------------
//
Vector3D acceleration;
CalculateDrag(
&acceleration,
localVelocity.linearMotion,
positiveLinearDragCoefficients,
negativeLinearDragCoefficients,
power
);
localAcceleration.linearMotion += acceleration;
acceleration.Multiply(angularDragCoefficients, localVelocity.angularMotion);
localAcceleration.angularMotion -= acceleration;
//
//---------------------------
// Apply gravity to the craft
//---------------------------
//
UpdateWorldMotion();
worldLinearAcceleration.y -= GetEnvironment()->gravityConstant;
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::ApplyLocalForce(
const Vector3D &force,
const Vector3D &moment
)
{
Check(this);
Check(&force);
Check(&moment);
Vector3D acceleration;
Verify(!Small_Enough(moverMass));
acceleration.Divide(force, moverMass);
ApplyLocalAcceleration(acceleration, moment);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::ApplyLocalAcceleration(
const Vector3D &acceleration,
const Vector3D &moment
)
{
Check(this);
Check(&acceleration);
Check(&moment);
localAcceleration.linearMotion += acceleration;
Vector3D torque;
torque.Cross(moment, acceleration);
torque *= momentOfInertia;
localAcceleration.angularMotion += torque;
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Mover::NoDeadReckoner()
{
Check(this);
//
//-------------------------------------------------------------------------
// If we are the replicant instance and we are not yet past the anticipated
// time for the next event, project out to the next event
//-------------------------------------------------------------------------
//
projectedOrigin = updateOrigin;
Check_Fpu();
return False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Mover::LinearDeadReckoner()
{
Check(this);
Logical lerp_mode;
Scalar time_slice;
//
//-------------------------------------------------------------------------
// If we are the replicant instance and we are not yet past the anticipated
// time for the next event, project out to the next event
//-------------------------------------------------------------------------
//
if (GetInstance() == ReplicantInstance && lastPerformance < nextUpdate)
{
time_slice = nextUpdate - lastUpdate;
lerp_mode = True;
}
else
{
time_slice = lastPerformance - lastUpdate;
lerp_mode = False;
}
//
//---------------------------------------
// Calculate the new position as p += v*t
//---------------------------------------
//
Vector3D position_delta;
position_delta.Multiply(updateVelocity.linearMotion, time_slice);
projectedOrigin.linearPosition.Add(
updateOrigin.linearPosition,
position_delta
);
//
//-------------------------------
// Handle projecting the rotation
//-------------------------------
//
position_delta.Multiply(updateVelocity.angularMotion, time_slice);
projectedOrigin.angularPosition.Add(
updateOrigin.angularPosition,
position_delta
);
projectedVelocity = updateVelocity;
Check_Fpu();
return lerp_mode;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Mover::AcceleratedDeadReckoner()
{
Check(this);
Logical lerp_mode;
Scalar time_slice;
//
//-------------------------------------------------------------------------
// If we are the replicant instance and we are not yet past the anticipated
// time for the next event, project out to the next event
//-------------------------------------------------------------------------
//
if (GetInstance() == ReplicantInstance && lastPerformance < nextUpdate)
{
time_slice = nextUpdate - lastUpdate;
lerp_mode = True;
}
else
{
time_slice = lastPerformance - lastUpdate;
lerp_mode = False;
}
//
//--------------------------------------------------
// Calculate the new position as p += v*t + a*.5*t*t
//--------------------------------------------------
//
Scalar half_t_squared = 0.5f * time_slice * time_slice;
Vector3D position_delta;
position_delta.Multiply(updateAcceleration.linearMotion, half_t_squared);
position_delta.AddScaled(
position_delta,
updateVelocity.linearMotion,
time_slice
);
projectedOrigin.linearPosition.Add(
updateOrigin.linearPosition,
position_delta
);
//
//-------------------------------
// Handle projecting the rotation
//-------------------------------
//
position_delta.Multiply(updateAcceleration.angularMotion, half_t_squared);
position_delta.AddScaled(
position_delta,
updateVelocity.angularMotion,
time_slice
);
projectedOrigin.angularPosition.Add(
updateOrigin.angularPosition,
position_delta
);
//
//-----------------------------------
// Calculate our velocity as v += a*t
//-----------------------------------
//
if (GetInstance() == ReplicantInstance)
{
projectedVelocity.linearMotion.AddScaled(
updateVelocity.linearMotion,
worldLinearAcceleration,
time_slice
);
projectedVelocity.angularMotion.AddScaled(
updateVelocity.angularMotion,
localAcceleration.angularMotion,
time_slice
);
}
Check_Fpu();
return lerp_mode;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::DeadReckon(Scalar time_slice)
{
Check(this);
//
//------------------------------
// Run the chosen dead reckoning
//------------------------------
//
Verify(GetInstance() == ReplicantInstance);
if (deadReckoner)
{
//
//---------------------------------------------------------------------
// Merge the projected origin with the current origin if we are in lerp
// mode. If not, just copy the projected origin into the local origin
//---------------------------------------------------------------------
//
if ((this->*deadReckoner)())
{
Scalar percent =
time_slice / ((nextUpdate - lastPerformance) + time_slice);
//
//------------------------------------------
// Do a spherical lerp on the angular motion
//------------------------------------------
//
localOrigin.angularPosition.Lerp(
localOrigin.angularPosition,
projectedOrigin.angularPosition,
percent
);
localVelocity.angularMotion.Lerp(
localVelocity.angularMotion,
projectedVelocity.angularMotion,
percent
);
//
//-------------------------
// Spline the linear motion
//-------------------------
//
#if 0
CubicCurve
spline(
localOrigin.linearPosition,
worldLinearVelocity,
projectedOrigin.linearPosition,
projectedVelocity.linearMotion
);
spline.Evaluate(
percent,
&localOrigin.linearPosition,
&worldLinearVelocity
);
#else
localOrigin.linearPosition.Lerp(
localOrigin.linearPosition,
projectedOrigin.linearPosition,
percent
);
worldLinearVelocity.Lerp(
worldLinearVelocity,
projectedVelocity.linearMotion,
percent
);
#endif
}
else
{
localOrigin = projectedOrigin;
worldLinearVelocity = projectedVelocity.linearMotion;
localVelocity.angularMotion = projectedVelocity.angularMotion;
}
//
//----------------------------------------------------
// Update the collision volume and the local variables
//----------------------------------------------------
//
if (IsCollisionVolume())
{
MoveCollisionVolume();
}
else
{
localToWorld = localOrigin;
}
UpdateLocalMotion();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::PerformAndWatch(
const Time &till,
MemoryStream *update_stream
)
{
Check(this);
Check(&till);
int i;
//
//--------------------------------------------------------------------------
// Make sure that the time into the simulations is stable. If a half-second
// delay occurs, or we are in stasis, just bring everything up to date
//--------------------------------------------------------------------------
//
Scalar time_slice = till - lastPerformance;
if (time_slice < SMALL)
{
Tell("No time!\n");
Bye_Bye:
WriteSimulationUpdate(update_stream);
return;
}
if (GetSimulationState() == StasisState || time_slice > 0.5f)
{
lastPerformance = till;
if (GetSimulationState() == StasisState)
{
lastUpdate = till;
}
if (subsystemArray)
{
Check_Pointer(subsystemArray);
for (i=0; i<subsystemCount; ++i)
{
if (subsystemArray[i])
{
Check(subsystemArray[i]);
subsystemArray[i]->SetLastPerformance(till);
}
}
}
//SetSimulationState(DefaultState);
goto Bye_Bye;
}
//
//------------------------------------
// Set up for local motion calculation
//------------------------------------
//
localVelocity.linearMotion.MultiplyByInverse(
worldLinearVelocity,
localToWorld
);
localAcceleration = Motion::Identity;
previousOrigin = localOrigin;
//
//-----------------------
// Process the subsystems
//-----------------------
//
Entity::PerformAndWatch(till, update_stream);
//
//-----------------------------------------------
// Make sure the position quaternion stays stable
//
// Frame-counting, so it only runs on the frame-coupled path - fixed
// steps do the same thing in BeginStep, counted in STEPS, because
// "every 20 frames" lands at a different point of the step sequence
// on every machine and rounding at different points is drift.
//-----------------------------------------------
//
if (Simulation::FixedStep() <= (Scalar) 0 && ++normalizeCount >= 20)
{
localOrigin.angularPosition.Normalize();
normalizeCount = 0;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// The per-STEP set-up. This is the same work Mover::PerformAndWatch does
// once per frame above - and once per frame is exactly wrong under fixed
// stepping: the thrusters ADD their forces into localAcceleration every
// step, so an accumulator cleared per frame carries step one's thrust
// into step two whenever a frame holds two steps. How many steps a frame
// holds depends on wall-clock jitter, which made identical runs diverge
// by a quarter of a metre while sitting still on the pad.
//
// Idempotent on purpose: the frame-level copy still runs first on every
// path, and repeating this at each step start is a recompute from
// current state, not an accumulation.
//
void
Mover::BeginStep()
{
Check(this);
localVelocity.linearMotion.MultiplyByInverse(
worldLinearVelocity,
localToWorld
);
localAcceleration = Motion::Identity;
previousOrigin = localOrigin;
if (++normalizeCount >= 20)
{
localOrigin.angularPosition.Normalize();
normalizeCount = 0;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::ReadUpdateRecord(Simulation::UpdateRecord *record)
{
Check(this);
Check_Pointer(record);
switch (record->recordID)
{
case DefaultUpdateModelBit:
{
//
//-------------------------------------------
// HACK - precalculation for next update time
//-------------------------------------------
//
nextUpdate = Now();
Scalar diff = nextUpdate - lastUpdate;
if (diff < 10.0f)
{
nextUpdate.ticks += nextUpdate.ticks - lastUpdate.ticks;
}
//
//---------------------------------------
// Handle updating the entity information
//---------------------------------------
//
Entity::ReadUpdateRecord(record);
//
//-----------------------
// Update the motion data
//-----------------------
//
UpdateRecord *update = (UpdateRecord*)record;
localAcceleration = update->localAcceleration;
worldLinearAcceleration = update->worldLinearAcceleration;
updateVelocity.linearMotion = update->worldLinearVelocity;
updateVelocity.angularMotion = update->localVelocity.angularMotion;
updateAcceleration.linearMotion = update->worldLinearAcceleration;
updateAcceleration.angularMotion = update->localAcceleration.angularMotion;
//
//-----------------------------------------
// Update the collision volume if necessary
//-----------------------------------------
//
if (IsCollisionVolume())
{
MoveCollisionVolume();
}
}
break;
default:
Entity::ReadUpdateRecord(record);
break;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::WriteUpdateRecord(
Simulation::UpdateRecord *record,
int update_model
)
{
Check(this);
Check_Pointer(record);
switch (update_model)
{
case DefaultUpdateModelBit:
{
Entity::WriteUpdateRecord(record, update_model);
UpdateRecord *update = (UpdateRecord*)record;
update->recordLength = sizeof(*update);
update->localVelocity = localVelocity;
update->localAcceleration = localAcceleration;
update->worldLinearVelocity = worldLinearVelocity;
update->worldLinearAcceleration = worldLinearAcceleration;
updateVelocity.linearMotion = worldLinearVelocity;
updateVelocity.angularMotion = localVelocity.angularMotion;
updateAcceleration.linearMotion = worldLinearAcceleration;
updateAcceleration.angularMotion = localAcceleration.angularMotion;
}
break;
default:
Entity::WriteUpdateRecord(record, update_model);
break;
}
Check_Fpu();
}
//#############################################################################
// Collision support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::MoveCollisionVolume()
{
//
//---------------------------------------------------
// Make sure that there is a collision volume to move
//---------------------------------------------------
//
Check(this);
if (!collisionVolumeCount)
{
Check_Fpu();
return;
}
//
//------------------------------------------------------------------------
// Set up the extents of the collision volume from the template and the
// current position. We must find the center point of the template volume
// and rotate it about the y axis
//------------------------------------------------------------------------
//
Check(collisionTemplate);
Check(collisionVolume);
Verify(collisionVolumeCount == 1);
Verify(collisionTemplate->solidType == BoxedSolid::YAxisCylinderType);
localToWorld = localOrigin;
Point3D centerPoint;
centerPoint.x = (collisionTemplate->minX + collisionTemplate->maxX) * 0.5f;
centerPoint.y = (collisionTemplate->minY + collisionTemplate->maxY) * 0.5f;
centerPoint.z = (collisionTemplate->minZ + collisionTemplate->maxZ) * 0.5f;
Vector3D radius;
radius.x = collisionTemplate->maxX - centerPoint.x;
radius.y = collisionTemplate->maxY - centerPoint.y;
radius.z = collisionTemplate->maxZ - centerPoint.z;
Point3D rotated;
rotated.Multiply(centerPoint, localToWorld);
collisionVolume->minX = rotated.x - radius.x;
collisionVolume->maxX = rotated.x + radius.x;
collisionVolume->minY = rotated.y - radius.y;
collisionVolume->maxY = rotated.y + radius.y;
collisionVolume->minZ = rotated.z - radius.z;
collisionVolume->maxZ = rotated.z + radius.z;
//
//------------------------------------------------------------
// Now, Find the smallest node containing our collision column
//------------------------------------------------------------
//
if (GetInstance() != ReplicantInstance)
{
InterestManager *interest_mgr =
application->GetInterestManager();
Check(interest_mgr);
InterestZone *zone =
interest_mgr->GetInterestZone(interestZoneID);
Check(zone);
BoxedSolidTree* tree = zone->GetCollisionRoot();
Check(tree);
containedByNode =
tree->FindSmallestNodeContainingColumn(*collisionVolume);
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolidCollisionList*
Mover::AllocateCollisionList()
{
Check(this);
//
//-----------------------------------------------------------
// Find the correct collision list to use, and reset to empty
//-----------------------------------------------------------
//
BoxedSolidCollisionList *collision_list;
if (lastCollisionList == collisionLists)
{
collision_list = &collisionLists[1];
}
else
{
collision_list = collisionLists;
}
Check(collision_list);
collision_list->Reset();
return collision_list;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolidCollisionList*
Mover::GetCurrentCollisions(BoxedSolidCollisionList *collision_list)
{
Check(this);
if (!collision_list)
{
collision_list = AllocateCollisionList();
}
Check(collision_list);
//
//---------------------------------
// Test against the tangible movers
//---------------------------------
//
Check(collisionAssistant);
CollisionAssistant::MovingEntityIterator iterator(collisionAssistant);
Entity *entity;
Check(collisionVolume);
while ((entity = iterator.ReadAndNext()) != NULL)
{
//
//------------------------------------------------------------------
// If we are checking against ourselves, or something more than 50m
// away, skip it
//------------------------------------------------------------------
//
Check(entity);
if (entity == this)
{
continue;
}
Vector3D delta;
delta.Subtract(
entity->localOrigin.linearPosition,
localOrigin.linearPosition
);
if (delta.LengthSquared() > 2500.0f)
{
continue;
}
//
//--------------------------------------------------
// If we have a mover class object, check against it
//--------------------------------------------------
//
if (entity->IsDerivedFrom(*Mover::GetClassDerivations()))
{
Mover *mover = (Mover*)entity;
Check(mover);
Check(mover->collisionVolume);
CheckAgainstBoxedSolidChain(collision_list, mover->collisionVolume);
}
//
//-----------------------------------------------------------------------
// If we have a door, check against its subsystems if we are close enough
// for it to matter
//-----------------------------------------------------------------------
//
else if (entity->IsDerivedFrom(*DoorFrame::GetClassDerivations()))
{
DoorFrame *door_frame = (DoorFrame*)entity;
Check(door_frame);
for (int i=0; i<door_frame->GetSubsystemCount(); ++i)
{
Door *door = (Door*)door_frame->GetSubsystem(i);
CheckAgainstBoxedSolidChain(
collision_list,
door->GetFirstBoxedSolid()
);
}
}
}
//
//------------------------------
// Test against the static world
//------------------------------
//
containedByNode->FindBoundingBoxesContaining(
collisionVolume,
*collisionVolume,
*collision_list
);
Check_Fpu();
return collision_list;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid*
Mover::FindBoxedSolidHitBy(
Line *line,
Entity *except
)
{
Check(this);
Check(line);
//
//-------------------------------------------------------------------------
// Calculate the midpoint of the line, and sweep a sphere out around the
// line from that point, including an extra 50 meters. This extra distance
// takes into account the doors...
//-------------------------------------------------------------------------
//
Point3D center;
Scalar radius = line->length * 0.5f;
line->Project(radius, &center);
//
//---------------------------------
// Test against the tangible movers
//---------------------------------
//
Check(collisionAssistant);
CollisionAssistant::MovingEntityIterator iterator(collisionAssistant);
Entity *entity;
BoxedSolid
*solid = NULL,
*result;
while ((entity = iterator.ReadAndNext()) != NULL)
{
//
//---------------------------------------------------------------
// If we are checking against ourselves or the exception, skip it
//---------------------------------------------------------------
//
Check(entity);
if (entity == this || except && except == entity)
{
continue;
}
//
//-------------------------------------------------------------------
// If we have a mover class object, check against it. If we have no
// collision volume, we are just using a line, so just run it against
// the collision volume chain
//-------------------------------------------------------------------
//
if (entity->IsDerivedFrom(*Mover::GetClassDerivations()))
{
Mover *mover = (Mover*)entity;
Check(mover);
Check(mover->collisionVolume);
//
//-----------------------------------------------------------------
// If the mover is close enough to the radius of the line, check it
//-----------------------------------------------------------------
//
Vector3D delta;
delta.Subtract(entity->localOrigin.linearPosition, center);
Scalar r2 =
mover->collisionVolume->maxX - mover->collisionVolume->minX;
r2 += mover->collisionVolume->maxY - mover->collisionVolume->minY;
r2 *= 0.5f;
r2 += radius;
if (delta.LengthSquared() > r2*r2)
{
continue;
}
result =
CheckLineAgainstBoxedSolidChain(line, mover->collisionVolume);
if (result)
{
Check(result);
solid = result;
}
}
//
//-----------------------------------------------------------------------
// If we have a door, check against its subsystems if we are close enough
// for it to matter
//-----------------------------------------------------------------------
//
else if (entity->IsDerivedFrom(*DoorFrame::GetClassDerivations()))
{
//
//-----------------------------------------------------------------
// If the mover is close enough to the radius of the line, check it
//-----------------------------------------------------------------
//
Vector3D delta;
delta.Subtract(entity->localOrigin.linearPosition, center);
Scalar r2 = radius + 50.0f;
if (delta.LengthSquared() > r2*r2)
{
continue;
}
DoorFrame *door_frame = (DoorFrame*)entity;
Check(door_frame);
for (int i=0; i<door_frame->GetSubsystemCount(); ++i)
{
Door *door = (Door*)door_frame->GetSubsystem(i);
result =
CheckLineAgainstBoxedSolidChain(
line,
door->GetFirstBoxedSolid()
);
if (result)
{
Check(result);
solid = result;
}
}
}
}
//
//------------------------------
// Test against the static world
//------------------------------
//
InterestManager *interest_mgr = application->GetInterestManager();
Check(interest_mgr);
InterestZone *zone = interest_mgr->GetInterestZone(interestZoneID);
Check(zone);
BoxedSolidTree* tree = zone->GetCollisionRoot();
Check(tree);
result = (BoxedSolid*)tree->FindBoundingBoxHitBy(line);
if (result)
{
Check(result);
solid = result;
}
Check_Fpu();
return solid;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolidCollisionList*
Mover::CollideCenterOfMotion(
Line *line,
BoxedSolidCollisionList *list
)
{
Check(this);
Check(line);
//
//-----------------------------------------------------------
// Find the correct collision list to use, and reset to empty
//-----------------------------------------------------------
//
if (!list)
{
list = AllocateCollisionList();
}
Check(list);
//
//------------------------------------------------------------------------
// If the length of the line has changed, we must reposition the collision
// volume appropriately
//------------------------------------------------------------------------
//
BoxedSolid *solid = FindBoxedSolidHitBy(line, NULL);
if (solid && IsCollisionVolume())
{
line->FindEnd(&localOrigin.linearPosition);
MoveCollisionVolume();
ExtentBox slice;
slice.Intersect(*collisionVolume, *solid);
Verify(list->GetCollisionsLeft());
list->AddCollisionToList(solid, slice);
}
Check_Fpu();
return list;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::ProcessCollisionList(
BoxedSolidCollisionList *collisions,
Scalar time_slice,
const Point3D &old_position,
Damage *damage
)
{
Check(this);
Check(collisions);
Verify(time_slice > 0.0f);
Check(&old_position);
Check_Pointer(damage);
damage->damageAmount = 0.0f;
damage->damageType = Damage::CollisionDamageType;
damage->impactPoint = Point3D::Identity;
if (collisions->GetCollisionCount())
{
//
//------------------------------------------------------------------
// Reduce the number of collisions we have to play with based on our
// velocity
//------------------------------------------------------------------
//
collisions->ReduceCollisionList(worldLinearVelocity);
//
//-----------------------------------------------------------------
// Setup up the totaling variables to handle averaging out multiple
// collisions
//-----------------------------------------------------------------
//
int total_collisions = 0;
Vector3D resultant_velocity = Vector3D::Identity;
Point3D resultant_position = Point3D::Identity;
Vector3D resultant_normal = Vector3D::Identity;
Vector3D initial_velocity = worldLinearVelocity;
Vector3D initial_position = localOrigin.linearPosition;
Scalar total_damage = 0.0f;
//
//---------------------------------------------------------------------
// For each hit in the list, process it, and if the collision is
// determined to be valid, bounce it and add the result into the others
//---------------------------------------------------------------------
//
for (int i=0; i<collisions->GetRealCollisions(); ++i)
{
//
//----------------------------------------------------------
// Make sure to bounce the vehicle from the correct location
//----------------------------------------------------------
//
worldLinearVelocity = initial_velocity;
localOrigin.linearPosition = initial_position;
damage->damageAmount = 0.0f;
ProcessCollision(
time_slice,
(*collisions)[i],
old_position,
damage
);
if (damage->damageAmount > 0.0f)
{
++total_collisions;
resultant_velocity += worldLinearVelocity;
resultant_position += localOrigin.linearPosition;
resultant_normal += damage->surfaceNormal;
total_damage += damage->damageAmount;
ExtentBox *box = &(*collisions)[i].collisionSlice;
damage->impactPoint.x += 0.5 *
(
box->minX + box->maxX
- (collisionVolume->minX - collisionVolume->maxX)
);
damage->impactPoint.y += 0.5 *
(
box->minY + box->maxY
- (collisionVolume->minY - collisionVolume->maxY)
);
damage->impactPoint.z += 0.5 *
(
box->minZ + box->maxZ
- (collisionVolume->minZ - collisionVolume->maxZ)
);
}
}
//
//-----------------------------------------------------------------
// If we collided with more than one thing, average out the results
//-----------------------------------------------------------------
//
if (total_collisions > 1)
{
worldLinearVelocity.Divide(resultant_velocity, total_collisions);
localOrigin.linearPosition.Divide(
resultant_position,
total_collisions
);
damage->surfaceNormal.Vector3D::Divide(
resultant_normal,
total_collisions
);
goto Figure_Normal;
}
//
//------------------------------------------------------
// Otherwise, just set up the positions from the results
//------------------------------------------------------
//
else if (total_collisions == 1)
{
worldLinearVelocity = resultant_velocity;
localOrigin.linearPosition = resultant_position;
damage->surfaceNormal.operator=(resultant_normal);
//
//---------------------------------------------------------
// Figure out the normal, and calculate the collision force
//---------------------------------------------------------
//
Figure_Normal:
if (Small_Enough(damage->surfaceNormal.LengthSquared()))
{
damage->surfaceNormal.x = 0.0f;
damage->surfaceNormal.y = 1.0f;
damage->surfaceNormal.z = 0.0f;
}
else
{
damage->surfaceNormal.Normalize(damage->surfaceNormal);
}
MoveCollisionVolume();
damage->damageAmount = total_damage;
damage->damageForce.Subtract(worldLinearVelocity, initial_velocity);
}
lastCollisionList = collisions;
}
else
{
lastCollisionList = NULL;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::ProcessCollision(
Scalar time_slice,
BoxedSolidCollision &collision,
const Point3D &old_position,
Damage *damage
)
{
Check(this);
Verify(time_slice > 0.0f);
Check(&collision);
Check(&old_position);
Check_Pointer(damage);
Scalar penetration;
//
//------------------------------------------------------------------------
// If we really have a collision, do a static bounce off of the normal
// generated. This is default behavior, and any derived class should make
// sure to handle any handshaking that needs to be done
//------------------------------------------------------------------------
//
if (
collisionVolume->ProcessCollision(
collision,
worldLinearVelocity,
lastCollisionList,
&damage->surfaceNormal,
&penetration
)
)
{
Max_Clamp(penetration, time_slice);
Scalar r = penetration / time_slice;
Scalar elasticity = elasticityCoefficient;
Scalar friction = frictionCoefficient;
damage->damageAmount =
StaticBounce(
old_position,
time_slice,
r,
damage->surfaceNormal,
&elasticity,
minimumBounceSpeed,
&friction
);
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::StartCollisionAssistant()
{
Check(this);
Verify(collisionAssistant == NULL);
collisionAssistant = CollisionAssistant::Make(this);
Register_Object(collisionAssistant);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
Mover::StaticBounce(
const Point3D &, //old_position,
Scalar delta_t,
Scalar penetration,
const Normal &normal,
Scalar *elasticity,
Scalar bounce_min,
Scalar *friction
)
{
Check(this);
Check(&normal);
Check_Pointer(elasticity);
Check_Pointer(friction);
Verify(penetration >= 0.0f && penetration <= 1.0f);
Verify(*elasticity >= 0.0f && *elasticity <= 1.0f);
Verify(*friction >= 0.0f);
Verify(delta_t > SMALL);
// penetration = 1.0f; // HACK - should keep stuff from going through the floor
//
//-----------------------------------------------------------------------
// Calculate the impact speed and vectors. If we didn't hit fast enough,
// don't do any bounce
//-----------------------------------------------------------------------
//
Scalar impact = worldLinearVelocity * normal;
Vector3D vn,vp;
vn.Multiply(normal, impact);
vp.Subtract(worldLinearVelocity, vn);
if (impact > 0.0f)
{
Check_Fpu();
return 0.0f;
}
if (-impact <= bounce_min * delta_t)
{
*elasticity = 0.0f;
}
//
//--------------------------------------
// Calculate the energy lost to friction
//--------------------------------------
//
Scalar resistance = vp.Length();
if (Small_Enough(resistance))
{
*friction = resistance = 0.0f;
}
else
{
resistance =
1.0f + *friction * (1.0f + *elasticity) * impact / resistance;
if (resistance < 0.0f)
{
*friction = resistance = 0.0f;
}
}
//
//----------------------------------------------------
// Compute the velocity delta created by the collision
//----------------------------------------------------
//
Scalar temp = resistance - 1.0f;
Vector3D delta_v;
delta_v.Multiply(worldLinearVelocity, temp);
temp = resistance + *elasticity;
delta_v.AddScaled(delta_v, vn, -temp);
//
//------------------------------------
// Figure out the kinetic energy stuff
//------------------------------------
//
temp = -1.0f - *elasticity;
vn *= temp;
vp.AddScaled(vn, worldLinearVelocity, 2.0f);
//
// Reflect the velocity vector
//
worldLinearVelocity += delta_v;
temp = penetration * delta_t;
delta_v *= temp;
localOrigin.linearPosition += delta_v;
//
// Compute the kinetic energy loss
//
Check_Fpu();
return -0.0005 * (vn * vp) * moverMass;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
Mover::DynamicBounce(
Mover *other,
Scalar delta_t,
Scalar penetration,
const Normal &normal,
Scalar *elasticity
)
{
Check(this);
Check(other);
Check(&normal);
Check_Pointer(elasticity);
Verify(penetration >= 0.0f && penetration <= 1.0f);
Verify(*elasticity >= 0.0f && *elasticity <= 1.0f);
Verify(delta_t > SMALL);
//
//------------------------------------------------------------------------
// Get the relative velocity of the other guy, and figure out the velocity
// delta along the normal
//------------------------------------------------------------------------
//
Scalar k1 = worldLinearVelocity.LengthSquared();
Scalar k2 = other->worldLinearVelocity.LengthSquared();
Scalar mass_ratio = other->moverMass / (moverMass + other->moverMass);
Check_Fpu();
Vector3D v;
v.Subtract(other->worldLinearVelocity, worldLinearVelocity);
Scalar temp = (1.0f + *elasticity) * (v*normal);
Vector3D delta_v;
delta_v.Multiply(normal, temp);
//
//-------------------------------------------------------------------------
// Figure out the kinetic energy loss in kilojoules, and bounce the primary
// mover
//
// There was an additional multiplication by mass ratio in system 3 code...
// we should make sure it is really needed...
//-------------------------------------------------------------------------
//
v.AddScaled(delta_v, v, -2.0f);
worldLinearVelocity.AddScaled(
worldLinearVelocity,
delta_v,
mass_ratio
);
localOrigin.linearPosition.AddScaled(
localOrigin.linearPosition,
delta_v,
delta_t * penetration
);
//
//----------------------------------------------------------
// Bounce the second object, and reset it's update values...
//----------------------------------------------------------
//
other->worldLinearVelocity.AddScaled(
other->worldLinearVelocity,
delta_v,
mass_ratio - 1.0f
);
other->localOrigin.linearPosition.AddScaled(
other->localOrigin.linearPosition,
delta_v,
delta_t
);
other->updateVelocity.linearMotion = other->worldLinearVelocity;
other->updateOrigin.linearPosition = other->localOrigin.linearPosition;
other->lastUpdate = Now();
//
//--------------------------------
// Return the result in kilojoules
//--------------------------------
//
k1 -= worldLinearVelocity.LengthSquared();
k2 -= other->worldLinearVelocity.LengthSquared();
Check_Fpu();
return 0.0005f * mass_ratio * (moverMass * k1 + other->moverMass * k2);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::CheckAgainstBoxedSolidChain(
BoxedSolidCollisionList *collisions,
BoxedSolid *chain
)
{
Check(this);
Check(collisions);
//
//----------------------------------------------------------------------
// If the two movers collided against with each other, add the result to
// the collision list
//----------------------------------------------------------------------
//
while (chain)
{
Check(chain);
ExtentBox slice;
if (chain->Intersects(*collisionVolume, &slice))
{
Verify(collisions->GetCollisionsLeft());
collisions->AddCollisionToList(chain, slice);
if (!collisions->GetCollisionsLeft())
{
return;
}
}
chain = chain->GetNextSolid();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Mover::CheckVolumeAgainstBoxedSolidChain(
BoxedSolidCollisionList *collisions,
BoxedSolid *chain
)
{
Check(this);
Check(collisions);
//
//----------------------------------------------------------------------
// If the two movers collided against with each other, add the result to
// the collision list
//----------------------------------------------------------------------
//
while (chain)
{
Check(chain);
ExtentBox slice;
if (chain->Intersects(*collisionVolume, &slice))
{
Verify(collisions->GetCollisionsLeft());
collisions->AddCollisionToList(collisionVolume, slice);
if (!collisions->GetCollisionsLeft())
{
return;
}
}
chain = chain->GetNextSolid();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedSolid*
Mover::CheckLineAgainstBoxedSolidChain(
Line *line,
BoxedSolid *chain
)
{
Check(this);
Check(line);
//
//----------------------------------------------------------------------
// If the two movers collided against with each other, add the result to
// the collision list
//----------------------------------------------------------------------
//
BoxedSolid *result = NULL;
while (chain)
{
Check(chain);
if (chain->HitBy(line))
{
result = chain;
}
chain = chain->GetNextSolid();
}
Check_Fpu();
return result;
}
//#############################################################################
// Construction and Destruction
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Mover::Mover(
Mover::MakeMessage *creation_message,
Mover::SharedData &virtual_data
):
Entity(creation_message, virtual_data)
{
Check_Pointer(this);
Check(creation_message);
Check(application);
ResourceFile *res_file = application->GetResourceFile();
Check(res_file);
//
//------------------------------
// Initialize the motion vectors
//------------------------------
//
localVelocity = creation_message->localVelocity;
localAcceleration = creation_message->localAcceleration;
worldLinearAcceleration.Multiply(
localAcceleration.linearMotion,
localToWorld
);
worldLinearVelocity.Multiply(
localVelocity.linearMotion,
localToWorld
);
updateVelocity.linearMotion = worldLinearVelocity;
updateVelocity.angularMotion = localVelocity.linearMotion;
updateAcceleration.linearMotion = worldLinearAcceleration;
updateAcceleration.angularMotion = localAcceleration.linearMotion;
nextUpdate = lastUpdate;
normalizeCount = 0;
if (IsInitialStasis())
{
SetSimulationState(StasisState);
}
collisionVolume = NULL;
collisionTemplate = NULL;
containedByNode = NULL;
collisionLists = NULL;
lastCollisionList = NULL;
collisionAssistant = NULL;
deadReckoner = NULL;
collisionVolumeCount = 0;
ResourceDescription *res =
res_file->SearchList(
resourceID,
ResourceDescription::GameModelResourceType
);
Check(res);
res->Lock();
ModelResource* model = (ModelResource*)res->resourceAddress;
Check_Pointer(model);
moverMass = model->moverMass;
Verify(!Small_Enough(model->momentOfInertia.x));
momentOfInertia.x = 1.0f/model->momentOfInertia.x;
Verify(!Small_Enough(model->momentOfInertia.y));
momentOfInertia.y = 1.0f/model->momentOfInertia.y;
Verify(!Small_Enough(model->momentOfInertia.z));
momentOfInertia.z = 1.0f/model->momentOfInertia.z;
positiveLinearDragCoefficients = model->positiveLinearDragCoefficients;
negativeLinearDragCoefficients = model->negativeLinearDragCoefficients;
angularDragCoefficients = model->angularDragCoefficients;
frictionCoefficient = model->frictionCoefficient;
elasticityCoefficient = model->elasticityCoefficient;
minimumBounceSpeed = model->minimumBounceSpeed;
//
//--------------------------------------------------------------------
// Read the collision information from the resource file, but for now,
// assume a VTV
//--------------------------------------------------------------------
//
collisionLists = new BoxedSolidCollisionList[2];
Register_Pointer(collisionLists);
res->Unlock();
if (IsCollisionVolume())
{
res =
res_file->SearchList(
resourceID,
ResourceDescription::BoxedSolidStreamResourceType
);
Check(res);
res->Lock();
BoxedSolidResource* box = (BoxedSolidResource*)res->resourceAddress;
Check_Pointer(box);
collisionVolumeCount = res->resourceSize / sizeof(BoxedSolidResource);
for (int i=0; i<collisionVolumeCount; ++i)
{
collisionTemplate =
BoxedSolid::MakeBoxedSolid(box, this, collisionTemplate);
Register_Object(collisionTemplate);
collisionVolume =
BoxedSolid::MakeBoxedSolid(box, this, collisionVolume);
Register_Object(collisionVolume);
++box;
}
res->Unlock();
MoveCollisionVolume();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Mover::CreateMakeMessage(
MakeMessage *creation_message,
NotationFile *model_file,
const ResourceDirectories *directories
)
{
Check(creation_message);
Check(model_file);
if (!Entity::CreateMakeMessage(creation_message, model_file, directories))
{
return False;
}
creation_message->messageLength = sizeof(Mover::MakeMessage);
creation_message->classToCreate = RegisteredClass::TrivialMoverClassID;
// creation_message->instanceFlags = DefaultFlags;
creation_message->localVelocity = Motion::Identity;
creation_message->localAcceleration = Motion::Identity;
Check_Fpu();
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
Mover::CreateModelResource(
ResourceFile *resource_file,
const char* model_name,
NotationFile *model_file,
const ResourceDirectories *,//directories,
ModelResource *model
)
{
Check(resource_file);
Check_Pointer(model_name);
Check(model_file);
//
//-----------------------------------------------------------------------
// If we were not provided a buffer to write the model data into, we must
// create it ourselves
//-----------------------------------------------------------------------
//
ModelResource *local_model = model;
if (!local_model)
{
local_model = new ModelResource;
Register_Pointer(local_model);
}
//
//-----------------
// Read in the mass
//-----------------
//
if (!model_file->GetEntry("gamedata", "MoverMass", &local_model->moverMass))
{
std::cerr << model_name << " missing MoverMass!\n";
Dump_And_Die:
if (!model)
{
Unregister_Pointer(local_model);
delete local_model;
}
Check_Fpu();
return -1;
}
//
//------------------------------
// Read in the moment of inertia
//------------------------------
//
const char* entry;
if (
!model_file->GetEntry(
"gamedata",
"MomentOfInertia",
&entry
)
)
{
std::cerr << model_name << " missing MomentOfInertia!\n";
goto Dump_And_Die;
}
sscanf(
entry,
"%f %f %f",
&local_model->momentOfInertia.x,
&local_model->momentOfInertia.y,
&local_model->momentOfInertia.z
);
//
//------------------------------
// Read in the drag coefficients
//------------------------------
//
if (
!model_file->GetEntry(
"gamedata",
"PositiveLinearDragCoefficients",
&entry
)
)
{
std::cerr << model_name << " missing PositiveLinearDragCoefficients!\n";
goto Dump_And_Die;
}
sscanf(
entry,
"%f %f %f",
&local_model->positiveLinearDragCoefficients.x,
&local_model->positiveLinearDragCoefficients.y,
&local_model->positiveLinearDragCoefficients.z
);
if (
!model_file->GetEntry(
"gamedata",
"NegativeLinearDragCoefficients",
&entry
)
)
{
std::cerr << model_name << " missing NegativeLinearDragCoefficients!\n";
goto Dump_And_Die;
}
sscanf(
entry,
"%f %f %f",
&local_model->negativeLinearDragCoefficients.x,
&local_model->negativeLinearDragCoefficients.y,
&local_model->negativeLinearDragCoefficients.z
);
//
//-------------------------
// Read in the angular drag
//-------------------------
//
if (
!model_file->GetEntry(
"gamedata",
"AngularDragCoefficients",
&entry
)
)
{
std::cerr << model_name << " missing AngularDragCoefficients!\n";
goto Dump_And_Die;
}
sscanf(
entry,
"%f %f %f",
&local_model->angularDragCoefficients.x,
&local_model->angularDragCoefficients.y,
&local_model->angularDragCoefficients.z
);
//
//---------------------
// Read in the friction
//---------------------
//
if (
!model_file->GetEntry(
"gamedata",
"FrictionCoefficient",
&local_model->frictionCoefficient
)
)
{
std::cerr << model_name << " missing FrictionCoefficient!\n";
goto Dump_And_Die;
}
//
//-----------------------
// Read in the elasticity
//-----------------------
//
if (
!model_file->GetEntry(
"gamedata",
"ElasticityCoefficient",
&local_model->elasticityCoefficient
)
)
{
std::cerr << model_name << " missing ElasticityCoefficient!\n";
goto Dump_And_Die;
}
//
//---------------------------------
// Read in the minimum bounce speed
//---------------------------------
//
if (
!model_file->GetEntry(
"gamedata",
"MinimumBounceSpeed",
&local_model->minimumBounceSpeed
)
)
{
std::cerr << model_name << " missing MinimumBounceSpeed!\n";
goto Dump_And_Die;
}
//
//-------------------------------------------------------------------------
// If we created the model buffer, then we have the responsibility to write
// it out to the resource file
//-------------------------------------------------------------------------
//
if (!model)
{
ResourceDescription *new_res =
resource_file->AddResource(
model_name,
ResourceDescription::GameModelResourceType,
1,
ResourceDescription::Preload,
local_model,
sizeof(*local_model)
);
Unregister_Pointer(local_model);
delete local_model;
Check(new_res);
Check_Fpu();
return new_res->resourceID;
}
else
{
Check_Fpu();
return 0;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Mover*
Mover::Make(Mover::MakeMessage *creation_message)
{
return new Mover(creation_message, DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Mover::~Mover()
{
Unregister_Pointer(collisionLists);
delete[] collisionLists;
if (IsCollisionVolume())
{
BoxedSolid *box = collisionTemplate;
while (box)
{
BoxedSolid *next_box = box->GetNextSolid();
Unregister_Object(box);
delete box;
box = next_box;
}
box = collisionVolume;
while (box)
{
BoxedSolid *next_box = box->GetNextSolid();
Unregister_Object(box);
delete box;
box = next_box;
}
}
if (collisionAssistant)
{
Unregister_Object(collisionAssistant);
delete collisionAssistant;
}
Check_Fpu();
}
Logical
Mover::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}