324 lines
9.6 KiB
C
324 lines
9.6 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// cm_pmove.c - player movement code
|
|
//=======================================================================
|
|
|
|
#include "cm_local.h"
|
|
#include "matrix_lib.h"
|
|
|
|
int characterID;
|
|
uint m_jumpTimer;
|
|
bool m_isStopped;
|
|
bool m_isAirBorne;
|
|
float m_maxStepHigh;
|
|
float m_yawAngle;
|
|
float m_maxTranslation;
|
|
vec3_t m_size;
|
|
vec3_t m_stepContact;
|
|
matrix4x4 m_matrix;
|
|
|
|
// all of the locals will be zeroed before each
|
|
// pmove, just to make damn sure we don't have
|
|
// any differences when running on client or server
|
|
typedef struct
|
|
{
|
|
entity_state_t *state;
|
|
usercmd_t *cmd;
|
|
physbody_t *body;
|
|
|
|
vec3_t origin;
|
|
vec3_t velocity;
|
|
vec3_t previous_origin;
|
|
vec3_t previous_velocity;
|
|
int previous_waterlevel;
|
|
|
|
vec3_t movedir; // already aligned by world
|
|
vec3_t forward, right, up;
|
|
float frametime;
|
|
int msec;
|
|
|
|
bool walking;
|
|
bool onladder;
|
|
trace_t groundtrace;
|
|
float impact_speed;
|
|
|
|
csurface_t *groundsurface;
|
|
cplane_t groundplane;
|
|
int groundcontents;
|
|
|
|
|
|
} pml_t;
|
|
|
|
pml_t pml;
|
|
|
|
/*
|
|
================
|
|
CM_UpdateViewAngles
|
|
|
|
This can be used as another entry point when only the viewangles
|
|
are being updated isntead of a full move
|
|
================
|
|
*/
|
|
void CM_UpdateViewAngles( entity_state_t *state, const usercmd_t *cmd )
|
|
{
|
|
short temp;
|
|
int i;
|
|
|
|
if( state->flags & FL_FROZEN )
|
|
{
|
|
return; // no view changes at all
|
|
}
|
|
if( state->health <= 0 )
|
|
{
|
|
return; // no view changes at all
|
|
}
|
|
if( state->ed_flags & ESF_NO_PREDICTION )
|
|
{
|
|
state->viewangles[YAW] = cmd->angles[YAW] - state->delta_angles[YAW];
|
|
state->viewangles[PITCH] = 0;
|
|
state->viewangles[ROLL] = 0;
|
|
}
|
|
|
|
// circularly clamp the angles with deltas
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
temp = cmd->angles[i] + state->delta_angles[i];
|
|
state->viewangles[i] = temp;
|
|
}
|
|
|
|
// don't let the player look up or down more than 90 degrees
|
|
if( state->viewangles[PITCH] > 89 && state->viewangles[PITCH] < 180 )
|
|
state->viewangles[PITCH] = 89;
|
|
else if( state->viewangles[PITCH] < 271 && state->viewangles[PITCH] >= 180 )
|
|
state->viewangles[PITCH] = 271;
|
|
}
|
|
|
|
|
|
void CM_CmdUpdateForce( void )
|
|
{
|
|
float fmove, smove;
|
|
int i;
|
|
|
|
fmove = pml.cmd->forwardmove;
|
|
smove = pml.cmd->sidemove;
|
|
|
|
// project moves down to flat plane
|
|
pml.forward[2] = pml.right[2] = 0;
|
|
VectorNormalize( pml.forward );
|
|
VectorNormalize( pml.right );
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
pml.movedir[i] = pml.forward[i] * fmove + pml.right[i] * smove;
|
|
|
|
ConvertDirectionToPhysic( pml.movedir );
|
|
|
|
if( pml.cmd->upmove > 0.0f )
|
|
{
|
|
m_jumpTimer = 4;
|
|
}
|
|
}
|
|
|
|
void CM_ServerMove( entity_state_t *state, usercmd_t *cmd, physbody_t *body )
|
|
{
|
|
float mass, Ixx, Iyy, Izz, dist, floor;
|
|
float deltaHeight, steerAngle, accelY, timestepInv;
|
|
vec3_t force, omega, torque, heading, velocity, step;
|
|
matrix4x4 matrix, collisionPadding, transpose;
|
|
|
|
pml.state = state;
|
|
pml.body = body;
|
|
pml.cmd = cmd;
|
|
|
|
CM_UpdateViewAngles( state, cmd );
|
|
AngleVectors( pml.state->viewangles, pml.forward, pml.right, pml.up );
|
|
CM_CmdUpdateForce(); // get movement direction
|
|
|
|
// Get the current world timestep
|
|
pml.frametime = NewtonGetTimeStep( gWorld );
|
|
timestepInv = 1.0f / pml.frametime;
|
|
|
|
// get the character mass
|
|
NewtonBodyGetMassMatrix( pml.body, &mass, &Ixx, &Iyy, &Izz );
|
|
|
|
// apply the gravity force, cheat a little with the character gravity
|
|
VectorSet( force, 0.0f, mass * -9.8f, 0.0f );
|
|
|
|
// Get the velocity vector
|
|
NewtonBodyGetVelocity( pml.body, &velocity[0] );
|
|
|
|
// determine if the character have to be snap to the ground
|
|
NewtonBodyGetMatrix( pml.body, &matrix[0][0] );
|
|
|
|
// if the floor is with in reach then the character must be snap to the ground
|
|
// the max allow distance for snapping i 0.25 of a meter
|
|
if( m_isAirBorne && !m_jumpTimer )
|
|
{
|
|
floor = CM_FindFloor( matrix[3], m_size[2] + 0.25f );
|
|
deltaHeight = ( matrix[3][1] - m_size[2] ) - floor;
|
|
|
|
if((deltaHeight < (0.25f - 0.001f)) && (deltaHeight > 0.01f))
|
|
{
|
|
// snap to floor only if the floor is lower than the character feet
|
|
accelY = - (deltaHeight * timestepInv + velocity[1]) * timestepInv;
|
|
force[1] = mass * accelY;
|
|
}
|
|
}
|
|
else if( m_jumpTimer == 4 )
|
|
{
|
|
vec3_t veloc = { 0.0f, 0.3f, 0.0f };
|
|
NewtonAddBodyImpulse( pml.body, &veloc[0], &matrix[3][0] );
|
|
}
|
|
|
|
m_jumpTimer = m_jumpTimer ? m_jumpTimer - 1 : 0;
|
|
|
|
{
|
|
float speed_factor;
|
|
vec3_t tmp1, tmp2, result;
|
|
|
|
speed_factor = sqrt( DotProduct( pml.movedir, pml.movedir ) + 1.0e-6f );
|
|
VectorScale( pml.movedir, 1.0f / speed_factor, heading );
|
|
|
|
VectorScale( heading, mass * 20.0f, tmp1 );
|
|
VectorScale( heading, 2.0f * DotProduct( velocity, heading ), tmp2 );
|
|
|
|
VectorSubtract( tmp1, tmp2, result );
|
|
VectorAdd( force, result, force );
|
|
NewtonBodySetForce( pml.body, &force[0] );
|
|
|
|
VectorScale( force, pml.frametime / mass, tmp1 );
|
|
VectorAdd( tmp1, velocity, step );
|
|
VectorScale( step, pml.frametime, step );
|
|
}
|
|
|
|
VectorSet(step, DotProduct(step,cm.matrix[0]),DotProduct(step,cm.matrix[1]),DotProduct(step,cm.matrix[2]));
|
|
Matrix4x4_LoadIdentity( collisionPadding );
|
|
|
|
step[1] = 0.0f;
|
|
|
|
dist = DotProduct( step, step );
|
|
|
|
if( dist > m_maxTranslation * m_maxTranslation )
|
|
{
|
|
// when the velocity is high enough that can miss collision we will enlarge the collision
|
|
// long the vector velocity
|
|
dist = sqrt( dist );
|
|
VectorScale( step, 1.0f / dist, step );
|
|
|
|
// make a rotation matrix that will align the velocity vector with the front vector
|
|
collisionPadding[0][0] = step[0];
|
|
collisionPadding[0][2] = -step[2];
|
|
collisionPadding[2][0] = step[2];
|
|
collisionPadding[2][2] = step[0];
|
|
|
|
// get the transpose of the matrix
|
|
Matrix4x4_Transpose( transpose, collisionPadding );
|
|
VectorScale( transpose[0], dist / m_maxTranslation, transpose[0] ); // scale factor
|
|
|
|
// calculate and oblique scale matrix by using a similar transformation matrix of the for, R'* S * R
|
|
Matrix4x4_Concat( collisionPadding, collisionPadding, transpose );
|
|
}
|
|
|
|
// set the collision modifierMatrix;
|
|
//NewtonConvexHullModifierSetMatrix( NewtonBodyGetCollision(pm->body), &collisionPadding[0][0]);
|
|
|
|
steerAngle = asin( bound( -1.0f, pml.forward[2], 1.0f ));
|
|
|
|
// calculate the torque vector
|
|
NewtonBodyGetOmega( pml.body, &omega[0]);
|
|
|
|
VectorSet( torque, 0.0f, 0.5f * Iyy * (steerAngle * timestepInv - omega[1] ) * timestepInv, 0.0f );
|
|
NewtonBodySetTorque( pml.body, &torque[0] );
|
|
|
|
|
|
// assume the character is on the air. this variable will be set to false if the contact detect
|
|
// the character is landed
|
|
m_isAirBorne = true;
|
|
VectorSet( m_stepContact, 0.0f, -m_size[2], 0.0f );
|
|
|
|
NewtonUpVectorSetPin( cms.upVector, &vec3_up[0] );
|
|
}
|
|
|
|
void CM_ClientMove( entity_state_t *state, usercmd_t *cmd, physbody_t *body )
|
|
{
|
|
|
|
}
|
|
|
|
physbody_t *Phys_CreatePlayer( edict_t *ed, cmodel_t *mod, const vec3_t origin, const matrix3x3 matrix )
|
|
{
|
|
NewtonCollision *col, *hull;
|
|
NewtonBody *body;
|
|
|
|
matrix4x4 trans;
|
|
vec3_t radius, mins, maxs, upDirection;
|
|
|
|
if( !cm_physics_model->integer )
|
|
return NULL;
|
|
|
|
// setup matrixes
|
|
Matrix4x4_LoadIdentity( trans );
|
|
|
|
if( mod )
|
|
{
|
|
// player m_size
|
|
VectorCopy( mod->mins, mins );
|
|
VectorCopy( mod->maxs, maxs );
|
|
VectorSubtract( maxs, mins, m_size );
|
|
VectorScale( m_size, 0.5f, radius );
|
|
}
|
|
ConvertDimensionToPhysic( m_size );
|
|
ConvertDimensionToPhysic( radius );
|
|
|
|
VectorSet( m_stepContact, 0.0f, -m_size[2], 0.0f );
|
|
m_maxTranslation = m_size[0] * 0.25f;
|
|
m_maxStepHigh = -m_size[2] * 0.5f;
|
|
|
|
VectorCopy( origin, trans[3] ); // copy position only
|
|
ConvertPositionToPhysic( trans[3] );
|
|
|
|
trans[3][1] = CM_FindFloor( trans[3], 32768 ) + radius[2]; // merge floor position
|
|
|
|
// place a sphere at the center
|
|
col = NewtonCreateSphere( gWorld, radius[0], radius[2], radius[1], NULL );
|
|
|
|
// wrap the character collision under a transform, modifier for tunneling trught walls avoidance
|
|
hull = NewtonCreateConvexHullModifier( gWorld, col );
|
|
NewtonReleaseCollision( gWorld, col );
|
|
body = NewtonCreateBody( gWorld, hull ); // create the rigid body
|
|
NewtonBodySetAutoFreeze( body, 0 ); // disable auto freeze management for the player
|
|
NewtonWorldUnfreezeBody( gWorld, body ); // keep the player always active
|
|
|
|
// reset angular and linear damping
|
|
NewtonBodySetLinearDamping( body, 0.0f );
|
|
NewtonBodySetAngularDamping( body, vec3_origin );
|
|
NewtonBodySetMaterialGroupID( body, characterID );// set material Id for this object
|
|
|
|
// setup generic callback to engine.dll
|
|
NewtonBodySetUserData( body, ed );
|
|
NewtonBodySetTransformCallback( body, Callback_ApplyTransform );
|
|
NewtonBodySetForceAndTorqueCallback( body, Callback_PmoveApplyForce );
|
|
NewtonBodySetMassMatrix( body, 20.0f, m_size[0], m_size[1], m_size[2] ); // 20 kg
|
|
NewtonBodySetMatrix(body, &trans[0][0] );// origin
|
|
|
|
// release the collision geometry when not need it
|
|
NewtonReleaseCollision( gWorld, hull );
|
|
|
|
// add and up vector constraint to help in keeping the body upright
|
|
VectorSet( upDirection, 0.0f, 1.0f, 0.0f );
|
|
cms.upVector = NewtonConstraintCreateUpVector( gWorld, &upDirection[0], body );
|
|
NewtonBodySetContinuousCollisionMode( body, 1 );
|
|
|
|
return (physbody_t *)body;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
PMOVE ENTRY POINT
|
|
===============================================================================
|
|
*/
|
|
void CM_PlayerMove( entity_state_t *state, usercmd_t *cmd, physbody_t *body, bool clientmove )
|
|
{
|
|
if( !cm_physics_model->integer ) return;
|
|
|
|
if( clientmove ) CM_ClientMove( state, cmd, body );
|
|
else CM_ServerMove( state, cmd, body );
|
|
} |