527 lines
16 KiB
C++
527 lines
16 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************************************************
|
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : WWPhys *
|
|
* *
|
|
* $Archive:: /Commando/Code/wwphys/trackedvehicle.cpp $*
|
|
* *
|
|
* Original Author:: Greg Hjelstrom *
|
|
* *
|
|
* $Author:: Byon_g $*
|
|
* *
|
|
* $Modtime:: 3/15/02 1:44p $*
|
|
* *
|
|
* $Revision:: 12 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* Functions: *
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#include "trackedvehicle.h"
|
|
#include "rendobj.h"
|
|
#include "lineseg.h"
|
|
#include "lookuptable.h"
|
|
#include "physcoltest.h"
|
|
#include "pscene.h"
|
|
#include "physcontrol.h"
|
|
#include "wwphysids.h"
|
|
#include "persistfactory.h"
|
|
#include "simpledefinitionfactory.h"
|
|
#include "wwhack.h"
|
|
#include "wwprofile.h"
|
|
#include "wheel.h"
|
|
#include "octbox.h"
|
|
#include "mesh.h"
|
|
#include "meshmdl.h"
|
|
#include "matinfo.h"
|
|
#include "vertmaterial.h"
|
|
#include "mapper.h"
|
|
#include <string.h>
|
|
|
|
|
|
DECLARE_FORCE_LINK(trackedvehicle);
|
|
|
|
|
|
|
|
static bool _is_left_track_name(const char * name)
|
|
{
|
|
const char * LEFT_TRACK_NAME0 = "V_TRACK-L";
|
|
const char * LEFT_TRACK_NAME1 = "V_TREAD-L";
|
|
const char * LEFT_TRACK_NAME2 = "V_TRACK_L";
|
|
const char * LEFT_TRACK_NAME3 = "V_TREAD_L";
|
|
|
|
/*
|
|
** Track skins can only be sub-objects of a hierarchy so check the name
|
|
** after the hierarchy name.
|
|
*/
|
|
const char * sub_name = strchr(name,'.');
|
|
if (sub_name != NULL) {
|
|
sub_name++;
|
|
if ( (strnicmp(sub_name,LEFT_TRACK_NAME0,strlen(LEFT_TRACK_NAME0)) == 0) ||
|
|
(strnicmp(sub_name,LEFT_TRACK_NAME1,strlen(LEFT_TRACK_NAME1)) == 0) ||
|
|
(strnicmp(sub_name,LEFT_TRACK_NAME2,strlen(LEFT_TRACK_NAME2)) == 0) ||
|
|
(strnicmp(sub_name,LEFT_TRACK_NAME3,strlen(LEFT_TRACK_NAME3)) == 0))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool _is_right_track_name(const char * name)
|
|
{
|
|
const char * RIGHT_TRACK_NAME0 = "V_TRACK-R";
|
|
const char * RIGHT_TRACK_NAME1 = "V_TREAD-R";
|
|
const char * RIGHT_TRACK_NAME2 = "V_TRACK_R";
|
|
const char * RIGHT_TRACK_NAME3 = "V_TREAD_R";
|
|
|
|
/*
|
|
** Track skins can only be sub-objects of a hierarchy so check the name
|
|
** after the hierarchy name.
|
|
*/
|
|
const char * sub_name = strchr(name,'.');
|
|
if (sub_name != NULL) {
|
|
sub_name++;
|
|
if ( (strnicmp(sub_name,RIGHT_TRACK_NAME0,strlen(RIGHT_TRACK_NAME0)) == 0) ||
|
|
(strnicmp(sub_name,RIGHT_TRACK_NAME1,strlen(RIGHT_TRACK_NAME1)) == 0) ||
|
|
(strnicmp(sub_name,RIGHT_TRACK_NAME2,strlen(RIGHT_TRACK_NAME2)) == 0) ||
|
|
(strnicmp(sub_name,RIGHT_TRACK_NAME3,strlen(RIGHT_TRACK_NAME3)) == 0))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**************************************************************************************
|
|
**
|
|
** TrackedVehicleClass Implementation
|
|
**
|
|
**************************************************************************************/
|
|
|
|
/*
|
|
** Declare a PersistFactory for TrackedVehicleClasses
|
|
*/
|
|
SimplePersistFactoryClass<TrackedVehicleClass,PHYSICS_CHUNKID_TRACKEDVEHICLE> _TrackedVehicleFactory;
|
|
|
|
/*
|
|
** Chunk-ID's used by tracked vehicle class
|
|
*/
|
|
enum
|
|
{
|
|
TRACKEDVEHICLE_CHUNK_VEHICLEPHYS = 0x00119801,
|
|
TRACKEDVEHICLE_CHUNK_VARIABLES,
|
|
};
|
|
|
|
|
|
// Debugging colors
|
|
const Vector3 SPRING_COLOR(1.0f,0.5f,0.0f);
|
|
const Vector3 SUSPENSION_FORCE_COLOR(1.0f,0.0f,0.0f);
|
|
|
|
TrackedVehicleClass::TrackedVehicleClass(void) :
|
|
LeftTrackMovement(0),
|
|
RightTrackMovement(0),
|
|
LeftTrackLastPosition(0,0,0),
|
|
RightTrackLastPosition(0,0,0)
|
|
{
|
|
}
|
|
|
|
void TrackedVehicleClass::Init(const TrackedVehicleDefClass & def)
|
|
{
|
|
VehiclePhysClass::Init(def);
|
|
}
|
|
|
|
TrackedVehicleClass::~TrackedVehicleClass(void)
|
|
{
|
|
}
|
|
|
|
void TrackedVehicleClass::Render(RenderInfoClass & rinfo)
|
|
{
|
|
/*
|
|
** Compute the track movement
|
|
*/
|
|
const Matrix3D & tm = Get_Transform();
|
|
float extent_y = ContactBox->InnerBox.Extent.Y;
|
|
float extent_z = ContactBox->InnerBox.Extent.Z;
|
|
Vector3 left_track_position = tm * Vector3(0,extent_y,-extent_z);
|
|
Vector3 right_track_position = tm * Vector3(0,-extent_y,-extent_z);
|
|
|
|
Vector3 forward;
|
|
tm.Get_X_Vector(&forward);
|
|
|
|
Vector3 move;
|
|
Vector3::Subtract(left_track_position,LeftTrackLastPosition,&move);
|
|
LeftTrackMovement = Vector3::Dot_Product(move,forward);
|
|
|
|
Vector3::Subtract(right_track_position,RightTrackLastPosition,&move);
|
|
RightTrackMovement = Vector3::Dot_Product(move,forward);
|
|
LeftTrackLastPosition = left_track_position;
|
|
RightTrackLastPosition = right_track_position;
|
|
|
|
/*
|
|
** Update the mappers
|
|
*/
|
|
const TrackedVehicleDefClass * def = Get_TrackedVehicleDef();
|
|
for (int i=0; i<TrackMappers.Count(); i++) {
|
|
float movement = 0.0f;
|
|
if (TrackMappers[i].TrackType == LEFT_TRACK) {
|
|
movement = LeftTrackMovement;
|
|
} else {
|
|
movement = RightTrackMovement;
|
|
}
|
|
|
|
TrackMappers[i].Mapper->Set_UV_Offset_Delta(
|
|
Vector2(def->TrackUScaleFactor * movement,def->TrackVScaleFactor * movement) );
|
|
}
|
|
|
|
/*
|
|
** Parent class render
|
|
*/
|
|
VehiclePhysClass::Render(rinfo);
|
|
}
|
|
|
|
void TrackedVehicleClass::Set_Model(RenderObjClass * model)
|
|
{
|
|
VehiclePhysClass::Set_Model(model);
|
|
Update_Cached_Model_Parameters();
|
|
}
|
|
|
|
void TrackedVehicleClass::Update_Cached_Model_Parameters(void)
|
|
{
|
|
/*
|
|
** Reset our array of mapper pointers
|
|
*/
|
|
TrackMappers.Delete_All(false);
|
|
|
|
/*
|
|
** Make sure that this model has unique meshes for its tracks
|
|
** and grab pointers to the mappers.
|
|
*/
|
|
if (Model != NULL) {
|
|
Grab_Track_Mappers(Model);
|
|
}
|
|
}
|
|
|
|
void TrackedVehicleClass::Grab_Track_Mappers(RenderObjClass * model)
|
|
{
|
|
for (int i=0; i<model->Get_Num_Sub_Objects(); i++) {
|
|
RenderObjClass * sub_obj = model->Get_Sub_Object(i);
|
|
if (sub_obj && (sub_obj->Class_ID() == RenderObjClass::CLASSID_MESH)) {
|
|
const char * name = sub_obj->Get_Name();
|
|
if (_is_left_track_name(name)) {
|
|
Add_Track_Mappers((MeshClass*)sub_obj,LEFT_TRACK);
|
|
}
|
|
if (_is_right_track_name(name)) {
|
|
Add_Track_Mappers((MeshClass*)sub_obj,RIGHT_TRACK);
|
|
}
|
|
}
|
|
|
|
Grab_Track_Mappers(sub_obj);
|
|
REF_PTR_RELEASE(sub_obj);
|
|
}
|
|
}
|
|
|
|
void TrackedVehicleClass::Add_Track_Mappers(MeshClass * mesh,int track_type)
|
|
{
|
|
/*
|
|
** First check if this model has a linear offset mapper in any of its vertex materials
|
|
*/
|
|
bool has_mapper = false;
|
|
MaterialInfoClass * matinfo = mesh->Get_Material_Info();
|
|
|
|
if (matinfo != NULL) {
|
|
for (int i=0; i<matinfo->Vertex_Material_Count(); i++) {
|
|
VertexMaterialClass * vmtl = matinfo->Peek_Vertex_Material(i);
|
|
if (vmtl != NULL) {
|
|
TextureMapperClass * mapper = vmtl->Get_Mapper(0);
|
|
if ((mapper != NULL) && (mapper->Mapper_ID() == TextureMapperClass::MAPPER_ID_LINEAR_OFFSET)) {
|
|
has_mapper = true;
|
|
}
|
|
REF_PTR_RELEASE(mapper);
|
|
}
|
|
}
|
|
REF_PTR_RELEASE(matinfo);
|
|
}
|
|
|
|
/*
|
|
** If it does, then make the mesh unique, re-dig out the mapper, and hang onto its pointer
|
|
*/
|
|
if (has_mapper) {
|
|
mesh->Make_Unique();
|
|
|
|
MaterialInfoClass * matinfo = mesh->Get_Material_Info();
|
|
|
|
if (matinfo != NULL) {
|
|
for (int i=0; i<matinfo->Vertex_Material_Count(); i++) {
|
|
VertexMaterialClass * vmtl = matinfo->Peek_Vertex_Material(i);
|
|
if (vmtl != NULL) {
|
|
TextureMapperClass * mapper = vmtl->Get_Mapper(0);
|
|
if ((mapper != NULL) && (mapper->Mapper_ID() == TextureMapperClass::MAPPER_ID_LINEAR_OFFSET)) {
|
|
TrackMapperStruct ts;
|
|
ts.Mapper = (LinearOffsetTextureMapperClass*)mapper;
|
|
ts.TrackType = track_type;
|
|
TrackMappers.Add(ts);
|
|
|
|
#if 0 //Paranoid debug logging
|
|
WWDEBUG_SAY(("Grabbing Track Mesh! \r\n"));
|
|
WWDEBUG_SAY((" container: 0x%X mesh: 0x%X vmtl: 0x%X mapper 0x%X\r\n",
|
|
(unsigned int)mesh->Get_Container(),
|
|
(unsigned int)mesh,
|
|
(unsigned int)vmtl,
|
|
(unsigned int)mapper));
|
|
WWDEBUG_SAY((" tracked vehicle: 0x%X\r\n",(unsigned int)this));
|
|
WWDEBUG_SAY((" Name = %s\r\n",mesh->Get_Name()));
|
|
#endif
|
|
}
|
|
REF_PTR_RELEASE(mapper);
|
|
}
|
|
}
|
|
REF_PTR_RELEASE(matinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TrackedVehicleClass::Compute_Force_And_Torque(Vector3 * force,Vector3 * torque)
|
|
{
|
|
{
|
|
WWPROFILE("TrackedVehicleClass::Compute_Force_And_Torque");
|
|
|
|
const TrackedVehicleDefClass * def = Get_TrackedVehicleDef();
|
|
WWASSERT(def != NULL);
|
|
|
|
/*
|
|
** Compute the left and right track torque
|
|
** Accept either strafe or turn as a turn command
|
|
*/
|
|
LeftTrackTorque = RightTrackTorque = 0.0f;
|
|
if (Controller) {
|
|
float forward = Controller->Get_Move_Forward();
|
|
float left = 0.0f;
|
|
if (WWMath::Fabs(Controller->Get_Turn_Left()) > WWMath::Fabs(Controller->Get_Move_Left())) {
|
|
left = Controller->Get_Turn_Left();
|
|
} else {
|
|
left = Controller->Get_Move_Left();
|
|
}
|
|
|
|
float turn_scale = 1.0f - WWMath::Fabs(left) * (1.0f - def->TurnTorqueScaleFactor);
|
|
#if 0
|
|
if (forward != 0.0f) {
|
|
turn_scale = 1.0f;
|
|
}
|
|
|
|
LeftTrackTorque = WWMath::Clamp((2.12f * forward - left),-1.0f,1.0f);
|
|
RightTrackTorque = WWMath::Clamp((2.12f * forward + left),-1.0f,1.0f);
|
|
|
|
LeftTrackTorque *= def->MaxEngineTorque * turn_scale;
|
|
RightTrackTorque *= def->MaxEngineTorque * turn_scale;
|
|
#else
|
|
LeftTrackTorque = (forward - left) * def->MaxEngineTorque * turn_scale;
|
|
RightTrackTorque = (forward + left) * def->MaxEngineTorque * turn_scale;
|
|
#endif
|
|
|
|
}
|
|
|
|
/*
|
|
** Update each wheel's torque.
|
|
*/
|
|
for (int iwheel = 0; iwheel<Wheels.Length(); iwheel++) {
|
|
|
|
if (Wheels[iwheel]->Get_Flag(WheelClass::LEFT_TRACK)) {
|
|
((TrackWheelClass *)Wheels[iwheel])->Set_Axle_Torque(LeftTrackTorque);
|
|
}
|
|
if (Wheels[iwheel]->Get_Flag(WheelClass::RIGHT_TRACK)) {
|
|
((TrackWheelClass *)Wheels[iwheel])->Set_Axle_Torque(RightTrackTorque);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Pass on to the base class
|
|
*/
|
|
VehiclePhysClass::Compute_Force_And_Torque(force,torque);
|
|
}
|
|
|
|
SuspensionElementClass * TrackedVehicleClass::Alloc_Suspension_Element(void)
|
|
{
|
|
return new TrackWheelClass;
|
|
}
|
|
|
|
/*
|
|
** Save-Load System
|
|
*/
|
|
const PersistFactoryClass & TrackedVehicleClass::Get_Factory (void) const
|
|
{
|
|
return _TrackedVehicleFactory;
|
|
}
|
|
|
|
bool TrackedVehicleClass::Save (ChunkSaveClass &csave)
|
|
{
|
|
csave.Begin_Chunk(TRACKEDVEHICLE_CHUNK_VEHICLEPHYS);
|
|
VehiclePhysClass::Save(csave);
|
|
csave.End_Chunk();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TrackedVehicleClass::Load (ChunkLoadClass &cload)
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
|
|
switch(cload.Cur_Chunk_ID())
|
|
{
|
|
case TRACKEDVEHICLE_CHUNK_VEHICLEPHYS:
|
|
VehiclePhysClass::Load(cload);
|
|
break;
|
|
|
|
default:
|
|
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
|
|
break;
|
|
}
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
SaveLoadSystemClass::Register_Post_Load_Callback(this);
|
|
return true;
|
|
}
|
|
|
|
void TrackedVehicleClass::On_Post_Load (void)
|
|
{
|
|
VehiclePhysClass::On_Post_Load();
|
|
}
|
|
|
|
|
|
/***********************************************************************************************
|
|
**
|
|
** TrackedVehicleDefClass Implementation
|
|
**
|
|
***********************************************************************************************/
|
|
|
|
SimplePersistFactoryClass<TrackedVehicleDefClass,PHYSICS_CHUNKID_TRACKEDVEHICLEDEF> _TrackedVehicleDefFactory;
|
|
DECLARE_DEFINITION_FACTORY(TrackedVehicleDefClass, CLASSID_TRACKEDVEHICLEDEF, "TrackedVehicle") _TrackedVehicleDefDefFactory;
|
|
|
|
/*
|
|
** Chunk ID's used by TrackedVehicleDefClass
|
|
*/
|
|
enum
|
|
{
|
|
TRACKEDVEHICLEDEF_CHUNK_VEHICLEPHYSDEF = 0406001454, // (parent class)
|
|
TRACKEDVEHICLEDEF_CHUNK_VARIABLES,
|
|
|
|
TRACKEDVEHICLEDEF_VARIABLE_MAXENGINETORQUE = 0x00,
|
|
TRACKEDVEHICLEDEF_VARIABLE_TRACKUSCALEFACTOR,
|
|
TRACKEDVEHICLEDEF_VARIABLE_TRACKVSCALEFACTOR,
|
|
TRACKEDVEHICLEDEF_VARIABLE_TURNTORQUESCALEFACTOR,
|
|
|
|
};
|
|
|
|
|
|
TrackedVehicleDefClass::TrackedVehicleDefClass(void) :
|
|
MaxEngineTorque(0.0f),
|
|
TrackUScaleFactor(25.0f),
|
|
TrackVScaleFactor(0.0f),
|
|
TurnTorqueScaleFactor(1.0f)
|
|
{
|
|
// make our parameters editable
|
|
FLOAT_EDITABLE_PARAM(TrackedVehicleDefClass, MaxEngineTorque, 0.0f, 100000.0f);
|
|
FLOAT_EDITABLE_PARAM(TrackedVehicleDefClass, TrackUScaleFactor, -1000.0f, 1000.0f);
|
|
FLOAT_EDITABLE_PARAM(TrackedVehicleDefClass, TrackVScaleFactor, -1000.0f, 1000.0f);
|
|
FLOAT_EDITABLE_PARAM(TrackedVehicleDefClass, TurnTorqueScaleFactor, 0.0f, 1.0f);
|
|
}
|
|
|
|
uint32 TrackedVehicleDefClass::Get_Class_ID (void) const
|
|
{
|
|
return CLASSID_TRACKEDVEHICLEDEF;
|
|
}
|
|
|
|
const PersistFactoryClass & TrackedVehicleDefClass::Get_Factory (void) const
|
|
{
|
|
return _TrackedVehicleDefFactory;
|
|
}
|
|
|
|
PersistClass * TrackedVehicleDefClass::Create(void) const
|
|
{
|
|
TrackedVehicleClass * obj = NEW_REF(TrackedVehicleClass,());
|
|
obj->Init(*this);
|
|
return obj;
|
|
}
|
|
|
|
bool TrackedVehicleDefClass::Save(ChunkSaveClass &csave)
|
|
{
|
|
csave.Begin_Chunk(TRACKEDVEHICLEDEF_CHUNK_VEHICLEPHYSDEF);
|
|
VehiclePhysDefClass::Save(csave);
|
|
csave.End_Chunk();
|
|
|
|
csave.Begin_Chunk(TRACKEDVEHICLEDEF_CHUNK_VARIABLES);
|
|
WRITE_MICRO_CHUNK(csave,TRACKEDVEHICLEDEF_VARIABLE_MAXENGINETORQUE,MaxEngineTorque);
|
|
WRITE_MICRO_CHUNK(csave,TRACKEDVEHICLEDEF_VARIABLE_TRACKUSCALEFACTOR,TrackUScaleFactor);
|
|
WRITE_MICRO_CHUNK(csave,TRACKEDVEHICLEDEF_VARIABLE_TRACKVSCALEFACTOR,TrackVScaleFactor);
|
|
WRITE_MICRO_CHUNK(csave,TRACKEDVEHICLEDEF_VARIABLE_TURNTORQUESCALEFACTOR,TurnTorqueScaleFactor);
|
|
csave.End_Chunk();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool TrackedVehicleDefClass::Load(ChunkLoadClass &cload)
|
|
{
|
|
while (cload.Open_Chunk()) {
|
|
|
|
switch(cload.Cur_Chunk_ID()) {
|
|
|
|
case TRACKEDVEHICLEDEF_CHUNK_VEHICLEPHYSDEF:
|
|
VehiclePhysDefClass::Load(cload);
|
|
break;
|
|
|
|
case TRACKEDVEHICLEDEF_CHUNK_VARIABLES:
|
|
while (cload.Open_Micro_Chunk()) {
|
|
switch(cload.Cur_Micro_Chunk_ID()) {
|
|
READ_MICRO_CHUNK(cload,TRACKEDVEHICLEDEF_VARIABLE_MAXENGINETORQUE,MaxEngineTorque);
|
|
READ_MICRO_CHUNK(cload,TRACKEDVEHICLEDEF_VARIABLE_TRACKUSCALEFACTOR,TrackUScaleFactor);
|
|
READ_MICRO_CHUNK(cload,TRACKEDVEHICLEDEF_VARIABLE_TRACKVSCALEFACTOR,TrackVScaleFactor);
|
|
READ_MICRO_CHUNK(cload,TRACKEDVEHICLEDEF_VARIABLE_TURNTORQUESCALEFACTOR,TurnTorqueScaleFactor);
|
|
}
|
|
cload.Close_Micro_Chunk();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
|
|
break;
|
|
}
|
|
|
|
cload.Close_Chunk();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool TrackedVehicleDefClass::Is_Type(const char * type_name)
|
|
{
|
|
if (stricmp(type_name,TrackedVehicleDefClass::Get_Type_Name()) == 0) {
|
|
return true;
|
|
} else {
|
|
return VehiclePhysDefClass::Is_Type(type_name);
|
|
}
|
|
}
|
|
|
|
|