This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/physic/cm_pmove.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 );
}