123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224 |
- #include "cm_local.h"
- #ifdef _XBOX
- #include "../renderer/tr_local.h"
- #endif
- /*
- ===============================================================================
- POSITION TESTING
- ===============================================================================
- */
- extern cvar_t *com_terrainPhysics;
- void VectorAdvance( const vec3_t veca, const float scale, const vec3_t vecb, vec3_t vecc);
- /*
- ================
- CM_TestBoxInBrush
- ================
- */
- void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) {
- int i;
- cplane_t *plane;
- float dist;
- float d1;
- cbrushside_t *side;
- 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;
- }
- // 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;
- #ifdef _XBOX
- plane = &cmg.planes[side->planeNum.GetValue()];
- #else
- plane = side->plane;
- #endif
- // 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_PlaneCollision
- Returns false for a quick getout
- ================
- */
- bool CM_PlaneCollision(traceWork_t *tw, cbrushside_t *side)
- {
- float dist, f;
- float d1, d2;
- #ifdef _XBOX
- cplane_t *plane = &cmg.planes[side->planeNum.GetValue()];
- #else
- cplane_t *plane = side->plane;
- #endif
- // 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.0f)
- {
- // endpoint is not in solid
- tw->getout = true;
- }
- if (d1 > 0.0f)
- {
- // startpoint is not in solid
- tw->startout = true;
- }
- // if completely in front of face, no intersection with the entire brush
- if ((d1 > 0.0f) && ( (d2 >= SURFACE_CLIP_EPSILON) || (d2 >= d1) ) )
- {
- return(false);
- }
- // if it doesn't cross the plane, the plane isn't relevent
- if ((d1 <= 0.0f) && (d2 <= 0.0f))
- {
- return(true);
- }
- // crosses face
- if (d1 > d2)
- { // enter
- f = (d1 - SURFACE_CLIP_EPSILON);
- if ( f < 0.0f )
- {
- f = 0.0f;
- if (f > tw->enterFrac)
- {
- tw->enterFrac = f;
- tw->clipplane = plane;
- tw->leadside = side;
- }
- }
- else if (f > tw->enterFrac * (d1 - d2) )
- {
- tw->enterFrac = f / (d1 - d2);
- tw->clipplane = plane;
- tw->leadside = side;
- }
- }
- else
- { // leave
- f = (d1 + SURFACE_CLIP_EPSILON);
- if ( f < (d1 - d2) )
- {
- f = 1.0f;
- if (f < tw->leaveFrac)
- {
- tw->leaveFrac = f;
- }
- }
- else if (f > tw->leaveFrac * (d1 - d2) )
- {
- tw->leaveFrac = f / (d1 - d2);
- }
- }
- return(true);
- }
- /*
- ================
- CM_TraceThroughBrush
- ================
- */
- void CM_TraceThroughBrush( traceWork_t *tw, trace_t &trace, cbrush_t *brush, bool infoOnly )
- {
- int i;
- cbrushside_t *side;
- tw->enterFrac = -1.0f;
- tw->leaveFrac = 1.0f;
- tw->clipplane = NULL;
- if ( !brush->numsides )
- {
- return;
- }
- tw->getout = false;
- tw->startout = false;
- tw->leadside = NULL;
- //
- // 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;
- if(!CM_PlaneCollision(tw, side))
- {
- return;
- }
- }
- //
- // all planes have been checked, and the trace was not
- // completely outside the brush
- //
- if (!tw->startout)
- {
- if(!infoOnly)
- {
- // original point was inside brush
- trace.startsolid = qtrue;
- if (!tw->getout)
- {
- trace.allsolid = qtrue;
- trace.fraction = 0.0f;
- }
- }
- tw->enterFrac = 0.0f;
- return;
- }
-
- if (tw->enterFrac < tw->leaveFrac)
- {
- if ((tw->enterFrac > -1.0f) && (tw->enterFrac < trace.fraction))
- {
- if (tw->enterFrac < 0.0f)
- {
- tw->enterFrac = 0.0f;
- }
- if(!infoOnly)
- {
- trace.fraction = tw->enterFrac;
- trace.plane = *tw->clipplane;
- trace.surfaceFlags = cmg.shaders[tw->leadside->shaderNum].surfaceFlags;
- // tw->trace.sideNum = tw->leadside - cmg.brushsides;
- trace.contents = brush->contents;
- }
- }
- }
- }
- #ifndef BSPC
- #ifndef _XBOX // Removing terrain from Xbox
- void CM_TraceThroughTerrain( traceWork_t *tw, trace_t &trace, cbrush_t *brush )
- {
- CCMLandScape *landscape;
- vec3_t tBegin, tEnd, tDistance, tStep;
- vec3_t baseStart;
- vec3_t baseEnd;
- int count;
- int i;
- float fraction;
- // At this point we know we may be colliding with a terrain brush (and we know we have a valid terrain structure)
- landscape = cmg.landScape;
- if (!landscape)
- {
- assert(landscape);
- Com_Error(ERR_FATAL,"Brush had surfaceparm terrain, but there is no Terrain entity on this map!");
- }
- // Check for absolutely no connection
- if(!CM_GenericBoxCollide(tw->bounds, landscape->GetBounds()))
- {
- return;
- }
- // Now we know that at least some part of the trace needs to collide with the terrain
- // The regular brush collision is handled elsewhere, so advance the ray to an edge in the terrain brush
- CM_TraceThroughBrush( tw, trace, brush, true );
-
- // Remember the base entering and leaving fractions
- tw->baseEnterFrac = tw->enterFrac;
- tw->baseLeaveFrac = tw->leaveFrac;
- // Reset to full spread within the brush
- tw->enterFrac = -1.0f;
- tw->leaveFrac = 1.0f;
- // Work out the corners of the AABB when the trace first hits the terrain brush and when it leaves
- VectorAdvance(tw->start, tw->baseEnterFrac, tw->end, tBegin);
- VectorAdvance(tw->start, tw->baseLeaveFrac, tw->end, tEnd);
- VectorSubtract(tEnd, tBegin, tDistance);
- // Calculate number of iterations to process
- count = ceilf(VectorLength(tDistance) / (landscape->GetPatchScalarSize() * TERRAIN_STEP_MAGIC));
- count = 1;
- fraction = trace.fraction;
- VectorScale(tDistance, 1.0f / count, tStep);
- // Save the base start and end vectors
- VectorCopy ( tw->start, baseStart );
- VectorCopy ( tw->end, baseEnd );
- // Use the terrain vectors. Start both at the beginning since the
- // step will be added to the end as the first step of the loop
- VectorCopy ( tBegin, tw->start );
- VectorCopy ( tBegin, tw->end );
- // Step thru terrain patches moving on about 1 patch at a time
- for ( i = 0; i < count; i ++ )
- {
- // Add the step to the end
- VectorAdd(tw->end, tStep, tw->end);
- CM_CalcExtents(tBegin, tw->end, tw, tw->localBounds);
- landscape->PatchCollide(tw, trace, tw->start, tw->end, brush->checkcount);
-
- // If collision with something closer than water then just stop here
- if ( trace.fraction < fraction )
- {
- // Convert the fraction of this sub tract into the full trace's fraction
- trace.fraction = i * (1.0f / count) + (1.0f / count) * trace.fraction;
- break;
- }
- // Move the end to the start so the next trace starts
- // where this one left off
- VectorCopy(tw->end, tw->start);
- }
- // Put the original start and end back
- VectorCopy ( baseStart, tw->start );
- VectorCopy ( baseEnd, tw->end );
- // Convert to global fraction only if something was hit along the way
- if ( trace.fraction != 1.0 )
- {
- trace.fraction = tw->baseEnterFrac + ((tw->baseLeaveFrac - tw->baseEnterFrac) * trace.fraction);
- trace.contents = brush->contents;
- }
- // Collide with any water
- if ( tw->contents & CONTENTS_WATER )
- {
- fraction = landscape->WaterCollide(tw->start, tw->end, trace.fraction);
- if( fraction < trace.fraction )
- {
- VectorSet(trace.plane.normal, 0.0f, 0.0f, 1.0f);
- trace.contents = landscape->GetWaterContents();
- trace.fraction = fraction;
- trace.surfaceFlags = landscape->GetWaterSurfaceFlags();
- }
- }
- }
- #endif // _XBOX
- #endif
- #ifdef _XBOX
- static int CM_GetSurfaceIndex(int firstLeafSurface)
- {
- if(!tr.world ||
- firstLeafSurface > tr.world->nummarksurfaces ||
- firstLeafSurface < 0) {
- return cmg.leafsurfaces[ firstLeafSurface ] ;
- } else {
- return tr.world->marksurfaces[firstLeafSurface] - tr.world->surfaces;
- }
- }
- #endif
- /*
- ================
- CM_TestInLeaf
- ================
- */
- void CM_TestInLeaf( traceWork_t *tw, cLeaf_t *leaf, clipMap_t *local ) {
- 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 = local->leafbrushes[leaf->firstLeafBrush+k];
- b = &local->brushes[brushnum];
- if (b->checkcount == local->checkcount) {
- continue; // already checked this brush in another leaf
- }
- b->checkcount = local->checkcount;
- if ( !(b->contents & tw->contents)) {
- continue;
- }
-
- #ifndef BSPC
- #ifndef _XBOX // Removing terrain from Xbox
- if (com_terrainPhysics->integer && cmg.landScape && (b->contents & CONTENTS_TERRAIN) )
- {
- // Invalidate the checkcount for terrain as the terrain brush has to be processed
- // many times.
- b->checkcount--;
- CM_TraceThroughTerrain( tw, tw->trace, b );
- // If inside a terrain brush don't bother with regular brush collision
- continue;
- }
- #endif
- #endif
- 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++ ) {
- #ifdef _XBOX
- int index = CM_GetSurfaceIndex(leaf->firstLeafSurface + k);
- patch = cmg.surfaces[ index ];
- #else
- patch = local->surfaces[ local->leafsurfaces[ leaf->firstLeafSurface + k ] ];
- #endif
- if ( !patch ) {
- continue;
- }
- if ( patch->checkcount == local->checkcount ) {
- continue; // already checked this brush in another leaf
- }
- patch->checkcount = local->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_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;
- cmg.checkcount++;
- CM_BoxLeafnums_r( &ll, 0 );
- cmg.checkcount++;
- // test the contents of the leafs
- for (i=0 ; i < ll.count ; i++) {
- CM_TestInLeaf( tw, &cmg.leafs[leafs[i]], &cmg );
- if ( tw->trace.allsolid ) {
- break;
- }
- }
- }
- /*
- ===============================================================================
- BOX 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;
- enterFrac = -1.0;
- leaveFrac = 1.0;
- clipplane = NULL;
- if ( !brush->numsides ) {
- return;
- }
- // I'm not sure if test is strictly correct. Are all
- // bboxes axis aligned? Do I care? It seems to work
- // good enough...
- 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;
- }
- c_brush_traces++;
- getout = qfalse;
- startout = qfalse;
- leadside = NULL;
- //
- // 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;
- #ifdef _XBOX
- plane = &cmg.planes[side->planeNum.GetValue()];
- #else
- plane = side->plane;
- #endif
- // 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;
- tw->trace.contents |= brush->contents; //note, we always want to know the contents of something we're inside of
- if (!getout)
- { //endpoint was inside brush
- tw->trace.allsolid = qtrue;
- tw->trace.fraction = 0;
- }
- 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 = cmg.shaders[leadside->shaderNum].surfaceFlags;
- tw->trace.contents = brush->contents;
- }
- }
- }
- /*
- ================
- CM_PatchCollide
- By the time we get here we know the AABB is within the patch AABB ie there is a chance of collision
- The collision data is made up of bounds, 2 triangle planes
- There is an BB check for the terxel check to see if it is worth checking the planes.
- Collide with both triangles to find the shortest fraction
- ================
- */
- void CM_HandlePatchCollision(struct traceWork_s *tw, trace_t &trace, const vec3_t tStart, const vec3_t tEnd, CCMPatch *patch, int checkcount)
- {
- int numBrushes, i;
- cbrush_t *brush;
- // Get the collision data
- brush = patch->GetCollisionData();
- numBrushes = patch->GetNumBrushes();
- for(i = 0; i < numBrushes; i++, brush++)
- {
- if(brush->checkcount == checkcount)
- {
- return;
- }
- // Generic collision of terxel bounds to line segment bounds
- if(!CM_GenericBoxCollide(brush->bounds, tw->localBounds))
- {
- continue;
- }
- brush->checkcount = checkcount;
- //CM_TraceThroughBrush(tw, trace, brush, false );
- CM_TraceThroughBrush(tw, brush);
- if (trace.fraction <= 0.0)
- {
- break;
- }
- }
- }
- /*
- ================
- CM_GenericBoxCollide
- ================
- */
- bool CM_GenericBoxCollide(const vec3pair_t abounds, const vec3pair_t bbounds)
- {
- int i;
- // Check for completely no intersection
- for(i = 0; i < 3; i++)
- {
- if(abounds[1][i] < bbounds[0][i])
- {
- return(false);
- }
- if(abounds[0][i] > bbounds[1][i])
- {
- return(false);
- }
- }
- return(true);
- }
- /*
- ================
- CM_TraceToLeaf
- ================
- */
- void CM_TraceToLeaf( traceWork_t *tw, cLeaf_t *leaf, clipMap_t *local ) {
- 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 = local->leafbrushes[leaf->firstLeafBrush+k];
- b = &local->brushes[brushnum];
- if ( b->checkcount == local->checkcount ) {
- continue; // already checked this brush in another leaf
- }
- b->checkcount = local->checkcount;
- if ( !(b->contents & tw->contents) ) {
- continue;
- }
- #ifndef BSPC
- #ifndef _XBOX // Removing terrain from Xbox
- if ( com_terrainPhysics->integer && cmg.landScape && (b->contents & CONTENTS_TERRAIN) )
- {
- // Invalidate the checkcount for terrain as the terrain brush has to be processed
- // many times.
- b->checkcount--;
- CM_TraceThroughTerrain( tw, tw->trace, b );
- // If inside a terrain brush don't bother with regular brush collision
- continue;
- }
- #endif
- #endif
- //if (b->contents & CONTENTS_PLAYERCLIP) 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++ ) {
- #ifdef _XBOX
- int index = CM_GetSurfaceIndex(leaf->firstLeafSurface + k);
- patch = cmg.surfaces[ index ];
- #else
- patch = local->surfaces[ local->leafsurfaces[ leaf->firstLeafSurface + k ] ];
- #endif
- if ( !patch ) {
- continue;
- }
- if ( patch->checkcount == local->checkcount ) {
- continue; // already checked this patch in another leaf
- }
- patch->checkcount = local->checkcount;
- if ( !(patch->contents & tw->contents) ) {
- continue;
- }
- CM_TraceThroughPatch( tw, patch );
- if ( !tw->trace.fraction ) {
- return;
- }
- }
- }
- }
- //=========================================================================================
- /*
- ==================
- 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, clipMap_t *local, 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;
- #ifdef _XBOX
- if(!tr.world) {
- return;
- }
- #endif
- if (tw->trace.fraction <= p1f) {
- return; // already hit something nearer
- }
- // if < 0, we are in a leaf node
- if (num < 0) {
- CM_TraceToLeaf( tw, &local->leafs[-1-num], local );
- return;
- }
- //
- // find the point distances to the seperating plane
- // and the offset for the size of the box
- //
- node = local->nodes + num;
- #ifdef _XBOX
- plane = cmg.planes + tr.world->nodes[num].planeNum;
- #else mnode_s
- plane = node->plane;
- #endif
- // 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 {
- // this is silly
- offset = 2048;
- }
- }
- // see which sides we need to consider
- if ( t1 >= offset + 1 && t2 >= offset + 1 ) {
- CM_TraceThroughTree( tw, local, node->children[0], p1f, p2f, p1, p2 );
- return;
- }
- if ( t1 < -offset - 1 && t2 < -offset - 1 ) {
- CM_TraceThroughTree( tw, local, 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, local, 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, local, node->children[side^1], midf, p2f, mid, p2 );
- }
- void CM_CalcExtents(const vec3_t start, const vec3_t end, const traceWork_t *tw, vec3pair_t bounds)
- {
- int i;
- for ( i = 0 ; i < 3 ; i++ )
- {
- if ( start[i] < end[i] )
- {
- bounds[0][i] = start[i] + tw->size[0][i];
- bounds[1][i] = end[i] + tw->size[1][i];
- }
- else
- {
- bounds[0][i] = end[i] + tw->size[0][i];
- bounds[1][i] = start[i] + tw->size[1][i];
- }
- }
- }
- //======================================================================
- /*
- ==================
- CM_BoxTrace
- ==================
- */
- void CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
- const vec3_t mins, const vec3_t maxs,
- clipHandle_t model, int brushmask) {
- int i;
- traceWork_t tw;
- vec3_t offset;
- cmodel_t *cmod;
- clipMap_t *local = 0;
- cmod = CM_ClipHandleToModel( model, &local );
- local->checkcount++; // for multi-check avoidance
- c_traces++; // for statistics, may be zeroed
- // fill in a default trace
- memset( &tw, 0, sizeof(tw) - sizeof(tw.trace.G2CollisionMap));
- tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise
- if (!local->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];
- }
- 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
- //
- 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 ) {
- CM_TestInLeaf( &tw, &cmod->leaf, local );
- } 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 ) {
- CM_TraceToLeaf( &tw, &cmod->leaf, local );
- } else {
- CM_TraceThroughTree( &tw, local, 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]);
- }
- }
- *results = tw.trace;
- }
- /*
- ==================
- 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,
- const vec3_t mins, const vec3_t maxs,
- clipHandle_t model, int brushmask,
- const vec3_t origin, const vec3_t angles) {
- trace_t trace;
- vec3_t start_l, end_l;
- vec3_t a;
- vec3_t forward, right, up;
- vec3_t temp;
- qboolean rotated;
- vec3_t offset;
- vec3_t symetricSize[2];
- int i;
- 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;
- }
- if (rotated) {
- AngleVectors (angles, forward, right, up);
- VectorCopy (start_l, temp);
- start_l[0] = DotProduct (temp, forward);
- start_l[1] = -DotProduct (temp, right);
- start_l[2] = DotProduct (temp, up);
- VectorCopy (end_l, temp);
- end_l[0] = DotProduct (temp, forward);
- end_l[1] = -DotProduct (temp, right);
- end_l[2] = DotProduct (temp, up);
- }
- // sweep the box through the model
- CM_BoxTrace( &trace, start_l, end_l, symetricSize[0], symetricSize[1], model, brushmask);
- if ( rotated && trace.fraction != 1.0 ) {
- // FIXME: figure out how to do this with existing angles
- VectorNegate (angles, a);
- AngleVectors (a, forward, right, up);
- VectorCopy (trace.plane.normal, temp);
- trace.plane.normal[0] = DotProduct (temp, forward);
- trace.plane.normal[1] = -DotProduct (temp, right);
- trace.plane.normal[2] = DotProduct (temp, up);
- }
- 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;
- }
- /*
- =================
- CM_CullBox
- Returns true if culled out
- =================
- */
- bool CM_CullBox(const cplane_t *frustum, const vec3_t transformed[8])
- {
- int i, j;
- const cplane_t *frust;
- // check against frustum planes
- for (i=0, frust=frustum; i<4 ; i++, frust++)
- {
- for (j=0 ; j<8 ; j++)
- {
- if (DotProduct(transformed[j], frust->normal) > frust->dist)
- { // a point is in front
- break;
- }
- }
- if (j == 8)
- { // all points were behind one of the planes
- return true;
- }
- }
- return false;
- }
- /*
- =================
- CM_CullWorldBox
- Returns true if culled out
- =================
- */
- bool CM_CullWorldBox (const cplane_t *frustum, const vec3pair_t bounds)
- {
- int i;
- vec3_t transformed[8];
- for (i = 0 ; i < 8 ; i++)
- {
- transformed[i][0] = bounds[i & 1][0];
- transformed[i][1] = bounds[(i >> 1) & 1][1];
- transformed[i][2] = bounds[(i >> 2) & 1][2];
- }
- //rwwFIXMEFIXME: Was not ! before. But that seems the way it should be and it works that way. Why?
- return(!CM_CullBox(frustum, transformed));
- }
|