/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena 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. Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // // bg_slidemove.c -- part of bg_pmove functionality #include "../qcommon/q_shared.h" #include "bg_public.h" #include "bg_local.h" /* input: origin, velocity, bounds, groundPlane, trace function output: origin, velocity, impacts, stairup boolean */ /* ================== PM_SlideMove Returns qtrue if the velocity was clipped in some way ================== */ #define MAX_CLIP_PLANES 5 qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[ MAX_CLIP_PLANES ]; vec3_t primal_velocity; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; numbumps = 4; VectorCopy( pm->ps->velocity, primal_velocity ); if( gravity ) { VectorCopy( pm->ps->velocity, endVelocity ); endVelocity[ 2 ] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[ 2 ] = ( pm->ps->velocity[ 2 ] + endVelocity[ 2 ] ) * 0.5; primal_velocity[ 2 ] = endVelocity[ 2 ]; if( pml.groundPlane ) { // slide along the ground plane PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } } time_left = pml.frametime; // never turn against the ground plane if( pml.groundPlane ) { numplanes = 1; VectorCopy( pml.groundTrace.plane.normal, planes[ 0 ] ); } else { numplanes = 0; } // never turn against original velocity VectorNormalize2( pm->ps->velocity, planes[ numplanes ] ); numplanes++; for( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) { // calculate position we are trying to move to VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); // see if we can make it there pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask, qtrue, qfalse ); if( trace.allsolid ) { // entity is completely trapped in another solid pm->ps->velocity[ 2 ] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if( trace.fraction > 0 ) { // actually covered some distance VectorCopy( trace.endpos, pm->ps->origin ); } if( trace.fraction == 1 ) { break; // moved the entire distance } if( ( trace.plane.normal[ 2 ] < MIN_WALK_NORMAL ) && ( trace.plane.normal[ 2 ] > 0 ) ) { // treat steep walls as vertical trace.plane.normal[ 2 ] = 0; VectorNormalizeFast( trace.plane.normal ); } // save entity for contact PM_AddTouchEnt( trace.entityNum ); time_left -= time_left * trace.fraction; if( numplanes >= MAX_CLIP_PLANES ) { // this shouldn't really happen VectorClear( pm->ps->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, pm->ps->velocity, pm->ps->velocity ); break; } } if( i < numplanes ) { continue; } VectorCopy( trace.plane.normal, planes[ numplanes ] ); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for( i = 0; i < numplanes; i++ ) { into = DotProduct( pm->ps->velocity, planes[ i ] ); if( into >= 0.1 ) { continue; // move doesn't interact with the plane } // see how hard we are hitting things if( -into > pml.impactSpeed ) { pml.impactSpeed = -into; } // slide along the plane PM_ClipVelocity( pm->ps->velocity, planes[ i ], clipVelocity, OVERCLIP ); // slide along the plane PM_ClipVelocity( endVelocity, planes[ i ], endClipVelocity, OVERCLIP ); // see if there is a second plane that the new move enters for( j = 0; j < numplanes; j++ ) { if( j == i ) { continue; } if( DotProduct( clipVelocity, planes[ j ] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[ j ], clipVelocity, OVERCLIP ); PM_ClipVelocity( endClipVelocity, planes[ j ], endClipVelocity, OVERCLIP ); // see if it goes back into the first clip plane if( DotProduct( clipVelocity, planes[ i ] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct( planes[ i ], planes[ j ], dir ); VectorNormalize( dir ); d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct( planes[ i ], planes[ j ], dir ); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // 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.1 ) { continue; // move doesn't interact with the plane } // stop dead at a tripple plane interaction VectorClear( pm->ps->velocity ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } if( gravity ) { VectorCopy( endVelocity, pm->ps->velocity ); } return ( bumpcount != 0 ); } /* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( qboolean gravity ) { 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; VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); if ( PM_SlideMove( gravity ) == 0 ) { return; // we got exactly where we wanted to go first try } VectorCopy( start_o, down ); down[ 2 ] -= STEPSIZE; pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, qtrue, qfalse ); VectorSet( up, 0, 0, 1 ); // never step up when you still have up velocity if( pm->ps->velocity[ 2 ] > 0 && ( trace.fraction == 1.0f || DotProduct( trace.plane.normal, up ) < MIN_WALK_NORMAL ) ) { return; } if( pml.groundPlane && pml.groundTrace.plane.normal[ 2 ] >= MIN_WALK_NORMAL ) { bWasOnGoodGround = true; } else { bWasOnGoodGround = false; } VectorCopy( start_o, up ); up[ 2 ] += STEPSIZE; // test the player position if they were a stepheight higher pm->trace( &trace, up, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, qtrue, qfalse ); if( trace.allsolid ) { up[ 2 ] -= 9.0f; pm->trace( &trace, up, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, qtrue, qfalse ); if( trace.allsolid ) { return; } } VectorCopy( pm->ps->origin, nostep_o ); VectorCopy( pm->ps->velocity, nostep_v ); // try slidemove from this position VectorCopy( up, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); PM_SlideMove( gravity ); // push down the final amount VectorCopy( pm->ps->origin, down ); down[ 2 ] -= STEPSIZE; pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, qtrue, qfalse ); if( !trace.allsolid ) { if( bWasOnGoodGround && trace.fraction < 1.0 && trace.plane.normal[ 2 ] < MIN_WALK_NORMAL ) { VectorCopy( nostep_o, pm->ps->origin ); VectorCopy( nostep_v, pm->ps->velocity ); return; } VectorCopy( trace.endpos, pm->ps->origin ); } if ( trace.fraction < 1.0f ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } pm->stepped = qtrue; }