12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472 |
- /*
- ===========================================================================
- 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 Foobar; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- ===========================================================================
- */
- #include "cm_local.h"
- // always use bbox vs. bbox collision and never capsule vs. bbox or vice versa
- //#define ALWAYS_BBOX_VS_BBOX
- // always use capsule vs. capsule collision and never capsule vs. bbox or vice versa
- //#define ALWAYS_CAPSULE_VS_CAPSULE
- //#define CAPSULE_DEBUG
- /*
- ===============================================================================
- BASIC MATH
- ===============================================================================
- */
- /*
- ================
- RotatePoint
- ================
- */
- void RotatePoint(vec3_t point, /*const*/ vec3_t matrix[3]) { // bk: FIXME
- vec3_t tvec;
- VectorCopy(point, tvec);
- point[0] = DotProduct(matrix[0], tvec);
- point[1] = DotProduct(matrix[1], tvec);
- point[2] = DotProduct(matrix[2], tvec);
- }
- /*
- ================
- TransposeMatrix
- ================
- */
- void TransposeMatrix(/*const*/ vec3_t matrix[3], vec3_t transpose[3]) { // bk: FIXME
- int i, j;
- for (i = 0; i < 3; i++) {
- for (j = 0; j < 3; j++) {
- transpose[i][j] = matrix[j][i];
- }
- }
- }
- /*
- ================
- CreateRotationMatrix
- ================
- */
- void CreateRotationMatrix(const vec3_t angles, vec3_t matrix[3]) {
- AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
- VectorInverse(matrix[1]);
- }
- /*
- ================
- CM_ProjectPointOntoVector
- ================
- */
- void CM_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vDir, vec3_t vProj )
- {
- vec3_t pVec;
- VectorSubtract( point, vStart, pVec );
- // project onto the directional vector for this segment
- VectorMA( vStart, DotProduct( pVec, vDir ), vDir, vProj );
- }
- /*
- ================
- CM_DistanceFromLineSquared
- ================
- */
- float CM_DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2, vec3_t dir) {
- vec3_t proj, t;
- int j;
- CM_ProjectPointOntoVector(p, lp1, dir, proj);
- for (j = 0; j < 3; j++)
- if ((proj[j] > lp1[j] && proj[j] > lp2[j]) ||
- (proj[j] < lp1[j] && proj[j] < lp2[j]))
- break;
- if (j < 3) {
- if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))
- VectorSubtract(p, lp1, t);
- else
- VectorSubtract(p, lp2, t);
- return VectorLengthSquared(t);
- }
- VectorSubtract(p, proj, t);
- return VectorLengthSquared(t);
- }
- /*
- ================
- CM_VectorDistanceSquared
- ================
- */
- float CM_VectorDistanceSquared(vec3_t p1, vec3_t p2) {
- vec3_t dir;
- VectorSubtract(p2, p1, dir);
- return VectorLengthSquared(dir);
- }
- /*
- ================
- SquareRootFloat
- ================
- */
- float SquareRootFloat(float number) {
- long i;
- float x, y;
- const float f = 1.5F;
- x = number * 0.5F;
- y = number;
- i = * ( long * ) &y;
- i = 0x5f3759df - ( i >> 1 );
- y = * ( float * ) &i;
- y = y * ( f - ( x * y * y ) );
- y = y * ( f - ( x * y * y ) );
- return number * y;
- }
- /*
- ===============================================================================
- POSITION TESTING
- ===============================================================================
- */
- /*
- ================
- CM_TestBoxInBrush
- ================
- */
- void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) {
- int i;
- cplane_t *plane;
- float dist;
- float d1;
- cbrushside_t *side;
- float t;
- vec3_t startp;
- if (!brush->numsides) {
- return;
- }
- // special test for axial
- if ( tw->bounds[0][0] > brush->bounds[1][0]
- || tw->bounds[0][1] > brush->bounds[1][1]
- || tw->bounds[0][2] > brush->bounds[1][2]
- || tw->bounds[1][0] < brush->bounds[0][0]
- || tw->bounds[1][1] < brush->bounds[0][1]
- || tw->bounds[1][2] < brush->bounds[0][2]
- ) {
- return;
- }
- if ( tw->sphere.use ) {
- // the first six planes are the axial planes, so we only
- // need to test the remainder
- for ( i = 6 ; i < brush->numsides ; i++ ) {
- side = brush->sides + i;
- plane = side->plane;
- // adjust the plane distance apropriately for radius
- dist = plane->dist + tw->sphere.radius;
- // find the closest point on the capsule to the plane
- t = DotProduct( plane->normal, tw->sphere.offset );
- if ( t > 0 )
- {
- VectorSubtract( tw->start, tw->sphere.offset, startp );
- }
- else
- {
- VectorAdd( tw->start, tw->sphere.offset, startp );
- }
- d1 = DotProduct( startp, plane->normal ) - dist;
- // if completely in front of face, no intersection
- if ( d1 > 0 ) {
- return;
- }
- }
- } else {
- // the first six planes are the axial planes, so we only
- // need to test the remainder
- for ( i = 6 ; i < brush->numsides ; i++ ) {
- side = brush->sides + i;
- plane = side->plane;
- // adjust the plane distance apropriately for mins/maxs
- dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal );
- d1 = DotProduct( tw->start, plane->normal ) - dist;
- // if completely in front of face, no intersection
- if ( d1 > 0 ) {
- return;
- }
- }
- }
- // inside this brush
- tw->trace.startsolid = tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- tw->trace.contents = brush->contents;
- }
- /*
- ================
- CM_TestInLeaf
- ================
- */
- void CM_TestInLeaf( traceWork_t *tw, cLeaf_t *leaf ) {
- int k;
- int brushnum;
- cbrush_t *b;
- cPatch_t *patch;
- // test box position against all brushes in the leaf
- for (k=0 ; k<leaf->numLeafBrushes ; k++) {
- brushnum = cm.leafbrushes[leaf->firstLeafBrush+k];
- b = &cm.brushes[brushnum];
- if (b->checkcount == cm.checkcount) {
- continue; // already checked this brush in another leaf
- }
- b->checkcount = cm.checkcount;
- if ( !(b->contents & tw->contents)) {
- continue;
- }
-
- CM_TestBoxInBrush( tw, b );
- if ( tw->trace.allsolid ) {
- return;
- }
- }
- // test against all patches
- #ifdef BSPC
- if (1) {
- #else
- if ( !cm_noCurves->integer ) {
- #endif //BSPC
- for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) {
- patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ];
- if ( !patch ) {
- continue;
- }
- if ( patch->checkcount == cm.checkcount ) {
- continue; // already checked this brush in another leaf
- }
- patch->checkcount = cm.checkcount;
- if ( !(patch->contents & tw->contents)) {
- continue;
- }
-
- if ( CM_PositionTestInPatchCollide( tw, patch->pc ) ) {
- tw->trace.startsolid = tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- tw->trace.contents = patch->contents;
- return;
- }
- }
- }
- }
- /*
- ==================
- CM_TestCapsuleInCapsule
- capsule inside capsule check
- ==================
- */
- void CM_TestCapsuleInCapsule( traceWork_t *tw, clipHandle_t model ) {
- int i;
- vec3_t mins, maxs;
- vec3_t top, bottom;
- vec3_t p1, p2, tmp;
- vec3_t offset, symetricSize[2];
- float radius, halfwidth, halfheight, offs, r;
- CM_ModelBounds(model, mins, maxs);
- VectorAdd(tw->start, tw->sphere.offset, top);
- VectorSubtract(tw->start, tw->sphere.offset, bottom);
- for ( i = 0 ; i < 3 ; i++ ) {
- offset[i] = ( mins[i] + maxs[i] ) * 0.5;
- symetricSize[0][i] = mins[i] - offset[i];
- symetricSize[1][i] = maxs[i] - offset[i];
- }
- halfwidth = symetricSize[ 1 ][ 0 ];
- halfheight = symetricSize[ 1 ][ 2 ];
- radius = ( halfwidth > halfheight ) ? halfheight : halfwidth;
- offs = halfheight - radius;
- r = Square(tw->sphere.radius + radius);
- // check if any of the spheres overlap
- VectorCopy(offset, p1);
- p1[2] += offs;
- VectorSubtract(p1, top, tmp);
- if ( VectorLengthSquared(tmp) < r ) {
- tw->trace.startsolid = tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- }
- VectorSubtract(p1, bottom, tmp);
- if ( VectorLengthSquared(tmp) < r ) {
- tw->trace.startsolid = tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- }
- VectorCopy(offset, p2);
- p2[2] -= offs;
- VectorSubtract(p2, top, tmp);
- if ( VectorLengthSquared(tmp) < r ) {
- tw->trace.startsolid = tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- }
- VectorSubtract(p2, bottom, tmp);
- if ( VectorLengthSquared(tmp) < r ) {
- tw->trace.startsolid = tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- }
- // if between cylinder up and lower bounds
- if ( (top[2] >= p1[2] && top[2] <= p2[2]) ||
- (bottom[2] >= p1[2] && bottom[2] <= p2[2]) ) {
- // 2d coordinates
- top[2] = p1[2] = 0;
- // if the cylinders overlap
- VectorSubtract(top, p1, tmp);
- if ( VectorLengthSquared(tmp) < r ) {
- tw->trace.startsolid = tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- }
- }
- }
- /*
- ==================
- CM_TestBoundingBoxInCapsule
- bounding box inside capsule check
- ==================
- */
- void CM_TestBoundingBoxInCapsule( traceWork_t *tw, clipHandle_t model ) {
- vec3_t mins, maxs, offset, size[2];
- clipHandle_t h;
- cmodel_t *cmod;
- int i;
- // mins maxs of the capsule
- CM_ModelBounds(model, mins, maxs);
- // offset for capsule center
- for ( i = 0 ; i < 3 ; i++ ) {
- offset[i] = ( mins[i] + maxs[i] ) * 0.5;
- size[0][i] = mins[i] - offset[i];
- size[1][i] = maxs[i] - offset[i];
- tw->start[i] -= offset[i];
- tw->end[i] -= offset[i];
- }
- // replace the bounding box with the capsule
- tw->sphere.use = qtrue;
- tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2]: size[1][0];
- tw->sphere.halfheight = size[1][2];
- VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius );
- // replace the capsule with the bounding box
- h = CM_TempBoxModel(tw->size[0], tw->size[1], qfalse);
- // calculate collision
- cmod = CM_ClipHandleToModel( h );
- CM_TestInLeaf( tw, &cmod->leaf );
- }
- /*
- ==================
- CM_PositionTest
- ==================
- */
- #define MAX_POSITION_LEAFS 1024
- void CM_PositionTest( traceWork_t *tw ) {
- int leafs[MAX_POSITION_LEAFS];
- int i;
- leafList_t ll;
- // identify the leafs we are touching
- VectorAdd( tw->start, tw->size[0], ll.bounds[0] );
- VectorAdd( tw->start, tw->size[1], ll.bounds[1] );
- for (i=0 ; i<3 ; i++) {
- ll.bounds[0][i] -= 1;
- ll.bounds[1][i] += 1;
- }
- ll.count = 0;
- ll.maxcount = MAX_POSITION_LEAFS;
- ll.list = leafs;
- ll.storeLeafs = CM_StoreLeafs;
- ll.lastLeaf = 0;
- ll.overflowed = qfalse;
- cm.checkcount++;
- CM_BoxLeafnums_r( &ll, 0 );
- cm.checkcount++;
- // test the contents of the leafs
- for (i=0 ; i < ll.count ; i++) {
- CM_TestInLeaf( tw, &cm.leafs[leafs[i]] );
- if ( tw->trace.allsolid ) {
- break;
- }
- }
- }
- /*
- ===============================================================================
- TRACING
- ===============================================================================
- */
- /*
- ================
- CM_TraceThroughPatch
- ================
- */
- void CM_TraceThroughPatch( traceWork_t *tw, cPatch_t *patch ) {
- float oldFrac;
- c_patch_traces++;
- oldFrac = tw->trace.fraction;
- CM_TraceThroughPatchCollide( tw, patch->pc );
- if ( tw->trace.fraction < oldFrac ) {
- tw->trace.surfaceFlags = patch->surfaceFlags;
- tw->trace.contents = patch->contents;
- }
- }
- /*
- ================
- CM_TraceThroughBrush
- ================
- */
- void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) {
- int i;
- cplane_t *plane, *clipplane;
- float dist;
- float enterFrac, leaveFrac;
- float d1, d2;
- qboolean getout, startout;
- float f;
- cbrushside_t *side, *leadside;
- float t;
- vec3_t startp;
- vec3_t endp;
- enterFrac = -1.0;
- leaveFrac = 1.0;
- clipplane = NULL;
- if ( !brush->numsides ) {
- return;
- }
- c_brush_traces++;
- getout = qfalse;
- startout = qfalse;
- leadside = NULL;
- if ( tw->sphere.use ) {
- //
- // compare the trace against all planes of the brush
- // find the latest time the trace crosses a plane towards the interior
- // and the earliest time the trace crosses a plane towards the exterior
- //
- for (i = 0; i < brush->numsides; i++) {
- side = brush->sides + i;
- plane = side->plane;
- // adjust the plane distance apropriately for radius
- dist = plane->dist + tw->sphere.radius;
- // find the closest point on the capsule to the plane
- t = DotProduct( plane->normal, tw->sphere.offset );
- if ( t > 0 )
- {
- VectorSubtract( tw->start, tw->sphere.offset, startp );
- VectorSubtract( tw->end, tw->sphere.offset, endp );
- }
- else
- {
- VectorAdd( tw->start, tw->sphere.offset, startp );
- VectorAdd( tw->end, tw->sphere.offset, endp );
- }
- d1 = DotProduct( startp, plane->normal ) - dist;
- d2 = DotProduct( endp, plane->normal ) - dist;
- if (d2 > 0) {
- getout = qtrue; // endpoint is not in solid
- }
- if (d1 > 0) {
- startout = qtrue;
- }
- // if completely in front of face, no intersection with the entire brush
- if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) {
- return;
- }
- // if it doesn't cross the plane, the plane isn't relevent
- if (d1 <= 0 && d2 <= 0 ) {
- continue;
- }
- // crosses face
- if (d1 > d2) { // enter
- f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2);
- if ( f < 0 ) {
- f = 0;
- }
- if (f > enterFrac) {
- enterFrac = f;
- clipplane = plane;
- leadside = side;
- }
- } else { // leave
- f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2);
- if ( f > 1 ) {
- f = 1;
- }
- if (f < leaveFrac) {
- leaveFrac = f;
- }
- }
- }
- } else {
- //
- // compare the trace against all planes of the brush
- // find the latest time the trace crosses a plane towards the interior
- // and the earliest time the trace crosses a plane towards the exterior
- //
- for (i = 0; i < brush->numsides; i++) {
- side = brush->sides + i;
- plane = side->plane;
- // adjust the plane distance apropriately for mins/maxs
- dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal );
- d1 = DotProduct( tw->start, plane->normal ) - dist;
- d2 = DotProduct( tw->end, plane->normal ) - dist;
- if (d2 > 0) {
- getout = qtrue; // endpoint is not in solid
- }
- if (d1 > 0) {
- startout = qtrue;
- }
- // if completely in front of face, no intersection with the entire brush
- if (d1 > 0 && ( d2 >= SURFACE_CLIP_EPSILON || d2 >= d1 ) ) {
- return;
- }
- // if it doesn't cross the plane, the plane isn't relevent
- if (d1 <= 0 && d2 <= 0 ) {
- continue;
- }
- // crosses face
- if (d1 > d2) { // enter
- f = (d1-SURFACE_CLIP_EPSILON) / (d1-d2);
- if ( f < 0 ) {
- f = 0;
- }
- if (f > enterFrac) {
- enterFrac = f;
- clipplane = plane;
- leadside = side;
- }
- } else { // leave
- f = (d1+SURFACE_CLIP_EPSILON) / (d1-d2);
- if ( f > 1 ) {
- f = 1;
- }
- if (f < leaveFrac) {
- leaveFrac = f;
- }
- }
- }
- }
- //
- // all planes have been checked, and the trace was not
- // completely outside the brush
- //
- if (!startout) { // original point was inside brush
- tw->trace.startsolid = qtrue;
- if (!getout) {
- tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- tw->trace.contents = brush->contents;
- }
- return;
- }
-
- if (enterFrac < leaveFrac) {
- if (enterFrac > -1 && enterFrac < tw->trace.fraction) {
- if (enterFrac < 0) {
- enterFrac = 0;
- }
- tw->trace.fraction = enterFrac;
- tw->trace.plane = *clipplane;
- tw->trace.surfaceFlags = leadside->surfaceFlags;
- tw->trace.contents = brush->contents;
- }
- }
- }
- /*
- ================
- CM_TraceThroughLeaf
- ================
- */
- void CM_TraceThroughLeaf( traceWork_t *tw, cLeaf_t *leaf ) {
- int k;
- int brushnum;
- cbrush_t *b;
- cPatch_t *patch;
- // trace line against all brushes in the leaf
- for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) {
- brushnum = cm.leafbrushes[leaf->firstLeafBrush+k];
- b = &cm.brushes[brushnum];
- if ( b->checkcount == cm.checkcount ) {
- continue; // already checked this brush in another leaf
- }
- b->checkcount = cm.checkcount;
- if ( !(b->contents & tw->contents) ) {
- continue;
- }
- CM_TraceThroughBrush( tw, b );
- if ( !tw->trace.fraction ) {
- return;
- }
- }
- // trace line against all patches in the leaf
- #ifdef BSPC
- if (1) {
- #else
- if ( !cm_noCurves->integer ) {
- #endif
- for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) {
- patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ];
- if ( !patch ) {
- continue;
- }
- if ( patch->checkcount == cm.checkcount ) {
- continue; // already checked this patch in another leaf
- }
- patch->checkcount = cm.checkcount;
- if ( !(patch->contents & tw->contents) ) {
- continue;
- }
-
- CM_TraceThroughPatch( tw, patch );
- if ( !tw->trace.fraction ) {
- return;
- }
- }
- }
- }
- #define RADIUS_EPSILON 1.0f
- /*
- ================
- CM_TraceThroughSphere
- get the first intersection of the ray with the sphere
- ================
- */
- void CM_TraceThroughSphere( traceWork_t *tw, vec3_t origin, float radius, vec3_t start, vec3_t end ) {
- float l1, l2, length, scale, fraction;
- float a, b, c, d, sqrtd;
- vec3_t v1, dir, intersection;
- // if inside the sphere
- VectorSubtract(start, origin, dir);
- l1 = VectorLengthSquared(dir);
- if (l1 < Square(radius)) {
- tw->trace.fraction = 0;
- tw->trace.startsolid = qtrue;
- // test for allsolid
- VectorSubtract(end, origin, dir);
- l1 = VectorLengthSquared(dir);
- if (l1 < Square(radius)) {
- tw->trace.allsolid = qtrue;
- }
- return;
- }
- //
- VectorSubtract(end, start, dir);
- length = VectorNormalize(dir);
- //
- l1 = CM_DistanceFromLineSquared(origin, start, end, dir);
- VectorSubtract(end, origin, v1);
- l2 = VectorLengthSquared(v1);
- // if no intersection with the sphere and the end point is at least an epsilon away
- if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) {
- return;
- }
- //
- // | origin - (start + t * dir) | = radius
- // a = dir[0]^2 + dir[1]^2 + dir[2]^2;
- // b = 2 * (dir[0] * (start[0] - origin[0]) + dir[1] * (start[1] - origin[1]) + dir[2] * (start[2] - origin[2]));
- // c = (start[0] - origin[0])^2 + (start[1] - origin[1])^2 + (start[2] - origin[2])^2 - radius^2;
- //
- VectorSubtract(start, origin, v1);
- // dir is normalized so a = 1
- a = 1.0f;//dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2];
- b = 2.0f * (dir[0] * v1[0] + dir[1] * v1[1] + dir[2] * v1[2]);
- c = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON);
- d = b * b - 4.0f * c;// * a;
- if (d > 0) {
- sqrtd = SquareRootFloat(d);
- // = (- b + sqrtd) * 0.5f; // / (2.0f * a);
- fraction = (- b - sqrtd) * 0.5f; // / (2.0f * a);
- //
- if (fraction < 0) {
- fraction = 0;
- }
- else {
- fraction /= length;
- }
- if ( fraction < tw->trace.fraction ) {
- tw->trace.fraction = fraction;
- VectorSubtract(end, start, dir);
- VectorMA(start, fraction, dir, intersection);
- VectorSubtract(intersection, origin, dir);
- #ifdef CAPSULE_DEBUG
- l2 = VectorLength(dir);
- if (l2 < radius) {
- int bah = 1;
- }
- #endif
- scale = 1 / (radius+RADIUS_EPSILON);
- VectorScale(dir, scale, dir);
- VectorCopy(dir, tw->trace.plane.normal);
- VectorAdd( tw->modelOrigin, intersection, intersection);
- tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection);
- tw->trace.contents = CONTENTS_BODY;
- }
- }
- else if (d == 0) {
- //t1 = (- b ) / 2;
- // slide along the sphere
- }
- // no intersection at all
- }
- /*
- ================
- CM_TraceThroughVerticalCylinder
- get the first intersection of the ray with the cylinder
- the cylinder extends halfheight above and below the origin
- ================
- */
- void CM_TraceThroughVerticalCylinder( traceWork_t *tw, vec3_t origin, float radius, float halfheight, vec3_t start, vec3_t end) {
- float length, scale, fraction, l1, l2;
- float a, b, c, d, sqrtd;
- vec3_t v1, dir, start2d, end2d, org2d, intersection;
- // 2d coordinates
- VectorSet(start2d, start[0], start[1], 0);
- VectorSet(end2d, end[0], end[1], 0);
- VectorSet(org2d, origin[0], origin[1], 0);
- // if between lower and upper cylinder bounds
- if (start[2] <= origin[2] + halfheight &&
- start[2] >= origin[2] - halfheight) {
- // if inside the cylinder
- VectorSubtract(start2d, org2d, dir);
- l1 = VectorLengthSquared(dir);
- if (l1 < Square(radius)) {
- tw->trace.fraction = 0;
- tw->trace.startsolid = qtrue;
- VectorSubtract(end2d, org2d, dir);
- l1 = VectorLengthSquared(dir);
- if (l1 < Square(radius)) {
- tw->trace.allsolid = qtrue;
- }
- return;
- }
- }
- //
- VectorSubtract(end2d, start2d, dir);
- length = VectorNormalize(dir);
- //
- l1 = CM_DistanceFromLineSquared(org2d, start2d, end2d, dir);
- VectorSubtract(end2d, org2d, v1);
- l2 = VectorLengthSquared(v1);
- // if no intersection with the cylinder and the end point is at least an epsilon away
- if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) {
- return;
- }
- //
- //
- // (start[0] - origin[0] - t * dir[0]) ^ 2 + (start[1] - origin[1] - t * dir[1]) ^ 2 = radius ^ 2
- // (v1[0] + t * dir[0]) ^ 2 + (v1[1] + t * dir[1]) ^ 2 = radius ^ 2;
- // v1[0] ^ 2 + 2 * v1[0] * t * dir[0] + (t * dir[0]) ^ 2 +
- // v1[1] ^ 2 + 2 * v1[1] * t * dir[1] + (t * dir[1]) ^ 2 = radius ^ 2
- // t ^ 2 * (dir[0] ^ 2 + dir[1] ^ 2) + t * (2 * v1[0] * dir[0] + 2 * v1[1] * dir[1]) +
- // v1[0] ^ 2 + v1[1] ^ 2 - radius ^ 2 = 0
- //
- VectorSubtract(start, origin, v1);
- // dir is normalized so we can use a = 1
- a = 1.0f;// * (dir[0] * dir[0] + dir[1] * dir[1]);
- b = 2.0f * (v1[0] * dir[0] + v1[1] * dir[1]);
- c = v1[0] * v1[0] + v1[1] * v1[1] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON);
- d = b * b - 4.0f * c;// * a;
- if (d > 0) {
- sqrtd = SquareRootFloat(d);
- // = (- b + sqrtd) * 0.5f;// / (2.0f * a);
- fraction = (- b - sqrtd) * 0.5f;// / (2.0f * a);
- //
- if (fraction < 0) {
- fraction = 0;
- }
- else {
- fraction /= length;
- }
- if ( fraction < tw->trace.fraction ) {
- VectorSubtract(end, start, dir);
- VectorMA(start, fraction, dir, intersection);
- // if the intersection is between the cylinder lower and upper bound
- if (intersection[2] <= origin[2] + halfheight &&
- intersection[2] >= origin[2] - halfheight) {
- //
- tw->trace.fraction = fraction;
- VectorSubtract(intersection, origin, dir);
- dir[2] = 0;
- #ifdef CAPSULE_DEBUG
- l2 = VectorLength(dir);
- if (l2 <= radius) {
- int bah = 1;
- }
- #endif
- scale = 1 / (radius+RADIUS_EPSILON);
- VectorScale(dir, scale, dir);
- VectorCopy(dir, tw->trace.plane.normal);
- VectorAdd( tw->modelOrigin, intersection, intersection);
- tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection);
- tw->trace.contents = CONTENTS_BODY;
- }
- }
- }
- else if (d == 0) {
- //t[0] = (- b ) / 2 * a;
- // slide along the cylinder
- }
- // no intersection at all
- }
- /*
- ================
- CM_TraceCapsuleThroughCapsule
- capsule vs. capsule collision (not rotated)
- ================
- */
- void CM_TraceCapsuleThroughCapsule( traceWork_t *tw, clipHandle_t model ) {
- int i;
- vec3_t mins, maxs;
- vec3_t top, bottom, starttop, startbottom, endtop, endbottom;
- vec3_t offset, symetricSize[2];
- float radius, halfwidth, halfheight, offs, h;
- CM_ModelBounds(model, mins, maxs);
- // test trace bounds vs. capsule bounds
- if ( tw->bounds[0][0] > maxs[0] + RADIUS_EPSILON
- || tw->bounds[0][1] > maxs[1] + RADIUS_EPSILON
- || tw->bounds[0][2] > maxs[2] + RADIUS_EPSILON
- || tw->bounds[1][0] < mins[0] - RADIUS_EPSILON
- || tw->bounds[1][1] < mins[1] - RADIUS_EPSILON
- || tw->bounds[1][2] < mins[2] - RADIUS_EPSILON
- ) {
- return;
- }
- // top origin and bottom origin of each sphere at start and end of trace
- VectorAdd(tw->start, tw->sphere.offset, starttop);
- VectorSubtract(tw->start, tw->sphere.offset, startbottom);
- VectorAdd(tw->end, tw->sphere.offset, endtop);
- VectorSubtract(tw->end, tw->sphere.offset, endbottom);
- // calculate top and bottom of the capsule spheres to collide with
- for ( i = 0 ; i < 3 ; i++ ) {
- offset[i] = ( mins[i] + maxs[i] ) * 0.5;
- symetricSize[0][i] = mins[i] - offset[i];
- symetricSize[1][i] = maxs[i] - offset[i];
- }
- halfwidth = symetricSize[ 1 ][ 0 ];
- halfheight = symetricSize[ 1 ][ 2 ];
- radius = ( halfwidth > halfheight ) ? halfheight : halfwidth;
- offs = halfheight - radius;
- VectorCopy(offset, top);
- top[2] += offs;
- VectorCopy(offset, bottom);
- bottom[2] -= offs;
- // expand radius of spheres
- radius += tw->sphere.radius;
- // if there is horizontal movement
- if ( tw->start[0] != tw->end[0] || tw->start[1] != tw->end[1] ) {
- // height of the expanded cylinder is the height of both cylinders minus the radius of both spheres
- h = halfheight + tw->sphere.halfheight - radius;
- // if the cylinder has a height
- if ( h > 0 ) {
- // test for collisions between the cylinders
- CM_TraceThroughVerticalCylinder(tw, offset, radius, h, tw->start, tw->end);
- }
- }
- // test for collision between the spheres
- CM_TraceThroughSphere(tw, top, radius, startbottom, endbottom);
- CM_TraceThroughSphere(tw, bottom, radius, starttop, endtop);
- }
- /*
- ================
- CM_TraceBoundingBoxThroughCapsule
- bounding box vs. capsule collision
- ================
- */
- void CM_TraceBoundingBoxThroughCapsule( traceWork_t *tw, clipHandle_t model ) {
- vec3_t mins, maxs, offset, size[2];
- clipHandle_t h;
- cmodel_t *cmod;
- int i;
- // mins maxs of the capsule
- CM_ModelBounds(model, mins, maxs);
- // offset for capsule center
- for ( i = 0 ; i < 3 ; i++ ) {
- offset[i] = ( mins[i] + maxs[i] ) * 0.5;
- size[0][i] = mins[i] - offset[i];
- size[1][i] = maxs[i] - offset[i];
- tw->start[i] -= offset[i];
- tw->end[i] -= offset[i];
- }
- // replace the bounding box with the capsule
- tw->sphere.use = qtrue;
- tw->sphere.radius = ( size[1][0] > size[1][2] ) ? size[1][2]: size[1][0];
- tw->sphere.halfheight = size[1][2];
- VectorSet( tw->sphere.offset, 0, 0, size[1][2] - tw->sphere.radius );
- // replace the capsule with the bounding box
- h = CM_TempBoxModel(tw->size[0], tw->size[1], qfalse);
- // calculate collision
- cmod = CM_ClipHandleToModel( h );
- CM_TraceThroughLeaf( tw, &cmod->leaf );
- }
- //=========================================================================================
- /*
- ==================
- CM_TraceThroughTree
- Traverse all the contacted leafs from the start to the end position.
- If the trace is a point, they will be exactly in order, but for larger
- trace volumes it is possible to hit something in a later leaf with
- a smaller intercept fraction.
- ==================
- */
- void CM_TraceThroughTree( traceWork_t *tw, int num, float p1f, float p2f, vec3_t p1, vec3_t p2) {
- cNode_t *node;
- cplane_t *plane;
- float t1, t2, offset;
- float frac, frac2;
- float idist;
- vec3_t mid;
- int side;
- float midf;
- if (tw->trace.fraction <= p1f) {
- return; // already hit something nearer
- }
- // if < 0, we are in a leaf node
- if (num < 0) {
- CM_TraceThroughLeaf( tw, &cm.leafs[-1-num] );
- return;
- }
- //
- // find the point distances to the seperating plane
- // and the offset for the size of the box
- //
- node = cm.nodes + num;
- plane = node->plane;
- // adjust the plane distance apropriately for mins/maxs
- if ( plane->type < 3 ) {
- t1 = p1[plane->type] - plane->dist;
- t2 = p2[plane->type] - plane->dist;
- offset = tw->extents[plane->type];
- } else {
- t1 = DotProduct (plane->normal, p1) - plane->dist;
- t2 = DotProduct (plane->normal, p2) - plane->dist;
- if ( tw->isPoint ) {
- offset = 0;
- } else {
- #if 0 // bk010201 - DEAD
- // an axial brush right behind a slanted bsp plane
- // will poke through when expanded, so adjust
- // by sqrt(3)
- offset = fabs(tw->extents[0]*plane->normal[0]) +
- fabs(tw->extents[1]*plane->normal[1]) +
- fabs(tw->extents[2]*plane->normal[2]);
- offset *= 2;
- offset = tw->maxOffset;
- #endif
- // this is silly
- offset = 2048;
- }
- }
- // see which sides we need to consider
- if ( t1 >= offset + 1 && t2 >= offset + 1 ) {
- CM_TraceThroughTree( tw, node->children[0], p1f, p2f, p1, p2 );
- return;
- }
- if ( t1 < -offset - 1 && t2 < -offset - 1 ) {
- CM_TraceThroughTree( tw, node->children[1], p1f, p2f, p1, p2 );
- return;
- }
- // put the crosspoint SURFACE_CLIP_EPSILON pixels on the near side
- if ( t1 < t2 ) {
- idist = 1.0/(t1-t2);
- side = 1;
- frac2 = (t1 + offset + SURFACE_CLIP_EPSILON)*idist;
- frac = (t1 - offset + SURFACE_CLIP_EPSILON)*idist;
- } else if (t1 > t2) {
- idist = 1.0/(t1-t2);
- side = 0;
- frac2 = (t1 - offset - SURFACE_CLIP_EPSILON)*idist;
- frac = (t1 + offset + SURFACE_CLIP_EPSILON)*idist;
- } else {
- side = 0;
- frac = 1;
- frac2 = 0;
- }
- // move up to the node
- if ( frac < 0 ) {
- frac = 0;
- }
- if ( frac > 1 ) {
- frac = 1;
- }
-
- midf = p1f + (p2f - p1f)*frac;
- mid[0] = p1[0] + frac*(p2[0] - p1[0]);
- mid[1] = p1[1] + frac*(p2[1] - p1[1]);
- mid[2] = p1[2] + frac*(p2[2] - p1[2]);
- CM_TraceThroughTree( tw, node->children[side], p1f, midf, p1, mid );
- // go past the node
- if ( frac2 < 0 ) {
- frac2 = 0;
- }
- if ( frac2 > 1 ) {
- frac2 = 1;
- }
-
- midf = p1f + (p2f - p1f)*frac2;
- mid[0] = p1[0] + frac2*(p2[0] - p1[0]);
- mid[1] = p1[1] + frac2*(p2[1] - p1[1]);
- mid[2] = p1[2] + frac2*(p2[2] - p1[2]);
- CM_TraceThroughTree( tw, node->children[side^1], midf, p2f, mid, p2 );
- }
- //======================================================================
- /*
- ==================
- CM_Trace
- ==================
- */
- void CM_Trace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mins, vec3_t maxs,
- clipHandle_t model, const vec3_t origin, int brushmask, int capsule, sphere_t *sphere ) {
- int i;
- traceWork_t tw;
- vec3_t offset;
- cmodel_t *cmod;
- cmod = CM_ClipHandleToModel( model );
- cm.checkcount++; // for multi-check avoidance
- c_traces++; // for statistics, may be zeroed
- // fill in a default trace
- Com_Memset( &tw, 0, sizeof(tw) );
- tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise
- VectorCopy(origin, tw.modelOrigin);
- if (!cm.numNodes) {
- *results = tw.trace;
- return; // map not loaded, shouldn't happen
- }
- // allow NULL to be passed in for 0,0,0
- if ( !mins ) {
- mins = vec3_origin;
- }
- if ( !maxs ) {
- maxs = vec3_origin;
- }
- // set basic parms
- tw.contents = brushmask;
- // adjust so that mins and maxs are always symetric, which
- // avoids some complications with plane expanding of rotated
- // bmodels
- for ( i = 0 ; i < 3 ; i++ ) {
- offset[i] = ( mins[i] + maxs[i] ) * 0.5;
- tw.size[0][i] = mins[i] - offset[i];
- tw.size[1][i] = maxs[i] - offset[i];
- tw.start[i] = start[i] + offset[i];
- tw.end[i] = end[i] + offset[i];
- }
- // if a sphere is already specified
- if ( sphere ) {
- tw.sphere = *sphere;
- }
- else {
- tw.sphere.use = capsule;
- tw.sphere.radius = ( tw.size[1][0] > tw.size[1][2] ) ? tw.size[1][2]: tw.size[1][0];
- tw.sphere.halfheight = tw.size[1][2];
- VectorSet( tw.sphere.offset, 0, 0, tw.size[1][2] - tw.sphere.radius );
- }
- tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2];
- // tw.offsets[signbits] = vector to apropriate corner from origin
- tw.offsets[0][0] = tw.size[0][0];
- tw.offsets[0][1] = tw.size[0][1];
- tw.offsets[0][2] = tw.size[0][2];
- tw.offsets[1][0] = tw.size[1][0];
- tw.offsets[1][1] = tw.size[0][1];
- tw.offsets[1][2] = tw.size[0][2];
- tw.offsets[2][0] = tw.size[0][0];
- tw.offsets[2][1] = tw.size[1][1];
- tw.offsets[2][2] = tw.size[0][2];
- tw.offsets[3][0] = tw.size[1][0];
- tw.offsets[3][1] = tw.size[1][1];
- tw.offsets[3][2] = tw.size[0][2];
- tw.offsets[4][0] = tw.size[0][0];
- tw.offsets[4][1] = tw.size[0][1];
- tw.offsets[4][2] = tw.size[1][2];
- tw.offsets[5][0] = tw.size[1][0];
- tw.offsets[5][1] = tw.size[0][1];
- tw.offsets[5][2] = tw.size[1][2];
- tw.offsets[6][0] = tw.size[0][0];
- tw.offsets[6][1] = tw.size[1][1];
- tw.offsets[6][2] = tw.size[1][2];
- tw.offsets[7][0] = tw.size[1][0];
- tw.offsets[7][1] = tw.size[1][1];
- tw.offsets[7][2] = tw.size[1][2];
- //
- // calculate bounds
- //
- if ( tw.sphere.use ) {
- for ( i = 0 ; i < 3 ; i++ ) {
- if ( tw.start[i] < tw.end[i] ) {
- tw.bounds[0][i] = tw.start[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius;
- tw.bounds[1][i] = tw.end[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius;
- } else {
- tw.bounds[0][i] = tw.end[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius;
- tw.bounds[1][i] = tw.start[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius;
- }
- }
- }
- else {
- for ( i = 0 ; i < 3 ; i++ ) {
- if ( tw.start[i] < tw.end[i] ) {
- tw.bounds[0][i] = tw.start[i] + tw.size[0][i];
- tw.bounds[1][i] = tw.end[i] + tw.size[1][i];
- } else {
- tw.bounds[0][i] = tw.end[i] + tw.size[0][i];
- tw.bounds[1][i] = tw.start[i] + tw.size[1][i];
- }
- }
- }
- //
- // check for position test special case
- //
- if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) {
- if ( model ) {
- #ifdef ALWAYS_BBOX_VS_BBOX // bk010201 - FIXME - compile time flag?
- if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
- tw.sphere.use = qfalse;
- CM_TestInLeaf( &tw, &cmod->leaf );
- }
- else
- #elif defined(ALWAYS_CAPSULE_VS_CAPSULE)
- if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
- CM_TestCapsuleInCapsule( &tw, model );
- }
- else
- #endif
- if ( model == CAPSULE_MODEL_HANDLE ) {
- if ( tw.sphere.use ) {
- CM_TestCapsuleInCapsule( &tw, model );
- }
- else {
- CM_TestBoundingBoxInCapsule( &tw, model );
- }
- }
- else {
- CM_TestInLeaf( &tw, &cmod->leaf );
- }
- } else {
- CM_PositionTest( &tw );
- }
- } else {
- //
- // check for point special case
- //
- if ( tw.size[0][0] == 0 && tw.size[0][1] == 0 && tw.size[0][2] == 0 ) {
- tw.isPoint = qtrue;
- VectorClear( tw.extents );
- } else {
- tw.isPoint = qfalse;
- tw.extents[0] = tw.size[1][0];
- tw.extents[1] = tw.size[1][1];
- tw.extents[2] = tw.size[1][2];
- }
- //
- // general sweeping through world
- //
- if ( model ) {
- #ifdef ALWAYS_BBOX_VS_BBOX
- if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
- tw.sphere.use = qfalse;
- CM_TraceThroughLeaf( &tw, &cmod->leaf );
- }
- else
- #elif defined(ALWAYS_CAPSULE_VS_CAPSULE)
- if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) {
- CM_TraceCapsuleThroughCapsule( &tw, model );
- }
- else
- #endif
- if ( model == CAPSULE_MODEL_HANDLE ) {
- if ( tw.sphere.use ) {
- CM_TraceCapsuleThroughCapsule( &tw, model );
- }
- else {
- CM_TraceBoundingBoxThroughCapsule( &tw, model );
- }
- }
- else {
- CM_TraceThroughLeaf( &tw, &cmod->leaf );
- }
- } else {
- CM_TraceThroughTree( &tw, 0, 0, 1, tw.start, tw.end );
- }
- }
- // generate endpos from the original, unmodified start/end
- if ( tw.trace.fraction == 1 ) {
- VectorCopy (end, tw.trace.endpos);
- } else {
- for ( i=0 ; i<3 ; i++ ) {
- tw.trace.endpos[i] = start[i] + tw.trace.fraction * (end[i] - start[i]);
- }
- }
- // If allsolid is set (was entirely inside something solid), the plane is not valid.
- // If fraction == 1.0, we never hit anything, and thus the plane is not valid.
- // Otherwise, the normal on the plane should have unit length
- assert(tw.trace.allsolid ||
- tw.trace.fraction == 1.0 ||
- VectorLengthSquared(tw.trace.plane.normal) > 0.9999);
- *results = tw.trace;
- }
- /*
- ==================
- CM_BoxTrace
- ==================
- */
- void CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
- vec3_t mins, vec3_t maxs,
- clipHandle_t model, int brushmask, int capsule ) {
- CM_Trace( results, start, end, mins, maxs, model, vec3_origin, brushmask, capsule, NULL );
- }
- /*
- ==================
- CM_TransformedBoxTrace
- Handles offseting and rotation of the end points for moving and
- rotating entities
- ==================
- */
- void CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
- vec3_t mins, vec3_t maxs,
- clipHandle_t model, int brushmask,
- const vec3_t origin, const vec3_t angles, int capsule ) {
- trace_t trace;
- vec3_t start_l, end_l;
- qboolean rotated;
- vec3_t offset;
- vec3_t symetricSize[2];
- vec3_t matrix[3], transpose[3];
- int i;
- float halfwidth;
- float halfheight;
- float t;
- sphere_t sphere;
- if ( !mins ) {
- mins = vec3_origin;
- }
- if ( !maxs ) {
- maxs = vec3_origin;
- }
- // adjust so that mins and maxs are always symetric, which
- // avoids some complications with plane expanding of rotated
- // bmodels
- for ( i = 0 ; i < 3 ; i++ ) {
- offset[i] = ( mins[i] + maxs[i] ) * 0.5;
- symetricSize[0][i] = mins[i] - offset[i];
- symetricSize[1][i] = maxs[i] - offset[i];
- start_l[i] = start[i] + offset[i];
- end_l[i] = end[i] + offset[i];
- }
- // subtract origin offset
- VectorSubtract( start_l, origin, start_l );
- VectorSubtract( end_l, origin, end_l );
- // rotate start and end into the models frame of reference
- if ( model != BOX_MODEL_HANDLE &&
- (angles[0] || angles[1] || angles[2]) ) {
- rotated = qtrue;
- } else {
- rotated = qfalse;
- }
- halfwidth = symetricSize[ 1 ][ 0 ];
- halfheight = symetricSize[ 1 ][ 2 ];
- sphere.use = capsule;
- sphere.radius = ( halfwidth > halfheight ) ? halfheight : halfwidth;
- sphere.halfheight = halfheight;
- t = halfheight - sphere.radius;
- if (rotated) {
- // rotation on trace line (start-end) instead of rotating the bmodel
- // NOTE: This is still incorrect for bounding boxes because the actual bounding
- // box that is swept through the model is not rotated. We cannot rotate
- // the bounding box or the bmodel because that would make all the brush
- // bevels invalid.
- // However this is correct for capsules since a capsule itself is rotated too.
- CreateRotationMatrix(angles, matrix);
- RotatePoint(start_l, matrix);
- RotatePoint(end_l, matrix);
- // rotated sphere offset for capsule
- sphere.offset[0] = matrix[0][ 2 ] * t;
- sphere.offset[1] = -matrix[1][ 2 ] * t;
- sphere.offset[2] = matrix[2][ 2 ] * t;
- }
- else {
- VectorSet( sphere.offset, 0, 0, t );
- }
- // sweep the box through the model
- CM_Trace( &trace, start_l, end_l, symetricSize[0], symetricSize[1], model, origin, brushmask, capsule, &sphere );
- // if the bmodel was rotated and there was a collision
- if ( rotated && trace.fraction != 1.0 ) {
- // rotation of bmodel collision plane
- TransposeMatrix(matrix, transpose);
- RotatePoint(trace.plane.normal, transpose);
- }
- // re-calculate the end position of the trace because the trace.endpos
- // calculated by CM_Trace could be rotated and have an offset
- trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]);
- trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]);
- trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]);
- *results = trace;
- }
|