/*
**	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/wheel.h                               $*
 *                                                                                             *
 *              Original Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                      $Author:: Greg_h                                                      $*
 *                                                                                             *
 *                     $Modtime:: 11/19/01 3:47p                                              $*
 *                                                                                             *
 *                    $Revision:: 13                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#ifndef WHEEL_H
#define WHEEL_H

#include "vector3.h"
#include "matrix3d.h"

class VehiclePhysClass;
class RenderObjClass;


/*
** Some default suspension constants
*/
const float		DEFAULT_SPRING_CONSTANT					= 3.0f;
const float		DEFAULT_DAMPING_COEFFICIENT			= 0.75f;
const float		DEFAULT_SPRING_LENGTH					= 1.0f;

/**
** Wheels.  An instance of a wheel object will contain information on each
** wheel detected in the vehicle model.  Wheels use a pair of named bones.
** The "position" bone is used to move the wheel up and down according to 
** the suspension system and to rotate if this is a "steering" wheel.  The
** "rotation" bone is used to roll the wheel on the terrain.
**
** More Info on the "Position" and "Center" bones:
** - The "center" bone is only used in computing the radius of the wheel and
**   it is rotated about its z-axis to make the wheel appear to roll
** - The "position" bone is used in conjunction with the length parameter
**   to define the suspension springs.
** 
** Defining the "spring-segment" using the position bone:
** - The default location of the position bone is considered the point of maximum-
**   compression.  (this point should be *inside* the collision box for the model!) 
** - The spring will extend down the -z axis in the position bone's coordinate system
**
** Graphical constraints for the wheel
** - One of three methods can be used to graphically constrain the wheel with the
**   ground: Translation along the z-axis of the position bone, or rotation of
**   a rotation constraint bone.
** - Normal case: There are two bones for the wheel: position and center.  The position
**   bone is moved along its Z-axis to the point of collision with the ground
** - Translation: There is an additional translation bone (WheelTxx) which defines 
**   the axis that the wheel is to be translated along.
** - Fork/Rotation: a "fork" bone (WheelFxx) is rotated such that the Z-coordinate (in 
**   the fork's coordinate system) meets the ground.
** 
** Wheel Flags:
** - 'E' Engine. this wheel is connected to the engine and should exert its force 
** - 'S' Steering: The position bone for this wheel rotates about its Z-axis for steering
** - 's' Inverse Steering: The position bone for this wheel rotates the opposite way
** - 'L' Left Track: this wheel is part of the left track of a tracked vehicle
** - 'R' Right Track: this wheel is part of the right track of a tracked vehicle
** - 'F' Fake: just move, don't compute any forces
*/


/**
** SuspensionElementClass
** This class only has the functionality of a support strut with a contact point.  Derived
** wheel classes add in the forces exerted at the contact patch.
*/

class SuspensionElementClass
{
public:

	enum FlagsType
	{
		FAKE						= 0x0001,			// this wheel is for looks only (e.g. some of the tank wheels)
		STEERING					= 0x0002,			// this wheel turns with the steering input
		INV_STEERING			= 0x0004,			// this wheel turns opposite of the steering input
		TILT_STEERING			= 0x0008,			// this wheel turns with tilt abount the X axis of the vehicle (bikes).

		ENGINE					= 0x0010,			// this wheel exerts the engine force
		LEFT_TRACK				= 0x0020,			// this wheel is part of the left track of a tank
		RIGHT_TRACK				= 0x0040,			// this wheel is part of the right track of a tank
		
		DISABLED					= 0x0100,			// this wheel is disabled
		INCONTACT				= 0x0200,			// this wheel is in contact with the ground
		BRAKING					= 0x0400,			// this wheel is undergoing braking
		DEFAULT_FLAGS			= 0
	};

	SuspensionElementClass(void);
	virtual ~SuspensionElementClass(void);
	virtual void	Init(VehiclePhysClass * obj,int pbone,int rbone,int forkbone,int axisbone);

	/*
	** Accessors to properties
	*/
	bool				Get_Flag(FlagsType flag)					{ return ((Flags & flag) == flag); }
	void				Set_Flag(FlagsType flag,bool onoff)		{ (onoff ? Flags |= flag : Flags &= ~flag); }
	float				Get_Spring_Constant(void)					{ return SpringConstant; }
	void				Set_Spring_Constant(float k)				{ SpringConstant = k; }
	float				Get_Damping_Coefficient(void)				{ return DampingCoefficient; }
	void				Set_Damping_Coefficient(float k)			{ DampingCoefficient = k; }
	float				Get_Spring_Length(void)						{ return SpringLength; }
	void				Set_Spring_Length(float len)				{ SpringLength = len; }

