1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 //
23 // bg_slidemove.c -- part of bg_pmove functionality
24
25 #include "../qcommon/q_shared.h"
26 #include "bg_public.h"
27 #include "bg_local.h"
28
29 /*
30
31 input: origin, velocity, bounds, groundPlane, trace function
32
33 output: origin, velocity, impacts, stairup boolean
34
35 */
36
37 /*
38 ==================
39 PM_SlideMove
40
41 Returns qtrue if the velocity was clipped in some way
42 ==================
43 */
44 #define MAX_CLIP_PLANES 5
PM_SlideMove(qboolean gravity)45 qboolean PM_SlideMove( qboolean gravity ) {
46 int bumpcount, numbumps;
47 vec3_t dir;
48 float d;
49 int numplanes;
50 vec3_t planes[MAX_CLIP_PLANES];
51 vec3_t primal_velocity;
52 vec3_t clipVelocity;
53 int i, j, k;
54 trace_t trace;
55 vec3_t end;
56 float time_left;
57 float into;
58 vec3_t endVelocity;
59 vec3_t endClipVelocity;
60
61 numbumps = 4;
62
63 VectorCopy (pm->ps->velocity, primal_velocity);
64
65 if ( gravity ) {
66 VectorCopy( pm->ps->velocity, endVelocity );
67 endVelocity[2] -= pm->ps->gravity * pml.frametime;
68 pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5;
69 primal_velocity[2] = endVelocity[2];
70 if ( pml.groundPlane ) {
71 // slide along the ground plane
72 PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
73 pm->ps->velocity, OVERCLIP );
74 }
75 }
76
77 time_left = pml.frametime;
78
79 // never turn against the ground plane
80 if ( pml.groundPlane ) {
81 numplanes = 1;
82 VectorCopy( pml.groundTrace.plane.normal, planes[0] );
83 } else {
84 numplanes = 0;
85 }
86
87 // never turn against original velocity
88 VectorNormalize2( pm->ps->velocity, planes[numplanes] );
89 numplanes++;
90
91 for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) {
92
93 // calculate position we are trying to move to
94 VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end );
95
96 // see if we can make it there
97 pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask);
98
99 if (trace.allsolid) {
100 // entity is completely trapped in another solid
101 pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration
102 return qtrue;
103 }
104
105 if (trace.fraction > 0) {
106 // actually covered some distance
107 VectorCopy (trace.endpos, pm->ps->origin);
108 }
109
110 if (trace.fraction == 1) {
111 break; // moved the entire distance
112 }
113
114 // save entity for contact
115 PM_AddTouchEnt( trace.entityNum );
116
117 time_left -= time_left * trace.fraction;
118
119 if (numplanes >= MAX_CLIP_PLANES) {
120 // this shouldn't really happen
121 VectorClear( pm->ps->velocity );
122 return qtrue;
123 }
124
125 //
126 // if this is the same plane we hit before, nudge velocity
127 // out along it, which fixes some epsilon issues with
128 // non-axial planes
129 //
130 for ( i = 0 ; i < numplanes ; i++ ) {
131 if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) {
132 VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity );
133 break;
134 }
135 }
136 if ( i < numplanes ) {
137 continue;
138 }
139 VectorCopy (trace.plane.normal, planes[numplanes]);
140 numplanes++;
141
142 //
143 // modify velocity so it parallels all of the clip planes
144 //
145
146 // find a plane that it enters
147 for ( i = 0 ; i < numplanes ; i++ ) {
148 into = DotProduct( pm->ps->velocity, planes[i] );
149 if ( into >= 0.1 ) {
150 continue; // move doesn't interact with the plane
151 }
152
153 // see how hard we are hitting things
154 if ( -into > pml.impactSpeed ) {
155 pml.impactSpeed = -into;
156 }
157
158 // slide along the plane
159 PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP );
160
161 // slide along the plane
162 PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP );
163
164 // see if there is a second plane that the new move enters
165 for ( j = 0 ; j < numplanes ; j++ ) {
166 if ( j == i ) {
167 continue;
168 }
169 if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) {
170 continue; // move doesn't interact with the plane
171 }
172
173 // try clipping the move to the plane
174 PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP );
175 PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP );
176
177 // see if it goes back into the first clip plane
178 if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) {
179 continue;
180 }
181
182 // slide the original velocity along the crease
183 CrossProduct (planes[i], planes[j], dir);
184 VectorNormalize( dir );
185 d = DotProduct( dir, pm->ps->velocity );
186 VectorScale( dir, d, clipVelocity );
187
188 CrossProduct (planes[i], planes[j], dir);
189 VectorNormalize( dir );
190 d = DotProduct( dir, endVelocity );
191 VectorScale( dir, d, endClipVelocity );
192
193 // see if there is a third plane the the new move enters
194 for ( k = 0 ; k < numplanes ; k++ ) {
195 if ( k == i || k == j ) {
196 continue;
197 }
198 if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) {
199 continue; // move doesn't interact with the plane
200 }
201
202 // stop dead at a tripple plane interaction
203 VectorClear( pm->ps->velocity );
204 return qtrue;
205 }
206 }
207
208 // if we have fixed all interactions, try another move
209 VectorCopy( clipVelocity, pm->ps->velocity );
210 VectorCopy( endClipVelocity, endVelocity );
211 break;
212 }
213 }
214
215 if ( gravity ) {
216 VectorCopy( endVelocity, pm->ps->velocity );
217 }
218
219 // don't change velocity if in a timer (FIXME: is this correct?)
220 if ( pm->ps->pm_time ) {
221 VectorCopy( primal_velocity, pm->ps->velocity );
222 }
223
224 return ( bumpcount != 0 );
225 }
226
227 /*
228 ==================
229 PM_StepSlideMove
230
231 ==================
232 */
PM_StepSlideMove(qboolean gravity)233 void PM_StepSlideMove( qboolean gravity ) {
234 vec3_t start_o, start_v;
235 vec3_t down_o, down_v;
236 trace_t trace;
237 // float down_dist, up_dist;
238 // vec3_t delta, delta2;
239 vec3_t up, down;
240 float stepSize;
241
242 VectorCopy (pm->ps->origin, start_o);
243 VectorCopy (pm->ps->velocity, start_v);
244
245 if ( PM_SlideMove( gravity ) == 0 ) {
246 return; // we got exactly where we wanted to go first try
247 }
248
249 VectorCopy(start_o, down);
250 down[2] -= STEPSIZE;
251 pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
252 VectorSet(up, 0, 0, 1);
253 // never step up when you still have up velocity
254 if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 ||
255 DotProduct(trace.plane.normal, up) < 0.7)) {
256 return;
257 }
258
259 VectorCopy (pm->ps->origin, down_o);
260 VectorCopy (pm->ps->velocity, down_v);
261
262 VectorCopy (start_o, up);
263 up[2] += STEPSIZE;
264
265 // test the player position if they were a stepheight higher
266 pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask);
267 if ( trace.allsolid ) {
268 if ( pm->debugLevel ) {
269 Com_Printf("%i:bend can't step\n", c_pmove);
270 }
271 return; // can't step up
272 }
273
274 stepSize = trace.endpos[2] - start_o[2];
275 // try slidemove from this position
276 VectorCopy (trace.endpos, pm->ps->origin);
277 VectorCopy (start_v, pm->ps->velocity);
278
279 PM_SlideMove( gravity );
280
281 // push down the final amount
282 VectorCopy (pm->ps->origin, down);
283 down[2] -= stepSize;
284 pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
285 if ( !trace.allsolid ) {
286 VectorCopy (trace.endpos, pm->ps->origin);
287 }
288 if ( trace.fraction < 1.0 ) {
289 PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
290 }
291
292 #if 0
293 // if the down trace can trace back to the original position directly, don't step
294 pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask);
295 if ( trace.fraction == 1.0 ) {
296 // use the original move
297 VectorCopy (down_o, pm->ps->origin);
298 VectorCopy (down_v, pm->ps->velocity);
299 if ( pm->debugLevel ) {
300 Com_Printf("%i:bend\n", c_pmove);
301 }
302 } else
303 #endif
304 {
305 // use the step move
306 float delta;
307
308 delta = pm->ps->origin[2] - start_o[2];
309 if ( delta > 2 ) {
310 if ( delta < 7 ) {
311 PM_AddEvent( EV_STEP_4 );
312 } else if ( delta < 11 ) {
313 PM_AddEvent( EV_STEP_8 );
314 } else if ( delta < 15 ) {
315 PM_AddEvent( EV_STEP_12 );
316 } else {
317 PM_AddEvent( EV_STEP_16 );
318 }
319 }
320 if ( pm->debugLevel ) {
321 Com_Printf("%i:stepped\n", c_pmove);
322 }
323 }
324 }
325
326