Imports the current Win32 source for the pod-racing game 'Red Planet', built on the MUNGA engine and its L4 (Win32/DirectX) platform layer: - MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend - RP / RP_L4: Red Planet game logic and Win32 application - DivLoader, Setup1: asset loader and installer project - lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies Removed stale Subversion metadata and added .gitignore/.gitattributes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
402 lines
10 KiB
C++
402 lines
10 KiB
C++
#include "rp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "thruster.h"
|
|
#include "vtvmppr.h"
|
|
#include "..\munga\boxsolid.h"
|
|
#include "vtv.h"
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
Thruster::SharedData
|
|
Thruster::DefaultData(
|
|
Thruster::GetClassDerivations(),
|
|
Thruster::GetMessageHandlers(),
|
|
Thruster::GetAttributeIndex(),
|
|
Thruster::StateCount
|
|
);
|
|
|
|
Derivation* Thruster::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(Subsystem::GetClassDerivations(), "Thruster");
|
|
return &classDerivations;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Attribute Support
|
|
//
|
|
const Thruster::IndexEntry
|
|
Thruster::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(Thruster, ThrusterOffset, thrusterOffset),
|
|
ATTRIBUTE_ENTRY(Thruster, ThrusterPosition, thrusterPosition),
|
|
ATTRIBUTE_ENTRY(Thruster, CurrentAcceleration, currentAcceleration),
|
|
ATTRIBUTE_ENTRY(Thruster, CurrentHeight, currentHeight),
|
|
ATTRIBUTE_ENTRY(Thruster, MaxThrusterRotationRate, maxThrusterRotationRate),
|
|
ATTRIBUTE_ENTRY(Thruster, MomentArm, momentArm),
|
|
ATTRIBUTE_ENTRY(Thruster, MomentArmLength, momentArmLength)
|
|
};
|
|
|
|
Thruster::AttributeIndexSet& Thruster::GetAttributeIndex()
|
|
{
|
|
static Thruster::AttributeIndexSet attributeIndex(ELEMENTS(Thruster::AttributePointers),
|
|
Thruster::AttributePointers,
|
|
Subsystem::GetAttributeIndex()
|
|
);
|
|
return attributeIndex;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model Support
|
|
//
|
|
void
|
|
Thruster::PrimeThruster(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Get pointers to the other subsystems we will have to deal with inside the
|
|
// VTV
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
VTV* vtv = GetEntity();
|
|
Check(vtv);
|
|
if (vtv->GetSimulationState() == VTV::BurningState)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
VTVControlsMapper* controls =
|
|
Cast_Object(
|
|
VTVControlsMapper*,
|
|
vtv->GetSubsystem(VTV::ControlsMapperSubsystem)
|
|
);
|
|
Check(controls);
|
|
Vector3D power_demand = controls->powerDemand;
|
|
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// Rotate the engines so that they can match to power demand requests
|
|
//-------------------------------------------------------------------
|
|
//
|
|
Scalar desire,new_rad;
|
|
if (
|
|
controls->controlMode == VTVControlsMapper::BasicMode
|
|
&& Small_Enough(vtv->localVelocity.linearMotion.z, 1.0f)
|
|
)
|
|
{
|
|
desire = 0.0f;
|
|
}
|
|
else if (Small_Enough(power_demand.z) && Small_Enough(power_demand.y))
|
|
{
|
|
desire = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
desire = Arctan(power_demand.z, power_demand.y);
|
|
}
|
|
|
|
Check(thrusterPosition);
|
|
if (thrusterPosition->GetRadians() < desire)
|
|
{
|
|
new_rad =
|
|
thrusterPosition->GetRadians()
|
|
+ time_slice * maxThrusterRotationRate;
|
|
Max_Clamp(new_rad, desire);
|
|
vtv->thrusterAngle = new_rad;
|
|
}
|
|
else if (thrusterPosition->GetRadians() > desire)
|
|
{
|
|
new_rad =
|
|
thrusterPosition->GetRadians()
|
|
- time_slice * maxThrusterRotationRate;
|
|
Min_Clamp(new_rad, desire);
|
|
vtv->thrusterAngle = new_rad;
|
|
}
|
|
thrusterPosition->SetRotation(vtv->thrusterAngle);
|
|
|
|
if (!Close_Enough(desire, updatedPosition, 10.0f*RAD_PER_DEG))
|
|
{
|
|
ForceUpdate();
|
|
updatedPosition = desire;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// Calculate the vertical thrust based upon the height of each engine
|
|
//-------------------------------------------------------------------
|
|
//
|
|
Verify(vtv->GetMoverCollisionRoot());
|
|
BoundingBoxTreeNode *tree = vtv->GetMoverCollisionRoot();
|
|
|
|
currentHeight = 0.0f;
|
|
Point3D test_point;
|
|
test_point.Multiply(thrusterOffset, vtv->localToWorld);
|
|
tree->FindBoundingBoxUnder(test_point, ¤tHeight);
|
|
|
|
Vector3D force;
|
|
force.Multiply(power_demand, currentAcceleration);
|
|
if (power_demand.y > SMALL)
|
|
{
|
|
Vector3D ge = Vector3D::Identity;
|
|
ge.y =
|
|
vtv->groundEffectRange * power_demand.y * powerScale
|
|
/ (1.0f + vtv->groundEffectDomainSquared*currentHeight*currentHeight);
|
|
Check_Fpu();
|
|
Vector3D local_ge;
|
|
local_ge.MultiplyByInverse(ge, vtv->localToWorld);
|
|
force += local_ge;
|
|
}
|
|
#if defined(WOOBLY)
|
|
vtv->ApplyLocalAcceleration(force, momentArm);
|
|
#else
|
|
vtv->localAcceleration.linearMotion += force;
|
|
#endif
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Thruster::SlaveThruster(Scalar)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Get pointers to the other subsystems we will have to deal with inside the
|
|
// VTV
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
VTV* vtv = GetEntity();
|
|
Check(vtv);
|
|
if (vtv->GetSimulationState() == VTV::BurningState)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
VTVControlsMapper* controls =
|
|
Cast_Object(
|
|
VTVControlsMapper*,
|
|
vtv->GetSubsystem(VTV::ControlsMapperSubsystem)
|
|
);
|
|
Check(controls);
|
|
Vector3D power_demand = controls->powerDemand;
|
|
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// Rotate the engines so that they can match to power demand requests
|
|
//-------------------------------------------------------------------
|
|
//
|
|
thrusterPosition->SetRotation(vtv->thrusterAngle);
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// Calculate the vertical thrust based upon the height of each engine
|
|
//-------------------------------------------------------------------
|
|
//
|
|
Verify(vtv->GetMoverCollisionRoot());
|
|
BoundingBoxTreeNode *tree = vtv->GetMoverCollisionRoot();
|
|
|
|
currentHeight = 0.0f;
|
|
Point3D test_point;
|
|
test_point.Multiply(thrusterOffset, vtv->localToWorld);
|
|
tree->FindBoundingBoxUnder(test_point, ¤tHeight);
|
|
|
|
Vector3D force;
|
|
force.Multiply(power_demand, currentAcceleration);
|
|
if (power_demand.y > SMALL)
|
|
{
|
|
Vector3D ge = Vector3D::Identity;
|
|
ge.y =
|
|
vtv->groundEffectRange * power_demand.y * powerScale
|
|
/ (1.0f + vtv->groundEffectDomainSquared*currentHeight*currentHeight);
|
|
Check_Fpu();
|
|
Vector3D local_ge;
|
|
local_ge.MultiplyByInverse(ge, vtv->localToWorld);
|
|
force += local_ge;
|
|
}
|
|
#if defined(WOOBLY)
|
|
vtv->ApplyLocalAcceleration(force, momentArm);
|
|
#else
|
|
vtv->localAcceleration.linearMotion += force;
|
|
#endif
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Model Support
|
|
//
|
|
void
|
|
Thruster::PrimeDeadReckon(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Get pointers to the other subsystems we will have to deal with inside the
|
|
// VTV
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
VTV* vtv = GetEntity();
|
|
Check(vtv);
|
|
if (vtv->GetSimulationState() == VTV::BurningState)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
Check(thrusterPosition);
|
|
Scalar new_rad;
|
|
if (thrusterPosition->GetRadians() < updatedPosition)
|
|
{
|
|
new_rad =
|
|
thrusterPosition->GetRadians()
|
|
+ time_slice * maxThrusterRotationRate;
|
|
Max_Clamp(new_rad, updatedPosition);
|
|
vtv->thrusterAngle = new_rad;
|
|
}
|
|
else if (thrusterPosition->GetRadians() > updatedPosition)
|
|
{
|
|
new_rad =
|
|
thrusterPosition->GetRadians()
|
|
- time_slice * maxThrusterRotationRate;
|
|
Min_Clamp(new_rad, updatedPosition);
|
|
vtv->thrusterAngle = new_rad;
|
|
}
|
|
thrusterPosition->SetRotation(vtv->thrusterAngle);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Thruster::SlaveDeadReckon(Scalar)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Get pointers to the other subsystems we will have to deal with inside the
|
|
// VTV
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
VTV* vtv = GetEntity();
|
|
Check(vtv);
|
|
if (vtv->GetSimulationState() == VTV::BurningState)
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
|
|
thrusterPosition->SetRotation(vtv->thrusterAngle);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Thruster::ReadUpdateRecord(Simulation::UpdateRecord *message)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(message);
|
|
Subsystem::ReadUpdateRecord(message);
|
|
UpdateRecord* record = (UpdateRecord*) message;
|
|
|
|
updatedPosition = record->thrusterRotation;
|
|
|
|
Check_Fpu();
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Thruster::WriteUpdateRecord(Simulation::UpdateRecord *record, int update_model)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(record);
|
|
|
|
Subsystem::WriteUpdateRecord(record, update_model);
|
|
UpdateRecord* update = (UpdateRecord*) record;
|
|
|
|
update->recordLength = sizeof(*update);
|
|
update->thrusterRotation = updatedPosition;
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Thruster::Thruster(
|
|
VTV *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource
|
|
):
|
|
Subsystem(owner, subsystem_ID, subsystem_resource, DefaultData)
|
|
{
|
|
if (owner->GetInstance() != VTV::ReplicantInstance)
|
|
{
|
|
if (subsystem_resource->primeThruster)
|
|
{
|
|
SetPerformance(&Thruster::PrimeThruster);
|
|
}
|
|
else
|
|
{
|
|
SetPerformance(&Thruster::SlaveThruster);
|
|
}
|
|
}
|
|
else if (subsystem_resource->primeThruster)
|
|
{
|
|
SetPerformance(&Thruster::PrimeDeadReckon);
|
|
}
|
|
else
|
|
{
|
|
SetPerformance(&Thruster::SlaveDeadReckon);
|
|
}
|
|
|
|
//
|
|
// Get the offset from the linear matrix by peeliong
|
|
// off the translation part
|
|
//
|
|
EntitySegment *segment = owner->GetSegment(segmentIndex);
|
|
momentArm = thrusterOffset = segment->GetBaseOffset();
|
|
|
|
thrusterOffset.y = owner->GetCollisionTemplate()->minY;
|
|
momentArm.y = 0.0f;
|
|
momentArmLength = momentArm.Length();
|
|
powerScale = 0.0f;
|
|
//
|
|
// Get the thruster position from the joint subsystem of the VTV
|
|
//
|
|
JointSubsystem *joint_subsystem = owner->GetJointSubsystem();
|
|
Check(joint_subsystem);
|
|
|
|
thrusterPosition = joint_subsystem->GetJoint(subsystem_resource->jointIndex);
|
|
|
|
currentAcceleration = 0.0f;
|
|
currentHeight = 0.0f;
|
|
maxThrusterRotationRate = subsystem_resource->maxThrusterRotationRate;
|
|
updatedPosition = 0.0f;
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Thruster::~Thruster()
|
|
{
|
|
Check(this);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
Thruster::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|