	/*
	** Inputs from the parent object 
	*/
	float				Get_Steering_Angle(void)					{ return SteeringAngle; }
	void				Set_Steering_Angle(float angle)			{ SteeringAngle = angle; }

	/*
	** Accessors to state variables
	*/
	void				Get_Wheel_Position(Vector3 * set_pos)	{ WheelTM.Get_Translation(set_pos); }
	int				Get_Contact_Surface(void) const			{ return ContactSurface; }
	const Vector3 &Get_Contact_Point(void) const				{ return Contact; }
	const Vector3 &Get_Contact_Normal(void) const			{ return Normal; }
	const Matrix3D&Get_Wheel_Transform(void) const			{ return WheelTM; }
	virtual float	Get_Slip_Factor(void) const				{ return 1.0f; }
	virtual float	Get_Rotation_Delta(void) const			{ return 0.0f; }

	/*
	** Physics processing
	*/
	virtual void	Compute_Force_And_Torque(Vector3 * force,Vector3 * torque)		= 0;
	virtual void	Update_Model(void);

	virtual void	Non_Physical_Update(float suspension_fraction,float rotation);

protected:

	/*
	** Internal functions
	*/
	void				Intersect_Spring(void);
	void				Non_Physical_Intersect_Spring(float suspension_fraction);
	void				Translate_Wheel(RenderObjClass * model);
	void				Translate_Wheel_On_Axis(RenderObjClass * model);
	void				Rotate_Fork(RenderObjClass * model);


	/*
	** Properties
	*/
	VehiclePhysClass * Parent;					// Vehicle I am attached to.
	int				Flags;						// bit-field of flags used by this and derived classes
	int				PositionBone;				// Bone index of the position bone (wheel coordinate system)
	int				ForkBone;					// Bone index for the "fork" constraint (if not -1, we use fork constraints)
	int				AxisBone;					// Bone index for the translation axis bone (if not -1, we translate on its z-axis instead)
	
	Matrix3D			ObjWheelTM;					// Wheel coordinate system in object-space

	float				SpringConstant;			// spring
	float				DampingCoefficient;		// shock absorber
	float				SpringLength;				// unstretched/compressed length

	/*
	** Inputs
	*/
	float				SteeringAngle;				// current steering angle

	/*
	** State
	*/
	Vector3			SuspensionForce;			// current suspension force
	Matrix3D			WheelTM;						// current wheel coordinate system (positioned at contact point)
	Vector3			WheelP0;						// current position of the top of the spring
	Vector3			Contact;						// current contact point (only valid if INCONTACT is true)
	Vector3			Normal;						// current contact normal (only valid if INCONTACT is true)
	int				ContactSurface;			// current contact surface (only valid if INCONTACT is true)

	/*
	** Visual State
	*/
	Vector3			LastPoint;					// last position of the wheel (for rolling)

	/*
	** Constraint variables
	*/
	float				TranslationScale;			
	Vector3			ObjAxis;						// axis used for wheels with an overridden translation axis
	Matrix3D			ObjForkTM;					// these are constants used for "fork" constrained wheels
	float				ForkLength;					// I'm calling the back wheel of a motorcycle a "fork" this
	float				ForkZ;						// is not the best name... Its more like an "arm"
	float				ForkSin0;					// These constants are all used in the equation to determine
	float				ForkCos0;					// how far to rotate the "arm" or "fork" in order for the
	float				ForkA;						// wheel to touch the ground.
	float				ForkB;

	/*
	** The latest known location of the spring end points - this is used to determine if the spring has moved
	*/
	Vector3			SpringEndP1;
	Vector3			SpringEndP0;
private:
	
	// not implemented
	SuspensionElementClass(const SuspensionElementClass & that);
	SuspensionElementClass & operator = (const SuspensionElementClass & that);

};

/**
** WheelClass
** This class adds code which is common to all rolling wheels.  It adds a wheel radius
** and wheel rotation variables.
*/
class WheelClass : public SuspensionElementClass
{
public:
	WheelClass(void);
	virtual ~WheelClass(void);

