In order to integrate the RK4 physics that Errol wrote for us, and that I'm writing a scheduler for,
we're going to have to refactor the physics out of class Unit.
The idea is this:
Currently, class Unit is gargantuous; humongous; unmanageably big. It needs to be broken down and
its separate concerns separated; but we got no hero to do this. A more modest approach is to take
one piece out of it at a time, as needed. Right now we need to take the physics out, and have a class
that represents all variables and functions related to unit physics, and nothing else.
I'm suggesting the name "phys_unit" for this class.
My current plan is to allow for up to a maximum of 1024 phys_unit objects at run-time. The number
is not set in stone, though; but what I suggest BE set in stone is that we make a fixed size allocator,
--a fast, array-allocator--, so that there are no slow calls to new/malloc all over the place. Thus, if
we decide we'd better go for 2048 that's fine; but we should decide off-line what the maximum is,
rather than leave it to the runtime to decide. But, assuming 1024, for now, we got a fixed size array
allocator, very fast. Then...
There will be "frequency bins" for simulation, that will simulate units under their control at various
frequencies. The number of bins is fixed to 8, and the frequencies are:
16Hz, 8Hz, 4Hz, 2Hz, 1Hz, 0.5Hz, 0.25Hz and 0.125Hz.
Each bin has a bit array, of 1024 bits, each bit being a flag that signals that the unit at its offset in
the array allocator is simulated by this frequency bin.
Anyways, I got side-tracked to discussing the simulation scheduler...
The matter at hand is this phys_unit class that we want to separate from Unit, and that will be
allocated in this fixed size (1024) allocator. What I've done for now is make a first visual scan of Unit
in unit_generic.h, and copy to this class anything that seems relevant to physics. But it's rather big...
Code: Select all
#ifndef __PHYS_UNIT_H__
#define __PHYS_UNIT_H__
//For now, this is just an extract from class Unit (from /src/cmd/unit_generic.h) which may have
//to be further weeded out. It contains things that may be needed for physics considerations, and
//was meant to be physics-only, but currently spans various categories:
// * Propulsion: Engines, energy, thrusters, thrust and rotation limits, etceteras variables.
// * Forces and force resolving variables and function declarations.
// * Physics: Mass, moment of inertia, acceleration, position, etceteras.
// * Weapon hit stuff, which may be relevant if we want reactions to hits.
// * Some, but not all stuff relating to subunits, parent, etceteras.
// * Some collisions-relate stuff.
class physics_unit
{
public:
class Limits
{
public:
//max ypr--both pos/neg are symmetrical
float yaw;
float pitch;
float roll;
//side-side engine thrust max
float lateral;
//vertical engine thrust max
float vertical;
//forward engine thrust max
float forward;
//reverse engine thrust max
float retro;
//after burner acceleration max
float afterburn;
//the vector denoting the "front" of the turret cone!
Vector structurelimits;
//the minimum dot that the current heading can have with the structurelimit
float limitmin;
Limits() : yaw( 0 )
, pitch( 0 )
, roll( 0 )
, lateral( 0 )
, vertical( 0 )
, forward( 0 )
, retro( 0 )
, afterburn( 0 )
, structurelimits( 0, 0, 0 )
, limitmin( 0 ) {}
}
limits;
//Takes out of the collide table for this system.
void RemoveFromSystem();
void RequestPhysics(); //Requeues the unit so that it is simulated ASAP
bool InCorrectStarSystem( StarSystem* );
virtual int nummesh() const
{
return ( (int) meshdata.size() )-1;
}
//The owner of this unit. This may not collide with owner or units owned by owner. Do not dereference (may be dead pointer)
void *owner; //void ensures that it won't be referenced by accident
//The number of frames ahead this was put in the simulation queue
unsigned int sim_atom_multiplier;
//The number of frames ahead this is predicted to be scheduled in the next scheduling round
unsigned int predicted_priority;
//The previous state in last physics frame to interpolate within
Transformation prev_physical_state;
//The state of the current physics frame to interpolate within
Transformation curr_physical_state;
//When will physical simulation occur
unsigned int cur_sim_queue_slot;
//Used with subunit scheduling, to avoid the complex ickiness of having to synchronize scattered slots
unsigned int last_processed_sqs;
//Whether or not to schedule subunits for deferred physics processing - if not, they're processed at the same time the parent unit is being processed
bool do_subunit_scheduling;
//Does this unit require special scheduling?
enum schedulepriorityenum {scheduleDefault, scheduleAField, scheduleRoid}
schedule_priority;
//number of meshes (each with separate texture) this unit has
//The cumulative (incl subunits parents' transformation)
Matrix cumulative_transformation_matrix;
//The cumulative (incl subunits parents' transformation)
Transformation cumulative_transformation;
//The velocity this unit has in World Space
Vector cumulative_velocity;
//The force applied from outside accrued over the whole physics frame
Vector NetForce;
//The force applied by internal objects (thrusters)
Vector NetLocalForce;
//The torque applied from outside objects
Vector NetTorque;
//The torque applied from internal objects
Vector NetLocalTorque;
//the current velocities in LOCAL space (not world space)
Vector AngularVelocity;
Vector Velocity;
//mass of this unit (may change with cargo)
float Mass;
protected:
//are shields tight to the hull. zero means bubble
float shieldtight;
//fuel of this unit
float fuel;
float afterburnenergy; //short fix
int afterburntype; //0--energy, 1--fuel
//-1 means it is off. -2 means it doesn't exist. otherwise it's engaged to destination (positive number)
//Moment of intertia of this unit
float Momentofinertia;
Vector SavedAccel;
//What's the size of this unit
float rSize() const
{
return radial_size;
}
//Returns the current world space position
QVector Position() const
{
return cumulative_transformation.position;
}
const Matrix& GetTransformation() const
{
return cumulative_transformation_matrix;
}
//Returns the unit-space position
QVector LocalPosition() const
{
return curr_physical_state.position;
}
//Sets the unit-space position
void SetPosition( const QVector &pos );
///Sets the cumulative transformation matrix's position...for setting up to be out in the middle of nowhere
void SetCurPosition( const QVector &pos )
{
curr_physical_state.position = pos;
}
void SetPosAndCumPos( const QVector &pos )
{
SetPosition( pos );
cumulative_transformation_matrix.p = pos;
cumulative_transformation.position = pos;
}
//Rotates about the axis
void Rotate( const Vector &axis );
/**
* Fire engine takes a unit vector for direction
* and how fast the fuel speed and mass coming out are
*/
/*unit vector... might default to "r"*/
void FireEngines( const Vector &Direction, float FuelSpeed, float FMass );
//applies a force for the whole gameturn upon the center of mass
void ApplyForce( const Vector &Vforce );
//applies a force for the whole gameturn upon the center of mass, using local coordinates
void ApplyLocalForce( const Vector &Vforce );
//applies a force that is multipled by the mass of the ship
void Accelerate( const Vector &Vforce );
//Apply a torque in world level coords
void ApplyTorque( const Vector &Vforce, const QVector &Location );
//Applies a torque in local level coordinates
void ApplyLocalTorque( const Vector &Vforce, const Vector &Location );
//usually from thrusters remember if I have 2 balanced thrusters I should multiply their effect by 2 :)
void ApplyBalancedLocalTorque( const Vector &Vforce, const Vector &Location );
//convenient shortcut to applying torques with vector and position
void ApplyLocalTorque( const Vector &torque );
//Applies damage to the local area given by pnt
float ApplyLocalDamage( const Vector &pnt,
const Vector &normal,
float amt,
Unit *affectedSubUnit,
const GFXColor&,
float phasedamage = 0 );
//Applies damage from network data
void ApplyNetDamage( Vector &pnt, Vector &normal, float amt, float ppercentage, float spercentage, GFXColor &color );
//Applies damage to the pre-transformed area of the ship
void ApplyDamage( const Vector &pnt,
const Vector &normal,
float amt,
Unit *affectedSubUnit,
const GFXColor&,
void *ownerDoNotDereference,
float phasedamage = 0 );
//Clamps thrust to the limits struct
Vector ClampThrust( const Vector &thrust, bool afterburn );
//Takes a unit vector for direction of thrust and scales to limits
Vector MaxThrust( const Vector &thrust );
//Thrusts by ammt and clamps accordingly (afterburn or not)
virtual void Thrust( const Vector &amt, bool afterburn = false );
//Applies lateral thrust
void LateralThrust( float amt );
//Applies vertical thrust
void VerticalThrust( float amt );
//Applies forward thrust
void LongitudinalThrust( float amt );
//Clamps desired velocity to computer set limits
Vector ClampVelocity( const Vector &velocity, const bool afterburn );
//Clamps desired angular velocity to computer set limits
Vector ClampAngVel( const Vector &vel );
//Clamps desired torque to computer set limits of thrusters
Vector ClampTorque( const Vector &torque );
//scales unit size torque to limits in that direction
Vector MaxTorque( const Vector &torque );
//Applies a yaw of amt
void YawTorque( float amt );
//Applies a pitch of amt
void PitchTorque( float amt );
//Applies a roll of amt
void RollTorque( float amt );
//executes a repair if the repair bot is up to it
void Repair();
//Updates physics given unit space transformations and if this is the last physics frame in the current gfx frame
//Not needed here, so only in NetUnit and Unit classes
void UpdatePhysics( const Transformation &trans,
const Matrix &transmat,
const Vector &CumulativeVelocity,
bool ResolveLast,
UnitCollection *uc,
Unit *superunit );
virtual void UpdatePhysics2( const Transformation &trans,
const Transformation &old_physical_state,
const Vector &accel,
float difficulty,
const Matrix &transmat,
const Vector &CumulativeVelocity,
bool ResolveLast,
UnitCollection *uc = NULL );
//Useful if you want to override subunit processing, but not self-processing (Asteroids, people?)
virtual void UpdateSubunitPhysics( const Transformation &trans,
const Matrix &transmat,
const Vector &CumulativeVelocity,
bool ResolveLast,
UnitCollection *uc,
Unit *superunit );
//A helper for those who override UpdateSubunitPhysics - Process one subunit (also, an easier way of overriding subunit processing uniformly)
virtual void UpdateSubunitPhysics( Unit *subunit,
const Transformation &trans,
const Matrix &transmat,
const Vector &CumulativeVelocity,
bool ResolveLast,
UnitCollection *uc,
Unit *superunit );
void AddVelocity( float difficulty );
//Resolves forces of given unit on a physics frame
virtual Vector ResolveForces( const Transformation&, const Matrix& );
//Returns the pqr oritnattion of the unit in world space
void SetOrientation( QVector q, QVector r );
void SetOrientation( Quaternion Q );
void SetOrientation( QVector p, QVector q, QVector r );
void GetOrientation( Vector &p, Vector &q, Vector &r ) const;
Vector GetNetAcceleration();
//acceleration, retrieved from NetForce - not stable (partial during simulation), use GetAcceleration()
Vector GetAcceleration() const
{
return SavedAccel;
}
//acceleration, stable over the simulation
float GetMaxAccelerationInDirectionOf( const Vector &ref, bool afterburn ) const;
//Transforms a orientation vector Up a coordinate level. Does not take position into account
Vector UpCoordinateLevel( const Vector &v ) const;
//Transforms a orientation vector Down a coordinate level. Does not take position into account
Vector DownCoordinateLevel( const Vector &v ) const;
//Transforms a orientation vector from world space to local space. Does not take position into account
Vector ToLocalCoordinates( const Vector &v ) const;
//Transforms a orientation vector to world space. Does not take position into account
Vector ToWorldCoordinates( const Vector &v ) const;
//Returns unit-space ang velocity
const Vector& GetAngularVelocity() const
{
return AngularVelocity;
}
//Return unit-space velocity
const Vector& GetVelocity() const
{
return cumulative_velocity;
}
Vector GetWarpVelocity() const;
void SetVelocity( const Vector& );
void SetAngularVelocity( const Vector& );
float GetMoment() const
{
return Momentofinertia+fuel;
}
float GetMass() const
{
return Mass+fuel;
}
//returns the ammt of elasticity of collisions with this unit
float GetElasticity();
//returns given limits (should not be necessary with clamping functions)
const Limits& Limits() const
{
return limits;
}
//Sets if forces should resolve on this unit or not
void SetResolveForces( bool );
};
#endif
I think that thrusting concerns fall somewhere between physics, AI, user controls (fly by wire), and autopilot.
Sub-unit/parent concerns are a huge concern of physics, but they are also a huge concern of other parts of
the code. Perhaps Unit should embody a Composite pattern.
Collisions stuff...
This is a biggie: Thing is, collisions are really close to physics. I can't imagine a type of unit that would have
its physics simulated but NOT be part of the collision system, or viceversa. Secondly, the collisions scheduler
will be VERY different (algorithmically) from the physics scheduler... (long story) but it can share the parts
of the code related to using a 1024 bit array of flags IF our collision data structures use the same array
allocator as phys_unit; so I'm very tempted to upgrade phys_unit to phys_and_collision_unit.
Anyways, I only posted this here to encourage exchange of ideas on how to best do this surgery.