//===========================================================================// // 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)); }