	virtual void	Init(VehiclePhysClass * obj,int postion_bone,int rotation_bone=-1,int fork_bone=-1,int axis_bone=-1);
	virtual void	Compute_Force_And_Torque(Vector3 * force,Vector3 * torque);

	/*
	** Accessors
	*/
	bool				Is_Drive_Wheel(void)								{ return (Get_Flag(ENGINE)||Get_Flag(LEFT_TRACK)||Get_Flag(RIGHT_TRACK)); }
	float				Get_Slip_Factor(void) const					{ return SlipFactor; }
	float				Get_Rotation_Delta(void) const				{ return RotationDelta; }
	float				Get_Ideal_Drive_Wheel_Angular_Velocity(float max_avel);
	float				Get_Radius(void) const							{ return Radius; }

	/*
	** Inputs
	*/
	float				Get_Axle_Torque(void)						{ return AxleTorque; }
	void				Set_Axle_Torque(float torque)				{ AxleTorque = torque; }

protected:

	/*
	** Component functions for Compute_Force_And_Torque
	*/
	void				Compute_Suspension_Force(const Vector3 & pdot,const Vector3 & local_pdot,Vector3 * suspension_force);
	void				Apply_Forces(Vector3 * force,Vector3 * torque);
	
	/*
	** Derived wheel classes implement this to compute the traction forces
	*/
	virtual void	Compute_Traction_Forces(const Vector3 & local_pdot,float normal_force,float * set_lateral_force,float * set_tractive_force) = 0;

	/*
	** Properties 
	*/
	float				Radius;						// radius of the wheel (for rolling)
	int				RotationBone;				// bone index of the rotation bone
	float				Rotation;					// amount the wheel has rolled.
	float				RotationDelta;				// so we can keep the wheel rolling in the air

	/*
	** Inputs
	*/
	float				AxleTorque;					// engine torque applied to the axle

	/*
	** State
	*/
	Vector3			TractiveFrictionForce;	// current tractive friction force
	Vector3			LateralFrictionForce;	// current lateral friction force
	float				SlipFactor;					// fraction of force "clipped" off to traction circle
	float				IdealAngularVelocity;	// non-slipping angular velocity (IF IN CONTACT)

private:

	//not implemented
	WheelClass(const WheelClass & that);
	const WheelClass & operator = (const WheelClass & that);

};


/**
** WVWheelClass (Wheeled-Vehicle-Wheel)
** This wheel class is used by things like Humvees and Buggys.   
*/
class WVWheelClass : public WheelClass
{
public:
	WVWheelClass(void)						{ }
	virtual ~WVWheelClass(void)			{ }

	virtual void	Update_Model(void);
	virtual void	Non_Physical_Update(float suspension_fraction,float rotation);

protected:

	virtual void	Compute_Traction_Forces(const Vector3 & local_pdot,float normal_force,float * set_lateral_force,float * set_tractive_force);
	void				Roll_Wheel(void);
};


/**
** TrackWheelClass
** This wheel type is used by TrackedVehicleClass.  The differences between
** it and the WVWheelClass are minor, mainly in the logic for how the wheels
** roll and possibly the lack of a traction circle.  
*/
class TrackWheelClass : public WheelClass
{
public:

	TrackWheelClass(void)						{ }
	virtual ~TrackWheelClass(void)			{ }

	virtual void	Update_Model(void);
	virtual void	Non_Physical_Update(float suspension_fraction,float rotation);

protected:

	virtual void	Compute_Traction_Forces(const Vector3 & local_pdot,float normal_force,float * set_lateral_force,float * set_tractive_force);
	void				Roll_Wheel(void);

};


/**
** VTOLWheelClass
** This wheel type is used by the VTOL vehicles.  It simply resists movement in
** all directions and rolls depending on its motion (if it has a WheelC bone...)
*/
class VTOLWheelClass : public WheelClass
{
public:
	VTOLWheelClass(void)						{ }
	virtual ~VTOLWheelClass(void)			{ }

	virtual void	Update_Model(void);
	virtual void	Non_Physical_Update(float suspension_fraction,float rotation);

protected:

	virtual void	Compute_Traction_Forces(const Vector3 & local_pdot,float normal_force,float * set_lateral_force,float * set_tractive_force);
	void				Roll_Wheel(void);

};



#endif //WHEEL_H