Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1270 lines
36 KiB
C++
1270 lines
36 KiB
C++
//===========================================================================//
|
|
// File: mover.cc //
|
|
// Project: MUNGA Brick: Model Manager //
|
|
// Contents: Implementation details of moving entity class //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 01/21/95 JMA Initial coding. //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include "AdeptHeaders.hpp"
|
|
#include "Mover.hpp"
|
|
#include "EntityManager.hpp"
|
|
|
|
//#############################################################################
|
|
//################## Mover::ExecutionStateEngine #####################
|
|
//#############################################################################
|
|
|
|
const StateEngine::StateEntry
|
|
Mover::ExecutionStateEngine::StateEntries[]=
|
|
{
|
|
STATE_ENTRY(Mover__ExecutionStateEngine, StraightLineMotion),
|
|
STATE_ENTRY(Mover__ExecutionStateEngine, LinearDragMotion)
|
|
};
|
|
|
|
Mover::ExecutionStateEngine::ClassData*
|
|
Mover::ExecutionStateEngine::DefaultData = NULL;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::ExecutionStateEngine::InitializeClass()
|
|
{
|
|
Check_Object(StateEngine::DefaultData);
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
Mover__ExecutionStateEngineClassID,
|
|
"Adept::Mover::ExecutionStateEngine",
|
|
BaseClass::DefaultData,
|
|
ELEMENTS(StateEntries), StateEntries,
|
|
(Entity::ExecutionStateEngine::Factory)Make,
|
|
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
|
|
&FactoryRequest::ConstructFactoryRequest
|
|
);
|
|
Register_Object(DefaultData);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::ExecutionStateEngine::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Mover::ExecutionStateEngine*
|
|
Mover::ExecutionStateEngine::Make(
|
|
Mover *mover,
|
|
FactoryRequest *request
|
|
)
|
|
{
|
|
Check_Object(mover);
|
|
Check_Object(request);
|
|
Mover::ExecutionStateEngine *engine =
|
|
new Mover::ExecutionStateEngine(DefaultData, mover, request);
|
|
return engine;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::ExecutionStateEngine::TestInstance()
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################### Mover #################################
|
|
//#############################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Mover::ClassData*
|
|
Mover::DefaultData = NULL;
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::InitializeClass()
|
|
{
|
|
Check_Object(ExecutionStateEngine::DefaultData);
|
|
Verify(!DefaultData);
|
|
DefaultData =
|
|
new ClassData(
|
|
MoverClassID,
|
|
"Adept::Mover",
|
|
BaseClass::DefaultData,
|
|
0, NULL,
|
|
(Entity::Factory)Make,
|
|
(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
|
|
ExecutionStateEngine::DefaultData,
|
|
(Entity::GameModel::Factory)GameModel::ConstructGameModel,
|
|
NULL,
|
|
(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
|
|
(Entity::GameModel::ModelWrite)GameModel::WriteToText,
|
|
(Entity::GameModel::ModelSave)GameModel::SaveGameModel
|
|
);
|
|
Register_Object(DefaultData);
|
|
|
|
DIRECT_ATTRIBUTE(
|
|
Mover,
|
|
LocalSpaceVelocity,
|
|
localSpaceVelocity,
|
|
Motion3D
|
|
);
|
|
DIRECT_ATTRIBUTE(
|
|
Mover,
|
|
LocalSpaceAcceleration,
|
|
localSpaceAcceleration,
|
|
Motion3D
|
|
);
|
|
DIRECT_ATTRIBUTE(
|
|
Mover,
|
|
WorldSpaceVelocity,
|
|
worldSpaceVelocity,
|
|
Motion3D
|
|
);
|
|
DIRECT_ATTRIBUTE(
|
|
Mover,
|
|
WorldSpaceAcceleration,
|
|
worldSpaceAcceleration,
|
|
Motion3D
|
|
);
|
|
INDIRECT_STATE_ATTRIBUTE(
|
|
Mover,
|
|
ExecutionState,
|
|
executionState,
|
|
Mover__ExecutionStateEngine
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Mover__GameModel,
|
|
MoverMass,
|
|
moverMass,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Mover__GameModel,
|
|
MomentOfInertia,
|
|
momentOfInertia,
|
|
Vector3D
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Mover__GameModel,
|
|
LinearDragCoefficients,
|
|
linearDragCoefficients,
|
|
Vector3D
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Mover__GameModel,
|
|
AngularDragCoefficients,
|
|
angularDragCoefficients,
|
|
Vector3D
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Mover__GameModel,
|
|
FrictionCoefficient,
|
|
frictionCoefficient,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Mover__GameModel,
|
|
ElasticityCoefficient,
|
|
elasticityCoefficient,
|
|
Scalar
|
|
);
|
|
DIRECT_GAME_MODEL_ATTRIBUTE(
|
|
Mover__GameModel,
|
|
MinimumBounceSpeed,
|
|
minimumBounceSpeed,
|
|
Scalar
|
|
);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::TerminateClass()
|
|
{
|
|
Unregister_Object(DefaultData);
|
|
delete DefaultData;
|
|
DefaultData = NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Mover*
|
|
Mover::Make(
|
|
const CreateMessage *message,
|
|
ReplicatorID *base_id
|
|
)
|
|
{
|
|
Check_Object(message);
|
|
gos_PushCurrentHeap(g_LibraryHeap);
|
|
Mover *new_entity = new Mover(DefaultData, message, base_id, NULL);
|
|
Check_Object(new_entity);
|
|
gos_PopCurrentHeap();
|
|
return new_entity;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Mover::Mover(
|
|
ClassData *class_data,
|
|
const CreateMessage *message,
|
|
ReplicatorID *base_id,
|
|
ElementRenderer::Element *element
|
|
):
|
|
Entity(class_data, message, base_id, element)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(message);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Initialize the motion vectors. Note that this assignment only works
|
|
// because at the time of construction, there is no parent so parent space
|
|
// is equivalant to world space
|
|
//------------------------------------------------------------------------
|
|
//
|
|
initialWorldSpaceVelocity = message->worldSpaceVelocity;
|
|
initialWorldSpaceAcceleration = message->worldSpaceAcceleration;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Look at our initial state and see how to compute the current position
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (message->initialAge > 0.0f)
|
|
{
|
|
Check_Object(executionState);
|
|
int execution_state = executionState->GetState();
|
|
switch (execution_state)
|
|
{
|
|
case ExecutionStateEngine::StraightLineMotionState:
|
|
StraightLineMotionSimulation(gos_GetElapsedTime());
|
|
break;
|
|
|
|
case ExecutionStateEngine::LinearDragMotionState:
|
|
{
|
|
//
|
|
//--------------------------------------------
|
|
// figure out the drag numbers in parent space
|
|
//--------------------------------------------
|
|
//
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
localSpaceDrag.linearMotion = model->linearDragCoefficients;
|
|
localSpaceDrag.angularMotion = model->angularDragCoefficients;
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(GetLocalToWorld());
|
|
localSpaceAcceleration.linearMotion.Multiply(
|
|
worldSpaceAcceleration.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.angularMotion.Multiply(
|
|
worldSpaceAcceleration.angularMotion,
|
|
world_to_local
|
|
);
|
|
LinearDragMotionSimulation(gos_GetElapsedTime());
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto T_is_T0;
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// If we are at the beginning of the time, just copy the variables
|
|
//----------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
T_is_T0:
|
|
worldSpaceVelocity = message->worldSpaceVelocity;
|
|
worldSpaceAcceleration = message->worldSpaceAcceleration;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Initialize the local velocity vector
|
|
//-------------------------------------
|
|
//
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(initialLocalToParent);
|
|
localSpaceVelocity.linearMotion.Multiply(
|
|
worldSpaceVelocity.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceVelocity.angularMotion.Multiply(
|
|
worldSpaceVelocity.angularMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.linearMotion.Multiply(
|
|
worldSpaceAcceleration.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.angularMotion.Multiply(
|
|
worldSpaceAcceleration.angularMotion,
|
|
world_to_local
|
|
);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void Mover::Respawn(Entity::CreateMessage *message)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(message);
|
|
|
|
BaseClass::Respawn(message);
|
|
|
|
CreateMessage *mover_message = Cast_Pointer(CreateMessage *, message);
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Initialize the motion vectors. Note that this assignment only works
|
|
// because at the time of construction, there is no parent so parent space
|
|
// is equivalant to world space
|
|
//------------------------------------------------------------------------
|
|
//
|
|
initialWorldSpaceVelocity = mover_message->worldSpaceVelocity;
|
|
initialWorldSpaceAcceleration = mover_message->worldSpaceAcceleration;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Look at our initial state and see how to compute the current position
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (mover_message->initialAge > 0.0f)
|
|
{
|
|
Check_Object(executionState);
|
|
int execution_state = executionState->GetState();
|
|
switch (execution_state)
|
|
{
|
|
case ExecutionStateEngine::StraightLineMotionState:
|
|
StraightLineMotionSimulation(gos_GetElapsedTime());
|
|
break;
|
|
|
|
case ExecutionStateEngine::LinearDragMotionState:
|
|
{
|
|
//
|
|
//--------------------------------------------
|
|
// figure out the drag numbers in parent space
|
|
//--------------------------------------------
|
|
//
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
localSpaceDrag.linearMotion = model->linearDragCoefficients;
|
|
localSpaceDrag.angularMotion = model->angularDragCoefficients;
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(GetLocalToWorld());
|
|
localSpaceAcceleration.linearMotion.Multiply(
|
|
worldSpaceAcceleration.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.angularMotion.Multiply(
|
|
worldSpaceAcceleration.angularMotion,
|
|
world_to_local
|
|
);
|
|
LinearDragMotionSimulation(gos_GetElapsedTime());
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto T_is_T0;
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// If we are at the beginning of the time, just copy the variables
|
|
//----------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
T_is_T0:
|
|
worldSpaceVelocity = mover_message->worldSpaceVelocity;
|
|
worldSpaceAcceleration = mover_message->worldSpaceAcceleration;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Initialize the local velocity vector
|
|
//-------------------------------------
|
|
//
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(initialLocalToParent);
|
|
localSpaceVelocity.linearMotion.Multiply(
|
|
worldSpaceVelocity.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceVelocity.angularMotion.Multiply(
|
|
worldSpaceVelocity.angularMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.linearMotion.Multiply(
|
|
worldSpaceAcceleration.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.angularMotion.Multiply(
|
|
worldSpaceAcceleration.angularMotion,
|
|
world_to_local
|
|
);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::Reuse(
|
|
const CreateMessage *message,
|
|
ReplicatorID *base_id
|
|
)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(message);
|
|
BaseClass::Reuse(message, base_id);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Initialize the motion vectors. Note that this assignment only works
|
|
// because at the time of construction, there is no parent so parent space
|
|
// is equivalant to world space
|
|
//------------------------------------------------------------------------
|
|
//
|
|
initialWorldSpaceVelocity = message->worldSpaceVelocity;
|
|
initialWorldSpaceAcceleration = message->worldSpaceAcceleration;
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Look at our initial state and see how to compute the current position
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (message->initialAge > 0.0f)
|
|
{
|
|
Check_Object(executionState);
|
|
int execution_state = executionState->GetState();
|
|
switch (execution_state)
|
|
{
|
|
case ExecutionStateEngine::StraightLineMotionState:
|
|
StraightLineMotionSimulation(gos_GetElapsedTime());
|
|
break;
|
|
|
|
case ExecutionStateEngine::LinearDragMotionState:
|
|
{
|
|
//
|
|
//--------------------------------------------
|
|
// figure out the drag numbers in parent space
|
|
//--------------------------------------------
|
|
//
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
localSpaceDrag.linearMotion = model->linearDragCoefficients;
|
|
localSpaceDrag.angularMotion = model->angularDragCoefficients;
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(GetLocalToWorld());
|
|
localSpaceAcceleration.linearMotion.Multiply(
|
|
worldSpaceAcceleration.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.angularMotion.Multiply(
|
|
worldSpaceAcceleration.angularMotion,
|
|
world_to_local
|
|
);
|
|
LinearDragMotionSimulation(gos_GetElapsedTime());
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto T_is_T0;
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// If we are at the beginning of the time, just copy the variables
|
|
//----------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
T_is_T0:
|
|
worldSpaceVelocity = message->worldSpaceVelocity;
|
|
worldSpaceAcceleration = message->worldSpaceAcceleration;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Initialize the local velocity vector
|
|
//-------------------------------------
|
|
//
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(initialLocalToParent);
|
|
localSpaceVelocity.linearMotion.Multiply(
|
|
worldSpaceVelocity.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceVelocity.angularMotion.Multiply(
|
|
worldSpaceVelocity.angularMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.linearMotion.Multiply(
|
|
worldSpaceAcceleration.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.angularMotion.Multiply(
|
|
worldSpaceAcceleration.angularMotion,
|
|
world_to_local
|
|
);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Replicator::CreateMessage*
|
|
Mover::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(stream);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Make sure there is enough room for the factory request on the stream
|
|
//---------------------------------------------------------------------
|
|
//
|
|
stream->AllocateBytes(sizeof(CreateMessage));
|
|
BaseClass::SaveMakeMessage(stream, res_file);
|
|
CreateMessage *message =
|
|
Cast_Pointer(CreateMessage*, stream->GetPointer());
|
|
message->messageLength = sizeof(*message);
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// Set up the motion. This will absolutely not work if the age is anything
|
|
// other than zero
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
if(parentEntity)
|
|
{
|
|
Check_Object(parentEntity);
|
|
LinearMatrix4D parent_to_world = parentEntity->GetLocalToWorld();
|
|
message->worldSpaceVelocity.linearMotion.Multiply(
|
|
initialWorldSpaceVelocity.linearMotion,
|
|
parent_to_world
|
|
);
|
|
message->worldSpaceVelocity.angularMotion.Multiply(
|
|
initialWorldSpaceVelocity.angularMotion,
|
|
parent_to_world
|
|
);
|
|
message->worldSpaceAcceleration.linearMotion.Multiply(
|
|
initialWorldSpaceAcceleration.linearMotion,
|
|
parent_to_world
|
|
);
|
|
message->worldSpaceAcceleration.angularMotion.Multiply(
|
|
initialWorldSpaceAcceleration.angularMotion,
|
|
parent_to_world
|
|
);
|
|
}
|
|
else
|
|
{
|
|
message->worldSpaceVelocity = Motion3D::Identity;
|
|
message->worldSpaceAcceleration = Motion3D::Identity;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::PreCollisionExecute(Time till)
|
|
{
|
|
Check_Object(this);
|
|
PRECOLLISION_LOGIC("Mover");
|
|
|
|
//
|
|
//---------------------------------
|
|
// If we aren't executing, stop now
|
|
//---------------------------------
|
|
//
|
|
Check_Object(executionState);
|
|
int execution_state = executionState->GetState();
|
|
Verify(execution_state != ExecutionStateEngine::NeverExecuteState);
|
|
|
|
//
|
|
//---------------------------------------------
|
|
// Make sure that the local velocity is current
|
|
//---------------------------------------------
|
|
//
|
|
#if defined(_ARMOR)
|
|
{
|
|
const LinearMatrix4D &local_to_world = GetLocalToWorld();
|
|
Check_Object(&local_to_world);
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(local_to_world);
|
|
Vector3D velocity;
|
|
velocity.Multiply(worldSpaceVelocity.linearMotion, world_to_local);
|
|
Verify(velocity == localSpaceVelocity.linearMotion);
|
|
velocity.Multiply(worldSpaceVelocity.angularMotion, world_to_local);
|
|
Verify(velocity == localSpaceVelocity.angularMotion);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
//---------------------------
|
|
// Set up the mover variables
|
|
//---------------------------
|
|
//
|
|
Verify(Close_Enough(GetLocalToParent(), GetLocalToWorld()));
|
|
Verify(IsUsingPostCollision());
|
|
const GameModel *model = GetGameModel();
|
|
Check_Object(model);
|
|
|
|
//
|
|
//--------------------------
|
|
// Now do something for real
|
|
//--------------------------
|
|
//
|
|
switch (execution_state)
|
|
{
|
|
case ExecutionStateEngine::StraightLineMotionState:
|
|
StraightLineMotionSimulation(till);
|
|
break;
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Linear drag model requires us to rotate the initial world accelerations
|
|
// into local space each frame
|
|
//------------------------------------------------------------------------
|
|
//
|
|
case ExecutionStateEngine::LinearDragMotionState:
|
|
{
|
|
localSpaceDrag.linearMotion = model->linearDragCoefficients;
|
|
localSpaceDrag.angularMotion = model->angularDragCoefficients;
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(GetLocalToWorld());
|
|
localSpaceAcceleration.linearMotion.Multiply(
|
|
worldSpaceAcceleration.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceAcceleration.angularMotion.Multiply(
|
|
worldSpaceAcceleration.angularMotion,
|
|
world_to_local
|
|
);
|
|
LinearDragMotionSimulation(till);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
localSpaceAcceleration = Motion3D::Identity;
|
|
localSpaceDrag.linearMotion = model->linearDragCoefficients;
|
|
localSpaceDrag.angularMotion = model->angularDragCoefficients;
|
|
break;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// For pre-collision execution, call our parent last
|
|
//--------------------------------------------------
|
|
//
|
|
BaseClass::PreCollisionExecute(till);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::PostCollisionExecute(Time till)
|
|
{
|
|
Check_Object(this);
|
|
Verify(Close_Enough(GetLocalToParent(), GetLocalToWorld()));
|
|
POSTCOLLISION_LOGIC("Mover");
|
|
|
|
//
|
|
//----------------------------
|
|
// Update the local velocities
|
|
//----------------------------
|
|
//
|
|
LinearMatrix4D world_to_local;
|
|
world_to_local.Invert(GetLocalToWorld());
|
|
localSpaceVelocity.linearMotion.Multiply(
|
|
worldSpaceVelocity.linearMotion,
|
|
world_to_local
|
|
);
|
|
localSpaceVelocity.angularMotion.Multiply(
|
|
worldSpaceVelocity.angularMotion,
|
|
world_to_local
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// Reparameterize if we are in the linear drag state
|
|
//--------------------------------------------------
|
|
//
|
|
Check_Object(executionState);
|
|
int execution_state = executionState->GetState();
|
|
|
|
|
|
//this is an invalid check...
|
|
// it is possible that we were just set to this and are post executing...
|
|
// Verify(execution_state != ExecutionStateEngine::NeverExecuteState);
|
|
|
|
|
|
if (execution_state == ExecutionStateEngine::LinearDragMotionState)
|
|
{
|
|
lastParameterization = till;
|
|
initialLocalToParent = GetLocalToParent();
|
|
initialWorldSpaceVelocity = worldSpaceVelocity,
|
|
initialWorldSpaceAcceleration = worldSpaceAcceleration;
|
|
}
|
|
|
|
BaseClass::PostCollisionExecute(till);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::StraightLineMotionSimulation(Time till)
|
|
{
|
|
Check_Object(this);
|
|
Verify(Close_Enough(GetLocalToParent(), GetLocalToWorld()));
|
|
|
|
//
|
|
//-------------------------
|
|
// See how long to move for
|
|
//-------------------------
|
|
//
|
|
Scalar t = GetTimeParameter(till);
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Add the world linear velocity to the world position
|
|
//----------------------------------------------------
|
|
//
|
|
Point3D old_translation(initialLocalToParent);
|
|
Point3D new_translation;
|
|
new_translation.AddScaled(
|
|
old_translation,
|
|
worldSpaceVelocity.linearMotion,
|
|
t
|
|
);
|
|
|
|
//
|
|
//---------------------------------------------------
|
|
// Make a matrix representing the rotation this frame
|
|
//---------------------------------------------------
|
|
//
|
|
Vector3D spin_axis;
|
|
spin_axis.Multiply(worldSpaceVelocity.angularMotion, t);
|
|
UnitQuaternion spin;
|
|
spin = spin_axis;
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// Convert the EulerAngles into a matrix and create the new rotation matrix
|
|
// through concatenation
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
UnitQuaternion rotation;
|
|
rotation = initialLocalToParent;
|
|
UnitQuaternion desire;
|
|
desire.Multiply(rotation, spin);
|
|
LinearMatrix4D new_position;
|
|
new_position.BuildTranslation(new_translation);
|
|
new_position.BuildRotation(desire);
|
|
SetNewLocalToParent(new_position);
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::LinearDragMotionSimulation(Time till)
|
|
{
|
|
|
|
|
|
|
|
Check_Object(this);
|
|
Verify(Close_Enough(GetLocalToParent(), GetLocalToWorld()));
|
|
Verify(IsUsingPostCollision());
|
|
|
|
//
|
|
//-------------------------
|
|
// See how long to move for
|
|
//-------------------------
|
|
//
|
|
Scalar t = GetTimeParameter(till);
|
|
|
|
//
|
|
//---------------------------------
|
|
// Call the linear drag computation
|
|
//---------------------------------
|
|
//
|
|
LinearMatrix4D origin_to_local;
|
|
Motion3D new_v;
|
|
CalculateMotionWithLinearDrag(
|
|
t,
|
|
&origin_to_local,
|
|
&new_v,
|
|
LinearMatrix4D::Identity,
|
|
localSpaceVelocity,
|
|
localSpaceAcceleration,
|
|
localSpaceDrag
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// Now rotate the motions and delta into world space
|
|
//--------------------------------------------------
|
|
//
|
|
const LinearMatrix4D &local_to_world = GetLocalToWorld();
|
|
worldSpaceVelocity.linearMotion.Multiply(new_v.linearMotion, local_to_world);
|
|
worldSpaceVelocity.angularMotion.Multiply(new_v.angularMotion, local_to_world);
|
|
LinearMatrix4D new_local_to_parent;
|
|
new_local_to_parent.Multiply(origin_to_local, local_to_world);
|
|
new_local_to_parent.Normalize();
|
|
|
|
//
|
|
//--------------------------------
|
|
// Set the position of the locator
|
|
//--------------------------------
|
|
//
|
|
SetNewLocalToParent(new_local_to_parent);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::PlaceOnEntity(
|
|
LinearMatrix4D *position,
|
|
Motion3D *velocity,
|
|
const LinearMatrix4D& initial_offset,
|
|
const Motion3D& initial_velocity
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(position);
|
|
Check_Object(velocity);
|
|
|
|
BaseClass::PlaceOnEntity(
|
|
position,
|
|
velocity,
|
|
initial_offset,
|
|
initial_velocity
|
|
);
|
|
STOP(("Not implemented"));
|
|
velocity->linearMotion += worldSpaceVelocity.linearMotion;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::CalculateMotionWithLinearDrag(
|
|
Scalar time_slice,
|
|
LinearMatrix4D *new_local_to_parent,
|
|
Motion3D *new_velocity,
|
|
const LinearMatrix4D &old_local_to_parent,
|
|
const Motion3D &old_velocity,
|
|
const Motion3D &acceleration,
|
|
const Motion3D &drag
|
|
)
|
|
{
|
|
Check_Pointer(new_local_to_parent);
|
|
Check_Pointer(new_velocity);
|
|
Check_Object(&old_local_to_parent);
|
|
Check_Object(&old_velocity);
|
|
Check_Object(&acceleration);
|
|
Check_Object(&drag);
|
|
Verify(&old_local_to_parent != new_local_to_parent);
|
|
|
|
//
|
|
//-------------------------
|
|
// Extract the translations
|
|
//-------------------------
|
|
//
|
|
Point3D old_translation;
|
|
old_translation = old_local_to_parent;
|
|
Point3D new_translation;
|
|
|
|
//
|
|
//-----------------------------------------------------
|
|
// Spin through each axis and compute the linear motion
|
|
//-----------------------------------------------------
|
|
//
|
|
for (int axis=X_Axis; axis <= Z_Axis; ++axis)
|
|
{
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// If there is no drag, use the x += v*t + 0.5*a*t*t, v += a*t form
|
|
//-----------------------------------------------------------------
|
|
//
|
|
Scalar c = drag.linearMotion[axis];
|
|
if (Small_Enough(c))
|
|
{
|
|
Scalar delta_v = acceleration.linearMotion[axis]*time_slice;
|
|
new_translation[axis] =
|
|
old_translation[axis]
|
|
+ old_velocity.linearMotion[axis]*time_slice
|
|
+ 0.5f*delta_v*time_slice;
|
|
new_velocity->linearMotion[axis] =
|
|
old_velocity.linearMotion[axis] + delta_v;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Otherwise, use the continuous form
|
|
//-----------------------------------
|
|
//
|
|
else
|
|
{
|
|
Verify(c > 0.0f);
|
|
Scalar terminal_v = acceleration.linearMotion[axis] / c;
|
|
Scalar v_range = old_velocity.linearMotion[axis] - terminal_v;
|
|
Scalar delta_v = v_range * Exp(-time_slice*c);
|
|
new_velocity->linearMotion[axis] = terminal_v + delta_v;
|
|
new_translation[axis] =
|
|
old_translation[axis]
|
|
+ terminal_v*time_slice
|
|
+ (v_range - delta_v) / c;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------
|
|
// Spin through each axis and compute the angular motion
|
|
//------------------------------------------------------
|
|
//
|
|
Verify(time_slice > 0.0f);
|
|
Vector3D delta_q;
|
|
for (axis=X_Axis; axis <= Z_Axis; ++axis)
|
|
{
|
|
//
|
|
//-----------------------------------------------------------------
|
|
// If there is no drag, use the x += v*t + 0.5*a*t*t, v += a*t form
|
|
//-----------------------------------------------------------------
|
|
//
|
|
Scalar c = drag.angularMotion[axis];
|
|
if (Small_Enough(c))
|
|
{
|
|
Scalar delta_v = acceleration.angularMotion[axis]*time_slice;
|
|
delta_q[axis] =
|
|
old_velocity.angularMotion[axis]*time_slice
|
|
+ 0.5f*delta_v*time_slice;
|
|
new_velocity->angularMotion[axis] =
|
|
old_velocity.angularMotion[axis] + delta_v;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Otherwise, use the continuous form
|
|
//-----------------------------------
|
|
//
|
|
else
|
|
{
|
|
Verify(c > 0.0f);
|
|
Scalar terminal_v = acceleration.angularMotion[axis] / c;
|
|
Scalar v_range = old_velocity.angularMotion[axis] - terminal_v;
|
|
Scalar delta_v = v_range * Exp(-time_slice*c);
|
|
new_velocity->angularMotion[axis] = terminal_v + delta_v;
|
|
delta_q[axis] = terminal_v*time_slice + (v_range - delta_v) / c;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Now add this motion to the origin. For the angular motion, convert the
|
|
// delta into a quaternion first
|
|
//------------------------------------------------------------------------
|
|
//
|
|
LinearMatrix4D d;
|
|
d.BuildRotation(delta_q);
|
|
d.BuildTranslation(Point3D::Identity);
|
|
new_local_to_parent->Multiply(old_local_to_parent, d);
|
|
new_local_to_parent->BuildTranslation(new_translation);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::ApplyLocalAccelerationToTorque(
|
|
const Vector3D &acceleration,
|
|
const Vector3D &moment
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&acceleration);
|
|
Check_Object(&moment);
|
|
|
|
Vector3D torque;
|
|
torque.Cross(moment, acceleration);
|
|
const GameModel *model = GetGameModel();
|
|
Check_Pointer(model);
|
|
torque *= model->momentOfInertia;
|
|
localSpaceAcceleration.angularMotion += torque;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::ApplyLocalForceToTorque(
|
|
const Vector3D &force,
|
|
const Vector3D &moment
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&force);
|
|
Check_Object(&moment);
|
|
|
|
const GameModel *model = GetGameModel();
|
|
Check_Pointer(model);
|
|
Verify(!Small_Enough(model->moverMass));
|
|
Vector3D acceleration;
|
|
acceleration.Divide(force, model->moverMass);
|
|
ApplyLocalAccelerationToTorque(acceleration, moment);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::ApplyLocalAcceleration(
|
|
const Vector3D &acceleration,
|
|
const Vector3D &moment
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&acceleration);
|
|
Check_Object(&moment);
|
|
|
|
localSpaceAcceleration.linearMotion += acceleration;
|
|
ApplyLocalAccelerationToTorque(acceleration, moment);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::ApplyLocalForce(
|
|
const Vector3D &force,
|
|
const Vector3D &moment
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&force);
|
|
Check_Object(&moment);
|
|
|
|
const GameModel *model = GetGameModel();
|
|
Check_Pointer(model);
|
|
Verify(!Small_Enough(model->moverMass));
|
|
Vector3D acceleration;
|
|
acceleration.Divide(force, model->moverMass);
|
|
ApplyLocalAcceleration(acceleration, moment);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Scalar
|
|
Mover::StaticBounce(
|
|
const Point3D &, //old_position,
|
|
Scalar delta_t,
|
|
Scalar penetration,
|
|
const Normal3D &normal,
|
|
Scalar *elasticity,
|
|
Scalar bounce_min,
|
|
Scalar *friction
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&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 = worldSpaceVelocity.linearMotion * normal;
|
|
Vector3D vn,vp;
|
|
vn.Multiply(normal, impact);
|
|
vp.Subtract(worldSpaceVelocity.linearMotion, vn);
|
|
if (impact > 0.0f)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
if (-impact <= bounce_min * delta_t)
|
|
{
|
|
*elasticity = 0.0f;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Calculate the energy lost to friction
|
|
//--------------------------------------
|
|
//
|
|
Scalar resistance = vp.GetLength();
|
|
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(worldSpaceVelocity.linearMotion, 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, worldSpaceVelocity.linearMotion, 2.0f);
|
|
|
|
//
|
|
// Reflect the velocity vector
|
|
//
|
|
worldSpaceVelocity.linearMotion += delta_v;
|
|
temp = penetration * delta_t;
|
|
delta_v *= temp;
|
|
STOP(("Not implemented"));
|
|
// localOrigin.linearPosition += delta_v;
|
|
|
|
//
|
|
// Compute the kinetic energy loss
|
|
//
|
|
const GameModel *model = GetGameModel();
|
|
Check_Pointer(model);
|
|
return -0.0005f * (vn * vp) * model->moverMass;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Scalar
|
|
Mover::DynamicBounce(
|
|
Mover *other,
|
|
Scalar delta_t,
|
|
Scalar penetration,
|
|
const Normal3D &normal,
|
|
Scalar *elasticity
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(other);
|
|
Check_Object(&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 = worldSpaceVelocity.linearMotion.GetLengthSquared();
|
|
Scalar k2 = other->worldSpaceVelocity.linearMotion.GetLengthSquared();
|
|
|
|
const GameModel *model = GetGameModel();
|
|
Check_Pointer(model);
|
|
const GameModel *other_model = other->GetGameModel();
|
|
Check_Pointer(model);
|
|
Scalar mass_ratio =
|
|
other_model->moverMass / (model->moverMass + other_model->moverMass);
|
|
Vector3D v;
|
|
v.Subtract(other->worldSpaceVelocity.linearMotion, worldSpaceVelocity.linearMotion);
|
|
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);
|
|
worldSpaceVelocity.linearMotion.AddScaled(
|
|
worldSpaceVelocity.linearMotion,
|
|
delta_v,
|
|
mass_ratio
|
|
);
|
|
STOP(("Not implemented"));
|
|
#if 0
|
|
localOrigin.linearPosition.AddScaled(
|
|
localOrigin.linearPosition,
|
|
delta_v,
|
|
delta_t * penetration
|
|
);
|
|
#endif
|
|
|
|
//
|
|
//----------------------------------------------------------
|
|
// Bounce the second object, and reset it's update values...
|
|
//----------------------------------------------------------
|
|
//
|
|
other->worldSpaceVelocity.linearMotion.AddScaled(
|
|
other->worldSpaceVelocity.linearMotion,
|
|
delta_v,
|
|
mass_ratio - 1.0f
|
|
);
|
|
#if 0
|
|
other->localOrigin.linearPosition.AddScaled(
|
|
other->localOrigin.linearPosition,
|
|
delta_v,
|
|
delta_t
|
|
);
|
|
#endif
|
|
|
|
//
|
|
//--------------------------------
|
|
// Return the result in kilojoules
|
|
//--------------------------------
|
|
//
|
|
k1 -= worldSpaceVelocity.linearMotion.GetLengthSquared();
|
|
k2 -= other->worldSpaceVelocity.linearMotion.GetLengthSquared();
|
|
return
|
|
0.0005f * mass_ratio
|
|
* (model->moverMass * k1 + other_model->moverMass * k2);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Mover::TestInstance()
|
|
{
|
|
Verify(IsDerivedFrom(DefaultData));
|
|
}
|