1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "cm_local.h"
30
31 // always use bbox vs. bbox collision and never capsule vs. bbox or vice versa
32 //#define ALWAYS_BBOX_VS_BBOX
33 // always use capsule vs. capsule collision and never capsule vs. bbox or vice versa
34 //#define ALWAYS_CAPSULE_VS_CAPSULE
35
36 //#define CAPSULE_DEBUG
37
38 /*
39 ===============================================================================
40
41 BASIC MATH
42
43 ===============================================================================
44 */
45
46 /*
47 ================
48 RotatePoint
49 ================
50 */
RotatePoint(vec3_t point,const vec3_t matrix[3])51 void RotatePoint( vec3_t point, const vec3_t matrix[3] ) {
52 vec3_t tvec;
53
54 VectorCopy( point, tvec );
55 point[0] = DotProduct( matrix[0], tvec );
56 point[1] = DotProduct( matrix[1], tvec );
57 point[2] = DotProduct( matrix[2], tvec );
58 }
59
60 /*
61 ================
62 TransposeMatrix
63 ================
64 */
TransposeMatrix(const vec3_t matrix[3],vec3_t transpose[3])65 void TransposeMatrix( const vec3_t matrix[3], vec3_t transpose[3] ) {
66 int i, j;
67 for ( i = 0; i < 3; i++ ) {
68 for ( j = 0; j < 3; j++ ) {
69 transpose[i][j] = matrix[j][i];
70 }
71 }
72 }
73
74 /*
75 ================
76 CreateRotationMatrix
77 ================
78 */
CreateRotationMatrix(const vec3_t angles,vec3_t matrix[3])79 void CreateRotationMatrix( const vec3_t angles, vec3_t matrix[3] ) {
80 AngleVectors( angles, matrix[0], matrix[1], matrix[2] );
81 VectorInverse( matrix[1] );
82 }
83
84 /*
85 ================
86 CM_ProjectPointOntoVector
87 ================
88 */
CM_ProjectPointOntoVector(vec3_t point,vec3_t vStart,vec3_t vDir,vec3_t vProj)89 void CM_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vDir, vec3_t vProj ) {
90 vec3_t pVec;
91
92 VectorSubtract( point, vStart, pVec );
93 // project onto the directional vector for this segment
94 VectorMA( vStart, DotProduct( pVec, vDir ), vDir, vProj );
95 }
96
97 /*
98 ================
99 CM_DistanceFromLineSquared
100 ================
101 */
CM_DistanceFromLineSquared(vec3_t p,vec3_t lp1,vec3_t lp2,vec3_t dir)102 float CM_DistanceFromLineSquared( vec3_t p, vec3_t lp1, vec3_t lp2, vec3_t dir ) {
103 vec3_t proj, t;
104 int j;
105
106 CM_ProjectPointOntoVector( p, lp1, dir, proj );
107 for ( j = 0; j < 3; j++ )
108 if ( ( proj[j] > lp1[j] && proj[j] > lp2[j] ) ||
109 ( proj[j] < lp1[j] && proj[j] < lp2[j] ) ) {
110 break;
111 }
112 if ( j < 3 ) {
113 if ( fabs( proj[j] - lp1[j] ) < fabs( proj[j] - lp2[j] ) ) {
114 VectorSubtract( p, lp1, t );
115 } else {
116 VectorSubtract( p, lp2, t );
117 }
118 return VectorLengthSquared( t );
119 }
120 VectorSubtract( p, proj, t );
121 return VectorLengthSquared( t );
122 }
123
124 /*
125 ================
126 CM_VectorDistanceSquared
127 ================
128 */
CM_VectorDistanceSquared(vec3_t p1,vec3_t p2)129 float CM_VectorDistanceSquared( vec3_t p1, vec3_t p2 ) {
130 vec3_t dir;
131
132 VectorSubtract( p2, p1, dir );
133 return VectorLengthSquared( dir );
134 }
135
136 /*
137 ================
138 SquareRootFloat
139 ================
140 */
SquareRootFloat(float number)141 float SquareRootFloat( float number ) {
142 floatint_t t;
143 float x, y;
144 const float f = 1.5F;
145
146 x = number * 0.5F;
147 t.f = number;
148 t.i = 0x5f3759df - ( t.i >> 1 );
149 y = t.f;
150 y = y * ( f - ( x * y * y ) );
151 y = y * ( f - ( x * y * y ) );
152 return number * y;
153 }
154
155
156 /*
157 ===============================================================================
158
159 POSITION TESTING
160
161 ===============================================================================
162 */
163
164 /*
165 ================
166 CM_TestBoxInBrush
167 ================
168 */
CM_TestBoxInBrush(traceWork_t * tw,cbrush_t * brush)169 void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) {
170 int i;
171 cplane_t *plane;
172 float dist;
173 float d1;
174 cbrushside_t *side;
175 float t;
176 vec3_t startp;
177
178 if ( !brush->numsides ) {
179 return;
180 }
181
182 // special test for axial
183 // the first 6 brush planes are always axial
184 if ( tw->bounds[0][0] > brush->bounds[1][0]
185 || tw->bounds[0][1] > brush->bounds[1][1]
186 || tw->bounds[0][2] > brush->bounds[1][2]
187 || tw->bounds[1][0] < brush->bounds[0][0]
188 || tw->bounds[1][1] < brush->bounds[0][1]
189 || tw->bounds[1][2] < brush->bounds[0][2]
190 ) {
191 return;
192 }
193
194 if ( tw->sphere.use ) {
195 // the first six planes are the axial planes, so we only
196 // need to test the remainder
197 for ( i = 6 ; i < brush->numsides ; i++ ) {
198 side = brush->sides + i;
199 plane = side->plane;
200
201 // adjust the plane distance apropriately for radius
202 dist = plane->dist + tw->sphere.radius;
203 // find the closest point on the capsule to the plane
204 t = DotProduct( plane->normal, tw->sphere.offset );
205 if ( t > 0 ) {
206 VectorSubtract( tw->start, tw->sphere.offset, startp );
207 } else
208 {
209 VectorAdd( tw->start, tw->sphere.offset, startp );
210 }
211 d1 = DotProduct( startp, plane->normal ) - dist;
212 // if completely in front of face, no intersection
213 if ( d1 > 0 ) {
214 return;
215 }
216 }
217 } else {
218 // the first six planes are the axial planes, so we only
219 // need to test the remainder
220 for ( i = 6 ; i < brush->numsides ; i++ ) {
221 side = brush->sides + i;
222 plane = side->plane;
223
224 // adjust the plane distance apropriately for mins/maxs
225 dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal );
226
227 d1 = DotProduct( tw->start, plane->normal ) - dist;
228
229 // if completely in front of face, no intersection
230 if ( d1 > 0 ) {
231 return;
232 }
233 }
234 }
235
236 // inside this brush
237 tw->trace.startsolid = tw->trace.allsolid = qtrue;
238 tw->trace.fraction = 0;
239 tw->trace.contents = brush->contents;
240 }
241
242
243
244 /*
245 ================
246 CM_TestInLeaf
247 ================
248 */
CM_TestInLeaf(traceWork_t * tw,cLeaf_t * leaf)249 void CM_TestInLeaf( traceWork_t *tw, cLeaf_t *leaf ) {
250 int k;
251 int brushnum;
252 cbrush_t *b;
253 cPatch_t *patch;
254
255 // test box position against all brushes in the leaf
256 for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) {
257 brushnum = cm.leafbrushes[leaf->firstLeafBrush + k];
258 b = &cm.brushes[brushnum];
259 if ( b->checkcount == cm.checkcount ) {
260 continue; // already checked this brush in another leaf
261 }
262 b->checkcount = cm.checkcount;
263
264 if ( !( b->contents & tw->contents ) ) {
265 continue;
266 }
267
268 CM_TestBoxInBrush( tw, b );
269 if ( tw->trace.allsolid ) {
270 return;
271 }
272 }
273
274 // test against all patches
275 #ifdef BSPC
276 if ( 1 ) {
277 #else
278 if ( !cm_noCurves->integer ) {
279 #endif //BSPC
280 for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) {
281 patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ];
282 if ( !patch ) {
283 continue;
284 }
285 if ( patch->checkcount == cm.checkcount ) {
286 continue; // already checked this brush in another leaf
287 }
288 patch->checkcount = cm.checkcount;
289
290 if ( !( patch->contents & tw->contents ) ) {
291 continue;
292 }
293
294 if ( CM_PositionTestInPatchCollide( tw, patch->pc ) ) {
295 tw->trace.startsolid = tw->trace.allsolid = qtrue;
296 tw->trace.fraction = 0;
297 return;
298 }
299 }
300 }
301 }
302
303 /*
304 ==================
305 CM_TestCapsuleInCapsule
306
307 capsule inside capsule check
308 ==================
309 */
310 void CM_TestCapsuleInCapsule( traceWork_t *tw, clipHandle_t model ) {
311 int i;
312 vec3_t mins, maxs;
313 vec3_t top, bottom;
314 vec3_t p1, p2, tmp;
315 vec3_t offset, symetricSize[2];
316 float radius, halfwidth, halfheight, offs, r;
317
318 CM_ModelBounds( model, mins, maxs );
319
320 VectorAdd( tw->start, tw->sphere.offset, top );
321 VectorSubtract( tw->start, tw->sphere.offset, bottom );
322 for ( i = 0 ; i < 3 ; i++ ) {
323 offset[i] = ( mins[i] + maxs[i] ) * 0.5;
324 symetricSize[0][i] = mins[i] - offset[i];
325 symetricSize[1][i] = maxs[i] - offset[i];
326 }
327 halfwidth = symetricSize[ 1 ][ 0 ];
328 halfheight = symetricSize[ 1 ][ 2 ];
329 radius = ( halfwidth > halfheight ) ? halfheight : halfwidth;
330 offs = halfheight - radius;
331
332 r = Square( tw->sphere.radius + radius );
333 // check if any of the spheres overlap
334 VectorCopy( offset, p1 );
335 p1[2] += offs;
336 VectorSubtract( p1, top, tmp );
337 if ( VectorLengthSquared( tmp ) < r ) {
338 tw->trace.startsolid = tw->trace.allsolid = qtrue;
339 tw->trace.fraction = 0;
340 }
341 VectorSubtract( p1, bottom, tmp );
342 if ( VectorLengthSquared( tmp ) < r ) {
343 tw->trace.startsolid = tw->trace.allsolid = qtrue;
344 tw->trace.fraction = 0;
345 }
346 VectorCopy( offset, p2 );
347 p2[2] -= offs;
348 VectorSubtract( p2, top, tmp );
349 if ( VectorLengthSquared( tmp ) < r ) {
350 tw->trace.startsolid = tw->trace.allsolid = qtrue;
351 tw->trace.fraction = 0;
352 }
353 VectorSubtract( p2, bottom, tmp );
354 if ( VectorLengthSquared( tmp ) < r ) {
355 tw->trace.startsolid = tw->trace.allsolid = qtrue;
356 tw->trace.fraction = 0;
357 }
358 // if between cylinder up and lower bounds
359 if ( ( top[2] >= p1[2] && top[2] <= p2[2] ) ||
360 ( bottom[2] >= p1[2] && bottom[2] <= p2[2] ) ) {
361 // 2d coordinates
362 top[2] = p1[2] = 0;
363 // if the cylinders overlap
364 VectorSubtract( top, p1, tmp );
365 if ( VectorLengthSquared( tmp ) < r ) {
366 tw->trace.startsolid = tw->trace.allsolid = qtrue;
367 tw->trace.fraction = 0;
368 }
369 }
370 }
371
372 /*
373 ==================
374 CM_TestBoundingBoxInCapsule
375
376 bounding box inside capsule check
377 ==================
378 */
379 void CM_TestBoundingBoxInCapsule( traceWork_t *tw, clipHandle_t model ) {
380 vec3_t mins, maxs, offset, size[2];
381 clipHandle_t h;
382 cmodel_t *cmod;
383 int i;
384
385 // mins maxs of the capsule
386 CM_ModelBounds( model, mins, maxs );
387
388 // offset for capsule center
389 for ( i = 0 ; i < 3 ; i++ ) {
390 offset[i] = ( mins[i] + maxs[i] ) * 0.5;
391 size[0][i] = mins[i] - offset[i];
392 size[1][i] = maxs[i] - offset[i];
393 tw->start[i] -= offset[i];
394 tw->end[i] -= offset[i];
395 }
396
397 // replace the bounding box with the capsule
398 tw->sphere.use = qtrue;
399 tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2] : size[1][0];
400 tw->sphere.halfheight = size[1][2];
401 VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius );
402
403 // replace the capsule with the bounding box
404 h = CM_TempBoxModel( tw->size[0], tw->size[1], qfalse );
405 // calculate collision
406 cmod = CM_ClipHandleToModel( h );
407 CM_TestInLeaf( tw, &cmod->leaf );
408 }
409
410 /*
411 ==================
412 CM_PositionTest
413 ==================
414 */
415 #define MAX_POSITION_LEAFS 1024
416 void CM_PositionTest( traceWork_t *tw ) {
417 int leafs[MAX_POSITION_LEAFS];
418 int i;
419 leafList_t ll;
420
421 // identify the leafs we are touching
422 VectorAdd( tw->start, tw->size[0], ll.bounds[0] );
423 VectorAdd( tw->start, tw->size[1], ll.bounds[1] );
424
425 for ( i = 0 ; i < 3 ; i++ ) {
426 ll.bounds[0][i] -= 1;
427 ll.bounds[1][i] += 1;
428 }
429
430 ll.count = 0;
431 ll.maxcount = MAX_POSITION_LEAFS;
432 ll.list = leafs;
433 ll.storeLeafs = CM_StoreLeafs;
434 ll.lastLeaf = 0;
435 ll.overflowed = qfalse;
436
437 cm.checkcount++;
438
439 CM_BoxLeafnums_r( &ll, 0 );
440
441
442 cm.checkcount++;
443
444 // test the contents of the leafs
445 for ( i = 0 ; i < ll.count ; i++ ) {
446 CM_TestInLeaf( tw, &cm.leafs[leafs[i]] );
447 if ( tw->trace.allsolid ) {
448 break;
449 }
450 }
451 }
452
453 /*
454 ===============================================================================
455
456 TRACING
457
458 ===============================================================================
459 */
460
461
462 /*
463 ================
464 CM_TraceThroughPatch
465 ================
466 */
467
468 void CM_TraceThroughPatch( traceWork_t *tw, cPatch_t *patch ) {
469 float oldFrac;
470
471 c_patch_traces++;
472
473 oldFrac = tw->trace.fraction;
474
475 CM_TraceThroughPatchCollide( tw, patch->pc );
476
477 if ( tw->trace.fraction < oldFrac ) {
478 tw->trace.surfaceFlags = patch->surfaceFlags;
479 tw->trace.contents = patch->contents;
480 }
481 }
482
483 /*
484 ================
485 CM_TraceThroughBrush
486 ================
487 */
488 void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) {
489 int i;
490 cplane_t *plane, *clipplane;
491 float dist;
492 float enterFrac, leaveFrac;
493 float d1, d2;
494 qboolean getout, startout;
495 float f;
496 cbrushside_t *side, *leadside;
497 float t;
498 vec3_t startp;
499 vec3_t endp;
500
501 enterFrac = -1.0;
502 leaveFrac = 1.0;
503 clipplane = NULL;
504
505 if ( !brush->numsides ) {
506 return;
507 }
508
509 c_brush_traces++;
510
511 getout = qfalse;
512 startout = qfalse;
513
514 leadside = NULL;
515
516 if ( tw->sphere.use ) {
517 //
518 // compare the trace against all planes of the brush
519 // find the latest time the trace crosses a plane towards the interior
520 // and the earliest time the trace crosses a plane towards the exterior
521 //
522 for ( i = 0; i < brush->numsides; i++ ) {
523 side = brush->sides + i;
524 plane = side->plane;
525
526 // adjust the plane distance apropriately for radius
527 dist = plane->dist + tw->sphere.radius;
528
529 // find the closest point on the capsule to the plane
530 t = DotProduct( plane->normal, tw->sphere.offset );
531 if ( t > 0 ) {
532 VectorSubtract( tw->start, tw->sphere.offset, startp );
533 VectorSubtract( tw->end, tw->sphere.offset, endp );
534 } else
535 {
536 VectorAdd( tw->start, tw->sphere.offset, startp );
537 VectorAdd( tw->end, tw->sphere.offset, endp );
538 }
539
540 d1 = DotProduct( startp, plane->normal ) - dist;
541 d2 = DotProduct( endp, plane->normal ) - dist;
542
543 if ( d2 > 0 ) {
544 getout = qtrue; // endpoint is not in solid
545 }
546 if ( d1 > 0 ) {
547 startout = qtrue;
548 }
549
550 // if completely in front of face, no intersection with the entire brush
551 if ( d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) {
552 return;
553 }
554
555 // if it doesn't cross the plane, the plane isn't relevent
556 if ( d1 <= 0 && d2 <= 0 ) {
557 continue;
558 }
559
560 // crosses face
561 if ( d1 > d2 ) { // enter
562 f = ( d1 - SURFACE_CLIP_EPSILON ) / ( d1 - d2 );
563 if ( f < 0 ) {
564 f = 0;
565 }
566 if ( f > enterFrac ) {
567 enterFrac = f;
568 clipplane = plane;
569 leadside = side;
570 }
571 } else { // leave
572 f = ( d1 + SURFACE_CLIP_EPSILON ) / ( d1 - d2 );
573 if ( f > 1 ) {
574 f = 1;
575 }
576 if ( f < leaveFrac ) {
577 leaveFrac = f;
578 }
579 }
580 }
581 } else {
582 //
583 // compare the trace against all planes of the brush
584 // find the latest time the trace crosses a plane towards the interior
585 // and the earliest time the trace crosses a plane towards the exterior
586 //
587 for ( i = 0; i < brush->numsides; i++ ) {
588 side = brush->sides + i;
589 plane = side->plane;
590
591 // adjust the plane distance apropriately for mins/maxs
592 dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal );
593
594 d1 = DotProduct( tw->start, plane->normal ) - dist;
595 d2 = DotProduct( tw->end, plane->normal ) - dist;
596
597 if ( d2 > 0 ) {
598 getout = qtrue; // endpoint is not in solid
599 }
600 if ( d1 > 0 ) {
601 startout = qtrue;
602 }
603
604 // if completely in front of face, no intersection with the entire brush
605 if ( d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) {
606 return;
607 }
608
609 // if it doesn't cross the plane, the plane isn't relevent
610 if ( d1 <= 0 && d2 <= 0 ) {
611 continue;
612 }
613
614 // crosses face
615 if ( d1 > d2 ) { // enter
616 f = ( d1 - SURFACE_CLIP_EPSILON ) / ( d1 - d2 );
617 if ( f < 0 ) {
618 f = 0;
619 }
620 if ( f > enterFrac ) {
621 enterFrac = f;
622 clipplane = plane;
623 leadside = side;
624 }
625 } else { // leave
626 f = ( d1 + SURFACE_CLIP_EPSILON ) / ( d1 - d2 );
627 if ( f > 1 ) {
628 f = 1;
629 }
630 if ( f < leaveFrac ) {
631 leaveFrac = f;
632 }
633 }
634 }
635 }
636
637 //
638 // all planes have been checked, and the trace was not
639 // completely outside the brush
640 //
641 if ( !startout ) { // original point was inside brush
642 tw->trace.startsolid = qtrue;
643 if ( !getout ) {
644 tw->trace.allsolid = qtrue;
645 tw->trace.fraction = 0;
646 }
647 return;
648 }
649
650 if ( enterFrac < leaveFrac ) {
651 if ( enterFrac > -1 && enterFrac < tw->trace.fraction ) {
652 if ( enterFrac < 0 ) {
653 enterFrac = 0;
654 }
655 tw->trace.fraction = enterFrac;
656 if (clipplane != NULL) {
657 tw->trace.plane = *clipplane;
658 }
659 if (leadside != NULL) {
660 tw->trace.surfaceFlags = leadside->surfaceFlags;
661 }
662 tw->trace.contents = brush->contents;
663 }
664 }
665 }
666
667 /*
668 ================
669 CM_TraceThroughLeaf
670 ================
671 */
672 void CM_TraceThroughLeaf( traceWork_t *tw, cLeaf_t *leaf ) {
673 int k;
674 int brushnum;
675 cbrush_t *b;
676 cPatch_t *patch;
677
678 // trace line against all brushes in the leaf
679 for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) {
680 brushnum = cm.leafbrushes[leaf->firstLeafBrush + k];
681
682 b = &cm.brushes[brushnum];
683 if ( b->checkcount == cm.checkcount ) {
684 continue; // already checked this brush in another leaf
685 }
686 b->checkcount = cm.checkcount;
687
688 if ( !( b->contents & tw->contents ) ) {
689 continue;
690 }
691
692 if ( !CM_BoundsIntersect( tw->bounds[0], tw->bounds[1],
693 b->bounds[0], b->bounds[1] ) ) {
694 continue;
695 }
696
697 CM_TraceThroughBrush( tw, b );
698 if ( !tw->trace.fraction ) {
699 return;
700 }
701 }
702
703 // trace line against all patches in the leaf
704 #ifdef BSPC
705 if ( 1 ) {
706 #else
707 if ( !cm_noCurves->integer ) {
708 #endif
709 for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) {
710 patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ];
711 if ( !patch ) {
712 continue;
713 }
714 if ( patch->checkcount == cm.checkcount ) {
715 continue; // already checked this patch in another leaf
716 }
717 patch->checkcount = cm.checkcount;
718
719 if ( !( patch->contents & tw->contents ) ) {
720 continue;
721 }
722
723 CM_TraceThroughPatch( tw, patch );
724 if ( !tw->trace.fraction ) {
725 return;
726 }
727 }
728 }
729 }
730
731 #define RADIUS_EPSILON 1.0f
732
733 /*
734 ================
735 CM_TraceThroughSphere
736
737 get the first intersection of the ray with the sphere
738 ================
739 */
740 void CM_TraceThroughSphere( traceWork_t *tw, vec3_t origin, float radius, vec3_t start, vec3_t end ) {
741 float l1, l2, length, scale, fraction;
742 //float a;
743 float b, c, d, sqrtd;
744 vec3_t v1, dir, intersection;
745
746 // if inside the sphere
747 VectorSubtract( start, origin, dir );
748 l1 = VectorLengthSquared( dir );
749 if ( l1 < Square( radius ) ) {
750 tw->trace.fraction = 0;
751 tw->trace.startsolid = qtrue;
752 // test for allsolid
753 VectorSubtract( end, origin, dir );
754 l1 = VectorLengthSquared( dir );
755 if ( l1 < Square( radius ) ) {
756 tw->trace.allsolid = qtrue;
757 }
758 return;
759 }
760 //
761 VectorSubtract( end, start, dir );
762 length = VectorNormalize( dir );
763 //
764 l1 = CM_DistanceFromLineSquared( origin, start, end, dir );
765 VectorSubtract( end, origin, v1 );
766 l2 = VectorLengthSquared( v1 );
767 // if no intersection with the sphere and the end point is at least an epsilon away
768 if ( l1 >= Square( radius ) && l2 > Square( radius + SURFACE_CLIP_EPSILON ) ) {
769 return;
770 }
771 //
772 // | origin - (start + t * dir) | = radius
773 // a = dir[0]^2 + dir[1]^2 + dir[2]^2;
774 // b = 2 * (dir[0] * (start[0] - origin[0]) + dir[1] * (start[1] - origin[1]) + dir[2] * (start[2] - origin[2]));
775 // c = (start[0] - origin[0])^2 + (start[1] - origin[1])^2 + (start[2] - origin[2])^2 - radius^2;
776 //
777 VectorSubtract( start, origin, v1 );
778 // dir is normalized so a = 1
779 //a = 1.0f; //dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2];
780 b = 2.0f * ( dir[0] * v1[0] + dir[1] * v1[1] + dir[2] * v1[2] );
781 c = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] - ( radius + RADIUS_EPSILON ) * ( radius + RADIUS_EPSILON );
782
783 d = b * b - 4.0f * c; // * a;
784 if ( d > 0 ) {
785 sqrtd = SquareRootFloat( d );
786 // = (- b + sqrtd) * 0.5f; // / (2.0f * a);
787 fraction = ( -b - sqrtd ) * 0.5f; // / (2.0f * a);
788 //
789 if ( fraction < 0 ) {
790 fraction = 0;
791 } else {
792 fraction /= length;
793 }
794 if ( fraction < tw->trace.fraction ) {
795 tw->trace.fraction = fraction;
796 VectorSubtract( end, start, dir );
797 VectorMA( start, fraction, dir, intersection );
798 VectorSubtract( intersection, origin, dir );
799 #ifdef CAPSULE_DEBUG
800 l2 = VectorLength( dir );
801 if ( l2 < radius ) {
802 int bah = 1;
803 }
804 #endif
805 scale = 1 / ( radius + RADIUS_EPSILON );
806 VectorScale( dir, scale, dir );
807 VectorCopy( dir, tw->trace.plane.normal );
808 VectorAdd( tw->modelOrigin, intersection, intersection );
809 tw->trace.plane.dist = DotProduct( tw->trace.plane.normal, intersection );
810 tw->trace.contents = CONTENTS_BODY;
811 }
812 } else if ( d == 0 ) {
813 //t1 = (- b ) / 2;
814 // slide along the sphere
815 }
816 // no intersection at all
817 }
818
819 /*
820 ================
821 CM_TraceThroughVerticalCylinder
822
823 get the first intersection of the ray with the cylinder
824 the cylinder extends halfheight above and below the origin
825 ================
826 */
827 void CM_TraceThroughVerticalCylinder( traceWork_t *tw, vec3_t origin, float radius, float halfheight, vec3_t start, vec3_t end ) {
828 float length, scale, fraction, l1, l2;
829 //float a;
830 float b, c, d, sqrtd;
831 vec3_t v1, dir, start2d, end2d, org2d, intersection;
832
833 // 2d coordinates
834 VectorSet( start2d, start[0], start[1], 0 );
835 VectorSet( end2d, end[0], end[1], 0 );
836 VectorSet( org2d, origin[0], origin[1], 0 );
837 // if between lower and upper cylinder bounds
838 if ( start[2] <= origin[2] + halfheight &&
839 start[2] >= origin[2] - halfheight ) {
840 // if inside the cylinder
841 VectorSubtract( start2d, org2d, dir );
842 l1 = VectorLengthSquared( dir );
843 if ( l1 < Square( radius ) ) {
844 tw->trace.fraction = 0;
845 tw->trace.startsolid = qtrue;
846 VectorSubtract( end2d, org2d, dir );
847 l1 = VectorLengthSquared( dir );
848 if ( l1 < Square( radius ) ) {
849 tw->trace.allsolid = qtrue;
850 }
851 return;
852 }
853 }
854 //
855 VectorSubtract( end2d, start2d, dir );
856 length = VectorNormalize( dir );
857 //
858 l1 = CM_DistanceFromLineSquared( org2d, start2d, end2d, dir );
859 VectorSubtract( end2d, org2d, v1 );
860 l2 = VectorLengthSquared( v1 );
861 // if no intersection with the cylinder and the end point is at least an epsilon away
862 if ( l1 >= Square( radius ) && l2 > Square( radius + SURFACE_CLIP_EPSILON ) ) {
863 return;
864 }
865 //
866 //
867 // (start[0] - origin[0] - t * dir[0]) ^ 2 + (start[1] - origin[1] - t * dir[1]) ^ 2 = radius ^ 2
868 // (v1[0] + t * dir[0]) ^ 2 + (v1[1] + t * dir[1]) ^ 2 = radius ^ 2;
869 // v1[0] ^ 2 + 2 * v1[0] * t * dir[0] + (t * dir[0]) ^ 2 +
870 // v1[1] ^ 2 + 2 * v1[1] * t * dir[1] + (t * dir[1]) ^ 2 = radius ^ 2
871 // t ^ 2 * (dir[0] ^ 2 + dir[1] ^ 2) + t * (2 * v1[0] * dir[0] + 2 * v1[1] * dir[1]) +
872 // v1[0] ^ 2 + v1[1] ^ 2 - radius ^ 2 = 0
873 //
874 VectorSubtract( start, origin, v1 );
875 // dir is normalized so we can use a = 1
876 //a = 1.0f; // * (dir[0] * dir[0] + dir[1] * dir[1]);
877 b = 2.0f * ( v1[0] * dir[0] + v1[1] * dir[1] );
878 c = v1[0] * v1[0] + v1[1] * v1[1] - ( radius + RADIUS_EPSILON ) * ( radius + RADIUS_EPSILON );
879
880 d = b * b - 4.0f * c; // * a;
881 if ( d > 0 ) {
882 sqrtd = SquareRootFloat( d );
883 // = (- b + sqrtd) * 0.5f;// / (2.0f * a);
884 fraction = ( -b - sqrtd ) * 0.5f; // / (2.0f * a);
885 //
886 if ( fraction < 0 ) {
887 fraction = 0;
888 } else {
889 fraction /= length;
890 }
891 if ( fraction < tw->trace.fraction ) {
892 VectorSubtract( end, start, dir );
893 VectorMA( start, fraction, dir, intersection );
894 // if the intersection is between the cylinder lower and upper bound
895 if ( intersection[2] <= origin[2] + halfheight &&
896 intersection[2] >= origin[2] - halfheight ) {
897 //
898 tw->trace.fraction = fraction;
899 VectorSubtract( intersection, origin, dir );
900 dir[2] = 0;
901 #ifdef CAPSULE_DEBUG
902 l2 = VectorLength( dir );
903 if ( l2 <= radius ) {
904 int bah = 1;
905 }
906 #endif
907 scale = 1 / ( radius + RADIUS_EPSILON );
908 VectorScale( dir, scale, dir );
909 VectorCopy( dir, tw->trace.plane.normal );
910 VectorAdd( tw->modelOrigin, intersection, intersection );
911 tw->trace.plane.dist = DotProduct( tw->trace.plane.normal, intersection );
912 tw->trace.contents = CONTENTS_BODY;
913 }
914 }
915 } else if ( d == 0 ) {
916 //t[0] = (- b ) / 2 * a;
917 // slide along the cylinder
918 }
919 // no intersection at all
920 }
921
922 /*
923 ================
924 CM_TraceCapsuleThroughCapsule
925
926 capsule vs. capsule collision (not rotated)
927 ================
928 */
929 void CM_TraceCapsuleThroughCapsule( traceWork_t *tw, clipHandle_t model ) {
930 int i;
931 vec3_t mins, maxs;
932 vec3_t top, bottom, starttop, startbottom, endtop, endbottom;
933 vec3_t offset, symetricSize[2];
934 float radius, halfwidth, halfheight, offs, h;
935
936 CM_ModelBounds( model, mins, maxs );
937 // test trace bounds vs. capsule bounds
938 if ( tw->bounds[0][0] > maxs[0] + RADIUS_EPSILON
939 || tw->bounds[0][1] > maxs[1] + RADIUS_EPSILON
940 || tw->bounds[0][2] > maxs[2] + RADIUS_EPSILON
941 || tw->bounds[1][0] < mins[0] - RADIUS_EPSILON
942 || tw->bounds[1][1] < mins[1] - RADIUS_EPSILON
943 || tw->bounds[1][2] < mins[2] - RADIUS_EPSILON
944 ) {
945 return;
946 }
947 // top origin and bottom origin of each sphere at start and end of trace
948 VectorAdd( tw->start, tw->sphere.offset, starttop );
949 VectorSubtract( tw->start, tw->sphere.offset, startbottom );
950 VectorAdd( tw->end, tw->sphere.offset, endtop );
951 VectorSubtract( tw->end, tw->sphere.offset, endbottom );
952
953 // calculate top and bottom of the capsule spheres to collide with
954 for ( i = 0 ; i < 3 ; i++ ) {
955 offset[i] = ( mins[i] + maxs[i] ) * 0.5;
956 symetricSize[0][i] = mins[i] - offset[i];
957 symetricSize[1][i] = maxs[i] - offset[i];
958 }
959 halfwidth = symetricSize[ 1 ][ 0 ];
960 halfheight = symetricSize[ 1 ][ 2 ];
961 radius = ( halfwidth > halfheight ) ? halfheight : halfwidth;
962 offs = halfheight - radius;
963 VectorCopy( offset, top );
964 top[2] += offs;
965 VectorCopy( offset, bottom );
966 bottom[2] -= offs;
967 // expand radius of spheres
968 radius += tw->sphere.radius;
969 // if there is horizontal movement
970 if ( tw->start[0] != tw->end[0] || tw->start[1] != tw->end[1] ) {
971 // height of the expanded cylinder is the height of both cylinders minus the radius of both spheres
972 h = halfheight + tw->sphere.halfheight - radius;
973 // if the cylinder has a height
974 if ( h > 0 ) {
975 // test for collisions between the cylinders
976 CM_TraceThroughVerticalCylinder( tw, offset, radius, h, tw->start, tw->end );
977 }
978 }
979 // test for collision between the spheres
980 CM_TraceThroughSphere( tw, top, radius, startbottom, endbottom );
981 CM_TraceThroughSphere( tw, bottom, radius, starttop, endtop );
982 }
983
984 /*
985 ================
986 CM_TraceBoundingBoxThroughCapsule
987
988 bounding box vs. capsule collision
989 ================
990 */
991 void CM_TraceBoundingBoxThroughCapsule( traceWork_t *tw, clipHandle_t model ) {
992 vec3_t mins, maxs, offset, size[2];
993 clipHandle_t h;
994 cmodel_t *cmod;
995 int i;
996
997 // mins maxs of the capsule
998 CM_ModelBounds( model, mins, maxs );
999
1000 // offset for capsule center
1001 for ( i = 0 ; i < 3 ; i++ ) {
1002 offset[i] = ( mins[i] + maxs[i] ) * 0.5;
1003 size[0][i] = mins[i] - offset[i];
1004 size[1][i] = maxs[i] - offset[i];
1005 tw->start[i] -= offset[i];
1006 tw->end[i] -= offset[i];
1007 }
1008
1009 // replace the bounding box with the capsule
1010 tw->sphere.use = qtrue;
1011 tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2] : size[1][0];
1012 tw->sphere.halfheight = size[1][2];
1013 VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius );
1014
1015 // replace the capsule with the bounding box
1016 h = CM_TempBoxModel( tw->size[0], tw->size[1], qfalse );
1017 // calculate collision
1018 cmod = CM_ClipHandleToModel( h );
1019 CM_TraceThroughLeaf( tw, &cmod->leaf );
1020 }
1021
1022 //=========================================================================================
1023
1024 /*
1025 ==================
1026 CM_TraceThroughTree
1027
1028 Traverse all the contacted leafs from the start to the end position.
1029 If the trace is a point, they will be exactly in order, but for larger
1030 trace volumes it is possible to hit something in a later leaf with
1031 a smaller intercept fraction.
1032 ==================
1033 */
1034 void CM_TraceThroughTree( traceWork_t *tw, int num, float p1f, float p2f, vec3_t p1, vec3_t p2 ) {
1035 cNode_t *node;
1036 cplane_t *plane;
1037 float t1, t2, offset;
1038 float frac, frac2;
1039 float idist;
1040 vec3_t mid;
1041 int side;
1042 float midf;
1043
1044 if ( tw->trace.fraction <= p1f ) {
1045 return; // already hit something nearer
1046 }
1047
1048 // if < 0, we are in a leaf node
1049 if ( num < 0 ) {
1050 CM_TraceThroughLeaf( tw, &cm.leafs[-1 - num] );
1051 return;
1052 }
1053
1054 //
1055 // find the point distances to the separating plane
1056 // and the offset for the size of the box
1057 //
1058 node = cm.nodes + num;
1059 plane = node->plane;
1060
1061 // adjust the plane distance apropriately for mins/maxs
1062 if ( plane->type < 3 ) {
1063 t1 = p1[plane->type] - plane->dist;
1064 t2 = p2[plane->type] - plane->dist;
1065 offset = tw->extents[plane->type];
1066 } else {
1067 t1 = DotProduct( plane->normal, p1 ) - plane->dist;
1068 t2 = DotProduct( plane->normal, p2 ) - plane->dist;
1069 if ( tw->isPoint ) {
1070 offset = 0;
1071 } else {
1072 // this is silly
1073 offset = 2048;
1074 }
1075 }
1076
1077 // see which sides we need to consider
1078 if ( t1 >= offset + 1 && t2 >= offset + 1 ) {
1079 CM_TraceThroughTree( tw, node->children[0], p1f, p2f, p1, p2 );
1080 return;
1081 }
1082 if ( t1 < -offset - 1 && t2 < -offset - 1 ) {
1083 CM_TraceThroughTree( tw, node->children[1], p1f, p2f, p1, p2 );
1084 return;
1085 }
1086
1087 // put the crosspoint SURFACE_CLIP_EPSILON pixels on the near side
1088 if ( t1 < t2 ) {
1089 idist = 1.0 / ( t1 - t2 );
1090 side = 1;
1091 frac2 = ( t1 + offset + SURFACE_CLIP_EPSILON ) * idist;
1092 frac = ( t1 - offset + SURFACE_CLIP_EPSILON ) * idist;
1093 } else if ( t1 > t2 ) {
1094 idist = 1.0 / ( t1 - t2 );
1095 side = 0;
1096 frac2 = ( t1 - offset - SURFACE_CLIP_EPSILON ) * idist;
1097 frac = ( t1 + offset + SURFACE_CLIP_EPSILON ) * idist;
1098 } else {
1099 side = 0;
1100 frac = 1;
1101 frac2 = 0;
1102 }
1103
1104 // move up to the node
1105 if ( frac < 0 ) {
1106 frac = 0;
1107 }
1108 if ( frac > 1 ) {
1109 frac = 1;
1110 }
1111
1112 midf = p1f + ( p2f - p1f ) * frac;
1113
1114 mid[0] = p1[0] + frac * ( p2[0] - p1[0] );
1115 mid[1] = p1[1] + frac * ( p2[1] - p1[1] );
1116 mid[2] = p1[2] + frac * ( p2[2] - p1[2] );
1117
1118 CM_TraceThroughTree( tw, node->children[side], p1f, midf, p1, mid );
1119
1120
1121 // go past the node
1122 if ( frac2 < 0 ) {
1123 frac2 = 0;
1124 }
1125 if ( frac2 > 1 ) {
1126 frac2 = 1;
1127 }
1128
1129 midf = p1f + ( p2f - p1f ) * frac2;
1130
1131 mid[0] = p1[0] + frac2 * ( p2[0] - p1[0] );
1132 mid[1] = p1[1] + frac2 * ( p2[1] - p1[1] );
1133 mid[2] = p1[2] + frac2 * ( p2[2] - p1[2] );
1134
1135 CM_TraceThroughTree( tw, node->children[side ^ 1], midf, p2f, mid, p2 );
1136 }
1137
1138
1139 //======================================================================
1140
1141
1142 /*
1143 ==================
1144 CM_Trace
1145 ==================
1146 */
1147 void CM_Trace( trace_t *results, const vec3_t start, const vec3_t end,
1148 const vec3_t mins, const vec3_t maxs,
1149 clipHandle_t model, const vec3_t origin, int brushmask, int capsule, sphere_t *sphere ) {
1150 int i;
1151 traceWork_t tw;
1152 vec3_t offset;
1153 cmodel_t *cmod;
1154
1155 cmod = CM_ClipHandleToModel( model );
1156
1157 cm.checkcount++; // for multi-check avoidance
1158
1159 c_traces++; // for statistics, may be zeroed
1160
1161 // fill in a default trace
1162 memset( &tw, 0, sizeof( tw ) );
1163 tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise
1164 VectorCopy( origin, tw.modelOrigin );
1165
1166 if ( !cm.numNodes ) {
1167 *results = tw.trace;
1168
1169 return; // map not loaded, shouldn't happen
1170 }
1171
1172 // allow NULL to be passed in for 0,0,0
1173 if ( !mins ) {
1174 mins = vec3_origin;
1175 }
1176 if ( !maxs ) {
1177 maxs = vec3_origin;
1178 }
1179
1180 // set basic parms
1181 tw.contents = brushmask;
1182
1183 // adjust so that mins and maxs are always symetric, which
1184 // avoids some complications with plane expanding of rotated
1185 // bmodels
1186 for ( i = 0 ; i < 3 ; i++ ) {
1187 offset[i] = ( mins[i] + maxs[i] ) * 0.5;
1188 tw.size[0][i] = mins[i] - offset[i];
1189 tw.size[1][i] = maxs[i] - offset[i];
1190 tw.start[i] = start[i] + offset[i];
1191 tw.end[i] = end[i] + offset[i];
1192 }
1193
1194 // if a sphere is already specified
1195 if ( sphere ) {
1196 tw.sphere = *sphere;
1197 } else {
1198 tw.sphere.use = capsule;
1199 tw.sphere.radius = ( tw.size[1][0] > tw.size[1][2] ) ? tw.size[1][2] : tw.size[1][0];
1200 tw.sphere.halfheight = tw.size[1][2];
1201 VectorSet( tw.sphere.offset, 0, 0, tw.size[1][2] - tw.sphere.radius );
1202 }
1203
1204 tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2];
1205
1206 // tw.offsets[signbits] = vector to apropriate corner from origin
1207 tw.offsets[0][0] = tw.size[0][0];
1208 tw.offsets[0][1] = tw.size[0][1];
1209 tw.offsets[0][2] = tw.size[0][2];
1210
1211 tw.offsets[1][0] = tw.size[1][0];
1212 tw.offsets[1][1] = tw.size[0][1];
1213 tw.offsets[1][2] = tw.size[0][2];
1214
1215 tw.offsets[2][0] = tw.size[0][0];
1216 tw.offsets[2][1] = tw.size[1][1];
1217 tw.offsets[2][2] = tw.size[0][2];
1218
1219 tw.offsets[3][0] = tw.size[1][0];
1220 tw.offsets[3][1] = tw.size[1][1];
1221 tw.offsets[3][2] = tw.size[0][2];
1222
1223 tw.offsets[4][0] = tw.size[0][0];
1224 tw.offsets[4][1] = tw.size[0][1];
1225 tw.offsets[4][2] = tw.size[1][2];
1226
1227 tw.offsets[5][0] = tw.size[1][0];
1228 tw.offsets[5][1] = tw.size[0][1];
1229 tw.offsets[5][2] = tw.size[1][2];
1230
1231 tw.offsets[6][0] = tw.size[0][0];
1232 tw.offsets[6][1] = tw.size[1][1];
1233 tw.offsets[6][2] = tw.size[1][2];
1234
1235 tw.offsets[7][0] = tw.size[1][0];
1236 tw.offsets[7][1] = tw.size[1][1];
1237 tw.offsets[7][2] = tw.size[1][2];
1238
1239 //
1240 // calculate bounds
1241 //
1242 if ( tw.sphere.use ) {
1243 for ( i = 0 ; i < 3 ; i++ ) {
1244 if ( tw.start[i] < tw.end[i] ) {
1245 tw.bounds[0][i] = tw.start[i] - fabs( tw.sphere.offset[i] ) - tw.sphere.radius;
1246 tw.bounds[1][i] = tw.end[i] + fabs( tw.sphere.offset[i] ) + tw.sphere.radius;
1247 } else {
1248 tw.bounds[0][i] = tw.end[i] - fabs( tw.sphere.offset[i] ) - tw.sphere.radius;
1249 tw.bounds[1][i] = tw.start[i] + fabs( tw.sphere.offset[i] ) + tw.sphere.radius;
1250 }
1251 }
1252 } else {
1253 for ( i = 0 ; i < 3 ; i++ ) {
1254 if ( tw.start[i] < tw.end[i] ) {
1255 tw.bounds[0][i] = tw.start[i] + tw.size[0][i];
1256 tw.bounds[1][i] = tw.end[i] + tw.size[1][i];
1257 } else {
1258 tw.bounds[0][i] = tw.end[i] + tw.size[0][i];
1259 tw.bounds[1][i] = tw.start[i] + tw.size[1][i];
1260 }
1261 }
1262 }
1263
1264 //
1265 // check for position test special case
1266 //
1267 if ( start[0] == end[0] && start[1] == end[1] && start[2] == end[2] ) {
1268 if ( model ) {
1269 #ifdef ALWAYS_BBOX_VS_BBOX
1270 if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE ) {
1271 tw.sphere.use = qfalse;
1272 CM_TestInLeaf( &tw, &cmod->leaf );
1273 } else
1274 #elif defined( ALWAYS_CAPSULE_VS_CAPSULE )
1275 if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE ) {
1276 CM_TestCapsuleInCapsule( &tw, model );
1277 } else
1278 #endif
1279 if ( model == CAPSULE_MODEL_HANDLE ) {
1280 if ( tw.sphere.use ) {
1281 CM_TestCapsuleInCapsule( &tw, model );
1282 } else {
1283 CM_TestBoundingBoxInCapsule( &tw, model );
1284 }
1285 } else {
1286 CM_TestInLeaf( &tw, &cmod->leaf );
1287 }
1288 } else {
1289 CM_PositionTest( &tw );
1290 }
1291 } else {
1292 //
1293 // check for point special case
1294 //
1295 if ( tw.size[0][0] == 0 && tw.size[0][1] == 0 && tw.size[0][2] == 0 ) {
1296 tw.isPoint = qtrue;
1297 VectorClear( tw.extents );
1298 } else {
1299 tw.isPoint = qfalse;
1300 tw.extents[0] = tw.size[1][0];
1301 tw.extents[1] = tw.size[1][1];
1302 tw.extents[2] = tw.size[1][2];
1303 }
1304
1305 //
1306 // general sweeping through world
1307 //
1308 if ( model ) {
1309 #ifdef ALWAYS_BBOX_VS_BBOX
1310 if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE ) {
1311 tw.sphere.use = qfalse;
1312 CM_TraceThroughLeaf( &tw, &cmod->leaf );
1313 } else
1314 #elif defined( ALWAYS_CAPSULE_VS_CAPSULE )
1315 if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE ) {
1316 CM_TraceCapsuleThroughCapsule( &tw, model );
1317 } else
1318 #endif
1319 if ( model == CAPSULE_MODEL_HANDLE ) {
1320 if ( tw.sphere.use ) {
1321 CM_TraceCapsuleThroughCapsule( &tw, model );
1322 } else {
1323 CM_TraceBoundingBoxThroughCapsule( &tw, model );
1324 }
1325 } else {
1326 CM_TraceThroughLeaf( &tw, &cmod->leaf );
1327 }
1328 } else {
1329 CM_TraceThroughTree( &tw, 0, 0, 1, tw.start, tw.end );
1330 }
1331 }
1332
1333 // generate endpos from the original, unmodified start/end
1334 if ( tw.trace.fraction == 1 ) {
1335 VectorCopy( end, tw.trace.endpos );
1336 } else {
1337 for ( i = 0 ; i < 3 ; i++ ) {
1338 tw.trace.endpos[i] = start[i] + tw.trace.fraction * ( end[i] - start[i] );
1339 }
1340 }
1341
1342 *results = tw.trace;
1343 }
1344
1345 /*
1346 ==================
1347 CM_BoxTrace
1348 ==================
1349 */
1350 void CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
1351 const vec3_t mins, const vec3_t maxs,
1352 clipHandle_t model, int brushmask, int capsule ) {
1353 CM_Trace( results, start, end, mins, maxs, model, vec3_origin, brushmask, capsule, NULL );
1354 }
1355
1356 /*
1357 ==================
1358 CM_TransformedBoxTrace
1359
1360 Handles offseting and rotation of the end points for moving and
1361 rotating entities
1362 ==================
1363 */
1364 void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
1365 const vec3_t mins, const vec3_t maxs,
1366 clipHandle_t model, int brushmask,
1367 const vec3_t origin, const vec3_t angles, int capsule ) {
1368 trace_t trace;
1369 vec3_t start_l, end_l;
1370 qboolean rotated;
1371 vec3_t offset;
1372 vec3_t symetricSize[2];
1373 vec3_t matrix[3], transpose[3];
1374 int i;
1375 float halfwidth;
1376 float halfheight;
1377 float t;
1378 sphere_t sphere;
1379
1380 if ( !mins ) {
1381 mins = vec3_origin;
1382 }
1383 if ( !maxs ) {
1384 maxs = vec3_origin;
1385 }
1386
1387 // adjust so that mins and maxs are always symetric, which
1388 // avoids some complications with plane expanding of rotated
1389 // bmodels
1390 for ( i = 0 ; i < 3 ; i++ ) {
1391 offset[i] = ( mins[i] + maxs[i] ) * 0.5;
1392 symetricSize[0][i] = mins[i] - offset[i];
1393 symetricSize[1][i] = maxs[i] - offset[i];
1394 start_l[i] = start[i] + offset[i];
1395 end_l[i] = end[i] + offset[i];
1396 }
1397
1398 // subtract origin offset
1399 VectorSubtract( start_l, origin, start_l );
1400 VectorSubtract( end_l, origin, end_l );
1401
1402 // rotate start and end into the models frame of reference
1403 if ( model != BOX_MODEL_HANDLE &&
1404 ( angles[0] || angles[1] || angles[2] ) ) {
1405 rotated = qtrue;
1406 } else {
1407 rotated = qfalse;
1408 }
1409
1410 halfwidth = symetricSize[ 1 ][ 0 ];
1411 halfheight = symetricSize[ 1 ][ 2 ];
1412
1413 sphere.use = capsule;
1414 sphere.radius = ( halfwidth > halfheight ) ? halfheight : halfwidth;
1415 sphere.halfheight = halfheight;
1416 t = halfheight - sphere.radius;
1417
1418 if ( rotated ) {
1419 // rotation on trace line (start-end) instead of rotating the bmodel
1420 // NOTE: This is still incorrect for bounding boxes because the actual bounding
1421 // box that is swept through the model is not rotated. We cannot rotate
1422 // the bounding box or the bmodel because that would make all the brush
1423 // bevels invalid.
1424 // However this is correct for capsules since a capsule itself is rotated too.
1425 CreateRotationMatrix( angles, matrix );
1426 // NOTE TTimo gcc doesn't like the vec3_t m[3] to const vec3_t m[3] casting
1427 RotatePoint( start_l, (const vec3_t *)matrix );
1428 RotatePoint( end_l, (const vec3_t *)matrix );
1429 // rotated sphere offset for capsule
1430 sphere.offset[0] = matrix[0][ 2 ] * t;
1431 sphere.offset[1] = -matrix[1][ 2 ] * t;
1432 sphere.offset[2] = matrix[2][ 2 ] * t;
1433 } else {
1434 VectorSet( sphere.offset, 0, 0, t );
1435 }
1436
1437 // sweep the box through the model
1438 CM_Trace( &trace, start_l, end_l, symetricSize[0], symetricSize[1], model, origin, brushmask, capsule, &sphere );
1439
1440 // if the bmodel was rotated and there was a collision
1441 if ( rotated && trace.fraction != 1.0 ) {
1442 // rotation of bmodel collision plane
1443 TransposeMatrix( (const vec3_t *)matrix, transpose );
1444 RotatePoint( trace.plane.normal, (const vec3_t *)transpose );
1445 }
1446
1447 // re-calculate the end position of the trace because the trace.endpos
1448 // calculated by CM_Trace could be rotated and have an offset
1449 trace.endpos[0] = start[0] + trace.fraction * ( end[0] - start[0] );
1450 trace.endpos[1] = start[1] + trace.fraction * ( end[1] - start[1] );
1451 trace.endpos[2] = start[2] + trace.fraction * ( end[2] - start[2] );
1452
1453 *results = trace;
1454 }
1455