openmohaa/code/fgame/g_vmove.cpp

637 lines
14 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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 2 of the License,
or (at your option) any later version.
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// g_vmove.cpp : Vehicle movement code
//
#include "g_local.h"
#include "entity.h"
#include "movegrid.h"
typedef struct {
qboolean validGroundTrace;
float previous_origin[ 3 ];
float previous_velocity[ 3 ];
} vml_t;
vmove_t *vm;
vml_t vml;
void VM_ClipVelocity
(
float *in,
float *normal,
float *out,
float overbounce
)
{
float backoff;
float dir_z;
float normal2[ 3 ];
if( normal[ 2 ] >= 0.70f )
{
if( in[ 0 ] == 0.0f && in[ 1 ] == 0.0f )
{
VectorClear( out );
return;
}
normal2[ 0 ] = in[ 0 ] + DotProduct2D( in, normal );
normal2[ 1 ] = in[ 1 ] + DotProduct2D( in, normal );
normal2[ 2 ] = normal[ 2 ] * DotProduct2D( in, in );
VectorNormalize( normal2 );
dir_z = -normal2[ 2 ];
out[ 0 ] = in[ 0 ];
out[ 1 ] = in[ 1 ];
out[ 2 ] = DotProduct2D( in, normal2 ) / dir_z;
}
else
{
backoff = DotProduct( in, normal );
if( backoff < 0 )
backoff *= overbounce;
else
backoff /= overbounce;
out[ 0 ] = in[ 0 ] - normal[ 0 ] * backoff;
out[ 1 ] = in[ 1 ] - normal[ 1 ] * backoff;
out[ 2 ] = in[ 2 ] - normal[ 2 ] * backoff;
}
}
void VM_AddTouchEnt
(
int entityNum
)
{
int i;
if( entityNum == ENTITYNUM_NONE || entityNum == ENTITYNUM_WORLD )
{
return;
}
if( vm->numtouch > 32 )
{
return;
}
// see if it is already added
for( i = 0; i < vm->numtouch; i++ )
{
if( vm->touchents[ i ] == entityNum )
return;
}
// add it
vm->touchents[ vm->numtouch ] = entityNum;
vm->numtouch++;
}
qboolean VM_SlideMove
(
qboolean gravity
)
{
int bumpcount;
vec3_t dir;
float d;
int numplanes;
vec3_t planes[ 5 ];
vec3_t clipVelocity;
int i;
int j;
int k;
trace_t trace;
vec3_t end;
float time_left;
if( gravity )
{
vm->vs->velocity[ 2 ] = vm->vs->velocity[ 2 ] - vm->frametime * sv_gravity->integer;
if( vm->vs->groundPlane )
VM_ClipVelocity( vm->vs->velocity, vm->vs->groundTrace.plane.normal, vm->vs->velocity, OVERCLIP );
}
time_left = vm->frametime;
if( vm->vs->groundPlane ) {
numplanes = 1;
VectorCopy( vm->vs->groundTrace.plane.normal, planes[ 0 ] );
}
else {
numplanes = 0;
}
// never turn against original velocity
VectorNormalize2( vm->vs->velocity, planes[ numplanes ] );
numplanes++;
for( bumpcount = 0; bumpcount < 4; bumpcount++ )
{
// calculate position we are trying to move to
VectorMA( vm->vs->origin, time_left, vm->vs->velocity, end );
// see if we can make it there
gi.Trace( &trace, vm->vs->origin, vm->mins, vm->maxs, end, vm->vs->entityNum, vm->tracemask, qtrue, qfalse );
if( trace.allsolid )
break;
if( trace.fraction > 0 ) {
// actually covered some distance
VectorCopy( trace.endpos, vm->vs->origin );
}
if( trace.fraction == 1 )
return bumpcount != 0;
memcpy( &vm->vs->groundTrace, &trace, sizeof( vm->vs->groundTrace ) );
vml.validGroundTrace = true;
// save entity for contact
VM_AddTouchEnt( trace.entityNum );
time_left -= time_left * trace.fraction;
if( numplanes >= MAX_CLIP_PLANES )
{
VectorClear( vm->vs->velocity );
return qtrue;
}
//
// if this is the same plane we hit before, nudge velocity
// out along it, which fixes some epsilon issues with
// non-axial planes
//
for( i = 0; i < numplanes; i++ )
{
if( DotProduct( trace.plane.normal, planes[ i ] ) > 0.99 )
{
VectorAdd( trace.plane.normal, vm->vs->velocity, vm->vs->velocity );
break;
}
}
if( i >= numplanes )
{
//
// modify velocity so it parallels all of the clip planes
//
// find a plane that it enters
for( i = 0; i < numplanes; i++ )
{
if( DotProduct( vm->vs->velocity, planes[ i ] ) >= 0.1 ) {
continue; // move doesn't interact with the plane
}
// slide along the plane
VM_ClipVelocity( vm->vs->velocity, planes[ i ], clipVelocity, OVERCLIP );
// see if there is a second plane that the new move enters
for( j = 0; j < numplanes; j++ )
{
if( j == i ) {
continue;
}
// slide along the plane
VM_ClipVelocity( vm->vs->velocity, planes[ j ], clipVelocity, OVERCLIP );
if( DotProduct( clipVelocity, planes[ j ] ) >= 0.0f ) {
continue; // move doesn't interact with the plane
}
// slide the original velocity along the crease
CrossProduct( planes[ i ], planes[ j ], dir );
VectorNormalize( dir );
d = DotProduct( dir, vm->vs->velocity );
VectorScale( dir, d, clipVelocity );
// see if there is a third plane the the new move enters
for( k = 0; k < numplanes; k++ )
{
if( k == i || k == j ) {
continue;
}
if( DotProduct( clipVelocity, planes[ k ] ) >= 0.1f ) {
continue; // move doesn't interact with the plane
}
// stop dead at a tripple plane interaction
VectorClear( vm->vs->velocity );
return qtrue;
}
}
// if we have fixed all interactions, try another move
VectorCopy( clipVelocity, vm->vs->velocity );
break;
}
}
}
if( vm->vs->velocity[ 0 ] || vm->vs->velocity[ 1 ] )
{
if( vm->vs->groundPlane )
{
VectorCopy( vm->vs->velocity, dir );
VectorNegate( dir, dir );
VectorNormalize( dir );
VM_AddTouchEnt( trace.entityNum );
VectorAdd( vm->vs->obstacle_normal, dir, vm->vs->obstacle_normal );
}
VectorClear( vm->vs->velocity );
return true;
}
vm->vs->velocity[ 2 ] = 0;
return false;
}
static void VM_GroundTraceInternal2( void );
void VM_GroundTraceInternal
(
void
)
{
VM_GroundTraceInternal2();
VM_AddTouchEnt( vm->vs->groundTrace.entityNum );
}
void VM_GroundTraceInternal2
(
void
)
{
if( vm->vs->groundTrace.fraction == 1.0f )
{
vm->vs->groundEntityNum = ENTITYNUM_NONE;
vm->vs->groundPlane = qfalse;
vm->vs->walking = qfalse;
return;
}
if( vm->vs->velocity[ 2 ] > 0.0f )
{
if( DotProduct( vm->vs->velocity, vm->vs->groundTrace.plane.normal ) > 10.0f )
{
vm->vs->groundEntityNum = ENTITYNUM_NONE;
vm->vs->groundPlane = qfalse;
vm->vs->walking = qfalse;
return;
}
}
// slopes that are too steep will not be considered onground
if( vm->vs->groundTrace.plane.normal[ 2 ] < MIN_WALK_NORMAL )
{
vec3_t oldvel;
float d;
VectorCopy( vm->vs->velocity, oldvel );
VectorSet( vm->vs->velocity, 0, 0, -1.0f / vm->frametime );
VM_SlideMove( qfalse );
d = VectorLength( vm->vs->velocity );
VectorCopy( oldvel, vm->vs->velocity );
if( d > ( 0.1f / vm->frametime ) )
{
vm->vs->groundEntityNum = ENTITYNUM_NONE;
vm->vs->groundPlane = qtrue;
vm->vs->walking = qfalse;
return;
}
}
vm->vs->groundPlane = qtrue;
vm->vs->walking = qtrue;
vm->vs->groundEntityNum = vm->vs->groundTrace.entityNum;
}
void VM_GroundTrace
(
void
)
{
float point[ 3 ];
point[ 0 ] = vm->vs->origin[ 0 ];
point[ 1 ] = vm->vs->origin[ 1 ];
point[ 2 ] = vm->vs->origin[ 2 ] - 0.25f;
gi.Trace( &vm->vs->groundTrace, vm->vs->origin, vm->mins, vm->maxs, point, vm->vs->entityNum, vm->tracemask, qtrue, qfalse );
VM_GroundTraceInternal();
}
void VM_StepSlideMove
(
void
)
{
vec3_t start_o;
vec3_t start_v;
vec3_t nostep_o;
vec3_t nostep_v;
trace_t trace;
qboolean bWasOnGoodGround;
vec3_t up;
vec3_t down;
qboolean start_hit_wall;
vec3_t start_wall_normal;
qboolean first_hit_wall;
vec3_t first_wall_normal;
vec3_t start_hit_origin;
vec3_t first_hit_origin;
trace_t nostep_groundTrace;
VectorCopy( vm->vs->origin, start_o );
VectorCopy( vm->vs->velocity, start_v );
start_hit_wall = vm->vs->hit_obstacle;
VectorCopy( vm->vs->hit_origin, start_hit_origin );
VectorCopy( vm->vs->obstacle_normal, start_wall_normal );
if( VM_SlideMove( qtrue ) == 0 )
{
if( !vml.validGroundTrace )
VM_GroundTrace();
return;
}
VectorCopy( start_o, down );
down[ 2 ] -= STEPSIZE;
gi.Trace( &trace, start_o, vm->mins, vm->maxs, down, vm->vs->entityNum, vm->tracemask, qtrue, qfalse );
VectorSet( up, 0, 0, 1 );
// never step up when you still have up velocity
if( vm->vs->velocity[ 2 ] > 0 && ( trace.fraction == 1.0f ||
DotProduct( trace.plane.normal, up ) < MIN_WALK_NORMAL ) )
{
if( !vml.validGroundTrace )
VM_GroundTrace();
else
VM_GroundTraceInternal();
return;
}
if( vm->vs->groundPlane && vm->vs->groundTrace.plane.normal[ 2 ] >= MIN_WALK_NORMAL )
bWasOnGoodGround = qtrue;
else
bWasOnGoodGround = qfalse;
VectorCopy( vm->vs->origin, nostep_o );
VectorCopy( vm->vs->velocity, nostep_v );
memcpy( &nostep_groundTrace, &vm->vs->groundTrace, sizeof( trace_t ) );
VectorCopy( start_o, vm->vs->origin );
VectorCopy( start_v, vm->vs->velocity );
first_hit_wall = vm->vs->hit_obstacle;
VectorCopy( vm->vs->hit_origin, first_hit_origin );
VectorCopy( vm->vs->obstacle_normal, first_wall_normal );
vm->vs->hit_obstacle = start_hit_wall;
VectorCopy( start_hit_origin, vm->vs->hit_origin );
VectorCopy( start_wall_normal, vm->vs->obstacle_normal );
VM_SlideMove( qtrue );
VectorCopy( vm->vs->origin, down );
down[ 2 ] -= STEPSIZE * 2;
// test the player position if they were a stepheight higher
gi.Trace( &trace, up, vm->mins, vm->maxs, up, vm->vs->entityNum, vm->tracemask, qtrue, qfalse );
if( trace.entityNum > ENTITYNUM_NONE )
{
VectorCopy( nostep_o, vm->vs->origin );
VectorCopy( nostep_v, vm->vs->velocity );
memcpy( &vm->vs->groundTrace, &nostep_groundTrace, sizeof( vm->vs->groundTrace ) );
vm->vs->hit_obstacle = first_hit_wall;
VectorCopy( first_hit_origin, vm->vs->hit_origin );
VectorCopy( first_wall_normal, vm->vs->obstacle_normal );
if( !vml.validGroundTrace )
VM_GroundTrace();
else
VM_GroundTraceInternal();
return;
}
if( !trace.allsolid )
{
memcpy( &vm->vs->groundTrace, &trace, sizeof( vm->vs->groundTrace ) );
vml.validGroundTrace = qtrue;
if( bWasOnGoodGround && trace.fraction && trace.plane.normal[ 2 ] < MIN_WALK_NORMAL )
{
VectorCopy( nostep_o, vm->vs->origin );
VectorCopy( nostep_v, vm->vs->velocity );
if( first_hit_wall )
{
vm->vs->hit_obstacle = first_hit_wall;
VectorCopy( first_hit_origin, vm->vs->hit_origin );
VectorCopy( first_wall_normal, vm->vs->obstacle_normal );
}
VM_GroundTraceInternal();
return;
}
VectorCopy( trace.endpos, vm->vs->origin );
}
if( trace.fraction < 1.0f )
VM_ClipVelocity( vm->vs->velocity, trace.plane.normal, vm->vs->velocity, OVERCLIP );
if( !vml.validGroundTrace )
VM_GroundTrace();
else
VM_GroundTraceInternal();
}
void VM_Friction( void ) {
vec3_t vec;
float *vel;
float speed, newspeed, control;
float drop;
vel = vm->vs->velocity;
VectorCopy( vel, vec );
if( vm->vs->walking )
{
// ignore slope movement
vec[ 2 ] = 0;
}
speed = VectorLength( vec );
if( speed < 1 )
{
// allow sinking underwater
vel[ 0 ] = 0;
vel[ 1 ] = 0;
return;
}
drop = 0;
if( vm->vs->walking )
{
control = ( speed < 50.0f ) ? 50.0f : speed;
// if getting knocked back, no friction
drop += control * 6.0f * vm->frametime;
}
// scale the velocity
newspeed = speed - drop;
if( newspeed < 0 )
{
newspeed = 0;
}
newspeed /= speed;
vel[ 0 ] = vel[ 0 ] * newspeed;
vel[ 1 ] = vel[ 1 ] * newspeed;
vel[ 2 ] = vel[ 2 ] * newspeed;
}
void VM_ClipVelocity2D
(
float *in,
float *normal,
float *out,
float overbounce
)
{
float backoff;
float dir_z;
float normal2[ 3 ];
if( normal[ 2 ] >= 0.70f )
{
if( in[ 0 ] == 0.0f && in[ 1 ] == 0.0f )
{
VectorClear( out );
return;
}
normal2[ 0 ] = in[ 0 ] + DotProduct2D( in, normal );
normal2[ 1 ] = in[ 1 ] + DotProduct2D( in, normal );
normal2[ 2 ] = normal[ 2 ] * DotProduct2D( in, in );
VectorNormalize( normal2 );
dir_z = -normal2[ 2 ];
out[ 0 ] = in[ 0 ];
out[ 1 ] = in[ 1 ];
out[ 2 ] = DotProduct2D( in, normal2 ) / -normal2[ 2 ];
}
else
{
backoff = DotProduct2D( in, normal );
if( backoff < 0 )
backoff *= overbounce;
else
backoff /= overbounce;
out[ 0 ] = in[ 0 ] - normal[ 0 ] * backoff;
out[ 1 ] = in[ 1 ] - normal[ 1 ] * backoff;
out[ 2 ] = -( backoff * normal[ 2 ] );
}
}
void VmoveSingle
(
vmove_t *vmove
)
{
float point[ 3 ];
trace_t trace;
vm = vmove;
vmove->numtouch = 0;
vmove->vs->hit_obstacle = false;
VectorCopy( vec_origin, vmove->vs->obstacle_normal );
memset( &vml, 0, sizeof( vml_t ) );
VectorCopy( vmove->vs->origin, vml.previous_origin );
VectorCopy( vmove->vs->velocity, vml.previous_velocity );
VM_GroundTraceInternal2();
if( vmove->vs->walking )
{
float wishdir[ 3 ];
VM_Friction();
VM_ClipVelocity2D( vm->vs->desired_dir, vm->vs->groundTrace.plane.normal, wishdir, OVERCLIP );
VectorNormalize( wishdir );
vm->vs->velocity[ 0 ] = vm->desired_speed * wishdir[ 0 ];
vm->vs->velocity[ 1 ] = vm->desired_speed * wishdir[ 1 ];
}
else if( vmove->vs->groundPlane )
{
VM_ClipVelocity( vm->vs->velocity, vm->vs->groundTrace.plane.normal, vm->vs->velocity, OVERCLIP );
}
VM_StepSlideMove();
if( !vm->vs->walking && vml.previous_velocity[ 2 ] >= 0.0f && vm->vs->velocity[ 2 ] <= 0.0f )
{
point[ 0 ] = vm->vs->origin[ 0 ];
point[ 1 ] = vm->vs->origin[ 1 ];
point[ 2 ] = vm->vs->origin[ 2 ] - 18.0f;
gi.Trace( &trace, vm->vs->origin, vm->mins, vm->maxs, point, vm->vs->entityNum, vm->tracemask, qtrue, qfalse );
if( trace.fraction < 1.0f && !trace.allsolid )
{
VM_GroundTrace();
return;
}
}
}