diff --git a/src/cm/CollisionModel_contacts.cpp b/src/cm/CollisionModel_contacts.cpp deleted file mode 100644 index d13e0ff..0000000 --- a/src/cm/CollisionModel_contacts.cpp +++ /dev/null @@ -1,48 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - -/* -=============================================================================== - -Retrieving contacts - -=============================================================================== -*/ - -/* -================== -idCollisionModelManagerLocal::Contacts -================== -*/ -int idCollisionModelManagerLocal::Contacts( contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &origin, const idMat3 &modelAxis ) { - trace_t results; - idVec3 end; - - // same as Translation but instead of storing the first collision we store all collisions as contacts - idCollisionModelManagerLocal::getContacts = true; - idCollisionModelManagerLocal::contacts = contacts; - idCollisionModelManagerLocal::maxContacts = maxContacts; - idCollisionModelManagerLocal::numContacts = 0; - end = start + dir.SubVec3(0) * depth; - idCollisionModelManagerLocal::Translation( &results, start, end, trm, trmAxis, contentMask, model, origin, modelAxis ); - if ( dir.SubVec3(1).LengthSqr() != 0.0f ) { - // FIXME: rotational contacts - } - idCollisionModelManagerLocal::getContacts = false; - idCollisionModelManagerLocal::maxContacts = 0; - - return idCollisionModelManagerLocal::numContacts; -} diff --git a/src/cm/CollisionModel_contents.cpp b/src/cm/CollisionModel_contents.cpp deleted file mode 100644 index c9f23ed..0000000 --- a/src/cm/CollisionModel_contents.cpp +++ /dev/null @@ -1,614 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - -/* -=============================================================================== - -Contents test - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::TestTrmVertsInBrush - - returns true if any of the trm vertices is inside the brush -================ -*/ -bool idCollisionModelManagerLocal::TestTrmVertsInBrush( cm_traceWork_t *tw, cm_brush_t *b ) { - int i, j, numVerts, bestPlane; - float d, bestd; - idVec3 *p; - - if ( b->checkcount == idCollisionModelManagerLocal::checkCount ) { - return false; - } - b->checkcount = idCollisionModelManagerLocal::checkCount; - - if ( !(b->contents & tw->contents) ) { - return false; - } - - // if the brush bounds don't intersect the trace bounds - if ( !b->bounds.IntersectsBounds( tw->bounds ) ) { - return false; - } - - if ( tw->pointTrace ) { - numVerts = 1; - } - else { - numVerts = tw->numVerts; - } - - for ( j = 0; j < numVerts; j++ ) { - p = &tw->vertices[j].p; - - // see if the point is inside the brush - bestPlane = 0; - bestd = -idMath::INFINITY; - for ( i = 0; i < b->numPlanes; i++ ) { - d = b->planes[i].Distance( *p ); - if ( d >= 0.0f ) { - break; - } - if ( d > bestd ) { - bestd = d; - bestPlane = i; - } - } - if ( i >= b->numPlanes ) { - tw->trace.fraction = 0.0f; - tw->trace.c.type = CONTACT_TRMVERTEX; - tw->trace.c.normal = b->planes[bestPlane].Normal(); - tw->trace.c.dist = b->planes[bestPlane].Dist(); - tw->trace.c.contents = b->contents; - tw->trace.c.material = b->material; - tw->trace.c.point = *p; - tw->trace.c.modelFeature = 0; - tw->trace.c.trmFeature = j; - return true; - } - } - return false; -} - -/* -================ -CM_SetTrmEdgeSidedness -================ -*/ -#define CM_SetTrmEdgeSidedness( edge, bpl, epl, bitNum ) { \ - if ( !(edge->sideSet & (1<side = (edge->side & ~(1<sideSet |= (1 << bitNum); \ - } \ -} - -/* -================ -CM_SetTrmPolygonSidedness -================ -*/ -#define CM_SetTrmPolygonSidedness( v, plane, bitNum ) { \ - if ( !((v)->sideSet & (1<p ); \ - /* cannot use float sign bit because it is undetermined when fl == 0.0f */ \ - if ( fl < 0.0f ) { \ - (v)->side |= (1 << bitNum); \ - } \ - else { \ - (v)->side &= ~(1 << bitNum); \ - } \ - (v)->sideSet |= (1 << bitNum); \ - } \ -} - -/* -================ -idCollisionModelManagerLocal::TestTrmInPolygon - - returns true if the trm intersects the polygon -================ -*/ -bool idCollisionModelManagerLocal::TestTrmInPolygon( cm_traceWork_t *tw, cm_polygon_t *p ) { - int i, j, k, edgeNum, flip, trmEdgeNum, bitNum, bestPlane; - int sides[MAX_TRACEMODEL_VERTS]; - float d, bestd; - cm_trmEdge_t *trmEdge; - cm_edge_t *edge; - cm_vertex_t *v, *v1, *v2; - - // if already checked this polygon - if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) { - return false; - } - p->checkcount = idCollisionModelManagerLocal::checkCount; - - // if this polygon does not have the right contents behind it - if ( !(p->contents & tw->contents) ) { - return false; - } - - // if the polygon bounds don't intersect the trace bounds - if ( !p->bounds.IntersectsBounds( tw->bounds ) ) { - return false; - } - - // bounds should cross polygon plane - switch( tw->bounds.PlaneSide( p->plane ) ) { - case PLANESIDE_CROSS: - break; - case PLANESIDE_FRONT: - if ( tw->model->isConvex ) { - tw->quickExit = true; - return true; - } - default: - return false; - } - - // if the trace model is convex - if ( tw->isConvex ) { - // test if any polygon vertices are inside the trm - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = tw->model->edges + abs(edgeNum); - // if this edge is already tested - if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - - for ( j = 0; j < 2; j++ ) { - v = &tw->model->vertices[edge->vertexNum[j]]; - // if this vertex is already tested - if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - - bestPlane = 0; - bestd = -idMath::INFINITY; - for ( k = 0; k < tw->numPolys; k++ ) { - d = tw->polys[k].plane.Distance( v->p ); - if ( d >= 0.0f ) { - break; - } - if ( d > bestd ) { - bestd = d; - bestPlane = k; - } - } - if ( k >= tw->numPolys ) { - tw->trace.fraction = 0.0f; - tw->trace.c.type = CONTACT_MODELVERTEX; - tw->trace.c.normal = -tw->polys[bestPlane].plane.Normal(); - tw->trace.c.dist = -tw->polys[bestPlane].plane.Dist(); - tw->trace.c.contents = p->contents; - tw->trace.c.material = p->material; - tw->trace.c.point = v->p; - tw->trace.c.modelFeature = edge->vertexNum[j]; - tw->trace.c.trmFeature = 0; - return true; - } - } - } - } - - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = tw->model->edges + abs(edgeNum); - // reset sidedness cache if this is the first time we encounter this edge - if ( edge->checkcount != idCollisionModelManagerLocal::checkCount ) { - edge->sideSet = 0; - } - // pluecker coordinate for edge - tw->polygonEdgePlueckerCache[i].FromLine( tw->model->vertices[edge->vertexNum[0]].p, - tw->model->vertices[edge->vertexNum[1]].p ); - v = &tw->model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]]; - // reset sidedness cache if this is the first time we encounter this vertex - if ( v->checkcount != idCollisionModelManagerLocal::checkCount ) { - v->sideSet = 0; - } - v->checkcount = idCollisionModelManagerLocal::checkCount; - } - - // get side of polygon for each trm vertex - for ( i = 0; i < tw->numVerts; i++ ) { - d = p->plane.Distance( tw->vertices[i].p ); - sides[i] = d < 0.0f ? -1 : 1; - } - - // test if any trm edges go through the polygon - for ( i = 1; i <= tw->numEdges; i++ ) { - // if the trm edge does not cross the polygon plane - if ( sides[tw->edges[i].vertexNum[0]] == sides[tw->edges[i].vertexNum[1]] ) { - continue; - } - // check from which side to which side the trm edge goes - flip = INTSIGNBITSET( sides[tw->edges[i].vertexNum[0]] ); - // test if trm edge goes through the polygon between the polygon edges - for ( j = 0; j < p->numEdges; j++ ) { - edgeNum = p->edges[j]; - edge = tw->model->edges + abs(edgeNum); -#if 1 - CM_SetTrmEdgeSidedness( edge, tw->edges[i].pl, tw->polygonEdgePlueckerCache[j], i ); - if ( INTSIGNBITSET(edgeNum) ^ ((edge->side >> i) & 1) ^ flip ) { - break; - } -#else - d = tw->edges[i].pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[j] ); - if ( flip ) { - d = -d; - } - if ( edgeNum > 0 ) { - if ( d <= 0.0f ) { - break; - } - } - else { - if ( d >= 0.0f ) { - break; - } - } -#endif - } - if ( j >= p->numEdges ) { - tw->trace.fraction = 0.0f; - tw->trace.c.type = CONTACT_EDGE; - tw->trace.c.normal = p->plane.Normal(); - tw->trace.c.dist = p->plane.Dist(); - tw->trace.c.contents = p->contents; - tw->trace.c.material = p->material; - tw->trace.c.point = tw->vertices[tw->edges[i].vertexNum[ !flip ]].p; - tw->trace.c.modelFeature = *reinterpret_cast(&p); - tw->trace.c.trmFeature = i; - return true; - } - } - - // test if any polygon edges go through the trm polygons - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = tw->model->edges + abs(edgeNum); - if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - edge->checkcount = idCollisionModelManagerLocal::checkCount; - - for ( j = 0; j < tw->numPolys; j++ ) { -#if 1 - v1 = tw->model->vertices + edge->vertexNum[0]; - CM_SetTrmPolygonSidedness( v1, tw->polys[j].plane, j ); - v2 = tw->model->vertices + edge->vertexNum[1]; - CM_SetTrmPolygonSidedness( v2, tw->polys[j].plane, j ); - // if the polygon edge does not cross the trm polygon plane - if ( !(((v1->side ^ v2->side) >> j) & 1) ) { - continue; - } - flip = (v1->side >> j) & 1; -#else - float d1, d2; - - v1 = tw->model->vertices + edge->vertexNum[0]; - d1 = tw->polys[j].plane.Distance( v1->p ); - v2 = tw->model->vertices + edge->vertexNum[1]; - d2 = tw->polys[j].plane.Distance( v2->p ); - // if the polygon edge does not cross the trm polygon plane - if ( (d1 >= 0.0f && d2 >= 0.0f) || (d1 <= 0.0f && d2 <= 0.0f) ) { - continue; - } - flip = false; - if ( d1 < 0.0f ) { - flip = true; - } -#endif - // test if polygon edge goes through the trm polygon between the trm polygon edges - for ( k = 0; k < tw->polys[j].numEdges; k++ ) { - trmEdgeNum = tw->polys[j].edges[k]; - trmEdge = tw->edges + abs(trmEdgeNum); -#if 1 - bitNum = abs(trmEdgeNum); - CM_SetTrmEdgeSidedness( edge, trmEdge->pl, tw->polygonEdgePlueckerCache[i], bitNum ); - if ( INTSIGNBITSET(trmEdgeNum) ^ ((edge->side >> bitNum) & 1) ^ flip ) { - break; - } -#else - d = trmEdge->pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ); - if ( flip ) { - d = -d; - } - if ( trmEdgeNum > 0 ) { - if ( d <= 0.0f ) { - break; - } - } - else { - if ( d >= 0.0f ) { - break; - } - } -#endif - } - if ( k >= tw->polys[j].numEdges ) { - tw->trace.fraction = 0.0f; - tw->trace.c.type = CONTACT_EDGE; - tw->trace.c.normal = -tw->polys[j].plane.Normal(); - tw->trace.c.dist = -tw->polys[j].plane.Dist(); - tw->trace.c.contents = p->contents; - tw->trace.c.material = p->material; - tw->trace.c.point = tw->model->vertices[edge->vertexNum[ !flip ]].p; - tw->trace.c.modelFeature = edgeNum; - tw->trace.c.trmFeature = j; - return true; - } - } - } - return false; -} - -/* -================ -idCollisionModelManagerLocal::PointNode -================ -*/ -cm_node_t *idCollisionModelManagerLocal::PointNode( const idVec3 &p, cm_model_t *model ) { - cm_node_t *node; - - node = model->node; - while ( node->planeType != -1 ) { - if (p[node->planeType] > node->planeDist) { - node = node->children[0]; - } - else { - node = node->children[1]; - } - - assert( node != NULL ); - } - return node; -} - -/* -================ -idCollisionModelManagerLocal::PointContents -================ -*/ -int idCollisionModelManagerLocal::PointContents( const idVec3 p, cmHandle_t model ) { - int i; - float d; - cm_node_t *node; - cm_brushRef_t *bref; - cm_brush_t *b; - idPlane *plane; - - node = idCollisionModelManagerLocal::PointNode( p, idCollisionModelManagerLocal::models[model] ); - for ( bref = node->brushes; bref; bref = bref->next ) { - b = bref->b; - // test if the point is within the brush bounds - for ( i = 0; i < 3; i++ ) { - if ( p[i] < b->bounds[0][i] ) { - break; - } - if ( p[i] > b->bounds[1][i] ) { - break; - } - } - if ( i < 3 ) { - continue; - } - // test if the point is inside the brush - plane = b->planes; - for ( i = 0; i < b->numPlanes; i++, plane++ ) { - d = plane->Distance( p ); - if ( d >= 0 ) { - break; - } - } - if ( i >= b->numPlanes ) { - return b->contents; - } - } - return 0; -} - -/* -================== -idCollisionModelManagerLocal::TransformedPointContents -================== -*/ -int idCollisionModelManagerLocal::TransformedPointContents( const idVec3 &p, cmHandle_t model, const idVec3 &origin, const idMat3 &modelAxis ) { - idVec3 p_l; - - // subtract origin offset - p_l = p - origin; - if ( modelAxis.IsRotated() ) { - p_l *= modelAxis; - } - return idCollisionModelManagerLocal::PointContents( p_l, model ); -} - - -/* -================== -idCollisionModelManagerLocal::ContentsTrm -================== -*/ -int idCollisionModelManagerLocal::ContentsTrm( trace_t *results, const idVec3 &start, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { - int i; - bool model_rotated, trm_rotated; - idMat3 invModelAxis, tmpAxis; - idVec3 dir; - ALIGN16( cm_traceWork_t tw; ) - - // fast point case - if ( !trm || ( trm->bounds[1][0] - trm->bounds[0][0] <= 0.0f && - trm->bounds[1][1] - trm->bounds[0][1] <= 0.0f && - trm->bounds[1][2] - trm->bounds[0][2] <= 0.0f ) ) { - - results->c.contents = idCollisionModelManagerLocal::TransformedPointContents( start, model, modelOrigin, modelAxis ); - results->fraction = ( results->c.contents == 0 ); - results->endpos = start; - results->endAxis = trmAxis; - - return results->c.contents; - } - - idCollisionModelManagerLocal::checkCount++; - - tw.trace.fraction = 1.0f; - tw.trace.c.contents = 0; - tw.trace.c.type = CONTACT_NONE; - tw.contents = contentMask; - tw.isConvex = true; - tw.rotation = false; - tw.positionTest = true; - tw.pointTrace = false; - tw.quickExit = false; - tw.numContacts = 0; - tw.model = idCollisionModelManagerLocal::models[model]; - tw.start = start - modelOrigin; - tw.end = tw.start; - - model_rotated = modelAxis.IsRotated(); - if ( model_rotated ) { - invModelAxis = modelAxis.Transpose(); - } - - // setup trm structure - idCollisionModelManagerLocal::SetupTrm( &tw, trm ); - - trm_rotated = trmAxis.IsRotated(); - - // calculate vertex positions - if ( trm_rotated ) { - for ( i = 0; i < tw.numVerts; i++ ) { - // rotate trm around the start position - tw.vertices[i].p *= trmAxis; - } - } - for ( i = 0; i < tw.numVerts; i++ ) { - // set trm at start position - tw.vertices[i].p += tw.start; - } - if ( model_rotated ) { - for ( i = 0; i < tw.numVerts; i++ ) { - // rotate trm around model instead of rotating the model - tw.vertices[i].p *= invModelAxis; - } - } - - // add offset to start point - if ( trm_rotated ) { - dir = trm->offset * trmAxis; - tw.start += dir; - tw.end += dir; - } else { - tw.start += trm->offset; - tw.end += trm->offset; - } - if ( model_rotated ) { - // rotate trace instead of model - tw.start *= invModelAxis; - tw.end *= invModelAxis; - } - - - // setup trm vertices - tw.size.Clear(); - for ( i = 0; i < tw.numVerts; i++ ) { - // get axial trm size after rotations - tw.size.AddPoint( tw.vertices[i].p - tw.start ); - } - - // setup trm edges - for ( i = 1; i <= tw.numEdges; i++ ) { - // edge start, end and pluecker coordinate - tw.edges[i].start = tw.vertices[tw.edges[i].vertexNum[0]].p; - tw.edges[i].end = tw.vertices[tw.edges[i].vertexNum[1]].p; - tw.edges[i].pl.FromLine( tw.edges[i].start, tw.edges[i].end ); - } - - // setup trm polygons - if ( trm_rotated & model_rotated ) { - tmpAxis = trmAxis * invModelAxis; - for ( i = 0; i < tw.numPolys; i++ ) { - tw.polys[i].plane *= tmpAxis; - } - } else if ( trm_rotated ) { - for ( i = 0; i < tw.numPolys; i++ ) { - tw.polys[i].plane *= trmAxis; - } - } else if ( model_rotated ) { - for ( i = 0; i < tw.numPolys; i++ ) { - tw.polys[i].plane *= invModelAxis; - } - } - for ( i = 0; i < tw.numPolys; i++ ) { - tw.polys[i].plane.FitThroughPoint( tw.edges[abs(tw.polys[i].edges[0])].start ); - } - - // bounds for full trace, a little bit larger for epsilons - for ( i = 0; i < 3; i++ ) { - if ( tw.start[i] < tw.end[i] ) { - tw.bounds[0][i] = tw.start[i] + tw.size[0][i] - CM_BOX_EPSILON; - tw.bounds[1][i] = tw.end[i] + tw.size[1][i] + CM_BOX_EPSILON; - } else { - tw.bounds[0][i] = tw.end[i] + tw.size[0][i] - CM_BOX_EPSILON; - tw.bounds[1][i] = tw.start[i] + tw.size[1][i] + CM_BOX_EPSILON; - } - if ( idMath::Fabs(tw.size[0][i]) > idMath::Fabs(tw.size[1][i]) ) { - tw.extents[i] = idMath::Fabs( tw.size[0][i] ) + CM_BOX_EPSILON; - } else { - tw.extents[i] = idMath::Fabs( tw.size[1][i] ) + CM_BOX_EPSILON; - } - } - - // trace through the model - idCollisionModelManagerLocal::TraceThroughModel( &tw ); - - *results = tw.trace; - results->fraction = ( results->c.contents == 0 ); - results->endpos = start; - results->endAxis = trmAxis; - - return results->c.contents; -} - -/* -================== -idCollisionModelManagerLocal::Contents -================== -*/ -int idCollisionModelManagerLocal::Contents( const idVec3 &start, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { - trace_t results; - - if ( model < 0 || model > idCollisionModelManagerLocal::maxModels || model > MAX_SUBMODELS ) { - common->Printf("idCollisionModelManagerLocal::Contents: invalid model handle\n"); - return 0; - } - if ( !idCollisionModelManagerLocal::models || !idCollisionModelManagerLocal::models[model] ) { - common->Printf("idCollisionModelManagerLocal::Contents: invalid model\n"); - return 0; - } - - return ContentsTrm( &results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); -} diff --git a/src/cm/CollisionModel_debug.cpp b/src/cm/CollisionModel_debug.cpp deleted file mode 100644 index 031db3d..0000000 --- a/src/cm/CollisionModel_debug.cpp +++ /dev/null @@ -1,498 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - - -/* -=============================================================================== - -Visualisation code - -=============================================================================== -*/ - -const char *cm_contentsNameByIndex[] = { -// HUMANHEAD: Redid these as they were out of sync - "none", // - "solid", // bit 0 - "opaque", // 1 - "water", // 2 - "playerclip", // 3 - "monsterclip", // 4 - "moveableclip", // 5 - "ikclip", // 6 - "blood", // 7 - "body", // 8 - "projectile", // 9 - "corpse", // 10 - "rendermodel", // 11 - "trigger", // 12 - "aas_solid", // 13 - "aas_obstacle", // 14 - "flashlight_trigger", // 15 - - // HUMANHEAD pdm: added our contents - "forcefield", // 16 - "spiritbridge", // 17 - "areaportal", // 18 - "nocsg", // 19 - "block_radiusdamage", // 20 - "shootable", // 21 - "deathvolume", // 22 - "vehicleclip", // 23 - "owner_to_owner", // 24 - "game_portal", // 25 - "shootablebyarrow", // 26 - "hunterclip", // 27 - - // END HUMANHEAD - NULL -}; - -int cm_contentsFlagByIndex[] = { -// HUMANHEAD: Redid these as they were out of sync - -1, // -1 - CONTENTS_SOLID, // bit 0 - CONTENTS_OPAQUE, // 1 - CONTENTS_WATER, // 2 - CONTENTS_PLAYERCLIP, // 3 - CONTENTS_MONSTERCLIP, // 4 - CONTENTS_MOVEABLECLIP, // 5 - CONTENTS_IKCLIP, // 6 - CONTENTS_BLOOD, // 7 - CONTENTS_BODY, // 8 - CONTENTS_PROJECTILE, // 9 - CONTENTS_CORPSE, // 10 - CONTENTS_RENDERMODEL, // 11 - CONTENTS_TRIGGER, // 12 - CONTENTS_AAS_SOLID, // 13 - CONTENTS_AAS_OBSTACLE, // 14 - CONTENTS_FLASHLIGHT_TRIGGER, // 15 - - // HUMANHEAD pdm: added our contents - CONTENTS_FORCEFIELD, // 16 - CONTENTS_SPIRITBRIDGE, // 17 - CONTENTS_AREAPORTAL, // 18 - CONTENTS_NOCSG, // 19 - CONTENTS_BLOCK_RADIUSDAMAGE, // 20 - CONTENTS_SHOOTABLE, // 21 - CONTENTS_DEATHVOLUME, // 22 - CONTENTS_VEHICLECLIP, // 23 - CONTENTS_OWNER_TO_OWNER, // 24 - CONTENTS_GAME_PORTAL, // 25 - CONTENTS_SHOOTABLEBYARROW, // 26 - CONTENTS_HUNTERCLIP, // 27 - // END HUMANHEAD - 0 -}; - -idCVar cm_drawMask( "cm_drawMask", "none", CVAR_GAME, "collision mask", cm_contentsNameByIndex, idCmdSystem::ArgCompletion_String ); -idCVar cm_drawColor( "cm_drawColor", "1 0 0 .5", CVAR_GAME, "color used to draw the collision models" ); -idCVar cm_drawFilled( "cm_drawFilled", "0", CVAR_GAME | CVAR_BOOL, "draw filled polygons" ); -idCVar cm_drawInternal( "cm_drawInternal", "1", CVAR_GAME | CVAR_BOOL, "draw internal edges green" ); -idCVar cm_drawNormals( "cm_drawNormals", "0", CVAR_GAME | CVAR_BOOL, "draw polygon and edge normals" ); -idCVar cm_backFaceCull( "cm_backFaceCull", "0", CVAR_GAME | CVAR_BOOL, "cull back facing polygons" ); -idCVar cm_debugCollision( "cm_debugCollision", "0", CVAR_GAME | CVAR_BOOL, "debug the collision detection" ); - -static idVec4 cm_color; - -/* -================ -idCollisionModelManagerLocal::ContentsFromString -================ -*/ -int idCollisionModelManagerLocal::ContentsFromString( const char *string ) const { - int i, contents = 0; - idLexer src( string, idStr::Length( string ), "ContentsFromString" ); - idToken token; - - while( src.ReadToken( &token ) ) { - if ( token == "," ) { - continue; - } - for ( i = 1; cm_contentsNameByIndex[i] != NULL; i++ ) { - if ( token.Icmp( cm_contentsNameByIndex[i] ) == 0 ) { - contents |= cm_contentsFlagByIndex[i]; - break; - } - } - } - - return contents; -} - -/* -================ -idCollisionModelManagerLocal::StringFromContents -================ -*/ -const char *idCollisionModelManagerLocal::StringFromContents( const int contents ) const { - int i, length = 0; - static char contentsString[MAX_STRING_CHARS]; - - contentsString[0] = '\0'; - - for ( i = 1; cm_contentsFlagByIndex[i] != 0; i++ ) { - if ( contents & cm_contentsFlagByIndex[i] ) { - if ( length != 0 ) { - length += idStr::snPrintf( contentsString + length, sizeof( contentsString ) - length, "," ); - } - length += idStr::snPrintf( contentsString + length, sizeof( contentsString ) - length, cm_contentsNameByIndex[i] ); - } - } - - return contentsString; -} - -/* -================ -idCollisionModelManagerLocal::DrawEdge -================ -*/ -void idCollisionModelManagerLocal::DrawEdge( cm_model_t *model, int edgeNum, const idVec3 &origin, const idMat3 &axis ) { - int side; - cm_edge_t *edge; - idVec3 start, end, mid; - bool isRotated; - - isRotated = axis.IsRotated(); - - edge = model->edges + abs(edgeNum); - side = edgeNum < 0; - - start = model->vertices[edge->vertexNum[side]].p; - end = model->vertices[edge->vertexNum[!side]].p; - if ( isRotated ) { - start *= axis; - end *= axis; - } - start += origin; - end += origin; - - if ( edge->internal ) { - if ( cm_drawInternal.GetBool() ) { - session->rw->DebugArrow( colorGreen, start, end, 1 ); - } - } else { - if ( edge->numUsers > 2 ) { - session->rw->DebugArrow( colorBlue, start, end, 1 ); - } else { - session->rw->DebugArrow( cm_color, start, end, 1 ); - } - } - - if ( cm_drawNormals.GetBool() ) { - mid = (start + end) * 0.5f; - if ( isRotated ) { - end = mid + 5 * (axis * edge->normal); - } else { - end = mid + 5 * edge->normal; - } - session->rw->DebugArrow( colorCyan, mid, end, 1 ); - } -} - -/* -================ -idCollisionModelManagerLocal::DrawPolygon -================ -*/ -void idCollisionModelManagerLocal::DrawPolygon( cm_model_t *model, cm_polygon_t *p, const idVec3 &origin, const idMat3 &axis, const idVec3 &viewOrigin ) { - int i, edgeNum; - cm_edge_t *edge; - idVec3 center, end, dir; - - if ( cm_backFaceCull.GetBool() ) { - edgeNum = p->edges[0]; - edge = model->edges + abs(edgeNum); - dir = model->vertices[edge->vertexNum[0]].p - viewOrigin; - if ( dir * p->plane.Normal() > 0.0f ) { - return; - } - } - - if ( cm_drawNormals.GetBool() ) { - center = vec3_origin; - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = model->edges + abs(edgeNum); - center += model->vertices[edge->vertexNum[edgeNum < 0]].p; - } - center *= (1.0f / p->numEdges); - if ( axis.IsRotated() ) { - center = center * axis + origin; - end = center + 5 * (axis * p->plane.Normal()); - } else { - center += origin; - end = center + 5 * p->plane.Normal(); - } - session->rw->DebugArrow( colorMagenta, center, end, 1 ); - } - - if ( cm_drawFilled.GetBool() ) { - idFixedWinding winding; - for ( i = p->numEdges - 1; i >= 0; i-- ) { - edgeNum = p->edges[i]; - edge = model->edges + abs(edgeNum); - winding += origin + model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p * axis; - } - session->rw->DebugPolygon( cm_color, winding ); - } else { - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = model->edges + abs(edgeNum); - if ( edge->checkcount == checkCount ) { - continue; - } - edge->checkcount = checkCount; - DrawEdge( model, edgeNum, origin, axis ); - } - } -} - -/* -================ -idCollisionModelManagerLocal::DrawNodePolygons -================ -*/ -void idCollisionModelManagerLocal::DrawNodePolygons( cm_model_t *model, cm_node_t *node, - const idVec3 &origin, const idMat3 &axis, - const idVec3 &viewOrigin, const float radius ) { - int i; - cm_polygon_t *p; - cm_polygonRef_t *pref; - - while (1) { - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - if ( radius ) { - // polygon bounds should overlap with trace bounds - for ( i = 0; i < 3; i++ ) { - if ( p->bounds[0][i] > viewOrigin[i] + radius ) { - break; - } - if ( p->bounds[1][i] < viewOrigin[i] - radius ) { - break; - } - } - if ( i < 3 ) { - continue; - } - } - if ( p->checkcount == checkCount ) { - continue; - } - if ( !( p->contents & cm_contentsFlagByIndex[cm_drawMask.GetInteger()] ) ) { - continue; - } - - DrawPolygon( model, p, origin, axis, viewOrigin ); - p->checkcount = checkCount; - } - if ( node->planeType == -1 ) { - break; - } - if ( radius && viewOrigin[node->planeType] > node->planeDist + radius ) { - node = node->children[0]; - } else if ( radius && viewOrigin[node->planeType] < node->planeDist - radius ) { - node = node->children[1]; - } else { - DrawNodePolygons( model, node->children[1], origin, axis, viewOrigin, radius ); - node = node->children[0]; - } - } -} - -/* -================ -idCollisionModelManagerLocal::DrawModel -================ -*/ -void idCollisionModelManagerLocal::DrawModel( cmHandle_t handle, const idVec3 &modelOrigin, const idMat3 &modelAxis, - const idVec3 &viewOrigin, const float radius ) { - - cm_model_t *model; - idVec3 viewPos; - - if ( handle < 0 && handle >= numModels ) { - return; - } - - if ( cm_drawColor.IsModified() ) { - sscanf( cm_drawColor.GetString(), "%f %f %f %f", &cm_color.x, &cm_color.y, &cm_color.z, &cm_color.w ); - cm_drawColor.ClearModified(); - } - - model = models[ handle ]; - viewPos = (viewOrigin - modelOrigin) * modelAxis.Transpose(); - checkCount++; - DrawNodePolygons( model, model->node, modelOrigin, modelAxis, viewPos, radius ); -} - -/* -=============================================================================== - -Speed test code - -=============================================================================== -*/ - -static idCVar cm_testCollision( "cm_testCollision", "0", CVAR_GAME | CVAR_BOOL, "" ); -static idCVar cm_testRotation( "cm_testRotation", "1", CVAR_GAME | CVAR_BOOL, "" ); -static idCVar cm_testModel( "cm_testModel", "0", CVAR_GAME | CVAR_INTEGER, "" ); -static idCVar cm_testTimes( "cm_testTimes", "1000", CVAR_GAME | CVAR_INTEGER, "" ); -static idCVar cm_testRandomMany( "cm_testRandomMany", "0", CVAR_GAME | CVAR_BOOL, "" ); -static idCVar cm_testOrigin( "cm_testOrigin", "0 0 0", CVAR_GAME, "" ); -static idCVar cm_testReset( "cm_testReset", "0", CVAR_GAME | CVAR_BOOL, "" ); -static idCVar cm_testBox( "cm_testBox", "-16 -16 0 16 16 64", CVAR_GAME, "" ); -static idCVar cm_testBoxRotation( "cm_testBoxRotation", "0 0 0", CVAR_GAME, "" ); -static idCVar cm_testWalk( "cm_testWalk", "1", CVAR_GAME | CVAR_BOOL, "" ); -static idCVar cm_testLength( "cm_testLength", "1024", CVAR_GAME | CVAR_FLOAT, "" ); -static idCVar cm_testRadius( "cm_testRadius", "64", CVAR_GAME | CVAR_FLOAT, "" ); -static idCVar cm_testAngle( "cm_testAngle", "60", CVAR_GAME | CVAR_FLOAT, "" ); - -static int total_translation; -static int min_translation = 999999; -static int max_translation = -999999; -static int num_translation = 0; -static int total_rotation; -static int min_rotation = 999999; -static int max_rotation = -999999; -static int num_rotation = 0; -static idVec3 start; -static idVec3 *testend; - -#include "../sys/sys_public.h" - -void idCollisionModelManagerLocal::DebugOutput( const idVec3 &origin ) { - int i, k, t; - char buf[128]; - idVec3 end; - idAngles boxAngles; - idMat3 modelAxis, boxAxis; - idBounds bounds; - trace_t trace; - - if ( !cm_testCollision.GetBool() ) { - return; - } - - testend = (idVec3 *) Mem_Alloc( cm_testTimes.GetInteger() * sizeof(idVec3) ); - - if ( cm_testReset.GetBool() || ( cm_testWalk.GetBool() && !start.Compare( start ) ) ) { - total_translation = total_rotation = 0; - min_translation = min_rotation = 999999; - max_translation = max_rotation = -999999; - num_translation = num_rotation = 0; - cm_testReset.SetBool( false ); - } - - if ( cm_testWalk.GetBool() ) { - start = origin; - cm_testOrigin.SetString( va( "%1.2f %1.2f %1.2f", start[0], start[1], start[2] ) ); - } else { - sscanf( cm_testOrigin.GetString(), "%f %f %f", &start[0], &start[1], &start[2] ); - } - - sscanf( cm_testBox.GetString(), "%f %f %f %f %f %f", &bounds[0][0], &bounds[0][1], &bounds[0][2], - &bounds[1][0], &bounds[1][1], &bounds[1][2] ); - sscanf( cm_testBoxRotation.GetString(), "%f %f %f", &boxAngles[0], &boxAngles[1], &boxAngles[2] ); - boxAxis = boxAngles.ToMat3(); - modelAxis.Identity(); - - idTraceModel itm( bounds ); - idRandom random( 0 ); - idTimer timer; - - if ( cm_testRandomMany.GetBool() ) { - // if many traces in one random direction - for ( i = 0; i < 3; i++ ) { - testend[0][i] = start[i] + random.CRandomFloat() * cm_testLength.GetFloat(); - } - for ( k = 1; k < cm_testTimes.GetInteger(); k++ ) { - testend[k] = testend[0]; - } - } else { - // many traces each in a different random direction - for ( k = 0; k < cm_testTimes.GetInteger(); k++ ) { - for ( i = 0; i < 3; i++ ) { - testend[k][i] = start[i] + random.CRandomFloat() * cm_testLength.GetFloat(); - } - } - } - - // translational collision detection - timer.Clear(); - timer.Start(); - for ( i = 0; i < cm_testTimes.GetInteger(); i++ ) { - Translation( &trace, start, testend[i], &itm, boxAxis, CONTENTS_SOLID|CONTENTS_PLAYERCLIP, cm_testModel.GetInteger(), vec3_origin, modelAxis ); - } - timer.Stop(); - t = timer.Milliseconds(); - if ( t < min_translation ) min_translation = t; - if ( t > max_translation ) max_translation = t; - num_translation++; - total_translation += t; - if ( cm_testTimes.GetInteger() > 9999 ) { - sprintf( buf, "%3dK", (int ) ( cm_testTimes.GetInteger() / 1000 ) ); - } else { - sprintf( buf, "%4d", cm_testTimes.GetInteger() ); - } - common->Printf("%s translations: %4d milliseconds, (min = %d, max = %d, av = %1.1f)\n", buf, t, min_translation, max_translation, (float) total_translation / num_translation ); - - if ( cm_testRandomMany.GetBool() ) { - // if many traces in one random direction - for ( i = 0; i < 3; i++ ) { - testend[0][i] = start[i] + random.CRandomFloat() * cm_testRadius.GetFloat(); - } - for ( k = 1; k < cm_testTimes.GetInteger(); k++ ) { - testend[k] = testend[0]; - } - } else { - // many traces each in a different random direction - for ( k = 0; k < cm_testTimes.GetInteger(); k++ ) { - for ( i = 0; i < 3; i++ ) { - testend[k][i] = start[i] + random.CRandomFloat() * cm_testRadius.GetFloat(); - } - } - } - - if ( cm_testRotation.GetBool() ) { - // rotational collision detection - idVec3 vec( random.CRandomFloat(), random.CRandomFloat(), random.RandomFloat() ); - vec.Normalize(); - idRotation rotation( vec3_origin, vec, cm_testAngle.GetFloat() ); - - timer.Clear(); - timer.Start(); - for ( i = 0; i < cm_testTimes.GetInteger(); i++ ) { - rotation.SetOrigin( testend[i] ); - Rotation( &trace, start, rotation, &itm, boxAxis, CONTENTS_SOLID|CONTENTS_PLAYERCLIP, cm_testModel.GetInteger(), vec3_origin, modelAxis ); - } - timer.Stop(); - t = timer.Milliseconds(); - if ( t < min_rotation ) min_rotation = t; - if ( t > max_rotation ) max_rotation = t; - num_rotation++; - total_rotation += t; - if ( cm_testTimes.GetInteger() > 9999 ) { - sprintf( buf, "%3dK", (int ) ( cm_testTimes.GetInteger() / 1000 ) ); - } else { - sprintf( buf, "%4d", cm_testTimes.GetInteger() ); - } - common->Printf("%s rotation: %4d milliseconds, (min = %d, max = %d, av = %1.1f)\n", buf, t, min_rotation, max_rotation, (float) total_rotation / num_rotation ); - } - - Mem_Free( testend ); - testend = NULL; -} diff --git a/src/cm/CollisionModel_files.cpp b/src/cm/CollisionModel_files.cpp deleted file mode 100644 index eeacb8b..0000000 --- a/src/cm/CollisionModel_files.cpp +++ /dev/null @@ -1,598 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - -#define CM_FILE_EXT "cm" -#define CM_FILEID "CM" -#define CM_FILEVERSION "1.00" - - -/* -=============================================================================== - -Writing of collision model file - -=============================================================================== -*/ - -void CM_GetNodeBounds( idBounds *bounds, cm_node_t *node ); -int CM_GetNodeContents( cm_node_t *node ); - - -/* -================ -idCollisionModelManagerLocal::WriteNodes -================ -*/ -void idCollisionModelManagerLocal::WriteNodes( idFile *fp, cm_node_t *node ) { - fp->WriteFloatString( "\t( %d %f )\n", node->planeType, node->planeDist ); - if ( node->planeType != -1 ) { - WriteNodes( fp, node->children[0] ); - WriteNodes( fp, node->children[1] ); - } -} - -/* -================ -idCollisionModelManagerLocal::CountPolygonMemory -================ -*/ -int idCollisionModelManagerLocal::CountPolygonMemory( cm_node_t *node ) const { - cm_polygonRef_t *pref; - cm_polygon_t *p; - int memory; - - memory = 0; - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - if ( p->checkcount == checkCount ) { - continue; - } - p->checkcount = checkCount; - - memory += sizeof( cm_polygon_t ) + ( p->numEdges - 1 ) * sizeof( p->edges[0] ); - } - if ( node->planeType != -1 ) { - memory += CountPolygonMemory( node->children[0] ); - memory += CountPolygonMemory( node->children[1] ); - } - return memory; -} - -/* -================ -idCollisionModelManagerLocal::WritePolygons -================ -*/ -void idCollisionModelManagerLocal::WritePolygons( idFile *fp, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - int i; - - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - if ( p->checkcount == checkCount ) { - continue; - } - p->checkcount = checkCount; - fp->WriteFloatString( "\t%d (", p->numEdges ); - for ( i = 0; i < p->numEdges; i++ ) { - fp->WriteFloatString( " %d", p->edges[i] ); - } - fp->WriteFloatString( " ) ( %f %f %f ) %f", p->plane.Normal()[0], p->plane.Normal()[1], p->plane.Normal()[2], p->plane.Dist() ); - fp->WriteFloatString( " ( %f %f %f )", p->bounds[0][0], p->bounds[0][1], p->bounds[0][2] ); - fp->WriteFloatString( " ( %f %f %f )", p->bounds[1][0], p->bounds[1][1], p->bounds[1][2] ); - fp->WriteFloatString( " \"%s\"\n", p->material->GetName() ); - } - if ( node->planeType != -1 ) { - WritePolygons( fp, node->children[0] ); - WritePolygons( fp, node->children[1] ); - } -} - -/* -================ -idCollisionModelManagerLocal::CountBrushMemory -================ -*/ -int idCollisionModelManagerLocal::CountBrushMemory( cm_node_t *node ) const { - cm_brushRef_t *bref; - cm_brush_t *b; - int memory; - - memory = 0; - for ( bref = node->brushes; bref; bref = bref->next ) { - b = bref->b; - if ( b->checkcount == checkCount ) { - continue; - } - b->checkcount = checkCount; - - memory += sizeof( cm_brush_t ) + ( b->numPlanes - 1 ) * sizeof( b->planes[0] ); - } - if ( node->planeType != -1 ) { - memory += CountBrushMemory( node->children[0] ); - memory += CountBrushMemory( node->children[1] ); - } - return memory; -} - -/* -================ -idCollisionModelManagerLocal::WriteBrushes -================ -*/ -void idCollisionModelManagerLocal::WriteBrushes( idFile *fp, cm_node_t *node ) { - cm_brushRef_t *bref; - cm_brush_t *b; - int i; - - for ( bref = node->brushes; bref; bref = bref->next ) { - b = bref->b; - if ( b->checkcount == checkCount ) { - continue; - } - b->checkcount = checkCount; - fp->WriteFloatString( "\t%d {\n", b->numPlanes ); - for ( i = 0; i < b->numPlanes; i++ ) { - fp->WriteFloatString( "\t\t( %f %f %f ) %f\n", b->planes[i].Normal()[0], b->planes[i].Normal()[1], b->planes[i].Normal()[2], b->planes[i].Dist() ); - } - fp->WriteFloatString( "\t} ( %f %f %f )", b->bounds[0][0], b->bounds[0][1], b->bounds[0][2] ); - fp->WriteFloatString( " ( %f %f %f ) \"%s\"\n", b->bounds[1][0], b->bounds[1][1], b->bounds[1][2], StringFromContents( b->contents ) ); - } - if ( node->planeType != -1 ) { - WriteBrushes( fp, node->children[0] ); - WriteBrushes( fp, node->children[1] ); - } -} - -/* -================ -idCollisionModelManagerLocal::WriteCollisionModel -================ -*/ -void idCollisionModelManagerLocal::WriteCollisionModel( idFile *fp, cm_model_t *model ) { - int i, polygonMemory, brushMemory; - - fp->WriteFloatString( "collisionModel \"%s\" {\n", model->name.c_str() ); - // vertices - fp->WriteFloatString( "\tvertices { /* numVertices = */ %d\n", model->numVertices ); - for ( i = 0; i < model->numVertices; i++ ) { - fp->WriteFloatString( "\t/* %d */ ( %f %f %f )\n", i, model->vertices[i].p[0], model->vertices[i].p[1], model->vertices[i].p[2] ); - } - fp->WriteFloatString( "\t}\n" ); - // edges - fp->WriteFloatString( "\tedges { /* numEdges = */ %d\n", model->numEdges ); - for ( i = 0; i < model->numEdges; i++ ) { - fp->WriteFloatString( "\t/* %d */ ( %d %d ) %d %d\n", i, model->edges[i].vertexNum[0], model->edges[i].vertexNum[1], model->edges[i].internal, model->edges[i].numUsers ); - } - fp->WriteFloatString( "\t}\n" ); - // nodes - fp->WriteFloatString( "\tnodes {\n" ); - WriteNodes( fp, model->node ); - fp->WriteFloatString( "\t}\n" ); - // polygons - checkCount++; - polygonMemory = CountPolygonMemory( model->node ); - fp->WriteFloatString( "\tpolygons /* polygonMemory = */ %d {\n", polygonMemory ); - checkCount++; - WritePolygons( fp, model->node ); - fp->WriteFloatString( "\t}\n" ); - // brushes - checkCount++; - brushMemory = CountBrushMemory( model->node ); - fp->WriteFloatString( "\tbrushes /* brushMemory = */ %d {\n", brushMemory ); - checkCount++; - WriteBrushes( fp, model->node ); - fp->WriteFloatString( "\t}\n" ); - // closing brace - fp->WriteFloatString( "}\n" ); -} - -/* -================ -idCollisionModelManagerLocal::WriteCollisionModelsToFile -================ -*/ -void idCollisionModelManagerLocal::WriteCollisionModelsToFile( const char *filename, int firstModel, int lastModel, unsigned int mapFileCRC ) { - int i; - idFile *fp; - idStr name; - - name = filename; - name.SetFileExtension( CM_FILE_EXT ); - - common->Printf( "writing %s\n", name.c_str() ); - // _D3XP was saving to fs_cdpath - fp = fileSystem->OpenFileWrite( name, "fs_devpath" ); - if ( !fp ) { - common->Warning( "idCollisionModelManagerLocal::WriteCollisionModelsToFile: Error opening file %s\n", name.c_str() ); - return; - } - - // write file id and version - fp->WriteFloatString( "%s \"%s\"\n\n", CM_FILEID, CM_FILEVERSION ); - // write the map file crc - fp->WriteFloatString( "%u\n\n", mapFileCRC ); - - // write the collision models - for ( i = firstModel; i < lastModel; i++ ) { - WriteCollisionModel( fp, models[ i ] ); - } - - fileSystem->CloseFile( fp ); -} - -/* -================ -idCollisionModelManagerLocal::WriteCollisionModelForMapEntity -================ -*/ -bool idCollisionModelManagerLocal::WriteCollisionModelForMapEntity( const idMapEntity *mapEnt, const char *filename, const bool testTraceModel ) { - idFile *fp; - idStr name; - cm_model_t *model; - - SetupHash(); - model = CollisionModelForMapEntity( mapEnt ); - model->name = filename; - - name = filename; - name.SetFileExtension( CM_FILE_EXT ); - - common->Printf( "writing %s\n", name.c_str() ); - fp = fileSystem->OpenFileWrite( name, "fs_devpath" ); - if ( !fp ) { - common->Printf( "idCollisionModelManagerLocal::WriteCollisionModelForMapEntity: Error opening file %s\n", name.c_str() ); - FreeModel( model ); - return false; - } - - // write file id and version - fp->WriteFloatString( "%s \"%s\"\n\n", CM_FILEID, CM_FILEVERSION ); - // write the map file crc - fp->WriteFloatString( "%u\n\n", 0 ); - - // write the collision model - WriteCollisionModel( fp, model ); - - fileSystem->CloseFile( fp ); - - if ( testTraceModel ) { - idTraceModel trm; - TrmFromModel( model, trm ); - } - - FreeModel( model ); - - return true; -} - - -/* -=============================================================================== - -Loading of collision model file - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::ParseVertices -================ -*/ -void idCollisionModelManagerLocal::ParseVertices( idLexer *src, cm_model_t *model ) { - int i; - - src->ExpectTokenString( "{" ); - model->numVertices = src->ParseInt(); - model->maxVertices = model->numVertices; - model->vertices = (cm_vertex_t *) Mem_Alloc( model->maxVertices * sizeof( cm_vertex_t ) ); - for ( i = 0; i < model->numVertices; i++ ) { - src->Parse1DMatrix( 3, model->vertices[i].p.ToFloatPtr() ); - model->vertices[i].side = 0; - model->vertices[i].sideSet = 0; - model->vertices[i].checkcount = 0; - } - src->ExpectTokenString( "}" ); -} - -/* -================ -idCollisionModelManagerLocal::ParseEdges -================ -*/ -void idCollisionModelManagerLocal::ParseEdges( idLexer *src, cm_model_t *model ) { - int i; - - src->ExpectTokenString( "{" ); - model->numEdges = src->ParseInt(); - model->maxEdges = model->numEdges; - model->edges = (cm_edge_t *) Mem_Alloc( model->maxEdges * sizeof( cm_edge_t ) ); - for ( i = 0; i < model->numEdges; i++ ) { - src->ExpectTokenString( "(" ); - model->edges[i].vertexNum[0] = src->ParseInt(); - model->edges[i].vertexNum[1] = src->ParseInt(); - src->ExpectTokenString( ")" ); - model->edges[i].side = 0; - model->edges[i].sideSet = 0; - model->edges[i].internal = src->ParseInt(); - model->edges[i].numUsers = src->ParseInt(); - model->edges[i].normal = vec3_origin; - model->edges[i].checkcount = 0; - model->numInternalEdges += model->edges[i].internal; - } - src->ExpectTokenString( "}" ); -} - -/* -================ -idCollisionModelManagerLocal::ParseNodes -================ -*/ -cm_node_t *idCollisionModelManagerLocal::ParseNodes( idLexer *src, cm_model_t *model, cm_node_t *parent ) { - cm_node_t *node; - - model->numNodes++; - node = AllocNode( model, model->numNodes < NODE_BLOCK_SIZE_SMALL ? NODE_BLOCK_SIZE_SMALL : NODE_BLOCK_SIZE_LARGE ); - node->brushes = NULL; - node->polygons = NULL; - node->parent = parent; - src->ExpectTokenString( "(" ); - node->planeType = src->ParseInt(); - node->planeDist = src->ParseFloat(); - src->ExpectTokenString( ")" ); - if ( node->planeType != -1 ) { - node->children[0] = ParseNodes( src, model, node ); - node->children[1] = ParseNodes( src, model, node ); - } - return node; -} - -/* -================ -idCollisionModelManagerLocal::ParsePolygons -================ -*/ -void idCollisionModelManagerLocal::ParsePolygons( idLexer *src, cm_model_t *model ) { - cm_polygon_t *p; - int i, numEdges; - idVec3 normal; - idToken token; - - if ( src->CheckTokenType( TT_NUMBER, 0, &token ) ) { - model->polygonBlock = (cm_polygonBlock_t *) Mem_Alloc( sizeof( cm_polygonBlock_t ) + token.GetIntValue() ); - model->polygonBlock->bytesRemaining = token.GetIntValue(); - model->polygonBlock->next = ( (byte *) model->polygonBlock ) + sizeof( cm_polygonBlock_t ); - } - - src->ExpectTokenString( "{" ); - while ( !src->CheckTokenString( "}" ) ) { - // parse polygon - numEdges = src->ParseInt(); - p = AllocPolygon( model, numEdges ); - p->numEdges = numEdges; - src->ExpectTokenString( "(" ); - for ( i = 0; i < p->numEdges; i++ ) { - p->edges[i] = src->ParseInt(); - } - src->ExpectTokenString( ")" ); - src->Parse1DMatrix( 3, normal.ToFloatPtr() ); - p->plane.SetNormal( normal ); - p->plane.SetDist( src->ParseFloat() ); - src->Parse1DMatrix( 3, p->bounds[0].ToFloatPtr() ); - src->Parse1DMatrix( 3, p->bounds[1].ToFloatPtr() ); - src->ExpectTokenType( TT_STRING, 0, &token ); - // get material - p->material = declManager->FindMaterial( token ); - p->contents = p->material->GetContentFlags(); - p->checkcount = 0; - // filter polygon into tree - R_FilterPolygonIntoTree( model, model->node, NULL, p ); - } -} - -/* -================ -idCollisionModelManagerLocal::ParseBrushes -================ -*/ -void idCollisionModelManagerLocal::ParseBrushes( idLexer *src, cm_model_t *model ) { - cm_brush_t *b; - int i, numPlanes; - idVec3 normal; - idToken token; - - if ( src->CheckTokenType( TT_NUMBER, 0, &token ) ) { - model->brushBlock = (cm_brushBlock_t *) Mem_Alloc( sizeof( cm_brushBlock_t ) + token.GetIntValue() ); - model->brushBlock->bytesRemaining = token.GetIntValue(); - model->brushBlock->next = ( (byte *) model->brushBlock ) + sizeof( cm_brushBlock_t ); - } - - src->ExpectTokenString( "{" ); - while ( !src->CheckTokenString( "}" ) ) { - // parse brush - numPlanes = src->ParseInt(); - b = AllocBrush( model, numPlanes ); - b->numPlanes = numPlanes; - src->ExpectTokenString( "{" ); - for ( i = 0; i < b->numPlanes; i++ ) { - src->Parse1DMatrix( 3, normal.ToFloatPtr() ); - b->planes[i].SetNormal( normal ); - b->planes[i].SetDist( src->ParseFloat() ); - } - src->ExpectTokenString( "}" ); - src->Parse1DMatrix( 3, b->bounds[0].ToFloatPtr() ); - src->Parse1DMatrix( 3, b->bounds[1].ToFloatPtr() ); - src->ReadToken( &token ); - if ( token.type == TT_NUMBER ) { - b->contents = token.GetIntValue(); // old .cm files use a single integer - } else { - b->contents = ContentsFromString( token ); - } - b->checkcount = 0; - b->primitiveNum = 0; - // filter brush into tree - R_FilterBrushIntoTree( model, model->node, NULL, b ); - } -} - -/* -================ -idCollisionModelManagerLocal::ParseCollisionModel -================ -*/ -bool idCollisionModelManagerLocal::ParseCollisionModel( idLexer *src ) { - cm_model_t *model; - idToken token; - - if ( numModels >= MAX_SUBMODELS ) { - common->Error( "LoadModel: no free slots" ); - return false; - } - model = AllocModel(); - models[numModels ] = model; - numModels++; - // parse the file - src->ExpectTokenType( TT_STRING, 0, &token ); - model->name = token; - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - if (anyInlinedProcClipMats) { - if (token.Cmpn(PROC_CLIPMODEL_STRING_PRFX, strlen(PROC_CLIPMODEL_STRING_PRFX)) == 0) { - numInlinedProcClipModels++; - } - } -#endif - //HUMANHEAD END - src->ExpectTokenString( "{" ); - while ( !src->CheckTokenString( "}" ) ) { - - src->ReadToken( &token ); - - if ( token == "vertices" ) { - ParseVertices( src, model ); - continue; - } - - if ( token == "edges" ) { - ParseEdges( src, model ); - continue; - } - - if ( token == "nodes" ) { - src->ExpectTokenString( "{" ); - model->node = ParseNodes( src, model, NULL ); - src->ExpectTokenString( "}" ); - continue; - } - - if ( token == "polygons" ) { - ParsePolygons( src, model ); - continue; - } - - if ( token == "brushes" ) { - ParseBrushes( src, model ); - continue; - } - - src->Error( "ParseCollisionModel: bad token \"%s\"", token.c_str() ); - } - // calculate edge normals - checkCount++; - CalculateEdgeNormals( model, model->node ); - // get model bounds from brush and polygon bounds - CM_GetNodeBounds( &model->bounds, model->node ); - // get model contents - model->contents = CM_GetNodeContents( model->node ); - // total memory used by this model - model->usedMemory = model->numVertices * sizeof(cm_vertex_t) + - model->numEdges * sizeof(cm_edge_t) + - model->polygonMemory + - model->brushMemory + - model->numNodes * sizeof(cm_node_t) + - model->numPolygonRefs * sizeof(cm_polygonRef_t) + - model->numBrushRefs * sizeof(cm_brushRef_t); - - return true; -} - -/* -================ -idCollisionModelManagerLocal::LoadCollisionModelFile -================ -*/ -bool idCollisionModelManagerLocal::LoadCollisionModelFile( const char *name, unsigned int mapFileCRC ) { - idStr fileName; - idToken token; - idLexer *src; - unsigned int crc; - - // load it - fileName = name; - fileName.SetFileExtension( CM_FILE_EXT ); - src = new idLexer( fileName ); - src->SetFlags( LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE ); - if ( !src->IsLoaded() ) { - delete src; - return false; - } - - if ( !src->ExpectTokenString( CM_FILEID ) ) { - common->Warning( "%s is not an CM file.", fileName.c_str() ); - delete src; - return false; - } - - if ( !src->ReadToken( &token ) || token != CM_FILEVERSION ) { - common->Warning( "%s has version %s instead of %s", fileName.c_str(), token.c_str(), CM_FILEVERSION ); - delete src; - return false; - } - - if ( !src->ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) { - common->Warning( "%s has no map file CRC", fileName.c_str() ); - delete src; - return false; - } - - crc = token.GetUnsignedLongValue(); - if ( mapFileCRC && crc != mapFileCRC ) { - common->Printf( "%s is out of date\n", fileName.c_str() ); - delete src; - return false; - } - - // parse the file - while ( 1 ) { - if ( !src->ReadToken( &token ) ) { - break; - } - - if ( token == "collisionModel" ) { - if ( !ParseCollisionModel( src ) ) { - delete src; - return false; - } - continue; - } - - src->Error( "idCollisionModelManagerLocal::LoadCollisionModelFile: bad token \"%s\"", token.c_str() ); - } - - delete src; - - return true; -} diff --git a/src/cm/CollisionModel_load.cpp b/src/cm/CollisionModel_load.cpp deleted file mode 100644 index e31a835..0000000 --- a/src/cm/CollisionModel_load.cpp +++ /dev/null @@ -1,3955 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - - It is more important to minimize the number of collision polygons - than it is to minimize the number of edges used for collision - detection (total edges - internal edges). - - Stitching the world tends to minimize the number of edges used - for collision detection (more internal edges). However stitching - also results in more collision polygons which usually makes a - stitched world slower. - - In an average map over 30% of all edges is internal. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - - -idCollisionModelManagerLocal collisionModelManagerLocal; -idCollisionModelManager * collisionModelManager = &collisionModelManagerLocal; - -cm_windingList_t * cm_windingList; -cm_windingList_t * cm_outList; -cm_windingList_t * cm_tmpList; - -idHashIndex * cm_vertexHash; -idHashIndex * cm_edgeHash; - -idBounds cm_modelBounds; -int cm_vertexShift; - - -/* -=============================================================================== - -Proc BSP tree for data pruning - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::ParseProcNodes -================ -*/ -void idCollisionModelManagerLocal::ParseProcNodes( idLexer *src ) { - int i; - - src->ExpectTokenString( "{" ); - - numProcNodes = src->ParseInt(); - if ( numProcNodes < 0 ) { - src->Error( "ParseProcNodes: bad numProcNodes" ); - } - procNodes = (cm_procNode_t *)Mem_ClearedAlloc( numProcNodes * sizeof( cm_procNode_t ) ); - - for ( i = 0; i < numProcNodes; i++ ) { - cm_procNode_t *node; - - node = &procNodes[i]; - - src->Parse1DMatrix( 4, node->plane.ToFloatPtr() ); - node->children[0] = src->ParseInt(); - node->children[1] = src->ParseInt(); - } - - src->ExpectTokenString( "}" ); -} - -//HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS -typedef struct basicSurf_s { - idDrawVert *verts; - int numVerts; - glIndex_t *indices; - int numIndices; - const idMaterial *mat; -} basicSurf_t; -/* -================ -idCollisionModelManagerLocal::CheckProcModelSurfClip -================ -*/ -void idCollisionModelManagerLocal::CheckProcModelSurfClip(idLexer *src) { - idToken token; - int i, j; - idList basicSurfs; - bool parseOver = false; - - src->ExpectTokenString( "{" ); - - // parse the name - //unfortunately, this isn't designer-recognizable and tends to bunch things up, so can't be used instead of material name - //however, we can expect only _area* models to contain inlined geometry - src->ExpectAnyToken( &token ); - if (token.Cmpn("_area", strlen("_area")) != 0) { - parseOver = true; - } - - int numSurfaces = src->ParseInt(); - if ( numSurfaces < 0 ) { - src->Error( "idCollisionModelManagerLocal::CheckProcModelSurfClip: bad numSurfaces" ); - } - - cm_node_t *node; - cm_model_t *model = NULL; - idBounds totalBounds; - - totalBounds.Zero(); - - for ( i = 0 ; i < numSurfaces ; i++ ) { - src->ExpectTokenString( "{" ); - - src->ExpectAnyToken( &token ); - - //check if this material is in the material list - if (!parseOver) { - for (j = 0; j < inlinedProcClipModelMats.Num(); j++) { - if (inlinedProcClipModelMats[j] == token) { - break; - } - } - } - if (parseOver || j == inlinedProcClipModelMats.Num()) { //just parse over this surf - int nV, nI; - nV = src->ParseInt(); - nI = src->ParseInt(); - for ( j = 0 ; j < nV ; j++ ) { - float vec[8]; - src->Parse1DMatrix( 8, vec ); - } - for ( j = 0 ; j < nI ; j++ ) { - src->ParseInt(); - } - src->ExpectTokenString( "}" ); - continue; - } - - const idMaterial *mat = declManager->FindMaterial( token ); - - //we have a surface we want to use - if (!model) { - model = AllocModel(); - - model->name = va("%s%i", PROC_CLIPMODEL_STRING_PRFX, numInlinedProcClipModels); - node = AllocNode( model, NODE_BLOCK_SIZE_SMALL ); - node->planeType = -1; - model->node = node; - - model->maxVertices = 0; - model->numVertices = 0; - model->maxEdges = 0; - model->numEdges = 0; - } - - int numVerts = src->ParseInt(); - int numIndices = src->ParseInt(); - - model->maxVertices += numVerts; - model->maxEdges += numIndices; - - idDrawVert *verts = (idDrawVert *)Mem_Alloc(numVerts*sizeof(idDrawVert)); - glIndex_t *indices = (glIndex_t *)Mem_Alloc(numIndices*sizeof(glIndex_t)); - - //parse the actual verts and tris - for ( j = 0 ; j < numVerts ; j++ ) { - float vec[8]; - - src->Parse1DMatrix( 8, vec ); - - verts[j].xyz[0] = vec[0]; - verts[j].xyz[1] = vec[1]; - verts[j].xyz[2] = vec[2]; - verts[j].st[0] = vec[3]; - verts[j].st[1] = vec[4]; - verts[j].normal[0] = vec[5]; - verts[j].normal[1] = vec[6]; - verts[j].normal[2] = vec[7]; - } - - for ( j = 0 ; j < numIndices ; j++ ) { - indices[j] = src->ParseInt(); - } - src->ExpectTokenString( "}" ); - - //calculate a bounds for the surface - idBounds surfBounds; - surfBounds.Zero(); - SIMDProcessor->MinMax(surfBounds[0], surfBounds[1], verts, numVerts); - totalBounds.AddBounds(surfBounds); - - basicSurf_t bs; - bs.verts = verts; - bs.numVerts = numVerts; - bs.indices = indices; - bs.numIndices = numIndices; - bs.mat = mat; - basicSurfs.Append(bs); - } - - src->ExpectTokenString( "}" ); - - if (model) { //we put together a model from the proc surfs - idFixedWinding w; - idPlane plane; - - assert(basicSurfs.Num() > 0); - - model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) ); - model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) ); - - assert(cm_vertexHash && cm_edgeHash); //after all, this should only be called while building models - cm_vertexHash->ResizeIndex( model->maxVertices ); - cm_edgeHash->ResizeIndex( model->maxEdges ); - ClearHash(totalBounds); - - for (i = 0; i < basicSurfs.Num(); i++) { - for ( j = 0; j < basicSurfs[i].numIndices; j += 3 ) { - w.Clear(); - w += basicSurfs[i].verts[ basicSurfs[i].indices[ j + 2 ] ].xyz; - w += basicSurfs[i].verts[ basicSurfs[i].indices[ j + 1 ] ].xyz; - w += basicSurfs[i].verts[ basicSurfs[i].indices[ j + 0 ] ].xyz; - w.GetPlane( plane ); - plane = -plane; - PolygonFromWinding( model, &w, plane, basicSurfs[i].mat, 1 ); - } - //free up the temp proc surf memory now that we're done with it - Mem_Free(basicSurfs[i].verts); - Mem_Free(basicSurfs[i].indices); - } - - // create a BSP tree for the model - model->node = CreateAxialBSPTree( model, model->node ); - - model->isConvex = false; - - FinishModel( model ); - - //add our new clipmodel to the list - models[PROC_CLIPMODEL_INDEX_START+numInlinedProcClipModels] = model; - numInlinedProcClipModels++; - } -} -#endif -//HUMANHEAD END - -/* -================ -idCollisionModelManagerLocal::LoadProcBSP - - FIXME: if the nodes would be at the start of the .proc file it would speed things up considerably -================ -*/ -void idCollisionModelManagerLocal::LoadProcBSP( const char *name ) { - idStr filename; - idToken token; - idLexer *src; - - // load it - filename = name; - filename.SetFileExtension( PROC_FILE_EXT ); - src = new idLexer( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE ); - if ( !src->IsLoaded() ) { - common->Warning( "idCollisionModelManagerLocal::LoadProcBSP: couldn't load %s", filename.c_str() ); - delete src; - return; - } - - if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) { - common->Warning( "idCollisionModelManagerLocal::LoadProcBSP: bad id '%s' instead of '%s'", token.c_str(), PROC_FILE_ID ); - delete src; - return; - } - - // parse the file - while ( 1 ) { - if ( !src->ReadToken( &token ) ) { - break; - } - - if ( token == "model" ) { - //HUMANHEAD rww - parse model surfs to create clip models -#if _HH_INLINED_PROC_CLIPMODELS - if (inlinedProcClipModelMats.Num() > 0) { - CheckProcModelSurfClip(src); - } - //HUMANHEAD END - else { - src->SkipBracedSection(); - } -#else - src->SkipBracedSection(); -#endif - continue; - } - - if ( token == "shadowModel" ) { - src->SkipBracedSection(); - continue; - } - - if ( token == "interAreaPortals" ) { - src->SkipBracedSection(); - continue; - } - - if ( token == "nodes" ) { - ParseProcNodes( src ); - break; - } - - src->Error( "idCollisionModelManagerLocal::LoadProcBSP: bad token \"%s\"", token.c_str() ); - } - - delete src; -} - -/* -=============================================================================== - -Free map - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::Clear -================ -*/ -void idCollisionModelManagerLocal::Clear( void ) { - mapName.Clear(); - mapFileTime = 0; - loaded = 0; - checkCount = 0; - maxModels = 0; - numModels = 0; - models = NULL; - memset( trmPolygons, 0, sizeof( trmPolygons ) ); - trmBrushes[0] = NULL; - trmMaterial = NULL; - numProcNodes = 0; - procNodes = NULL; - getContacts = false; - contacts = NULL; - maxContacts = 0; - numContacts = 0; - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - inlinedProcClipModelMats.Clear(); - numInlinedProcClipModels = 0; - anyInlinedProcClipMats = false; -#endif - //HUMANHEAD END -} - -/* -================ -idCollisionModelManagerLocal::RemovePolygonReferences_r -================ -*/ -void idCollisionModelManagerLocal::RemovePolygonReferences_r( cm_node_t *node, cm_polygon_t *p ) { - cm_polygonRef_t *pref; - - while( node ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - if ( pref->p == p ) { - pref->p = NULL; - // cannot return here because we can have links down the tree due to polygon merging - //return; - } - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - if ( p->bounds[0][node->planeType] > node->planeDist ) { - node = node->children[0]; - } - else if ( p->bounds[1][node->planeType] < node->planeDist ) { - node = node->children[1]; - } - else { - RemovePolygonReferences_r( node->children[1], p ); - node = node->children[0]; - } - } -} - -/* -================ -idCollisionModelManagerLocal::RemoveBrushReferences_r -================ -*/ -void idCollisionModelManagerLocal::RemoveBrushReferences_r( cm_node_t *node, cm_brush_t *b ) { - cm_brushRef_t *bref; - - while( node ) { - for ( bref = node->brushes; bref; bref = bref->next ) { - if ( bref->b == b ) { - bref->b = NULL; - return; - } - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - if ( b->bounds[0][node->planeType] > node->planeDist ) { - node = node->children[0]; - } - else if ( b->bounds[1][node->planeType] < node->planeDist ) { - node = node->children[1]; - } - else { - RemoveBrushReferences_r( node->children[1], b ); - node = node->children[0]; - } - } -} - -/* -================ -idCollisionModelManagerLocal::FreeNode -================ -*/ -void idCollisionModelManagerLocal::FreeNode( cm_node_t *node ) { - // don't free the node here - // the nodes are allocated in blocks which are freed when the model is freed -} - -/* -================ -idCollisionModelManagerLocal::FreePolygonReference -================ -*/ -void idCollisionModelManagerLocal::FreePolygonReference( cm_polygonRef_t *pref ) { - // don't free the polygon reference here - // the polygon references are allocated in blocks which are freed when the model is freed -} - -/* -================ -idCollisionModelManagerLocal::FreeBrushReference -================ -*/ -void idCollisionModelManagerLocal::FreeBrushReference( cm_brushRef_t *bref ) { - // don't free the brush reference here - // the brush references are allocated in blocks which are freed when the model is freed -} - -/* -================ -idCollisionModelManagerLocal::FreePolygon -================ -*/ -void idCollisionModelManagerLocal::FreePolygon( cm_model_t *model, cm_polygon_t *poly ) { - model->numPolygons--; - model->polygonMemory -= sizeof( cm_polygon_t ) + ( poly->numEdges - 1 ) * sizeof( poly->edges[0] ); - if ( model->polygonBlock == NULL ) { - Mem_Free( poly ); - } -} - -/* -================ -idCollisionModelManagerLocal::FreeBrush -================ -*/ -void idCollisionModelManagerLocal::FreeBrush( cm_model_t *model, cm_brush_t *brush ) { - model->numBrushes--; - model->brushMemory -= sizeof( cm_brush_t ) + ( brush->numPlanes - 1 ) * sizeof( brush->planes[0] ); - if ( model->brushBlock == NULL ) { - Mem_Free( brush ); - } -} - -/* -================ -idCollisionModelManagerLocal::FreeTree_r -================ -*/ -void idCollisionModelManagerLocal::FreeTree_r( cm_model_t *model, cm_node_t *headNode, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - cm_brushRef_t *bref; - cm_brush_t *b; - - // free all polygons at this node - for ( pref = node->polygons; pref; pref = node->polygons ) { - p = pref->p; - if ( p ) { - // remove all other references to this polygon - RemovePolygonReferences_r( headNode, p ); - FreePolygon( model, p ); - } - node->polygons = pref->next; - FreePolygonReference( pref ); - } - // free all brushes at this node - for ( bref = node->brushes; bref; bref = node->brushes ) { - b = bref->b; - if ( b ) { - // remove all other references to this brush - RemoveBrushReferences_r( headNode, b ); - FreeBrush( model, b ); - } - node->brushes = bref->next; - FreeBrushReference( bref ); - } - // recurse down the tree - if ( node->planeType != -1 ) { - FreeTree_r( model, headNode, node->children[0] ); - node->children[0] = NULL; - FreeTree_r( model, headNode, node->children[1] ); - node->children[1] = NULL; - } - FreeNode( node ); -} - -/* -================ -idCollisionModelManagerLocal::FreeModel -================ -*/ -void idCollisionModelManagerLocal::FreeModel( cm_model_t *model ) { - cm_polygonRefBlock_t *polygonRefBlock, *nextPolygonRefBlock; - cm_brushRefBlock_t *brushRefBlock, *nextBrushRefBlock; - cm_nodeBlock_t *nodeBlock, *nextNodeBlock; - - // free the tree structure - if ( model->node ) { - FreeTree_r( model, model->node, model->node ); - } - // free blocks with polygon references - for ( polygonRefBlock = model->polygonRefBlocks; polygonRefBlock; polygonRefBlock = nextPolygonRefBlock ) { - nextPolygonRefBlock = polygonRefBlock->next; - Mem_Free( polygonRefBlock ); - } - // free blocks with brush references - for ( brushRefBlock = model->brushRefBlocks; brushRefBlock; brushRefBlock = nextBrushRefBlock ) { - nextBrushRefBlock = brushRefBlock->next; - Mem_Free( brushRefBlock ); - } - // free blocks with nodes - for ( nodeBlock = model->nodeBlocks; nodeBlock; nodeBlock = nextNodeBlock ) { - nextNodeBlock = nodeBlock->next; - Mem_Free( nodeBlock ); - } - // free block allocated polygons - Mem_Free( model->polygonBlock ); - // free block allocated brushes - Mem_Free( model->brushBlock ); - // free edges - Mem_Free( model->edges ); - // free vertices - Mem_Free( model->vertices ); - // free the model - delete model; -} - -/* -================ -idCollisionModelManagerLocal::FreeMap -================ -*/ -void idCollisionModelManagerLocal::FreeMap( void ) { - int i; - - if ( !loaded ) { - Clear(); - return; - } - - for ( i = 0; i < maxModels; i++ ) { - if ( !models[i] ) { - continue; - } - FreeModel( models[i] ); - } - - FreeTrmModelStructure(); - - Mem_Free( models ); - - Clear(); - - ShutdownHash(); -} - -/* -================ -idCollisionModelManagerLocal::FreeTrmModelStructure -================ -*/ -void idCollisionModelManagerLocal::FreeTrmModelStructure( void ) { - int i; - - assert( models ); - if ( !models[MAX_SUBMODELS] ) { - return; - } - - for ( i = 0; i < MAX_TRACEMODEL_POLYS; i++ ) { - FreePolygon( models[MAX_SUBMODELS], trmPolygons[i]->p ); - } - FreeBrush( models[MAX_SUBMODELS], trmBrushes[0]->b ); - - models[MAX_SUBMODELS]->node->polygons = NULL; - models[MAX_SUBMODELS]->node->brushes = NULL; - FreeModel( models[MAX_SUBMODELS] ); -} - - -/* -=============================================================================== - -Edge normals - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::CalculateEdgeNormals -================ -*/ -#define SHARP_EDGE_DOT -0.7f - -void idCollisionModelManagerLocal::CalculateEdgeNormals( cm_model_t *model, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - cm_edge_t *edge; - float dot, s; - int i, edgeNum; - idVec3 dir; - - while( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - // if we checked this polygon already - if ( p->checkcount == checkCount ) { - continue; - } - p->checkcount = checkCount; - - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = model->edges + abs( edgeNum ); - if ( edge->normal[0] == 0.0f && edge->normal[1] == 0.0f && edge->normal[2] == 0.0f ) { - // if the edge is only used by this polygon - if ( edge->numUsers == 1 ) { - dir = model->vertices[ edge->vertexNum[edgeNum < 0]].p - model->vertices[ edge->vertexNum[edgeNum > 0]].p; - edge->normal = p->plane.Normal().Cross( dir ); - edge->normal.Normalize(); - } else { - // the edge is used by more than one polygon - edge->normal = p->plane.Normal(); - } - } else { - dot = edge->normal * p->plane.Normal(); - // if the two planes make a very sharp edge - if ( dot < SHARP_EDGE_DOT ) { - // max length normal pointing outside both polygons - dir = model->vertices[ edge->vertexNum[edgeNum > 0]].p - model->vertices[ edge->vertexNum[edgeNum < 0]].p; - edge->normal = edge->normal.Cross( dir ) + p->plane.Normal().Cross( -dir ); - edge->normal *= ( 0.5f / ( 0.5f + 0.5f * SHARP_EDGE_DOT ) ) / edge->normal.Length(); - model->numSharpEdges++; - } else { - s = 0.5f / ( 0.5f + 0.5f * dot ); - edge->normal = s * ( edge->normal + p->plane.Normal() ); - } - } - } - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - CalculateEdgeNormals( model, node->children[1] ); - node = node->children[0]; - } -} - -/* -=============================================================================== - -Trace model to general collision model - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::AllocModel -================ -*/ -cm_model_t *idCollisionModelManagerLocal::AllocModel( void ) { - cm_model_t *model; - - model = new cm_model_t; - model->contents = 0; - model->isConvex = false; - model->maxVertices = 0; - model->numVertices = 0; - model->vertices = NULL; - model->maxEdges = 0; - model->numEdges = 0; - model->edges= NULL; - model->node = NULL; - model->nodeBlocks = NULL; - model->polygonRefBlocks = NULL; - model->brushRefBlocks = NULL; - model->polygonBlock = NULL; - model->brushBlock = NULL; - model->numPolygons = model->polygonMemory = - model->numBrushes = model->brushMemory = - model->numNodes = model->numBrushRefs = - model->numPolygonRefs = model->numInternalEdges = - model->numSharpEdges = model->numRemovedPolys = - model->numMergedPolys = model->usedMemory = 0; - - return model; -} - -/* -================ -idCollisionModelManagerLocal::AllocNode -================ -*/ -cm_node_t *idCollisionModelManagerLocal::AllocNode( cm_model_t *model, int blockSize ) { - int i; - cm_node_t *node; - cm_nodeBlock_t *nodeBlock; - - if ( !model->nodeBlocks || !model->nodeBlocks->nextNode ) { - nodeBlock = (cm_nodeBlock_t *) Mem_ClearedAlloc( sizeof( cm_nodeBlock_t ) + blockSize * sizeof(cm_node_t) ); - nodeBlock->nextNode = (cm_node_t *) ( ( (byte *) nodeBlock ) + sizeof( cm_nodeBlock_t ) ); - nodeBlock->next = model->nodeBlocks; - model->nodeBlocks = nodeBlock; - node = nodeBlock->nextNode; - for ( i = 0; i < blockSize - 1; i++ ) { - node->parent = node + 1; - node = node->parent; - } - node->parent = NULL; - } - - node = model->nodeBlocks->nextNode; - model->nodeBlocks->nextNode = node->parent; - node->parent = NULL; - - return node; -} - -/* -================ -idCollisionModelManagerLocal::AllocPolygonReference -================ -*/ -cm_polygonRef_t *idCollisionModelManagerLocal::AllocPolygonReference( cm_model_t *model, int blockSize ) { - int i; - cm_polygonRef_t *pref; - cm_polygonRefBlock_t *prefBlock; - - if ( !model->polygonRefBlocks || !model->polygonRefBlocks->nextRef ) { - prefBlock = (cm_polygonRefBlock_t *) Mem_Alloc( sizeof( cm_polygonRefBlock_t ) + blockSize * sizeof(cm_polygonRef_t) ); - prefBlock->nextRef = (cm_polygonRef_t *) ( ( (byte *) prefBlock ) + sizeof( cm_polygonRefBlock_t ) ); - prefBlock->next = model->polygonRefBlocks; - model->polygonRefBlocks = prefBlock; - pref = prefBlock->nextRef; - for ( i = 0; i < blockSize - 1; i++ ) { - pref->next = pref + 1; - pref = pref->next; - } - pref->next = NULL; - } - - pref = model->polygonRefBlocks->nextRef; - model->polygonRefBlocks->nextRef = pref->next; - - return pref; -} - -/* -================ -idCollisionModelManagerLocal::AllocBrushReference -================ -*/ -cm_brushRef_t *idCollisionModelManagerLocal::AllocBrushReference( cm_model_t *model, int blockSize ) { - int i; - cm_brushRef_t *bref; - cm_brushRefBlock_t *brefBlock; - - if ( !model->brushRefBlocks || !model->brushRefBlocks->nextRef ) { - brefBlock = (cm_brushRefBlock_t *) Mem_Alloc( sizeof(cm_brushRefBlock_t) + blockSize * sizeof(cm_brushRef_t) ); - brefBlock->nextRef = (cm_brushRef_t *) ( ( (byte *) brefBlock ) + sizeof(cm_brushRefBlock_t) ); - brefBlock->next = model->brushRefBlocks; - model->brushRefBlocks = brefBlock; - bref = brefBlock->nextRef; - for ( i = 0; i < blockSize - 1; i++ ) { - bref->next = bref + 1; - bref = bref->next; - } - bref->next = NULL; - } - - bref = model->brushRefBlocks->nextRef; - model->brushRefBlocks->nextRef = bref->next; - - return bref; -} - -/* -================ -idCollisionModelManagerLocal::AllocPolygon -================ -*/ -cm_polygon_t *idCollisionModelManagerLocal::AllocPolygon( cm_model_t *model, int numEdges ) { - cm_polygon_t *poly; - int size; - - size = sizeof( cm_polygon_t ) + ( numEdges - 1 ) * sizeof( poly->edges[0] ); - model->numPolygons++; - model->polygonMemory += size; - if ( model->polygonBlock && model->polygonBlock->bytesRemaining >= size ) { - poly = (cm_polygon_t *) model->polygonBlock->next; - model->polygonBlock->next += size; - model->polygonBlock->bytesRemaining -= size; - } else { - poly = (cm_polygon_t *) Mem_Alloc( size ); - } - return poly; -} - -/* -================ -idCollisionModelManagerLocal::AllocBrush -================ -*/ -cm_brush_t *idCollisionModelManagerLocal::AllocBrush( cm_model_t *model, int numPlanes ) { - cm_brush_t *brush; - int size; - - size = sizeof( cm_brush_t ) + ( numPlanes - 1 ) * sizeof( brush->planes[0] ); - model->numBrushes++; - model->brushMemory += size; - if ( model->brushBlock && model->brushBlock->bytesRemaining >= size ) { - brush = (cm_brush_t *) model->brushBlock->next; - model->brushBlock->next += size; - model->brushBlock->bytesRemaining -= size; - } else { - brush = (cm_brush_t *) Mem_Alloc( size ); - } - return brush; -} - -/* -================ -idCollisionModelManagerLocal::AddPolygonToNode -================ -*/ -void idCollisionModelManagerLocal::AddPolygonToNode( cm_model_t *model, cm_node_t *node, cm_polygon_t *p ) { - cm_polygonRef_t *pref; - - pref = AllocPolygonReference( model, model->numPolygonRefs < REFERENCE_BLOCK_SIZE_SMALL ? REFERENCE_BLOCK_SIZE_SMALL : REFERENCE_BLOCK_SIZE_LARGE ); - pref->p = p; - pref->next = node->polygons; - node->polygons = pref; - model->numPolygonRefs++; -} - -/* -================ -idCollisionModelManagerLocal::AddBrushToNode -================ -*/ -void idCollisionModelManagerLocal::AddBrushToNode( cm_model_t *model, cm_node_t *node, cm_brush_t *b ) { - cm_brushRef_t *bref; - - bref = AllocBrushReference( model, model->numBrushRefs < REFERENCE_BLOCK_SIZE_SMALL ? REFERENCE_BLOCK_SIZE_SMALL : REFERENCE_BLOCK_SIZE_LARGE ); - bref->b = b; - bref->next = node->brushes; - node->brushes = bref; - model->numBrushRefs++; -} - -/* -================ -idCollisionModelManagerLocal::SetupTrmModelStructure -================ -*/ -void idCollisionModelManagerLocal::SetupTrmModelStructure( void ) { - int i; - cm_node_t *node; - cm_model_t *model; - - // setup model - model = AllocModel(); - - assert( models ); - models[MAX_SUBMODELS] = model; - // create node to hold the collision data - node = (cm_node_t *) AllocNode( model, 1 ); - node->planeType = -1; - model->node = node; - // allocate vertex and edge arrays - model->numVertices = 0; - model->maxVertices = MAX_TRACEMODEL_VERTS; - model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) ); - model->numEdges = 0; - model->maxEdges = MAX_TRACEMODEL_EDGES+1; - model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) ); - // create a material for the trace model polygons - trmMaterial = declManager->FindMaterial( "_tracemodel", false ); - if ( !trmMaterial ) { - common->FatalError( "_tracemodel material not found" ); - } - - // allocate polygons - for ( i = 0; i < MAX_TRACEMODEL_POLYS; i++ ) { - trmPolygons[i] = AllocPolygonReference( model, MAX_TRACEMODEL_POLYS ); - trmPolygons[i]->p = AllocPolygon( model, MAX_TRACEMODEL_POLYEDGES ); - trmPolygons[i]->p->bounds.Clear(); - trmPolygons[i]->p->plane.Zero(); - trmPolygons[i]->p->checkcount = 0; - trmPolygons[i]->p->contents = -1; // all contents - trmPolygons[i]->p->material = trmMaterial; - trmPolygons[i]->p->numEdges = 0; - } - // allocate brush for position test - trmBrushes[0] = AllocBrushReference( model, 1 ); - trmBrushes[0]->b = AllocBrush( model, MAX_TRACEMODEL_POLYS ); - trmBrushes[0]->b->primitiveNum = 0; - trmBrushes[0]->b->bounds.Clear(); - trmBrushes[0]->b->checkcount = 0; - trmBrushes[0]->b->contents = -1; // all contents - trmBrushes[0]->b->material = trmMaterial; //HUMANHEAD rww - trmBrushes[0]->b->numPlanes = 0; -} - -/* -================ -idCollisionModelManagerLocal::SetupTrmModel - -Trace models (item boxes, etc) are converted to collision models on the fly, using the last model slot -as a reusable temporary buffer -================ -*/ -cmHandle_t idCollisionModelManagerLocal::SetupTrmModel( const idTraceModel &trm, const idMaterial *material ) { - int i, j; - cm_vertex_t *vertex; - cm_edge_t *edge; - cm_polygon_t *poly; - cm_model_t *model; - const traceModelVert_t *trmVert; - const traceModelEdge_t *trmEdge; - const traceModelPoly_t *trmPoly; - - assert( models ); - - if ( material == NULL ) { - material = trmMaterial; - } - - model = models[MAX_SUBMODELS]; - model->node->brushes = NULL; - model->node->polygons = NULL; - // if not a valid trace model - if ( trm.type == TRM_INVALID || !trm.numPolys ) { - return TRACE_MODEL_HANDLE; - } - // vertices - model->numVertices = trm.numVerts; - vertex = model->vertices; - trmVert = trm.verts; - for ( i = 0; i < trm.numVerts; i++, vertex++, trmVert++ ) { - vertex->p = *trmVert; - vertex->sideSet = 0; - } - // edges - model->numEdges = trm.numEdges; - edge = model->edges + 1; - trmEdge = trm.edges + 1; - for ( i = 0; i < trm.numEdges; i++, edge++, trmEdge++ ) { - edge->vertexNum[0] = trmEdge->v[0]; - edge->vertexNum[1] = trmEdge->v[1]; - edge->normal = trmEdge->normal; - edge->internal = false; - edge->sideSet = 0; - } - // polygons - model->numPolygons = trm.numPolys; - trmPoly = trm.polys; - for ( i = 0; i < trm.numPolys; i++, trmPoly++ ) { - poly = trmPolygons[i]->p; - poly->numEdges = trmPoly->numEdges; - for ( j = 0; j < trmPoly->numEdges; j++ ) { - poly->edges[j] = trmPoly->edges[j]; - } - poly->plane.SetNormal( trmPoly->normal ); - poly->plane.SetDist( trmPoly->dist ); - poly->bounds = trmPoly->bounds; - poly->material = material; - // link polygon at node - trmPolygons[i]->next = model->node->polygons; - model->node->polygons = trmPolygons[i]; - } - // if the trace model is convex - if ( trm.isConvex ) { - // setup brush for position test - trmBrushes[0]->b->numPlanes = trm.numPolys; - for ( i = 0; i < trm.numPolys; i++ ) { - trmBrushes[0]->b->planes[i] = trmPolygons[i]->p->plane; - } - trmBrushes[0]->b->bounds = trm.bounds; - // link brush at node - trmBrushes[0]->next = model->node->brushes; - model->node->brushes = trmBrushes[0]; - } - // model bounds - model->bounds = trm.bounds; - // convex - model->isConvex = trm.isConvex; - - return TRACE_MODEL_HANDLE; -} - -/* -=============================================================================== - -Optimisation, removal of polygons contained within brushes or solid - -=============================================================================== -*/ - -/* -============ -idCollisionModelManagerLocal::R_ChoppedAwayByProcBSP -============ -*/ -int idCollisionModelManagerLocal::R_ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius ) { - int res; - idFixedWinding back; - cm_procNode_t *node; - float dist; - - do { - node = procNodes + nodeNum; - dist = node->plane.Normal() * origin + node->plane[3]; - if ( dist > radius ) { - res = SIDE_FRONT; - } - else if ( dist < -radius ) { - res = SIDE_BACK; - } - else { - res = w->Split( &back, node->plane, CHOP_EPSILON ); - } - if ( res == SIDE_FRONT ) { - nodeNum = node->children[0]; - } - else if ( res == SIDE_BACK ) { - nodeNum = node->children[1]; - } - else if ( res == SIDE_ON ) { - // continue with the side the winding faces - if ( node->plane.Normal() * normal > 0.0f ) { - nodeNum = node->children[0]; - } - else { - nodeNum = node->children[1]; - } - } - else { - // if either node is not solid - if ( node->children[0] < 0 || node->children[1] < 0 ) { - return false; - } - // only recurse if the node is not solid - if ( node->children[1] > 0 ) { - if ( !R_ChoppedAwayByProcBSP( node->children[1], &back, normal, origin, radius ) ) { - return false; - } - } - nodeNum = node->children[0]; - } - } while ( nodeNum > 0 ); - if ( nodeNum < 0 ) { - return false; - } - return true; -} - -/* -============ -idCollisionModelManagerLocal::ChoppedAwayByProcBSP -============ -*/ -int idCollisionModelManagerLocal::ChoppedAwayByProcBSP( const idFixedWinding &w, const idPlane &plane, int contents ) { - idFixedWinding neww; - idBounds bounds; - float radius; - idVec3 origin; - - // if the .proc file has no BSP tree - if ( procNodes == NULL ) { - return false; - } - // don't chop if the polygon is not solid - if ( !(contents & CONTENTS_SOLID) ) { - return false; - } - // make a local copy of the winding - neww = w; - neww.GetBounds( bounds ); - origin = (bounds[1] - bounds[0]) * 0.5f; - radius = origin.Length() + CHOP_EPSILON; - origin = bounds[0] + origin; - // - return R_ChoppedAwayByProcBSP( 0, &neww, plane.Normal(), origin, radius ); -} - -/* -============= -idCollisionModelManagerLocal::ChopWindingWithBrush - - returns the least number of winding fragments outside the brush -============= -*/ -void idCollisionModelManagerLocal::ChopWindingListWithBrush( cm_windingList_t *list, cm_brush_t *b ) { - int i, k, res, startPlane, planeNum, bestNumWindings; - idFixedWinding back, front; - idPlane plane; - bool chopped; - int sidedness[MAX_POINTS_ON_WINDING]; - float dist; - - if ( b->numPlanes > MAX_POINTS_ON_WINDING ) { - return; - } - - // get sidedness for the list of windings - for ( i = 0; i < b->numPlanes; i++ ) { - plane = -b->planes[i]; - - dist = plane.Distance( list->origin ); - if ( dist > list->radius ) { - sidedness[i] = SIDE_FRONT; - } - else if ( dist < -list->radius ) { - sidedness[i] = SIDE_BACK; - } - else { - sidedness[i] = list->bounds.PlaneSide( plane ); - if ( sidedness[i] == PLANESIDE_FRONT ) { - sidedness[i] = SIDE_FRONT; - } - else if ( sidedness[i] == PLANESIDE_BACK ) { - sidedness[i] = SIDE_BACK; - } - else { - sidedness[i] = SIDE_CROSS; - } - } - } - - cm_outList->numWindings = 0; - for ( k = 0; k < list->numWindings; k++ ) { - // - startPlane = 0; - bestNumWindings = 1 + b->numPlanes; - chopped = false; - do { - front = list->w[k]; - cm_tmpList->numWindings = 0; - for ( planeNum = startPlane, i = 0; i < b->numPlanes; i++, planeNum++ ) { - - if ( planeNum >= b->numPlanes ) { - planeNum = 0; - } - - res = sidedness[planeNum]; - - if ( res == SIDE_CROSS ) { - plane = -b->planes[planeNum]; - res = front.Split( &back, plane, CHOP_EPSILON ); - } - - // NOTE: disabling this can create gaps at places where Z-fighting occurs - // Z-fighting should not occur but what if there is a decal brush side - // with exactly the same size as another brush side ? - // only leave windings on a brush if the winding plane and brush side plane face the same direction - if ( res == SIDE_ON && list->primitiveNum >= 0 && (list->normal * b->planes[planeNum].Normal()) > 0 ) { - // return because all windings in the list will be on this brush side plane - return; - } - - if ( res == SIDE_BACK ) { - if ( cm_outList->numWindings >= MAX_WINDING_LIST ) { - common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST ); - return; - } - // winding and brush didn't intersect, store the original winding - cm_outList->w[cm_outList->numWindings] = list->w[k]; - cm_outList->numWindings++; - chopped = false; - break; - } - - if ( res == SIDE_CROSS ) { - if ( cm_tmpList->numWindings >= MAX_WINDING_LIST ) { - common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST ); - return; - } - // store the front winding in the temporary list - cm_tmpList->w[cm_tmpList->numWindings] = back; - cm_tmpList->numWindings++; - chopped = true; - } - - // if already found a start plane which generates less fragments - if ( cm_tmpList->numWindings >= bestNumWindings ) { - break; - } - } - - // find the best start plane to get the least number of fragments outside the brush - if ( cm_tmpList->numWindings < bestNumWindings ) { - bestNumWindings = cm_tmpList->numWindings; - // store windings from temporary list in the out list - for ( i = 0; i < cm_tmpList->numWindings; i++ ) { - if ( cm_outList->numWindings + i >= MAX_WINDING_LIST ) { - common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST ); - return; - } - cm_outList->w[cm_outList->numWindings+i] = cm_tmpList->w[i]; - } - // if only one winding left then we can't do any better - if ( bestNumWindings == 1 ) { - break; - } - } - - // try the next start plane - startPlane++; - - } while ( chopped && startPlane < b->numPlanes ); - // - if ( chopped ) { - cm_outList->numWindings += bestNumWindings; - } - } - for ( k = 0; k < cm_outList->numWindings; k++ ) { - list->w[k] = cm_outList->w[k]; - } - list->numWindings = cm_outList->numWindings; -} - -/* -============ -idCollisionModelManagerLocal::R_ChopWindingListWithTreeBrushes -============ -*/ -void idCollisionModelManagerLocal::R_ChopWindingListWithTreeBrushes( cm_windingList_t *list, cm_node_t *node ) { - int i; - cm_brushRef_t *bref; - cm_brush_t *b; - - while( 1 ) { - for ( bref = node->brushes; bref; bref = bref->next ) { - b = bref->b; - // if we checked this brush already - if ( b->checkcount == checkCount ) { - continue; - } - b->checkcount = checkCount; - // if the windings in the list originate from this brush - if ( b->primitiveNum == list->primitiveNum ) { - continue; - } - // if brush has a different contents - if ( b->contents != list->contents ) { - continue; - } - // brush bounds and winding list bounds should overlap - for ( i = 0; i < 3; i++ ) { - if ( list->bounds[0][i] > b->bounds[1][i] ) { - break; - } - if ( list->bounds[1][i] < b->bounds[0][i] ) { - break; - } - } - if ( i < 3 ) { - continue; - } - // chop windings in the list with brush - ChopWindingListWithBrush( list, b ); - // if all windings are chopped away we're done - if ( !list->numWindings ) { - return; - } - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - if ( list->bounds[0][node->planeType] > node->planeDist ) { - node = node->children[0]; - } - else if ( list->bounds[1][node->planeType] < node->planeDist ) { - node = node->children[1]; - } - else { - R_ChopWindingListWithTreeBrushes( list, node->children[1] ); - if ( !list->numWindings ) { - return; - } - node = node->children[0]; - } - } -} - -/* -============ -idCollisionModelManagerLocal::WindingOutsideBrushes - - Returns one winding which is not fully contained in brushes. - We always favor less polygons over a stitched world. - If the winding is partly contained and the contained pieces can be chopped off - without creating multiple winding fragments then the chopped winding is returned. -============ -*/ -idFixedWinding *idCollisionModelManagerLocal::WindingOutsideBrushes( idFixedWinding *w, const idPlane &plane, int contents, int primitiveNum, cm_node_t *headNode ) { - int i, windingLeft; - - cm_windingList->bounds.Clear(); - for ( i = 0; i < w->GetNumPoints(); i++ ) { - cm_windingList->bounds.AddPoint( (*w)[i].ToVec3() ); - } - - cm_windingList->origin = (cm_windingList->bounds[1] - cm_windingList->bounds[0]) * 0.5; - cm_windingList->radius = cm_windingList->origin.Length() + CHOP_EPSILON; - cm_windingList->origin = cm_windingList->bounds[0] + cm_windingList->origin; - cm_windingList->bounds[0] -= idVec3( CHOP_EPSILON, CHOP_EPSILON, CHOP_EPSILON ); - cm_windingList->bounds[1] += idVec3( CHOP_EPSILON, CHOP_EPSILON, CHOP_EPSILON ); - - cm_windingList->w[0] = *w; - cm_windingList->numWindings = 1; - cm_windingList->normal = plane.Normal(); - cm_windingList->contents = contents; - cm_windingList->primitiveNum = primitiveNum; - // - checkCount++; - R_ChopWindingListWithTreeBrushes( cm_windingList, headNode ); - // - if ( !cm_windingList->numWindings ) { - return NULL; - } - if ( cm_windingList->numWindings == 1 ) { - return &cm_windingList->w[0]; - } - // if not the world model - if ( numModels != 0 ) { - return w; - } - // check if winding fragments would be chopped away by the proc BSP tree - windingLeft = -1; - for ( i = 0; i < cm_windingList->numWindings; i++ ) { - if ( !ChoppedAwayByProcBSP( cm_windingList->w[i], plane, contents ) ) { - if ( windingLeft >= 0 ) { - return w; - } - windingLeft = i; - } - } - if ( windingLeft >= 0 ) { - return &cm_windingList->w[windingLeft]; - } - return NULL; -} - -/* -=============================================================================== - -Merging polygons - -=============================================================================== -*/ - -/* -============= -idCollisionModelManagerLocal::ReplacePolygons - - does not allow for a node to have multiple references to the same polygon -============= -*/ -void idCollisionModelManagerLocal::ReplacePolygons( cm_model_t *model, cm_node_t *node, cm_polygon_t *p1, cm_polygon_t *p2, cm_polygon_t *newp ) { - cm_polygonRef_t *pref, *lastpref, *nextpref; - cm_polygon_t *p; - bool linked; - - while( 1 ) { - linked = false; - lastpref = NULL; - for ( pref = node->polygons; pref; pref = nextpref ) { - nextpref = pref->next; - // - p = pref->p; - // if this polygon reference should change - if ( p == p1 || p == p2 ) { - // if the new polygon is already linked at this node - if ( linked ) { - if ( lastpref ) { - lastpref->next = nextpref; - } - else { - node->polygons = nextpref; - } - FreePolygonReference( pref ); - model->numPolygonRefs--; - } - else { - pref->p = newp; - linked = true; - lastpref = pref; - } - } - else { - lastpref = pref; - } - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - if ( p1->bounds[0][node->planeType] > node->planeDist && p2->bounds[0][node->planeType] > node->planeDist ) { - node = node->children[0]; - } - else if ( p1->bounds[1][node->planeType] < node->planeDist && p2->bounds[1][node->planeType] < node->planeDist ) { - node = node->children[1]; - } - else { - ReplacePolygons( model, node->children[1], p1, p2, newp ); - node = node->children[0]; - } - } -} - -/* -============= -idCollisionModelManagerLocal::TryMergePolygons -============= -*/ -#define CONTINUOUS_EPSILON 0.005f -#define NORMAL_EPSILON 0.01f - -cm_polygon_t *idCollisionModelManagerLocal::TryMergePolygons( cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2 ) { - int i, j, nexti, prevj; - int p1BeforeShare, p1AfterShare, p2BeforeShare, p2AfterShare; - int newEdges[CM_MAX_POLYGON_EDGES], newNumEdges; - int edgeNum, edgeNum1, edgeNum2, newEdgeNum1, newEdgeNum2; - cm_edge_t *edge; - cm_polygon_t *newp; - idVec3 delta, normal; - float dot; - bool keep1, keep2; - - if ( p1->material != p2->material ) { - return NULL; - } - if ( idMath::Fabs( p1->plane.Dist() - p2->plane.Dist() ) > NORMAL_EPSILON ) { - return NULL; - } - for ( i = 0; i < 3; i++ ) { - if ( idMath::Fabs( p1->plane.Normal()[i] - p2->plane.Normal()[i] ) > NORMAL_EPSILON ) { - return NULL; - } - if ( p1->bounds[0][i] > p2->bounds[1][i] ) { - return NULL; - } - if ( p1->bounds[1][i] < p2->bounds[0][i] ) { - return NULL; - } - } - // this allows for merging polygons with multiple shared edges - // polygons with multiple shared edges probably never occur tho ;) - p1BeforeShare = p1AfterShare = p2BeforeShare = p2AfterShare = -1; - for ( i = 0; i < p1->numEdges; i++ ) { - nexti = (i+1)%p1->numEdges; - for ( j = 0; j < p2->numEdges; j++ ) { - prevj = (j+p2->numEdges-1)%p2->numEdges; - // - if ( abs(p1->edges[i]) != abs(p2->edges[j]) ) { - // if the next edge of p1 and the previous edge of p2 are the same - if ( abs(p1->edges[nexti]) == abs(p2->edges[prevj]) ) { - // if both polygons don't use the edge in the same direction - if ( p1->edges[nexti] != p2->edges[prevj] ) { - p1BeforeShare = i; - p2AfterShare = j; - } - break; - } - } - // if both polygons don't use the edge in the same direction - else if ( p1->edges[i] != p2->edges[j] ) { - // if the next edge of p1 and the previous edge of p2 are not the same - if ( abs(p1->edges[nexti]) != abs(p2->edges[prevj]) ) { - p1AfterShare = nexti; - p2BeforeShare = prevj; - break; - } - } - } - } - if ( p1BeforeShare < 0 || p1AfterShare < 0 || p2BeforeShare < 0 || p2AfterShare < 0 ) { - return NULL; - } - - // check if the new polygon would still be convex - edgeNum = p1->edges[p1BeforeShare]; - edge = model->edges + abs(edgeNum); - delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p - - model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p; - normal = p1->plane.Normal().Cross( delta ); - normal.Normalize(); - - edgeNum = p2->edges[p2AfterShare]; - edge = model->edges + abs(edgeNum); - delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p - - model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p; - - dot = delta * normal; - if (dot < -CONTINUOUS_EPSILON) - return NULL; // not a convex polygon - keep1 = (bool)(dot > CONTINUOUS_EPSILON); - - edgeNum = p2->edges[p2BeforeShare]; - edge = model->edges + abs(edgeNum); - delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p - - model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p; - normal = p1->plane.Normal().Cross( delta ); - normal.Normalize(); - - edgeNum = p1->edges[p1AfterShare]; - edge = model->edges + abs(edgeNum); - delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p - - model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p; - - dot = delta * normal; - if (dot < -CONTINUOUS_EPSILON) - return NULL; // not a convex polygon - keep2 = (bool)(dot > CONTINUOUS_EPSILON); - - newEdgeNum1 = newEdgeNum2 = 0; - // get new edges if we need to replace colinear ones - if ( !keep1 ) { - edgeNum1 = p1->edges[p1BeforeShare]; - edgeNum2 = p2->edges[p2AfterShare]; - GetEdge( model, model->vertices[model->edges[abs(edgeNum1)].vertexNum[INTSIGNBITSET(edgeNum1)]].p, - model->vertices[model->edges[abs(edgeNum2)].vertexNum[INTSIGNBITNOTSET(edgeNum2)]].p, - &newEdgeNum1, -1 ); - if ( newEdgeNum1 == 0 ) { - keep1 = true; - } - } - if ( !keep2 ) { - edgeNum1 = p2->edges[p2BeforeShare]; - edgeNum2 = p1->edges[p1AfterShare]; - GetEdge( model, model->vertices[model->edges[abs(edgeNum1)].vertexNum[INTSIGNBITSET(edgeNum1)]].p, - model->vertices[model->edges[abs(edgeNum2)].vertexNum[INTSIGNBITNOTSET(edgeNum2)]].p, - &newEdgeNum2, -1 ); - if ( newEdgeNum2 == 0 ) { - keep2 = true; - } - } - // set edges for new polygon - newNumEdges = 0; - if ( !keep2 ) { - newEdges[newNumEdges++] = newEdgeNum2; - } - if ( p1AfterShare < p1BeforeShare ) { - for ( i = p1AfterShare + (!keep2); i <= p1BeforeShare - (!keep1); i++ ) { - newEdges[newNumEdges++] = p1->edges[i]; - } - } - else { - for ( i = p1AfterShare + (!keep2); i < p1->numEdges; i++ ) { - newEdges[newNumEdges++] = p1->edges[i]; - } - for ( i = 0; i <= p1BeforeShare - (!keep1); i++ ) { - newEdges[newNumEdges++] = p1->edges[i]; - } - } - if ( !keep1 ) { - newEdges[newNumEdges++] = newEdgeNum1; - } - if ( p2AfterShare < p2BeforeShare ) { - for ( i = p2AfterShare + (!keep1); i <= p2BeforeShare - (!keep2); i++ ) { - newEdges[newNumEdges++] = p2->edges[i]; - } - } - else { - for ( i = p2AfterShare + (!keep1); i < p2->numEdges; i++ ) { - newEdges[newNumEdges++] = p2->edges[i]; - } - for ( i = 0; i <= p2BeforeShare - (!keep2); i++ ) { - newEdges[newNumEdges++] = p2->edges[i]; - } - } - - newp = AllocPolygon( model, newNumEdges ); - memcpy( newp, p1, sizeof(cm_polygon_t) ); - memcpy( newp->edges, newEdges, newNumEdges * sizeof(int) ); - newp->numEdges = newNumEdges; - newp->checkcount = 0; - // increase usage count for the edges of this polygon - for ( i = 0; i < newp->numEdges; i++ ) { - if ( !keep1 && newp->edges[i] == newEdgeNum1 ) { - continue; - } - if ( !keep2 && newp->edges[i] == newEdgeNum2 ) { - continue; - } - model->edges[abs(newp->edges[i])].numUsers++; - } - // create new bounds from the merged polygons - newp->bounds = p1->bounds + p2->bounds; - - return newp; -} - -/* -============= -idCollisionModelManagerLocal::MergePolygonWithTreePolygons -============= -*/ -bool idCollisionModelManagerLocal::MergePolygonWithTreePolygons( cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon ) { - int i; - cm_polygonRef_t *pref; - cm_polygon_t *p, *newp; - - while( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - // - if ( p == polygon ) { - continue; - } - // - newp = TryMergePolygons( model, polygon, p ); - // if polygons were merged - if ( newp ) { - model->numMergedPolys++; - // replace links to the merged polygons with links to the new polygon - ReplacePolygons( model, model->node, polygon, p, newp ); - // decrease usage count for edges of both merged polygons - for ( i = 0; i < polygon->numEdges; i++ ) { - model->edges[abs(polygon->edges[i])].numUsers--; - } - for ( i = 0; i < p->numEdges; i++ ) { - model->edges[abs(p->edges[i])].numUsers--; - } - // free merged polygons - FreePolygon( model, polygon ); - FreePolygon( model, p ); - - return true; - } - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - if ( polygon->bounds[0][node->planeType] > node->planeDist ) { - node = node->children[0]; - } - else if ( polygon->bounds[1][node->planeType] < node->planeDist ) { - node = node->children[1]; - } - else { - if ( MergePolygonWithTreePolygons( model, node->children[1], polygon ) ) { - return true; - } - node = node->children[0]; - } - } - return false; -} - -/* -============= -idCollisionModelManagerLocal::MergeTreePolygons - - try to merge any two polygons with the same surface flags and the same contents -============= -*/ -void idCollisionModelManagerLocal::MergeTreePolygons( cm_model_t *model, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - bool merge; - - while( 1 ) { - do { - merge = false; - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - // if we checked this polygon already - if ( p->checkcount == checkCount ) { - continue; - } - p->checkcount = checkCount; - // try to merge this polygon with other polygons in the tree - if ( MergePolygonWithTreePolygons( model, model->node, p ) ) { - merge = true; - break; - } - } - } while (merge); - // if leaf node - if ( node->planeType == -1 ) { - break; - } - MergeTreePolygons( model, node->children[1] ); - node = node->children[0]; - } -} - -/* -=============================================================================== - -Find internal edges - -=============================================================================== -*/ - -/* - - if (two polygons have the same contents) - if (the normals of the two polygon planes face towards each other) - if (an edge is shared between the polygons) - if (the edge is not shared in the same direction) - then this is an internal edge - else - if (this edge is on the plane of the other polygon) - if (this edge if fully inside the winding of the other polygon) - then this edge is an internal edge - -*/ - -/* -============= -idCollisionModelManagerLocal::PointInsidePolygon -============= -*/ -bool idCollisionModelManagerLocal::PointInsidePolygon( cm_model_t *model, cm_polygon_t *p, idVec3 &v ) { - int i, edgeNum; - idVec3 *v1, *v2, dir1, dir2, vec; - cm_edge_t *edge; - - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = model->edges + abs(edgeNum); - // - v1 = &model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p; - v2 = &model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p; - dir1 = (*v2) - (*v1); - vec = v - (*v1); - dir2 = dir1.Cross( p->plane.Normal() ); - if ( vec * dir2 > VERTEX_EPSILON ) { - return false; - } - } - return true; -} - -/* -============= -idCollisionModelManagerLocal::FindInternalEdgesOnPolygon -============= -*/ -void idCollisionModelManagerLocal::FindInternalEdgesOnPolygon( cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2 ) { - int i, j, k, edgeNum; - cm_edge_t *edge; - idVec3 *v1, *v2, dir1, dir2; - float d; - - // bounds of polygons should overlap or touch - for ( i = 0; i < 3; i++ ) { - if ( p1->bounds[0][i] > p2->bounds[1][i] ) { - return; - } - if ( p1->bounds[1][i] < p2->bounds[0][i] ) { - return; - } - } - // - // FIXME: doubled geometry causes problems - // - for ( i = 0; i < p1->numEdges; i++ ) { - edgeNum = p1->edges[i]; - edge = model->edges + abs(edgeNum); - // if already an internal edge - if ( edge->internal ) { - continue; - } - // - v1 = &model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p; - v2 = &model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p; - // if either of the two vertices is outside the bounds of the other polygon - for ( k = 0; k < 3; k++ ) { - d = p2->bounds[1][k] + VERTEX_EPSILON; - if ( (*v1)[k] > d || (*v2)[k] > d ) { - break; - } - d = p2->bounds[0][k] - VERTEX_EPSILON; - if ( (*v1)[k] < d || (*v2)[k] < d ) { - break; - } - } - if ( k < 3 ) { - continue; - } - // - k = abs(edgeNum); - for ( j = 0; j < p2->numEdges; j++ ) { - if ( k == abs(p2->edges[j]) ) { - break; - } - } - // if the edge is shared between the two polygons - if ( j < p2->numEdges ) { - // if the edge is used by more than 2 polygons - if ( edge->numUsers > 2 ) { - // could still be internal but we'd have to test all polygons using the edge - continue; - } - // if the edge goes in the same direction for both polygons - if ( edgeNum == p2->edges[j] ) { - // the polygons can lay ontop of each other or one can obscure the other - continue; - } - } - // the edge was not shared - else { - // both vertices should be on the plane of the other polygon - d = p2->plane.Distance( *v1 ); - if ( idMath::Fabs(d) > VERTEX_EPSILON ) { - continue; - } - d = p2->plane.Distance( *v2 ); - if ( idMath::Fabs(d) > VERTEX_EPSILON ) { - continue; - } - } - // the two polygon plane normals should face towards each other - dir1 = (*v2) - (*v1); - dir2 = p1->plane.Normal().Cross( dir1 ); - if ( p2->plane.Normal() * dir2 < 0 ) { - //continue; - break; - } - // if the edge was not shared - if ( j >= p2->numEdges ) { - // both vertices of the edge should be inside the winding of the other polygon - if ( !PointInsidePolygon( model, p2, *v1 ) ) { - continue; - } - if ( !PointInsidePolygon( model, p2, *v2 ) ) { - continue; - } - } - // we got another internal edge - edge->internal = true; - model->numInternalEdges++; - } -} - -/* -============= -idCollisionModelManagerLocal::FindInternalPolygonEdges -============= -*/ -void idCollisionModelManagerLocal::FindInternalPolygonEdges( cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - - if ( polygon->material->GetCullType() == CT_TWO_SIDED || polygon->material->ShouldCreateBackSides() ) { - return; - } - - while( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - // - // FIXME: use some sort of additional checkcount because currently - // polygons can be checked multiple times - // - // if the polygons don't have the same contents - if ( p->contents != polygon->contents ) { - continue; - } - if ( p == polygon ) { - continue; - } - FindInternalEdgesOnPolygon( model, polygon, p ); - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - if ( polygon->bounds[0][node->planeType] > node->planeDist ) { - node = node->children[0]; - } - else if ( polygon->bounds[1][node->planeType] < node->planeDist ) { - node = node->children[1]; - } - else { - FindInternalPolygonEdges( model, node->children[1], polygon ); - node = node->children[0]; - } - } -} - -/* -============= -idCollisionModelManagerLocal::FindContainedEdges -============= -*/ -void idCollisionModelManagerLocal::FindContainedEdges( cm_model_t *model, cm_polygon_t *p ) { - int i, edgeNum; - cm_edge_t *edge; - idFixedWinding w; - - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - edge = model->edges + abs(edgeNum); - if ( edge->internal ) { - continue; - } - w.Clear(); - w += model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p; - w += model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p; - if ( ChoppedAwayByProcBSP( w, p->plane, p->contents ) ) { - edge->internal = true; - } - } -} - -/* -============= -idCollisionModelManagerLocal::FindInternalEdges -============= -*/ -void idCollisionModelManagerLocal::FindInternalEdges( cm_model_t *model, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - - while( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - // if we checked this polygon already - if ( p->checkcount == checkCount ) { - continue; - } - p->checkcount = checkCount; - - FindInternalPolygonEdges( model, model->node, p ); - - //FindContainedEdges( model, p ); - } - // if leaf node - if ( node->planeType == -1 ) { - break; - } - FindInternalEdges( model, node->children[1] ); - node = node->children[0]; - } -} - -/* -=============================================================================== - -Spatial subdivision - -=============================================================================== -*/ - -/* -================ -CM_FindSplitter -================ -*/ -static int CM_FindSplitter( const cm_node_t *node, const idBounds &bounds, int *planeType, float *planeDist ) { - int i, j, type, axis[3], polyCount; - float dist, t, bestt, size[3]; - cm_brushRef_t *bref; - cm_polygonRef_t *pref; - const cm_node_t *n; - bool forceSplit = false; - - for ( i = 0; i < 3; i++ ) { - size[i] = bounds[1][i] - bounds[0][i]; - axis[i] = i; - } - // sort on largest axis - for ( i = 0; i < 2; i++ ) { - if ( size[i] < size[i+1] ) { - t = size[i]; - size[i] = size[i+1]; - size[i+1] = t; - j = axis[i]; - axis[i] = axis[i+1]; - axis[i+1] = j; - i = -1; - } - } - // if the node is too small for further splits - if ( size[0] < MIN_NODE_SIZE ) { - polyCount = 0; - for ( pref = node->polygons; pref; pref = pref->next) { - polyCount++; - } - if ( polyCount > MAX_NODE_POLYGONS ) { - forceSplit = true; - } - } - // find an axial aligned splitter - for ( i = 0; i < 3; i++ ) { - // start with the largest axis first - type = axis[i]; - bestt = size[i]; - // if the node is small anough in this axis direction - if ( !forceSplit && bestt < MIN_NODE_SIZE ) { - break; - } - // find an axial splitter from the brush bounding boxes - // also try brushes from parent nodes - for ( n = node; n; n = n->parent ) { - for ( bref = n->brushes; bref; bref = bref->next) { - for ( j = 0; j < 2; j++ ) { - dist = bref->b->bounds[j][type]; - // if the splitter is already used or outside node bounds - if ( dist >= bounds[1][type] || dist <= bounds[0][type] ) { - continue; - } - // find the most centered splitter - t = abs((bounds[1][type] - dist) - (dist - bounds[0][type])); - if ( t < bestt ) { - bestt = t; - *planeType = type; - *planeDist = dist; - } - } - } - } - // find an axial splitter from the polygon bounding boxes - // also try brushes from parent nodes - for ( n = node; n; n = n->parent ) { - for ( pref = n->polygons; pref; pref = pref->next) { - for ( j = 0; j < 2; j++ ) { - dist = pref->p->bounds[j][type]; - // if the splitter is already used or outside node bounds - if ( dist >= bounds[1][type] || dist <= bounds[0][type] ) { - continue; - } - // find the most centered splitter - t = abs((bounds[1][type] - dist) - (dist - bounds[0][type])); - if ( t < bestt ) { - bestt = t; - *planeType = type; - *planeDist = dist; - } - } - } - } - // if we found a splitter on the largest axis - if ( bestt < size[i] ) { - // if forced split due to lots of polygons - if ( forceSplit ) { - return true; - } - // don't create splitters real close to the bounds - if ( bounds[1][type] - *planeDist > (MIN_NODE_SIZE*0.5f) && - *planeDist - bounds[0][type] > (MIN_NODE_SIZE*0.5f) ) { - return true; - } - } - } - return false; -} - -/* -================ -CM_R_InsideAllChildren -================ -*/ -static int CM_R_InsideAllChildren( cm_node_t *node, const idBounds &bounds ) { - assert(node != NULL); - if ( node->planeType != -1 ) { - if ( bounds[0][node->planeType] >= node->planeDist ) { - return false; - } - if ( bounds[1][node->planeType] <= node->planeDist ) { - return false; - } - if ( !CM_R_InsideAllChildren( node->children[0], bounds ) ) { - return false; - } - if ( !CM_R_InsideAllChildren( node->children[1], bounds ) ) { - return false; - } - } - return true; -} - -/* -================ -idCollisionModelManagerLocal::R_FilterPolygonIntoTree -================ -*/ -void idCollisionModelManagerLocal::R_FilterPolygonIntoTree( cm_model_t *model, cm_node_t *node, cm_polygonRef_t *pref, cm_polygon_t *p ) { - assert(node != NULL); - while ( node->planeType != -1 ) { - if ( CM_R_InsideAllChildren( node, p->bounds ) ) { - break; - } - if ( p->bounds[0][node->planeType] >= node->planeDist ) { - node = node->children[0]; - } - else if ( p->bounds[1][node->planeType] <= node->planeDist ) { - node = node->children[1]; - } - else { - R_FilterPolygonIntoTree( model, node->children[1], NULL, p ); - node = node->children[0]; - } - } - if ( pref ) { - pref->next = node->polygons; - node->polygons = pref; - } - else { - AddPolygonToNode( model, node, p ); - } -} - -/* -================ -idCollisionModelManagerLocal::R_FilterBrushIntoTree -================ -*/ -void idCollisionModelManagerLocal::R_FilterBrushIntoTree( cm_model_t *model, cm_node_t *node, cm_brushRef_t *pref, cm_brush_t *b ) { - assert(node != NULL); - while ( node->planeType != -1 ) { - if ( CM_R_InsideAllChildren( node, b->bounds ) ) { - break; - } - if ( b->bounds[0][node->planeType] >= node->planeDist ) { - node = node->children[0]; - } - else if ( b->bounds[1][node->planeType] <= node->planeDist ) { - node = node->children[1]; - } - else { - R_FilterBrushIntoTree( model, node->children[1], NULL, b ); - node = node->children[0]; - } - } - if ( pref ) { - pref->next = node->brushes; - node->brushes = pref; - } - else { - AddBrushToNode( model, node, b ); - } -} - -/* -================ -idCollisionModelManagerLocal::R_CreateAxialBSPTree - - a brush or polygon is linked in the node closest to the root where - the brush or polygon is inside all children -================ -*/ -cm_node_t *idCollisionModelManagerLocal::R_CreateAxialBSPTree( cm_model_t *model, cm_node_t *node, const idBounds &bounds ) { - int planeType; - float planeDist; - cm_polygonRef_t *pref, *nextpref, *prevpref; - cm_brushRef_t *bref, *nextbref, *prevbref; - cm_node_t *frontNode, *backNode, *n; - idBounds frontBounds, backBounds; - - if ( !CM_FindSplitter( node, bounds, &planeType, &planeDist ) ) { - node->planeType = -1; - return node; - } - // create two child nodes - frontNode = AllocNode( model, NODE_BLOCK_SIZE_LARGE ); - memset( frontNode, 0, sizeof(cm_node_t) ); - frontNode->parent = node; - frontNode->planeType = -1; - // - backNode = AllocNode( model, NODE_BLOCK_SIZE_LARGE ); - memset( backNode, 0, sizeof(cm_node_t) ); - backNode->parent = node; - backNode->planeType = -1; - // - model->numNodes += 2; - // set front node bounds - frontBounds = bounds; - frontBounds[0][planeType] = planeDist; - // set back node bounds - backBounds = bounds; - backBounds[1][planeType] = planeDist; - // - node->planeType = planeType; - node->planeDist = planeDist; - node->children[0] = frontNode; - node->children[1] = backNode; - // filter polygons and brushes down the tree if necesary - for ( n = node; n; n = n->parent ) { - prevpref = NULL; - for ( pref = n->polygons; pref; pref = nextpref) { - nextpref = pref->next; - // if polygon is not inside all children - if ( !CM_R_InsideAllChildren( n, pref->p->bounds ) ) { - // filter polygon down the tree - R_FilterPolygonIntoTree( model, n, pref, pref->p ); - if ( prevpref ) { - prevpref->next = nextpref; - } - else { - n->polygons = nextpref; - } - } - else { - prevpref = pref; - } - } - prevbref = NULL; - for ( bref = n->brushes; bref; bref = nextbref) { - nextbref = bref->next; - // if brush is not inside all children - if ( !CM_R_InsideAllChildren( n, bref->b->bounds ) ) { - // filter brush down the tree - R_FilterBrushIntoTree( model, n, bref, bref->b ); - if ( prevbref ) { - prevbref->next = nextbref; - } - else { - n->brushes = nextbref; - } - } - else { - prevbref = bref; - } - } - } - R_CreateAxialBSPTree( model, frontNode, frontBounds ); - R_CreateAxialBSPTree( model, backNode, backBounds ); - return node; -} - -/* -int cm_numSavedPolygonLinks; -int cm_numSavedBrushLinks; - -int CM_R_CountChildren( cm_node_t *node ) { - if ( node->planeType == -1 ) { - return 0; - } - return 2 + CM_R_CountChildren(node->children[0]) + CM_R_CountChildren(node->children[1]); -} - -void CM_R_TestOptimisation( cm_node_t *node ) { - int polyCount, brushCount, numChildren; - cm_polygonRef_t *pref; - cm_brushRef_t *bref; - - if ( node->planeType == -1 ) { - return; - } - polyCount = 0; - for ( pref = node->polygons; pref; pref = pref->next) { - polyCount++; - } - brushCount = 0; - for ( bref = node->brushes; bref; bref = bref->next) { - brushCount++; - } - if ( polyCount || brushCount ) { - numChildren = CM_R_CountChildren( node ); - cm_numSavedPolygonLinks += (numChildren - 1) * polyCount; - cm_numSavedBrushLinks += (numChildren - 1) * brushCount; - } - CM_R_TestOptimisation( node->children[0] ); - CM_R_TestOptimisation( node->children[1] ); -} -*/ - -/* -================ -idCollisionModelManagerLocal::CreateAxialBSPTree -================ -*/ -cm_node_t *idCollisionModelManagerLocal::CreateAxialBSPTree( cm_model_t *model, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_brushRef_t *bref; - idBounds bounds; - - // get head node bounds - bounds.Clear(); - for ( pref = node->polygons; pref; pref = pref->next) { - bounds += pref->p->bounds; - } - for ( bref = node->brushes; bref; bref = bref->next) { - bounds += bref->b->bounds; - } - - // create axial BSP tree from head node - node = R_CreateAxialBSPTree( model, node, bounds ); - - return node; -} - -/* -=============================================================================== - -Raw polygon and brush data - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::SetupHash -================ -*/ -void idCollisionModelManagerLocal::SetupHash( void ) { - if ( !cm_vertexHash ) { - cm_vertexHash = new idHashIndex( VERTEX_HASH_SIZE, 1024 ); - } - if ( !cm_edgeHash ) { - cm_edgeHash = new idHashIndex( EDGE_HASH_SIZE, 1024 ); - } - // init variables used during loading and optimization - if ( !cm_windingList ) { - cm_windingList = new cm_windingList_t; - } - if ( !cm_outList ) { - cm_outList = new cm_windingList_t; - } - if ( !cm_tmpList ) { - cm_tmpList = new cm_windingList_t; - } -} - -/* -================ -idCollisionModelManagerLocal::ShutdownHash -================ -*/ -void idCollisionModelManagerLocal::ShutdownHash( void ) { - delete cm_vertexHash; - cm_vertexHash = NULL; - delete cm_edgeHash; - cm_edgeHash = NULL; - delete cm_tmpList; - cm_tmpList = NULL; - delete cm_outList; - cm_outList = NULL; - delete cm_windingList; - cm_windingList = NULL; -} - -/* -================ -idCollisionModelManagerLocal::ClearHash -================ -*/ -void idCollisionModelManagerLocal::ClearHash( idBounds &bounds ) { - int i; - float f, max; - - cm_vertexHash->Clear(); - cm_edgeHash->Clear(); - - cm_modelBounds = bounds; - max = bounds[1].x - bounds[0].x; - f = bounds[1].y - bounds[0].y; - if ( f > max ) { - max = f; - } - cm_vertexShift = (float) max / VERTEX_HASH_BOXSIZE; - for ( i = 0; (1<> cm_vertexShift) & (VERTEX_HASH_BOXSIZE-1); - y = (((int)(vec[1] - cm_modelBounds[0].y + 0.5 )) >> cm_vertexShift) & (VERTEX_HASH_BOXSIZE-1); - - assert (x >= 0 && x < VERTEX_HASH_BOXSIZE && y >= 0 && y < VERTEX_HASH_BOXSIZE); - - return y * VERTEX_HASH_BOXSIZE + x; - */ - int x, y, z; - - x = (((int) (vec[0] - cm_modelBounds[0].x + 0.5)) + 2) >> 2; - y = (((int) (vec[1] - cm_modelBounds[0].y + 0.5)) + 2) >> 2; - z = (((int) (vec[2] - cm_modelBounds[0].z + 0.5)) + 2) >> 2; - return (x + y * VERTEX_HASH_BOXSIZE + z) & (VERTEX_HASH_SIZE-1); -} - -/* -================ -idCollisionModelManagerLocal::GetVertex -================ -*/ -int idCollisionModelManagerLocal::GetVertex( cm_model_t *model, const idVec3 &v, int *vertexNum ) { - int i, hashKey, vn; - idVec3 vert, *p; - - for (i = 0; i < 3; i++) { - if ( idMath::Fabs(v[i] - idMath::Rint(v[i])) < INTEGRAL_EPSILON ) - vert[i] = idMath::Rint(v[i]); - else - vert[i] = v[i]; - } - - hashKey = HashVec( vert ); - - for (vn = cm_vertexHash->First( hashKey ); vn >= 0; vn = cm_vertexHash->Next( vn ) ) { - p = &model->vertices[vn].p; - // first compare z-axis because hash is based on x-y plane - if (idMath::Fabs(vert[2] - (*p)[2]) < VERTEX_EPSILON && - idMath::Fabs(vert[0] - (*p)[0]) < VERTEX_EPSILON && - idMath::Fabs(vert[1] - (*p)[1]) < VERTEX_EPSILON ) - { - *vertexNum = vn; - return true; - } - } - - if ( model->numVertices >= model->maxVertices ) { - cm_vertex_t *oldVertices; - - // resize vertex array - model->maxVertices = (float) model->maxVertices * 1.5f + 1; - oldVertices = model->vertices; - model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) ); - memcpy( model->vertices, oldVertices, model->numVertices * sizeof(cm_vertex_t) ); - Mem_Free( oldVertices ); - - cm_vertexHash->ResizeIndex( model->maxVertices ); - } - model->vertices[model->numVertices].p = vert; - model->vertices[model->numVertices].checkcount = 0; - *vertexNum = model->numVertices; - // add vertice to hash - cm_vertexHash->Add( hashKey, model->numVertices ); - // - model->numVertices++; - return false; -} - -/* -================ -idCollisionModelManagerLocal::GetEdge -================ -*/ -int idCollisionModelManagerLocal::GetEdge( cm_model_t *model, const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ) { - int v2num, hashKey, e; - int found, *vertexNum; - - // the first edge is a dummy - if ( model->numEdges == 0 ) { - model->numEdges = 1; - } - - if ( v1num != -1 ) { - found = 1; - } - else { - found = GetVertex( model, v1, &v1num ); - } - found &= GetVertex( model, v2, &v2num ); - // if both vertices are the same or snapped onto each other - if ( v1num == v2num ) { - *edgeNum = 0; - return true; - } - hashKey = cm_edgeHash->GenerateKey( v1num, v2num ); - // if both vertices where already stored - if (found) { - for (e = cm_edgeHash->First( hashKey ); e >= 0; e = cm_edgeHash->Next( e ) ) - { - // NOTE: only allow at most two users that use the edge in opposite direction - if ( model->edges[e].numUsers != 1 ) { - continue; - } - - vertexNum = model->edges[e].vertexNum; - if ( vertexNum[0] == v2num ) { - if ( vertexNum[1] == v1num ) { - // negative for a reversed edge - *edgeNum = -e; - break; - } - } - /* - else if ( vertexNum[0] == v1num ) { - if ( vertexNum[1] == v2num ) { - *edgeNum = e; - break; - } - } - */ - } - // if edge found in hash - if ( e >= 0 ) { - model->edges[e].numUsers++; - return true; - } - } - if ( model->numEdges >= model->maxEdges ) { - cm_edge_t *oldEdges; - - // resize edge array - model->maxEdges = (float) model->maxEdges * 1.5f + 1; - oldEdges = model->edges; - model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) ); - memcpy( model->edges, oldEdges, model->numEdges * sizeof(cm_edge_t) ); - Mem_Free( oldEdges ); - - cm_edgeHash->ResizeIndex( model->maxEdges ); - } - // setup edge - model->edges[model->numEdges].vertexNum[0] = v1num; - model->edges[model->numEdges].vertexNum[1] = v2num; - model->edges[model->numEdges].internal = false; - model->edges[model->numEdges].checkcount = 0; - model->edges[model->numEdges].numUsers = 1; // used by one polygon atm - model->edges[model->numEdges].normal.Zero(); - // - *edgeNum = model->numEdges; - // add edge to hash - cm_edgeHash->Add( hashKey, model->numEdges ); - - model->numEdges++; - - return false; -} - -/* -================ -idCollisionModelManagerLocal::CreatePolygon -================ -*/ -void idCollisionModelManagerLocal::CreatePolygon( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ) { - int i, j, edgeNum, v1num; - int numPolyEdges, polyEdges[MAX_POINTS_ON_WINDING]; - idBounds bounds; - cm_polygon_t *p; - - // turn the winding into a sequence of edges - numPolyEdges = 0; - v1num = -1; // first vertex unknown - for ( i = 0, j = 1; i < w->GetNumPoints(); i++, j++ ) { - if ( j >= w->GetNumPoints() ) { - j = 0; - } - GetEdge( model, (*w)[i].ToVec3(), (*w)[j].ToVec3(), &polyEdges[numPolyEdges], v1num ); - if ( polyEdges[numPolyEdges] ) { - // last vertex of this edge is the first vertex of the next edge - v1num = model->edges[ abs(polyEdges[numPolyEdges]) ].vertexNum[ INTSIGNBITNOTSET(polyEdges[numPolyEdges]) ]; - // this edge is valid so keep it - numPolyEdges++; - } - } - // should have at least 3 edges - if ( numPolyEdges < 3 ) { - return; - } - // the polygon is invalid if some edge is found twice - for ( i = 0; i < numPolyEdges; i++ ) { - for ( j = i+1; j < numPolyEdges; j++ ) { - if ( abs(polyEdges[i]) == abs(polyEdges[j]) ) { - return; - } - } - } - // don't overflow max edges - if ( numPolyEdges > CM_MAX_POLYGON_EDGES ) { - common->Warning( "idCollisionModelManagerLocal::CreatePolygon: polygon has more than %d edges", numPolyEdges ); - numPolyEdges = CM_MAX_POLYGON_EDGES; - } - - w->GetBounds( bounds ); - - p = AllocPolygon( model, numPolyEdges ); - p->numEdges = numPolyEdges; - p->contents = material->GetContentFlags(); - p->material = material; - p->checkcount = 0; - p->plane = plane; - p->bounds = bounds; - for ( i = 0; i < numPolyEdges; i++ ) { - edgeNum = polyEdges[i]; - p->edges[i] = edgeNum; - } - R_FilterPolygonIntoTree( model, model->node, NULL, p ); -} - -/* -================ -idCollisionModelManagerLocal::PolygonFromWinding - - NOTE: for patches primitiveNum < 0 and abs(primitiveNum) is the real number -================ -*/ -void idCollisionModelManagerLocal::PolygonFromWinding( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ) { - int contents; - - contents = material->GetContentFlags(); - - // if this polygon is part of the world model - if ( numModels == 0 ) { - // if the polygon is fully chopped away by the proc bsp tree - if ( ChoppedAwayByProcBSP( *w, plane, contents ) ) { - model->numRemovedPolys++; - return; - } - } - - // get one winding that is not or only partly contained in brushes - w = WindingOutsideBrushes( w, plane, contents, primitiveNum, model->node ); - - // if the polygon is fully contained within a brush - if ( !w ) { - model->numRemovedPolys++; - return; - } - - if ( w->IsHuge() ) { - common->Warning( "idCollisionModelManagerLocal::PolygonFromWinding: model %s primitive %d is degenerate", model->name.c_str(), abs(primitiveNum) ); - return; - } - - CreatePolygon( model, w, plane, material, primitiveNum ); - - if ( material->GetCullType() == CT_TWO_SIDED || material->ShouldCreateBackSides() ) { - w->ReverseSelf(); - CreatePolygon( model, w, -plane, material, primitiveNum ); - } -} - -/* -================= -idCollisionModelManagerLocal::CreatePatchPolygons -================= -*/ -void idCollisionModelManagerLocal::CreatePatchPolygons( cm_model_t *model, idSurface_Patch &mesh, const idMaterial *material, int primitiveNum ) { - int i, j; - float dot; - int v1, v2, v3, v4; - idFixedWinding w; - idPlane plane; - idVec3 d1, d2; - - for ( i = 0; i < mesh.GetWidth() - 1; i++ ) { - for ( j = 0; j < mesh.GetHeight() - 1; j++ ) { - - v1 = j * mesh.GetWidth() + i; - v2 = v1 + 1; - v3 = v1 + mesh.GetWidth() + 1; - v4 = v1 + mesh.GetWidth(); - - d1 = mesh[v2].xyz - mesh[v1].xyz; - d2 = mesh[v3].xyz - mesh[v1].xyz; - plane.SetNormal( d1.Cross(d2) ); - if ( plane.Normalize() != 0.0f ) { - plane.FitThroughPoint( mesh[v1].xyz ); - dot = plane.Distance( mesh[v4].xyz ); - // if we can turn it into a quad - if ( idMath::Fabs(dot) < 0.1f ) { - w.Clear(); - w += mesh[v1].xyz; - w += mesh[v2].xyz; - w += mesh[v3].xyz; - w += mesh[v4].xyz; - - PolygonFromWinding( model, &w, plane, material, -primitiveNum ); - continue; - } - else { - // create one of the triangles - w.Clear(); - w += mesh[v1].xyz; - w += mesh[v2].xyz; - w += mesh[v3].xyz; - - PolygonFromWinding( model, &w, plane, material, -primitiveNum ); - } - } - // create the other triangle - d1 = mesh[v3].xyz - mesh[v1].xyz; - d2 = mesh[v4].xyz - mesh[v1].xyz; - plane.SetNormal( d1.Cross(d2) ); - if ( plane.Normalize() != 0.0f ) { - plane.FitThroughPoint( mesh[v1].xyz ); - - w.Clear(); - w += mesh[v1].xyz; - w += mesh[v3].xyz; - w += mesh[v4].xyz; - - PolygonFromWinding( model, &w, plane, material, -primitiveNum ); - } - } - } -} - -/* -================= -CM_EstimateVertsAndEdges -================= -*/ -static void CM_EstimateVertsAndEdges( const idMapEntity *mapEnt, int *numVerts, int *numEdges ) { - int j, width, height; - - *numVerts = *numEdges = 0; - for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) { - const idMapPrimitive *mapPrim; - mapPrim = mapEnt->GetPrimitive(j); - if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) { - // assume maximum tesselation without adding verts - width = static_cast(mapPrim)->GetWidth(); - height = static_cast(mapPrim)->GetHeight(); - *numVerts += width * height; - *numEdges += (width-1) * height + width * (height-1) + (width-1) * (height-1); - continue; - } - if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) { - // assume cylinder with a polygon with (numSides - 2) edges ontop and on the bottom - *numVerts += (static_cast(mapPrim)->GetNumSides() - 2) * 2; - *numEdges += (static_cast(mapPrim)->GetNumSides() - 2) * 3; - continue; - } - } -} - -/* -================= -idCollisionModelManagerLocal::ConverPatch -================= -*/ -void idCollisionModelManagerLocal::ConvertPatch( cm_model_t *model, const idMapPatch *patch, int primitiveNum ) { - const idMaterial *material; - idSurface_Patch *cp; - - material = declManager->FindMaterial( patch->GetMaterial() ); - if ( !( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) { - return; - } - //HUMANHEAD PCF rww 05/11/06 - can be used explicitly by surfaces which use alpha coverage but do not want collision anyway - if ( material->TestMaterialFlag(MF_SKIPCLIP) ) { - return; - } - //HUMANHEAD END - - // copy the patch - cp = new idSurface_Patch( *patch ); - - // if the patch has an explicit number of subdivisions use it to avoid cracks - if ( patch->GetExplicitlySubdivided() ) { - cp->SubdivideExplicit( patch->GetHorzSubdivisions(), patch->GetVertSubdivisions(), false, true ); - } else { - cp->Subdivide( DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_ERROR_CD, DEFAULT_CURVE_MAX_LENGTH_CD, false ); - } - - // create collision polygons for the patch - CreatePatchPolygons( model, *cp, material, primitiveNum ); - - delete cp; -} - -/* -================ -idCollisionModelManagerLocal::ConvertBrushSides -================ -*/ -void idCollisionModelManagerLocal::ConvertBrushSides( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ) { - int i, j; - idMapBrushSide *mapSide; - idFixedWinding w; - idPlane *planes; - const idMaterial *material; - - // fix degenerate planes - planes = (idPlane *) _alloca16( mapBrush->GetNumSides() * sizeof( planes[0] ) ); - for ( i = 0; i < mapBrush->GetNumSides(); i++ ) { - planes[i] = mapBrush->GetSide(i)->GetPlane(); - planes[i].FixDegeneracies( DEGENERATE_DIST_EPSILON ); - } - - // create a collision polygon for each brush side - for ( i = 0; i < mapBrush->GetNumSides(); i++ ) { - mapSide = mapBrush->GetSide(i); - material = declManager->FindMaterial( mapSide->GetMaterial() ); - if ( !( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) { - continue; - } - w.BaseForPlane( -planes[i] ); - for ( j = 0; j < mapBrush->GetNumSides() && w.GetNumPoints(); j++ ) { - if ( i == j ) { - continue; - } - w.ClipInPlace( -planes[j], 0 ); - } - - if ( w.GetNumPoints() ) { - PolygonFromWinding( model, &w, planes[i], material, primitiveNum ); - } - } -} - -/* -================ -idCollisionModelManagerLocal::ConvertBrush -================ -*/ -void idCollisionModelManagerLocal::ConvertBrush( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ) { - int i, j, contents; - idBounds bounds; - idMapBrushSide *mapSide; - cm_brush_t *brush; - idPlane *planes; - idFixedWinding w; - const idMaterial *material = NULL; - - contents = 0; - bounds.Clear(); - - // fix degenerate planes - planes = (idPlane *) _alloca16( mapBrush->GetNumSides() * sizeof( planes[0] ) ); - for ( i = 0; i < mapBrush->GetNumSides(); i++ ) { - planes[i] = mapBrush->GetSide(i)->GetPlane(); - planes[i].FixDegeneracies( DEGENERATE_DIST_EPSILON ); - } - - // we are only getting the bounds for the brush so there's no need - // to create a winding for the last brush side - for ( i = 0; i < mapBrush->GetNumSides() - 1; i++ ) { - mapSide = mapBrush->GetSide(i); - material = declManager->FindMaterial( mapSide->GetMaterial() ); - contents |= ( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ); - w.BaseForPlane( -planes[i] ); - for ( j = 0; j < mapBrush->GetNumSides() && w.GetNumPoints(); j++ ) { - if ( i == j ) { - continue; - } - w.ClipInPlace( -planes[j], 0 ); - } - - for ( j = 0; j < w.GetNumPoints(); j++ ) { - bounds.AddPoint( w[j].ToVec3() ); - } - } - if ( !contents ) { - return; - } - // create brush for position test - brush = AllocBrush( model, mapBrush->GetNumSides() ); - brush->checkcount = 0; - brush->contents = contents; - brush->material = material; - brush->primitiveNum = primitiveNum; - brush->bounds = bounds; - brush->numPlanes = mapBrush->GetNumSides(); - for (i = 0; i < mapBrush->GetNumSides(); i++) { - brush->planes[i] = planes[i]; - } - AddBrushToNode( model, model->node, brush ); -} - -/* -================ -CM_CountNodeBrushes -================ -*/ -static int CM_CountNodeBrushes( const cm_node_t *node ) { - int count; - cm_brushRef_t *bref; - - count = 0; - for ( bref = node->brushes; bref; bref = bref->next ) { - count++; - } - return count; -} - -/* -================ -CM_R_GetModelBounds -================ -*/ -static void CM_R_GetNodeBounds( idBounds *bounds, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_brushRef_t *bref; - - while ( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - bounds->AddPoint( pref->p->bounds[0] ); - bounds->AddPoint( pref->p->bounds[1] ); - } - for ( bref = node->brushes; bref; bref = bref->next ) { - bounds->AddPoint( bref->b->bounds[0] ); - bounds->AddPoint( bref->b->bounds[1] ); - } - if ( node->planeType == -1 ) { - break; - } - CM_R_GetNodeBounds( bounds, node->children[1] ); - node = node->children[0]; - } -} - -/* -================ -CM_GetNodeBounds -================ -*/ -void CM_GetNodeBounds( idBounds *bounds, cm_node_t *node ) { - bounds->Clear(); - CM_R_GetNodeBounds( bounds, node ); - if ( bounds->IsCleared() ) { - bounds->Zero(); - } -} - -/* -================ -CM_GetNodeContents -================ -*/ -int CM_GetNodeContents( cm_node_t *node ) { - int contents; - cm_polygonRef_t *pref; - cm_brushRef_t *bref; - - contents = 0; - while ( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - contents |= pref->p->contents; - } - for ( bref = node->brushes; bref; bref = bref->next ) { - contents |= bref->b->contents; - } - if ( node->planeType == -1 ) { - break; - } - contents |= CM_GetNodeContents( node->children[1] ); - node = node->children[0]; - } - return contents; -} - -/* -================== -idCollisionModelManagerLocal::RemapEdges -================== -*/ -void idCollisionModelManagerLocal::RemapEdges( cm_node_t *node, int *edgeRemap ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - int i; - - while ( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - // if we checked this polygon already - if ( p->checkcount == checkCount ) { - continue; - } - p->checkcount = checkCount; - for ( i = 0; i < p->numEdges; i++ ) { - if ( p->edges[i] < 0 ) { - p->edges[i] = -edgeRemap[ abs(p->edges[i]) ]; - } - else { - p->edges[i] = edgeRemap[ p->edges[i] ]; - } - } - } - if ( node->planeType == -1 ) { - break; - } - - RemapEdges( node->children[1], edgeRemap ); - node = node->children[0]; - } -} - -/* -================== -idCollisionModelManagerLocal::OptimizeArrays - - due to polygon merging and polygon removal the vertex and edge array - can have a lot of unused entries. -================== -*/ -void idCollisionModelManagerLocal::OptimizeArrays( cm_model_t *model ) { - int i, newNumVertices, newNumEdges, *v; - int *remap; - cm_edge_t *oldEdges; - cm_vertex_t *oldVertices; - - remap = (int *) Mem_ClearedAlloc( Max( model->numVertices, model->numEdges ) * sizeof( int ) ); - // get all used vertices - for ( i = 0; i < model->numEdges; i++ ) { - remap[ model->edges[i].vertexNum[0] ] = true; - remap[ model->edges[i].vertexNum[1] ] = true; - } - // create remap index and move vertices - newNumVertices = 0; - for ( i = 0; i < model->numVertices; i++ ) { - if ( remap[ i ] ) { - remap[ i ] = newNumVertices; - model->vertices[ newNumVertices ] = model->vertices[ i ]; - newNumVertices++; - } - } - model->numVertices = newNumVertices; - // change edge vertex indexes - for ( i = 1; i < model->numEdges; i++ ) { - v = model->edges[i].vertexNum; - v[0] = remap[ v[0] ]; - v[1] = remap[ v[1] ]; - } - - // create remap index and move edges - newNumEdges = 1; - for ( i = 1; i < model->numEdges; i++ ) { - // if the edge is used - if ( model->edges[ i ].numUsers ) { - remap[ i ] = newNumEdges; - model->edges[ newNumEdges ] = model->edges[ i ]; - newNumEdges++; - } - } - // change polygon edge indexes - checkCount++; - RemapEdges( model->node, remap ); - model->numEdges = newNumEdges; - - Mem_Free( remap ); - - // realloc vertices - oldVertices = model->vertices; - if ( oldVertices ) { - model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->numVertices * sizeof(cm_vertex_t) ); - memcpy( model->vertices, oldVertices, model->numVertices * sizeof(cm_vertex_t) ); - Mem_Free( oldVertices ); - } - - // realloc edges - oldEdges = model->edges; - if ( oldEdges ) { - model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->numEdges * sizeof(cm_edge_t) ); - memcpy( model->edges, oldEdges, model->numEdges * sizeof(cm_edge_t) ); - Mem_Free( oldEdges ); - } -} - -/* -================ -idCollisionModelManagerLocal::FinishModel -================ -*/ -void idCollisionModelManagerLocal::FinishModel( cm_model_t *model ) { - // try to merge polygons - checkCount++; - MergeTreePolygons( model, model->node ); - // find internal edges (no mesh can ever collide with internal edges) - checkCount++; - FindInternalEdges( model, model->node ); - // calculate edge normals - checkCount++; - CalculateEdgeNormals( model, model->node ); - - //common->Printf( "%s vertex hash spread is %d\n", model->name.c_str(), cm_vertexHash->GetSpread() ); - //common->Printf( "%s edge hash spread is %d\n", model->name.c_str(), cm_edgeHash->GetSpread() ); - - // remove all unused vertices and edges - OptimizeArrays( model ); - // get model bounds from brush and polygon bounds - CM_GetNodeBounds( &model->bounds, model->node ); - // get model contents - model->contents = CM_GetNodeContents( model->node ); - // total memory used by this model - model->usedMemory = model->numVertices * sizeof(cm_vertex_t) + - model->numEdges * sizeof(cm_edge_t) + - model->polygonMemory + - model->brushMemory + - model->numNodes * sizeof(cm_node_t) + - model->numPolygonRefs * sizeof(cm_polygonRef_t) + - model->numBrushRefs * sizeof(cm_brushRef_t); -} - -/* -================ -idCollisionModelManagerLocal::LoadRenderModel -================ -*/ -cm_model_t *idCollisionModelManagerLocal::LoadRenderModel( const char *fileName ) { - int i, j; - idRenderModel *renderModel; - const modelSurface_t *surf; - idFixedWinding w; - cm_node_t *node; - cm_model_t *model; - idPlane plane; - idBounds bounds; - bool collisionSurface; - idStr extension; - - // only load ASE and LWO models - idStr( fileName ).ExtractFileExtension( extension ); - if ( ( extension.Icmp( "ase" ) != 0 ) && ( extension.Icmp( "lwo" ) != 0 ) && ( extension.Icmp( "ma" ) != 0 ) ) { - return NULL; - } - - if ( !renderModelManager->CheckModel( fileName ) ) { - return NULL; - } - - renderModel = renderModelManager->FindModel( fileName ); - - model = AllocModel(); - model->name = fileName; - node = AllocNode( model, NODE_BLOCK_SIZE_SMALL ); - node->planeType = -1; - model->node = node; - - model->maxVertices = 0; - model->numVertices = 0; - model->maxEdges = 0; - model->numEdges = 0; - - bounds = renderModel->Bounds( NULL ); - - collisionSurface = false; - for ( i = 0; i < renderModel->NumSurfaces(); i++ ) { - surf = renderModel->Surface( i ); - if ( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) { - collisionSurface = true; - } - } - - for ( i = 0; i < renderModel->NumSurfaces(); i++ ) { - surf = renderModel->Surface( i ); - // if this surface has no contents - if ( ! ( surf->shader->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) { - continue; - } - // if the model has a collision surface and this surface is not a collision surface - if ( collisionSurface && !( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) ) { - continue; - } - // get max verts and edges - model->maxVertices += surf->geometry->numVerts; - model->maxEdges += surf->geometry->numIndexes; - } - - model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) ); - model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) ); - - // setup hash to speed up finding shared vertices and edges - SetupHash(); - - cm_vertexHash->ResizeIndex( model->maxVertices ); - cm_edgeHash->ResizeIndex( model->maxEdges ); - - ClearHash( bounds ); - - for ( i = 0; i < renderModel->NumSurfaces(); i++ ) { - surf = renderModel->Surface( i ); - // if this surface has no contents - if ( ! ( surf->shader->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) { - continue; - } - // if the model has a collision surface and this surface is not a collision surface - if ( collisionSurface && !( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) ) { - continue; - } - - for ( j = 0; j < surf->geometry->numIndexes; j += 3 ) { - w.Clear(); - w += surf->geometry->verts[ surf->geometry->indexes[ j + 2 ] ].xyz; - w += surf->geometry->verts[ surf->geometry->indexes[ j + 1 ] ].xyz; - w += surf->geometry->verts[ surf->geometry->indexes[ j + 0 ] ].xyz; - w.GetPlane( plane ); - plane = -plane; - PolygonFromWinding( model, &w, plane, surf->shader, 1 ); - } - } - - // create a BSP tree for the model - model->node = CreateAxialBSPTree( model, model->node ); - - model->isConvex = false; - - FinishModel( model ); - - // shutdown the hash - ShutdownHash(); - - common->Printf( "loaded collision model %s\n", model->name.c_str() ); - - return model; -} - -/* -================ -idCollisionModelManagerLocal::CollisionModelForMapEntity -================ -*/ -cm_model_t *idCollisionModelManagerLocal::CollisionModelForMapEntity( const idMapEntity *mapEnt ) { - cm_model_t *model; - idBounds bounds; - const char *name; - int i, brushCount; - - // if the entity has no primitives - if ( mapEnt->GetNumPrimitives() < 1 ) { - return NULL; - } - - // get a name for the collision model - mapEnt->epairs.GetString( "model", "", &name ); - if ( !name[0] ) { - mapEnt->epairs.GetString( "name", "", &name ); - if ( !name[0] ) { - if ( !numModels ) { - // first model is always the world - name = "worldMap"; - } - else { - name = "unnamed inline model"; - } - } - } - - model = AllocModel(); - model->node = AllocNode( model, NODE_BLOCK_SIZE_SMALL ); - - CM_EstimateVertsAndEdges( mapEnt, &model->maxVertices, &model->maxEdges ); - model->numVertices = 0; - model->numEdges = 0; - model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) ); - model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) ); - - cm_vertexHash->ResizeIndex( model->maxVertices ); - cm_edgeHash->ResizeIndex( model->maxEdges ); - - model->name = name; - model->isConvex = false; - - // convert brushes - for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) { - idMapPrimitive *mapPrim; - - mapPrim = mapEnt->GetPrimitive(i); - if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) { - ConvertBrush( model, static_cast(mapPrim), i ); - continue; - } - } - - // create an axial bsp tree for the model if it has more than just a bunch brushes - brushCount = CM_CountNodeBrushes( model->node ); - if ( brushCount > 4 ) { - model->node = CreateAxialBSPTree( model, model->node ); - } else { - model->node->planeType = -1; - } - - // get bounds for hash - if ( brushCount ) { - CM_GetNodeBounds( &bounds, model->node ); - } else { - bounds[0].Set( -256, -256, -256 ); - bounds[1].Set( 256, 256, 256 ); - } - - // different models do not share edges and vertices with each other, so clear the hash - ClearHash( bounds ); - - // create polygons from patches and brushes - for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) { - idMapPrimitive *mapPrim; - - mapPrim = mapEnt->GetPrimitive(i); - if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) { - ConvertPatch( model, static_cast(mapPrim), i ); - continue; - } - if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) { - ConvertBrushSides( model, static_cast(mapPrim), i ); - continue; - } - } - - FinishModel( model ); - - return model; -} - -/* -================ -idCollisionModelManagerLocal::FindModel -================ -*/ -cmHandle_t idCollisionModelManagerLocal::FindModel( const char *name ) { - int i; - - // check if this model is already loaded - for ( i = 0; i < numModels; i++ ) { - if ( !models[i]->name.Icmp( name ) ) { - break; - } - } - // if the model is already loaded - if ( i < numModels ) { - return i; - } - return -1; -} - -/* -================== -idCollisionModelManagerLocal::PrintModelInfo -================== -*/ -void idCollisionModelManagerLocal::PrintModelInfo( const cm_model_t *model ) { - common->Printf( "%6i vertices (%i KB)\n", model->numVertices, (model->numVertices * sizeof(cm_vertex_t))>>10 ); - common->Printf( "%6i edges (%i KB)\n", model->numEdges, (model->numEdges * sizeof(cm_edge_t))>>10 ); - common->Printf( "%6i polygons (%i KB)\n", model->numPolygons, model->polygonMemory>>10 ); - common->Printf( "%6i brushes (%i KB)\n", model->numBrushes, model->brushMemory>>10 ); - common->Printf( "%6i nodes (%i KB)\n", model->numNodes, (model->numNodes * sizeof(cm_node_t))>>10 ); - common->Printf( "%6i polygon refs (%i KB)\n", model->numPolygonRefs, (model->numPolygonRefs * sizeof(cm_polygonRef_t))>>10 ); - common->Printf( "%6i brush refs (%i KB)\n", model->numBrushRefs, (model->numBrushRefs * sizeof(cm_brushRef_t))>>10 ); - common->Printf( "%6i internal edges\n", model->numInternalEdges ); - common->Printf( "%6i sharp edges\n", model->numSharpEdges ); - common->Printf( "%6i contained polygons removed\n", model->numRemovedPolys ); - common->Printf( "%6i polygons merged\n", model->numMergedPolys ); - common->Printf( "%6i KB total memory used\n", model->usedMemory>>10 ); -} - -/* -================ -idCollisionModelManagerLocal::AccumulateModelInfo -================ -*/ -void idCollisionModelManagerLocal::AccumulateModelInfo( cm_model_t *model ) { - int i; - - memset( model, 0, sizeof( *model ) ); - // accumulate statistics of all loaded models - for ( i = 0; i < numModels; i++ ) { - model->numVertices += models[i]->numVertices; - model->numEdges += models[i]->numEdges; - model->numPolygons += models[i]->numPolygons; - model->polygonMemory += models[i]->polygonMemory; - model->numBrushes += models[i]->numBrushes; - model->brushMemory += models[i]->brushMemory; - model->numNodes += models[i]->numNodes; - model->numBrushRefs += models[i]->numBrushRefs; - model->numPolygonRefs += models[i]->numPolygonRefs; - model->numInternalEdges += models[i]->numInternalEdges; - model->numSharpEdges += models[i]->numSharpEdges; - model->numRemovedPolys += models[i]->numRemovedPolys; - model->numMergedPolys += models[i]->numMergedPolys; - model->usedMemory += models[i]->usedMemory; - } -} - -/* -================ -idCollisionModelManagerLocal::ModelInfo -================ -*/ -void idCollisionModelManagerLocal::ModelInfo( cmHandle_t model ) { - cm_model_t modelInfo; - - if ( model == -1 ) { - AccumulateModelInfo( &modelInfo ); - PrintModelInfo( &modelInfo ); - return; - } - if ( model < 0 || model > MAX_SUBMODELS || model > maxModels ) { - common->Printf( "idCollisionModelManagerLocal::ModelInfo: invalid model handle\n" ); - return; - } - if ( !models[model] ) { - common->Printf( "idCollisionModelManagerLocal::ModelInfo: invalid model\n" ); - return; - } - - PrintModelInfo( models[model] ); -} - -/* -================ -idCollisionModelManagerLocal::ListModels -================ -*/ -void idCollisionModelManagerLocal::ListModels( void ) { - int i, totalMemory; - - totalMemory = 0; - for ( i = 0; i < numModels; i++ ) { - common->Printf( "%4d: %5d KB %s\n", i, (models[i]->usedMemory>>10), models[i]->name.c_str() ); - totalMemory += models[i]->usedMemory; - } - common->Printf( "%4d KB in %d models\n", (totalMemory>>10), numModels ); -} - -/* -================ -idCollisionModelManagerLocal::BuildModels -================ -*/ -void idCollisionModelManagerLocal::BuildModels( const idMapFile *mapFile ) { - int i; - const idMapEntity *mapEnt; - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - const char *clipMat; - anyInlinedProcClipMats = false; - mapEnt = mapFile->GetEntity(0); - if (mapEnt) { - mapEnt->epairs.GetString("clipStaticMaterials1", "", &clipMat); - if (clipMat[0]) { - anyInlinedProcClipMats = true; - } - } -#endif - //HUMANHEAD END - - idTimer timer; - timer.Start(); - - if ( !LoadCollisionModelFile( mapFile->GetName(), mapFile->GetGeometryCRC() ) ) { - -// HUMANHEAD pdm: Support for level appending -#if DEATHWALK_AUTOLOAD - // Keep track of the first model added - // (there may already be collision models from the regular map loaded if this is a deathwalk map) - int firstModel = idCollisionModelManagerLocal::numModels; -#endif -// HUMANHEAD END - - if ( !mapFile->GetNumEntities() ) { - return; - } - - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - if (anyInlinedProcClipMats) { - assert(numInlinedProcClipModels == 0 && inlinedProcClipModelMats.Num() == 0); - numInlinedProcClipModels = 0; - mapEnt = mapFile->GetEntity(0); - inlinedProcClipModelMats.Clear(); - if (mapEnt) { - for (i = 0; 1; i++) { - mapEnt->epairs.GetString(va("clipStaticMaterials%i", i+1), "", &clipMat); - if (clipMat[0]) { //a list of materials specifying proc surfaces to be turned into clip - inlinedProcClipModelMats.Append(clipMat); - } - else { - break; - } - } - } - } -#endif - //HUMANHEAD END - - // load the .proc file bsp for data optimisation - LoadProcBSP( mapFile->GetName() ); - - // convert brushes and patches to collision data - for ( i = 0; i < mapFile->GetNumEntities(); i++ ) { - mapEnt = mapFile->GetEntity(i); - - if ( numModels >= MAX_SUBMODELS ) { - common->Error( "idCollisionModelManagerLocal::BuildModels: more than %d collision models", MAX_SUBMODELS ); - break; - } - models[numModels] = CollisionModelForMapEntity( mapEnt ); - if ( models[ numModels] ) { - numModels++; - } - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - if (numInlinedProcClipModels && numModels == PROC_CLIPMODEL_INDEX_START) { - numModels += numInlinedProcClipModels; - } -#endif - //HUMANHEAD END - } - - // free the proc bsp which is only used for data optimization - Mem_Free( procNodes ); - procNodes = NULL; - - // write the collision models to a file -// HUMANHEAD pdm: Support for level appending -#if DEATHWALK_AUTOLOAD - bool bAppending = session->ShouldAppendLevel() && !idStr::Icmp(mapFile->GetName(), session->GetDeathwalkMapName()); - if (bAppending) { - common->Printf("Appending collision data\n"); - // Rename the world collision model - if (models[firstModel]) { - models[firstModel]->name = "dw_worldMap"; - } - } - WriteCollisionModelsToFile( mapFile->GetName(), firstModel, numModels, mapFile->GetGeometryCRC() ); -#else - WriteCollisionModelsToFile( mapFile->GetName(), 0, numModels, mapFile->GetGeometryCRC() ); -#endif - } - - timer.Stop(); - - // print statistics on collision data - cm_model_t model; - AccumulateModelInfo( &model ); - common->Printf( "collision data:\n" ); - common->Printf( "%6i models\n", numModels ); - PrintModelInfo( &model ); - common->Printf( "%.0f msec to load collision data.\n", timer.Milliseconds() ); -} - - -/* -================ -idCollisionModelManagerLocal::LoadMap -================ -*/ -void idCollisionModelManagerLocal::LoadMap( const idMapFile *mapFile ) { - - if ( mapFile == NULL ) { - common->Error( "idCollisionModelManagerLocal::LoadMap: NULL mapFile" ); - } - - // check whether we can keep the current collision map based on the mapName and mapFileTime - if ( loaded ) { - if ( mapName.Icmp( mapFile->GetName() ) == 0 ) { - if ( mapFile->GetFileTime() == mapFileTime ) { - common->DPrintf( "Using loaded version\n" ); - return; - } - common->DPrintf( "Reloading modified map\n" ); - } - FreeMap(); - } - - // clear the collision map - Clear(); - - // models - maxModels = MAX_SUBMODELS; - numModels = 0; - models = (cm_model_t **) Mem_ClearedAlloc( (maxModels+1) * sizeof(cm_model_t *) ); - - // setup hash to speed up finding shared vertices and edges - SetupHash(); - - // setup trace model structure - SetupTrmModelStructure(); - - // build collision models - BuildModels( mapFile ); - - // save name and time stamp - mapName = mapFile->GetName(); - mapFileTime = mapFile->GetFileTime(); - loaded = true; - - // shutdown the hash - ShutdownHash(); -} - -/* -=================== -idCollisionModelManagerLocal::GetModelName -=================== -*/ -const char *idCollisionModelManagerLocal::GetModelName( cmHandle_t model ) const { - if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) { - common->Printf( "idCollisionModelManagerLocal::GetModelBounds: invalid model handle\n" ); - return ""; - } - return models[model]->name.c_str(); -} - -/* -=================== -idCollisionModelManagerLocal::GetModelBounds -=================== -*/ -bool idCollisionModelManagerLocal::GetModelBounds( cmHandle_t model, idBounds &bounds ) const { - - if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) { - common->Printf( "idCollisionModelManagerLocal::GetModelBounds: invalid model handle\n" ); - return false; - } - - bounds = models[model]->bounds; - return true; -} - -/* -=================== -idCollisionModelManagerLocal::GetModelContents -=================== -*/ -bool idCollisionModelManagerLocal::GetModelContents( cmHandle_t model, int &contents ) const { - if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) { - common->Printf( "idCollisionModelManagerLocal::GetModelContents: invalid model handle\n" ); - return false; - } - - contents = models[model]->contents; - - return true; -} - -/* -=================== -idCollisionModelManagerLocal::GetModelVertex -=================== -*/ -bool idCollisionModelManagerLocal::GetModelVertex( cmHandle_t model, int vertexNum, idVec3 &vertex ) const { - if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) { - common->Printf( "idCollisionModelManagerLocal::GetModelVertex: invalid model handle\n" ); - return false; - } - - if ( vertexNum < 0 || vertexNum >= models[model]->numVertices ) { - common->Printf( "idCollisionModelManagerLocal::GetModelVertex: invalid vertex number\n" ); - return false; - } - - vertex = models[model]->vertices[vertexNum].p; - - return true; -} - -/* -=================== -idCollisionModelManagerLocal::GetModelEdge -=================== -*/ -bool idCollisionModelManagerLocal::GetModelEdge( cmHandle_t model, int edgeNum, idVec3 &start, idVec3 &end ) const { - if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) { - common->Printf( "idCollisionModelManagerLocal::GetModelEdge: invalid model handle\n" ); - return false; - } - - edgeNum = abs( edgeNum ); - if ( edgeNum >= models[model]->numEdges ) { - common->Printf( "idCollisionModelManagerLocal::GetModelEdge: invalid edge number\n" ); - return false; - } - - start = models[model]->vertices[models[model]->edges[edgeNum].vertexNum[0]].p; - end = models[model]->vertices[models[model]->edges[edgeNum].vertexNum[1]].p; - - return true; -} - -/* -=================== -idCollisionModelManagerLocal::GetModelPolygon -=================== -*/ -bool idCollisionModelManagerLocal::GetModelPolygon( cmHandle_t model, int polygonNum, idFixedWinding &winding ) const { - int i, edgeNum; - cm_polygon_t *poly; - - if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) { - common->Printf( "idCollisionModelManagerLocal::GetModelPolygon: invalid model handle\n" ); - return false; - } - - poly = *reinterpret_cast(&polygonNum); - winding.Clear(); - for ( i = 0; i < poly->numEdges; i++ ) { - edgeNum = poly->edges[i]; - winding += models[model]->vertices[ models[model]->edges[abs(edgeNum)].vertexNum[INTSIGNBITSET(edgeNum)] ].p; - } - - return true; -} - -/* -================== -idCollisionModelManagerLocal::LoadModel -================== -*/ -cmHandle_t idCollisionModelManagerLocal::LoadModel( const char *modelName, const bool precache ) { - int handle; - - handle = FindModel( modelName ); - if ( handle >= 0 ) { - return handle; - } - - if ( numModels >= MAX_SUBMODELS ) { - common->Error( "idCollisionModelManagerLocal::LoadModel: no free slots\n" ); - return 0; - } - - // try to load a .cm file - if ( LoadCollisionModelFile( modelName, 0 ) ) { - handle = FindModel( modelName ); - if ( handle >= 0 ) { - return handle; - } else { - common->Warning( "idCollisionModelManagerLocal::LoadModel: collision file for '%s' contains different model", modelName ); - } - } - - // if only precaching .cm files do not waste memory converting render models - if ( precache ) { - return 0; - } - - // try to load a .ASE or .LWO model and convert it to a collision model - models[numModels] = LoadRenderModel( modelName ); - if ( models[numModels] != NULL ) { - numModels++; - return ( numModels - 1 ); - } - - return 0; -} - -/* -================== -idCollisionModelManagerLocal::TrmFromModel_r -================== -*/ -bool idCollisionModelManagerLocal::TrmFromModel_r( idTraceModel &trm, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_polygon_t *p; - int i; - - while ( 1 ) { - for ( pref = node->polygons; pref; pref = pref->next ) { - p = pref->p; - - if ( p->checkcount == checkCount ) { - continue; - } - - p->checkcount = checkCount; - - if ( trm.numPolys >= MAX_TRACEMODEL_POLYS ) { - return false; - } - // copy polygon properties - trm.polys[ trm.numPolys ].bounds = p->bounds; - trm.polys[ trm.numPolys ].normal = p->plane.Normal(); - trm.polys[ trm.numPolys ].dist = p->plane.Dist(); - trm.polys[ trm.numPolys ].numEdges = p->numEdges; - // copy edge index - for ( i = 0; i < p->numEdges; i++ ) { - trm.polys[ trm.numPolys ].edges[ i ] = p->edges[ i ]; - } - trm.numPolys++; - } - if ( node->planeType == -1 ) { - break; - } - if ( !TrmFromModel_r( trm, node->children[1] ) ) { - return false; - } - node = node->children[0]; - } - return true; -} - -/* -================== -idCollisionModelManagerLocal::TrmFromModel - - NOTE: polygon merging can merge colinear edges and as such might cause dangling edges. -================== -*/ -bool idCollisionModelManagerLocal::TrmFromModel( const cm_model_t *model, idTraceModel &trm ) { - int i, j, numEdgeUsers[MAX_TRACEMODEL_EDGES+1]; - - // if the model has too many vertices to fit in a trace model - if ( model->numVertices > MAX_TRACEMODEL_VERTS ) { - common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many vertices.\n", model->name.c_str() ); - PrintModelInfo( model ); - return false; - } - - // plus one because the collision model accounts for the first unused edge - if ( model->numEdges > MAX_TRACEMODEL_EDGES+1 ) { - common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many edges.\n", model->name.c_str() ); - PrintModelInfo( model ); - return false; - } - - trm.type = TRM_CUSTOM; - trm.numVerts = 0; - trm.numEdges = 1; - trm.numPolys = 0; - trm.bounds.Clear(); - - // copy polygons - checkCount++; - if ( !TrmFromModel_r( trm, model->node ) ) { - common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many polygons.\n", model->name.c_str() ); - PrintModelInfo( model ); - return false; - } - - // copy vertices - for ( i = 0; i < model->numVertices; i++ ) { - trm.verts[ i ] = model->vertices[ i ].p; - trm.bounds.AddPoint( trm.verts[ i ] ); - } - trm.numVerts = model->numVertices; - - // copy edges - for ( i = 0; i < model->numEdges; i++ ) { - trm.edges[ i ].v[0] = model->edges[ i ].vertexNum[0]; - trm.edges[ i ].v[1] = model->edges[ i ].vertexNum[1]; - } - // minus one because the collision model accounts for the first unused edge - trm.numEdges = model->numEdges - 1; - - // each edge should be used exactly twice - memset( numEdgeUsers, 0, sizeof(numEdgeUsers) ); - for ( i = 0; i < trm.numPolys; i++ ) { - for ( j = 0; j < trm.polys[i].numEdges; j++ ) { - numEdgeUsers[ abs( trm.polys[i].edges[j] ) ]++; - } - } - for ( i = 1; i <= trm.numEdges; i++ ) { - if ( numEdgeUsers[i] != 2 ) { - common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has dangling edges, the model has to be an enclosed hull.\n", model->name.c_str() ); - PrintModelInfo( model ); - return false; - } - } - - // assume convex - trm.isConvex = true; - // check if really convex - for ( i = 0; i < trm.numPolys; i++ ) { - // to be convex no vertices should be in front of any polygon plane - for ( j = 0; j < trm.numVerts; j++ ) { - if ( trm.polys[ i ].normal * trm.verts[ j ] - trm.polys[ i ].dist > 0.01f ) { - trm.isConvex = false; - break; - } - } - if ( j < trm.numVerts ) { - break; - } - } - - // offset to center of model - trm.offset = trm.bounds.GetCenter(); - - trm.GenerateEdgeNormals(); - - return true; -} - -/* -================== -idCollisionModelManagerLocal::TrmFromModel -================== -*/ -bool idCollisionModelManagerLocal::TrmFromModel( const char *modelName, idTraceModel &trm ) { - cmHandle_t handle; - - handle = LoadModel( modelName, false ); - if ( !handle ) { - common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s not found.\n", modelName ); - return false; - } - - return TrmFromModel( models[ handle ], trm ); -} - -// HUMANHEAD pdm: Support for level appending -#if DEATHWALK_AUTOLOAD -bool idCollisionModelManagerLocal::WillUseAlreadyLoadedCollisionMap(const idMapFile *mapFile) { - // Based on some code from idCollisionModelManagerLocal::LoadMap - return (idCollisionModelManagerLocal::loaded && - idCollisionModelManagerLocal::mapName.Icmp( mapFile->GetName() ) == 0 && - mapFile->GetFileTime() == idCollisionModelManagerLocal::mapFileTime); -} -void idCollisionModelManagerLocal::AppendMap( const idMapFile *mapFile ) { - // build collision models for our appended map - if (mapFile) { - idCollisionModelManagerLocal::BuildModels( mapFile ); - } -} -#endif -// HUMANHEAD END - -//HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS -int idCollisionModelManagerLocal::GetNumInlinedProcClipModels(void) { - return numInlinedProcClipModels; -} -#endif -//HUMANHEAD END diff --git a/src/cm/CollisionModel_local.h b/src/cm/CollisionModel_local.h deleted file mode 100644 index 6b027e9..0000000 --- a/src/cm/CollisionModel_local.h +++ /dev/null @@ -1,527 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "CollisionModel.h" - -#define MIN_NODE_SIZE 64.0f -#define MAX_NODE_POLYGONS 128 -#define CM_MAX_POLYGON_EDGES 64 -#define CIRCLE_APPROXIMATION_LENGTH 64.0f - -#define MAX_SUBMODELS 2048 -#define TRACE_MODEL_HANDLE MAX_SUBMODELS - -#define VERTEX_HASH_BOXSIZE (1<<6) // must be power of 2 -#define VERTEX_HASH_SIZE (VERTEX_HASH_BOXSIZE*VERTEX_HASH_BOXSIZE) -#define EDGE_HASH_SIZE (1<<14) - -#define NODE_BLOCK_SIZE_SMALL 8 -#define NODE_BLOCK_SIZE_LARGE 256 -#define REFERENCE_BLOCK_SIZE_SMALL 8 -#define REFERENCE_BLOCK_SIZE_LARGE 256 - -#define MAX_WINDING_LIST 128 // quite a few are generated at times -#define INTEGRAL_EPSILON 0.01f -#define VERTEX_EPSILON 0.1f -#define CHOP_EPSILON 0.1f - - -typedef struct cm_windingList_s { - int numWindings; // number of windings - idFixedWinding w[MAX_WINDING_LIST]; // windings - idVec3 normal; // normal for all windings - idBounds bounds; // bounds of all windings in list - idVec3 origin; // origin for radius - float radius; // radius relative to origin for all windings - int contents; // winding surface contents - int primitiveNum; // number of primitive the windings came from -} cm_windingList_t; - -/* -=============================================================================== - -Collision model - -=============================================================================== -*/ - -typedef struct cm_vertex_s { - idVec3 p; // vertex point - int checkcount; // for multi-check avoidance - unsigned long side; // each bit tells at which side this vertex passes one of the trace model edges - unsigned long sideSet; // each bit tells if sidedness for the trace model edge has been calculated yet -} cm_vertex_t; - -typedef struct cm_edge_s { - int checkcount; // for multi-check avoidance - unsigned short internal; // a trace model can never collide with internal edges - unsigned short numUsers; // number of polygons using this edge - unsigned long side; // each bit tells at which side of this edge one of the trace model vertices passes - unsigned long sideSet; // each bit tells if sidedness for the trace model vertex has been calculated yet - int vertexNum[2]; // start and end point of edge - idVec3 normal; // edge normal -} cm_edge_t; - -typedef struct cm_polygonBlock_s { - int bytesRemaining; - byte * next; -} cm_polygonBlock_t; - -typedef struct cm_polygon_s { - idBounds bounds; // polygon bounds - int checkcount; // for multi-check avoidance - int contents; // contents behind polygon - const idMaterial * material; // material - idPlane plane; // polygon plane - int numEdges; // number of edges - int edges[1]; // variable sized, indexes into cm_edge_t list -} cm_polygon_t; - -typedef struct cm_polygonRef_s { - cm_polygon_t * p; // pointer to polygon - struct cm_polygonRef_s *next; // next polygon in chain -} cm_polygonRef_t; - -typedef struct cm_polygonRefBlock_s { - cm_polygonRef_t * nextRef; // next polygon reference in block - struct cm_polygonRefBlock_s *next; // next block with polygon references -} cm_polygonRefBlock_t; - -typedef struct cm_brushBlock_s { - int bytesRemaining; - byte * next; -} cm_brushBlock_t; - -typedef struct cm_brush_s { - int checkcount; // for multi-check avoidance - idBounds bounds; // brush bounds - int contents; // contents of brush - const idMaterial * material; // material - int primitiveNum; // number of brush primitive - int numPlanes; // number of bounding planes - idPlane planes[1]; // variable sized -} cm_brush_t; - -typedef struct cm_brushRef_s { - cm_brush_t * b; // pointer to brush - struct cm_brushRef_s * next; // next brush in chain -} cm_brushRef_t; - -typedef struct cm_brushRefBlock_s { - cm_brushRef_t * nextRef; // next brush reference in block - struct cm_brushRefBlock_s *next; // next block with brush references -} cm_brushRefBlock_t; - -typedef struct cm_node_s { - int planeType; // node axial plane type - float planeDist; // node plane distance - cm_polygonRef_t * polygons; // polygons in node - cm_brushRef_t * brushes; // brushes in node - struct cm_node_s * parent; // parent of this node - struct cm_node_s * children[2]; // node children -} cm_node_t; - -typedef struct cm_nodeBlock_s { - cm_node_t * nextNode; // next node in block - struct cm_nodeBlock_s *next; // next block with nodes -} cm_nodeBlock_t; - -typedef struct cm_model_s { - idStr name; // model name - idBounds bounds; // model bounds - int contents; // all contents of the model ored together - bool isConvex; // set if model is convex - // model geometry - int maxVertices; // size of vertex array - int numVertices; // number of vertices - cm_vertex_t * vertices; // array with all vertices used by the model - int maxEdges; // size of edge array - int numEdges; // number of edges - cm_edge_t * edges; // array with all edges used by the model - cm_node_t * node; // first node of spatial subdivision - // blocks with allocated memory - cm_nodeBlock_t * nodeBlocks; // list with blocks of nodes - cm_polygonRefBlock_t * polygonRefBlocks; // list with blocks of polygon references - cm_brushRefBlock_t * brushRefBlocks; // list with blocks of brush references - cm_polygonBlock_t * polygonBlock; // memory block with all polygons - cm_brushBlock_t * brushBlock; // memory block with all brushes - // statistics - int numPolygons; - int polygonMemory; - int numBrushes; - int brushMemory; - int numNodes; - int numBrushRefs; - int numPolygonRefs; - int numInternalEdges; - int numSharpEdges; - int numRemovedPolys; - int numMergedPolys; - int usedMemory; -} cm_model_t; - -/* -=============================================================================== - -Data used during collision detection calculations - -=============================================================================== -*/ - -typedef struct cm_trmVertex_s { - int used; // true if this vertex is used for collision detection - idVec3 p; // vertex position - idVec3 endp; // end point of vertex after movement - int polygonSide; // side of polygon this vertex is on (rotational collision) - idPluecker pl; // pluecker coordinate for vertex movement - idVec3 rotationOrigin; // rotation origin for this vertex - idBounds rotationBounds; // rotation bounds for this vertex -} cm_trmVertex_t; - -typedef struct cm_trmEdge_s { - int used; // true when vertex is used for collision detection - idVec3 start; // start of edge - idVec3 end; // end of edge - int vertexNum[2]; // indexes into cm_traceWork_t->vertices - idPluecker pl; // pluecker coordinate for edge - idVec3 cross; // (z,-y,x) of cross product between edge dir and movement dir - idBounds rotationBounds; // rotation bounds for this edge - idPluecker plzaxis; // pluecker coordinate for rotation about the z-axis - unsigned short bitNum; // vertex bit number -} cm_trmEdge_t; - -typedef struct cm_trmPolygon_s { - int used; - idPlane plane; // polygon plane - int numEdges; // number of edges - int edges[MAX_TRACEMODEL_POLYEDGES]; // index into cm_traceWork_t->edges - idBounds rotationBounds; // rotation bounds for this polygon -} cm_trmPolygon_t; - -typedef struct cm_traceWork_s { - int numVerts; - cm_trmVertex_t vertices[MAX_TRACEMODEL_VERTS]; // trm vertices - int numEdges; - cm_trmEdge_t edges[MAX_TRACEMODEL_EDGES+1]; // trm edges - int numPolys; - cm_trmPolygon_t polys[MAX_TRACEMODEL_POLYS]; // trm polygons - cm_model_t *model; // model colliding with - idVec3 start; // start of trace - idVec3 end; // end of trace - idVec3 dir; // trace direction - idBounds bounds; // bounds of full trace - idBounds size; // bounds of transformed trm relative to start - idVec3 extents; // largest of abs(size[0]) and abs(size[1]) for BSP trace - int contents; // ignore polygons that do not have any of these contents flags - trace_t trace; // collision detection result - - bool rotation; // true if calculating rotational collision - bool pointTrace; // true if only tracing a point - bool positionTest; // true if not tracing but doing a position test - bool isConvex; // true if the trace model is convex - bool axisIntersectsTrm; // true if the rotation axis intersects the trace model - bool getContacts; // true if retrieving contacts - bool quickExit; // set to quickly stop the collision detection calculations - - idVec3 origin; // origin of rotation in model space - idVec3 axis; // rotation axis in model space - idMat3 matrix; // rotates axis of rotation to the z-axis - float angle; // angle for rotational collision - float maxTan; // max tangent of half the positive angle used instead of fraction - float radius; // rotation radius of trm start - idRotation modelVertexRotation; // inverse rotation for model vertices - - contactInfo_t *contacts; // array with contacts - int maxContacts; // max size of contact array - int numContacts; // number of contacts found - - idPlane heartPlane1; // polygons should be near anough the trace heart planes - float maxDistFromHeartPlane1; - idPlane heartPlane2; - float maxDistFromHeartPlane2; - idPluecker polygonEdgePlueckerCache[CM_MAX_POLYGON_EDGES]; - idPluecker polygonVertexPlueckerCache[CM_MAX_POLYGON_EDGES]; - idVec3 polygonRotationOriginCache[CM_MAX_POLYGON_EDGES]; -} cm_traceWork_t; - -/* -=============================================================================== - -Collision Map - -=============================================================================== -*/ - -typedef struct cm_procNode_s { - idPlane plane; - int children[2]; // negative numbers are (-1 - areaNumber), 0 = solid -} cm_procNode_t; - -class idCollisionModelManagerLocal : public idCollisionModelManager { -public: - // load collision models from a map file - void LoadMap( const idMapFile *mapFile ); - -// HUMANHEAD pdm: Support for level appending - virtual const char * ContentsName(const int contents) const { return StringFromContents(contents); } -#if DEATHWALK_AUTOLOAD - virtual void AppendMap( const idMapFile *mapFile ); - virtual bool WillUseAlreadyLoadedCollisionMap(const idMapFile *mapFile); -#endif -// HUMANHEAD END - - // frees all the collision models - void FreeMap( void ); - - // get clip handle for model - cmHandle_t LoadModel( const char *modelName, const bool precache ); - // sets up a trace model for collision with other trace models - cmHandle_t SetupTrmModel( const idTraceModel &trm, const idMaterial *material ); - // create trace model from a collision model, returns true if succesfull - bool TrmFromModel( const char *modelName, idTraceModel &trm ); - - // name of the model - const char * GetModelName( cmHandle_t model ) const; - // bounds of the model - bool GetModelBounds( cmHandle_t model, idBounds &bounds ) const; - // all contents flags of brushes and polygons ored together - bool GetModelContents( cmHandle_t model, int &contents ) const; - // get the vertex of a model - bool GetModelVertex( cmHandle_t model, int vertexNum, idVec3 &vertex ) const; - // get the edge of a model - bool GetModelEdge( cmHandle_t model, int edgeNum, idVec3 &start, idVec3 &end ) const; - // get the polygon of a model - bool GetModelPolygon( cmHandle_t model, int polygonNum, idFixedWinding &winding ) const; - - // translates a trm and reports the first collision if any - void Translation( trace_t *results, const idVec3 &start, const idVec3 &end, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ); - // rotates a trm and reports the first collision if any - void Rotation( trace_t *results, const idVec3 &start, const idRotation &rotation, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ); - // returns the contents the trm is stuck in or 0 if the trm is in free space - int Contents( const idVec3 &start, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ); - // stores all contact points of the trm with the model, returns the number of contacts - int Contacts( contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ); - // test collision detection - void DebugOutput( const idVec3 &origin ); - // draw a model - void DrawModel( cmHandle_t model, const idVec3 &origin, const idMat3 &axis, - const idVec3 &viewOrigin, const float radius ); - // print model information, use -1 handle for accumulated model info - void ModelInfo( cmHandle_t model ); - // list all loaded models - void ListModels( void ); - // write a collision model file for the map entity - bool WriteCollisionModelForMapEntity( const idMapEntity *mapEnt, const char *filename, const bool testTraceModel = true ); - - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - int GetNumInlinedProcClipModels(void); -#endif - //HUMANHEAD END - -private: // CollisionMap_translate.cpp - int TranslateEdgeThroughEdge( idVec3 &cross, idPluecker &l1, idPluecker &l2, float *fraction ); - void TranslateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge ); - void TranslateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int bitNum ); - void TranslatePointThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v ); - void TranslateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly, cm_polygon_t *poly, cm_vertex_t *v, idVec3 &endp, idPluecker &pl ); - bool TranslateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *p ); - void SetupTranslationHeartPlanes( cm_traceWork_t *tw ); - void SetupTrm( cm_traceWork_t *tw, const idTraceModel *trm ); - -private: // CollisionMap_rotate.cpp - int CollisionBetweenEdgeBounds( cm_traceWork_t *tw, const idVec3 &va, const idVec3 &vb, - const idVec3 &vc, const idVec3 &vd, float tanHalfAngle, - idVec3 &collisionPoint, idVec3 &collisionNormal ); - int RotateEdgeThroughEdge( cm_traceWork_t *tw, const idPluecker &pl1, - const idVec3 &vc, const idVec3 &vd, - const float minTan, float &tanHalfAngle ); - int EdgeFurthestFromEdge( cm_traceWork_t *tw, const idPluecker &pl1, - const idVec3 &vc, const idVec3 &vd, - float &tanHalfAngle, float &dir ); - void RotateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge ); - int RotatePointThroughPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane, - const float angle, const float minTan, float &tanHalfAngle ); - int PointFurthestFromPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane, - const float angle, float &tanHalfAngle, float &dir ); - int RotatePointThroughEpsilonPlane( const cm_traceWork_t *tw, const idVec3 &point, const idVec3 &endPoint, - const idPlane &plane, const float angle, const idVec3 &origin, - float &tanHalfAngle, idVec3 &collisionPoint, idVec3 &endDir ); - void RotateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int vertexNum); - void RotateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly, cm_polygon_t *poly, - cm_vertex_t *v, idVec3 &rotationOrigin ); - bool RotateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *p ); - void BoundsForRotation( const idVec3 &origin, const idVec3 &axis, const idVec3 &start, const idVec3 &end, idBounds &bounds ); - void Rotation180( trace_t *results, const idVec3 &rorg, const idVec3 &axis, - const float startAngle, const float endAngle, const idVec3 &start, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &origin, const idMat3 &modelAxis ); - -private: // CollisionMap_contents.cpp - bool TestTrmVertsInBrush( cm_traceWork_t *tw, cm_brush_t *b ); - bool TestTrmInPolygon( cm_traceWork_t *tw, cm_polygon_t *p ); - cm_node_t * PointNode( const idVec3 &p, cm_model_t *model ); - int PointContents( const idVec3 p, cmHandle_t model ); - int TransformedPointContents( const idVec3 &p, cmHandle_t model, const idVec3 &origin, const idMat3 &modelAxis ); - int ContentsTrm( trace_t *results, const idVec3 &start, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ); - -private: // CollisionMap_trace.cpp - void TraceTrmThroughNode( cm_traceWork_t *tw, cm_node_t *node ); - void TraceThroughAxialBSPTree_r( cm_traceWork_t *tw, cm_node_t *node, float p1f, float p2f, idVec3 &p1, idVec3 &p2); - void TraceThroughModel( cm_traceWork_t *tw ); - void RecurseProcBSP_r( trace_t *results, int parentNodeNum, int nodeNum, float p1f, float p2f, const idVec3 &p1, const idVec3 &p2 ); - -private: // CollisionMap_load.cpp - void Clear( void ); - void FreeTrmModelStructure( void ); - // model deallocation - void RemovePolygonReferences_r( cm_node_t *node, cm_polygon_t *p ); - void RemoveBrushReferences_r( cm_node_t *node, cm_brush_t *b ); - void FreeNode( cm_node_t *node ); - void FreePolygonReference( cm_polygonRef_t *pref ); - void FreeBrushReference( cm_brushRef_t *bref ); - void FreePolygon( cm_model_t *model, cm_polygon_t *poly ); - void FreeBrush( cm_model_t *model, cm_brush_t *brush ); - void FreeTree_r( cm_model_t *model, cm_node_t *headNode, cm_node_t *node ); - void FreeModel( cm_model_t *model ); - // merging polygons - void ReplacePolygons( cm_model_t *model, cm_node_t *node, cm_polygon_t *p1, cm_polygon_t *p2, cm_polygon_t *newp ); - cm_polygon_t * TryMergePolygons( cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2 ); - bool MergePolygonWithTreePolygons( cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon ); - void MergeTreePolygons( cm_model_t *model, cm_node_t *node ); - // finding internal edges - bool PointInsidePolygon( cm_model_t *model, cm_polygon_t *p, idVec3 &v ); - void FindInternalEdgesOnPolygon( cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2 ); - void FindInternalPolygonEdges( cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon ); - void FindInternalEdges( cm_model_t *model, cm_node_t *node ); - void FindContainedEdges( cm_model_t *model, cm_polygon_t *p ); - // loading of proc BSP tree - void ParseProcNodes( idLexer *src ); - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - void CheckProcModelSurfClip(idLexer *src); -#endif - //HUMANHEAD END - void LoadProcBSP( const char *name ); - // removal of contained polygons - int R_ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius ); - int ChoppedAwayByProcBSP( const idFixedWinding &w, const idPlane &plane, int contents ); - void ChopWindingListWithBrush( cm_windingList_t *list, cm_brush_t *b ); - void R_ChopWindingListWithTreeBrushes( cm_windingList_t *list, cm_node_t *node ); - idFixedWinding *WindingOutsideBrushes( idFixedWinding *w, const idPlane &plane, int contents, int patch, cm_node_t *headNode ); - // creation of axial BSP tree - cm_model_t * AllocModel( void ); - cm_node_t * AllocNode( cm_model_t *model, int blockSize ); - cm_polygonRef_t*AllocPolygonReference( cm_model_t *model, int blockSize ); - cm_brushRef_t * AllocBrushReference( cm_model_t *model, int blockSize ); - cm_polygon_t * AllocPolygon( cm_model_t *model, int numEdges ); - cm_brush_t * AllocBrush( cm_model_t *model, int numPlanes ); - void AddPolygonToNode( cm_model_t *model, cm_node_t *node, cm_polygon_t *p ); - void AddBrushToNode( cm_model_t *model, cm_node_t *node, cm_brush_t *b ); - void SetupTrmModelStructure( void ); - void R_FilterPolygonIntoTree( cm_model_t *model, cm_node_t *node, cm_polygonRef_t *pref, cm_polygon_t *p ); - void R_FilterBrushIntoTree( cm_model_t *model, cm_node_t *node, cm_brushRef_t *pref, cm_brush_t *b ); - cm_node_t * R_CreateAxialBSPTree( cm_model_t *model, cm_node_t *node, const idBounds &bounds ); - cm_node_t * CreateAxialBSPTree( cm_model_t *model, cm_node_t *node ); - // creation of raw polygons - void SetupHash(void); - void ShutdownHash(void); - void ClearHash( idBounds &bounds ); - int HashVec(const idVec3 &vec); - int GetVertex( cm_model_t *model, const idVec3 &v, int *vertexNum ); - int GetEdge( cm_model_t *model, const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ); - void CreatePolygon( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ); - void PolygonFromWinding( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ); - void CalculateEdgeNormals( cm_model_t *model, cm_node_t *node ); - void CreatePatchPolygons( cm_model_t *model, idSurface_Patch &mesh, const idMaterial *material, int primitiveNum ); - void ConvertPatch( cm_model_t *model, const idMapPatch *patch, int primitiveNum ); - void ConvertBrushSides( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ); - void ConvertBrush( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ); - void PrintModelInfo( const cm_model_t *model ); - void AccumulateModelInfo( cm_model_t *model ); - void RemapEdges( cm_node_t *node, int *edgeRemap ); - void OptimizeArrays( cm_model_t *model ); - void FinishModel( cm_model_t *model ); - void BuildModels( const idMapFile *mapFile ); - cmHandle_t FindModel( const char *name ); - cm_model_t * CollisionModelForMapEntity( const idMapEntity *mapEnt ); // brush/patch model from .map - cm_model_t * LoadRenderModel( const char *fileName ); // ASE/LWO models - bool TrmFromModel_r( idTraceModel &trm, cm_node_t *node ); - bool TrmFromModel( const cm_model_t *model, idTraceModel &trm ); - -private: // CollisionMap_files.cpp - // writing - void WriteNodes( idFile *fp, cm_node_t *node ); - int CountPolygonMemory( cm_node_t *node ) const; - void WritePolygons( idFile *fp, cm_node_t *node ); - int CountBrushMemory( cm_node_t *node ) const; - void WriteBrushes( idFile *fp, cm_node_t *node ); - void WriteCollisionModel( idFile *fp, cm_model_t *model ); - void WriteCollisionModelsToFile( const char *filename, int firstModel, int lastModel, unsigned int mapFileCRC ); - // loading - cm_node_t * ParseNodes( idLexer *src, cm_model_t *model, cm_node_t *parent ); - void ParseVertices( idLexer *src, cm_model_t *model ); - void ParseEdges( idLexer *src, cm_model_t *model ); - void ParsePolygons( idLexer *src, cm_model_t *model ); - void ParseBrushes( idLexer *src, cm_model_t *model ); - bool ParseCollisionModel( idLexer *src ); - bool LoadCollisionModelFile( const char *name, unsigned int mapFileCRC ); - -private: // CollisionMap_debug - int ContentsFromString( const char *string ) const; - const char * StringFromContents( const int contents ) const; - void DrawEdge( cm_model_t *model, int edgeNum, const idVec3 &origin, const idMat3 &axis ); - void DrawPolygon( cm_model_t *model, cm_polygon_t *p, const idVec3 &origin, const idMat3 &axis, - const idVec3 &viewOrigin ); - void DrawNodePolygons( cm_model_t *model, cm_node_t *node, const idVec3 &origin, const idMat3 &axis, - const idVec3 &viewOrigin, const float radius ); - -private: // collision map data - idStr mapName; - unsigned int mapFileTime; - int loaded; - // for multi-check avoidance - int checkCount; - // models - int maxModels; - int numModels; - cm_model_t ** models; - // polygons and brush for trm model - cm_polygonRef_t*trmPolygons[MAX_TRACEMODEL_POLYS]; - cm_brushRef_t * trmBrushes[1]; - const idMaterial *trmMaterial; - // for data pruning - int numProcNodes; - cm_procNode_t * procNodes; - // for retrieving contact points - bool getContacts; - contactInfo_t * contacts; - int maxContacts; - int numContacts; - //HUMANHEAD rww -#if _HH_INLINED_PROC_CLIPMODELS - idList inlinedProcClipModelMats; - int numInlinedProcClipModels; - bool anyInlinedProcClipMats; -#endif - //HUMANHEAD END -}; - -// for debugging -extern idCVar cm_debugCollision; diff --git a/src/cm/CollisionModel_rotate.cpp b/src/cm/CollisionModel_rotate.cpp deleted file mode 100644 index 4f71857..0000000 --- a/src/cm/CollisionModel_rotate.cpp +++ /dev/null @@ -1,1672 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - -/* -=============================================================================== - -Collision detection for rotational motion - -=============================================================================== -*/ - -// epsilon for round-off errors in epsilon calculations -#define CM_PL_RANGE_EPSILON 1e-4f -// if the collision point is this close to the rotation axis it is not considered a collision -#define ROTATION_AXIS_EPSILON (CM_CLIP_EPSILON*0.25f) - - -/* -================ -CM_RotatePoint - - rotates a point about an arbitrary axis using the tangent of half the rotation angle -================ -*/ -void CM_RotatePoint( idVec3 &point, const idVec3 &origin, const idVec3 &axis, const float tanHalfAngle ) { - double d, t, s, c; - idVec3 proj, v1, v2; - - point -= origin; - proj = axis * ( point * axis ); - v1 = point - proj; - v2 = axis.Cross( v1 ); - - // r = tan( a / 2 ); - // sin(a) = 2*r/(1+r*r); - // cos(a) = (1-r*r)/(1+r*r); - t = tanHalfAngle * tanHalfAngle; - d = 1.0f / ( 1.0f + t ); - s = 2.0f * tanHalfAngle * d; - c = ( 1.0f - t ) * d; - - point = v1 * c - v2 * s + proj + origin; -} - -/* -================ -CM_RotateEdge - - rotates an edge about an arbitrary axis using the tangent of half the rotation angle -================ -*/ -void CM_RotateEdge( idVec3 &start, idVec3 &end, const idVec3 &origin, const idVec3 &axis, const float tanHalfAngle ) { - double d, t, s, c; - idVec3 proj, v1, v2; - - // r = tan( a / 2 ); - // sin(a) = 2*r/(1+r*r); - // cos(a) = (1-r*r)/(1+r*r); - t = tanHalfAngle * tanHalfAngle; - d = 1.0f / ( 1.0f + t ); - s = 2.0f * tanHalfAngle * d; - c = ( 1.0f - t ) * d; - - start -= origin; - proj = axis * ( start * axis ); - v1 = start - proj; - v2 = axis.Cross( v1 ); - start = v1 * c - v2 * s + proj + origin; - - end -= origin; - proj = axis * ( end * axis ); - v1 = end - proj; - v2 = axis.Cross( v1 ); - end = v1 * c - v2 * s + proj + origin; -} - -/* -================ -idCollisionModelManagerLocal::CollisionBetweenEdgeBounds - - verifies if the collision of two edges occurs between the edge bounds - also calculates the collision point and collision plane normal if the collision occurs between the bounds -================ -*/ -int idCollisionModelManagerLocal::CollisionBetweenEdgeBounds( cm_traceWork_t *tw, const idVec3 &va, const idVec3 &vb, - const idVec3 &vc, const idVec3 &vd, float tanHalfAngle, - idVec3 &collisionPoint, idVec3 &collisionNormal ) { - float d1, d2, d; - idVec3 at, bt, dir, dir1, dir2; - idPluecker pl1, pl2; - - at = va; - bt = vb; - if ( tanHalfAngle != 0.0f ) { - CM_RotateEdge( at, bt, tw->origin, tw->axis, tanHalfAngle ); - } - - dir1 = (at - tw->origin).Cross( tw->axis ); - dir2 = (bt - tw->origin).Cross( tw->axis ); - if ( dir1 * dir1 > dir2 * dir2 ) { - dir = dir1; - } - else { - dir = dir2; - } - if ( tw->angle < 0.0f ) { - dir = -dir; - } - - pl1.FromLine( at, bt ); - pl2.FromRay( vc, dir ); - d1 = pl1.PermutedInnerProduct( pl2 ); - pl2.FromRay( vd, dir ); - d2 = pl1.PermutedInnerProduct( pl2 ); - if ( ( d1 > 0.0f && d2 > 0.0f ) || ( d1 < 0.0f && d2 < 0.0f ) ) { - return false; - } - - pl1.FromLine( vc, vd ); - pl2.FromRay( at, dir ); - d1 = pl1.PermutedInnerProduct( pl2 ); - pl2.FromRay( bt, dir ); - d2 = pl1.PermutedInnerProduct( pl2 ); - if ( ( d1 > 0.0f && d2 > 0.0f ) || ( d1 < 0.0f && d2 < 0.0f ) ) { - return false; - } - - // collision point on the edge at-bt - dir1 = (vd - vc).Cross( dir ); - d = dir1 * vc; - d1 = dir1 * at - d; - d2 = dir1 * bt - d; - if ( d1 == d2 ) { - return false; - } - collisionPoint = at + ( d1 / (d1 - d2) ) * ( bt - at ); - - // normal is cross product of the rotated edge va-vb and the edge vc-vd - collisionNormal.Cross( bt-at, vd-vc ); - - return true; -} - -/* -================ -idCollisionModelManagerLocal::RotateEdgeThroughEdge - - calculates the tangent of half the rotation angle at which the edges collide -================ -*/ -int idCollisionModelManagerLocal::RotateEdgeThroughEdge( cm_traceWork_t *tw, const idPluecker &pl1, - const idVec3 &vc, const idVec3 &vd, - const float minTan, float &tanHalfAngle ) { - double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2; - idVec3 ct, dt; - idPluecker pl2; - - /* - - a = start of line being rotated - b = end of line being rotated - pl1 = pluecker coordinate for line (a - b) - pl2 = pluecker coordinate for edge we might collide with (c - d) - t = rotation angle around the z-axis - solve pluecker inner product for t of rotating line a-b and line l2 - - // start point of rotated line during rotation - an[0] = a[0] * cos(t) + a[1] * sin(t) - an[1] = a[0] * -sin(t) + a[1] * cos(t) - an[2] = a[2]; - // end point of rotated line during rotation - bn[0] = b[0] * cos(t) + b[1] * sin(t) - bn[1] = b[0] * -sin(t) + b[1] * cos(t) - bn[2] = b[2]; - - pl1[0] = a[0] * b[1] - b[0] * a[1]; - pl1[1] = a[0] * b[2] - b[0] * a[2]; - pl1[2] = a[0] - b[0]; - pl1[3] = a[1] * b[2] - b[1] * a[2]; - pl1[4] = a[2] - b[2]; - pl1[5] = b[1] - a[1]; - - v[0] = (a[0] * cos(t) + a[1] * sin(t)) * (b[0] * -sin(t) + b[1] * cos(t)) - (b[0] * cos(t) + b[1] * sin(t)) * (a[0] * -sin(t) + a[1] * cos(t)); - v[1] = (a[0] * cos(t) + a[1] * sin(t)) * b[2] - (b[0] * cos(t) + b[1] * sin(t)) * a[2]; - v[2] = (a[0] * cos(t) + a[1] * sin(t)) - (b[0] * cos(t) + b[1] * sin(t)); - v[3] = (a[0] * -sin(t) + a[1] * cos(t)) * b[2] - (b[0] * -sin(t) + b[1] * cos(t)) * a[2]; - v[4] = a[2] - b[2]; - v[5] = (b[0] * -sin(t) + b[1] * cos(t)) - (a[0] * -sin(t) + a[1] * cos(t)); - - pl2[0] * v[4] + pl2[1] * v[5] + pl2[2] * v[3] + pl2[4] * v[0] + pl2[5] * v[1] + pl2[3] * v[2] = 0; - - v[0] = (a[0] * cos(t) + a[1] * sin(t)) * (b[0] * -sin(t) + b[1] * cos(t)) - (b[0] * cos(t) + b[1] * sin(t)) * (a[0] * -sin(t) + a[1] * cos(t)); - v[0] = (a[1] * b[1] - a[0] * b[0]) * cos(t) * sin(t) + (a[0] * b[1] + a[1] * b[0] * cos(t)^2) - (a[1] * b[0]) - ((b[1] * a[1] - b[0] * a[0]) * cos(t) * sin(t) + (b[0] * a[1] + b[1] * a[0]) * cos(t)^2 - (b[1] * a[0])) - v[0] = - (a[1] * b[0]) - ( - (b[1] * a[0])) - v[0] = (b[1] * a[0]) - (a[1] * b[0]) - - v[0] = (a[0]*b[1]) - (a[1]*b[0]); - v[1] = (a[0]*b[2] - b[0]*a[2]) * cos(t) + (a[1]*b[2] - b[1]*a[2]) * sin(t); - v[2] = (a[0]-b[0]) * cos(t) + (a[1]-b[1]) * sin(t); - v[3] = (b[0]*a[2] - a[0]*b[2]) * sin(t) + (a[1]*b[2] - b[1]*a[2]) * cos(t); - v[4] = a[2] - b[2]; - v[5] = (a[0]-b[0]) * sin(t) + (b[1]-a[1]) * cos(t); - - v[0] = (a[0]*b[1]) - (a[1]*b[0]); - v[1] = (a[0]*b[2] - b[0]*a[2]) * cos(t) + (a[1]*b[2] - b[1]*a[2]) * sin(t); - v[2] = (a[0]-b[0]) * cos(t) - (b[1]-a[1]) * sin(t); - v[3] = (a[0]*b[2] - b[0]*a[2]) * -sin(t) + (a[1]*b[2] - b[1]*a[2]) * cos(t); - v[4] = a[2] - b[2]; - v[5] = (a[0]-b[0]) * sin(t) + (b[1]-a[1]) * cos(t); - - v[0] = pl1[0]; - v[1] = pl1[1] * cos(t) + pl1[3] * sin(t); - v[2] = pl1[2] * cos(t) - pl1[5] * sin(t); - v[3] = pl1[3] * cos(t) - pl1[1] * sin(t); - v[4] = pl1[4]; - v[5] = pl1[5] * cos(t) + pl1[2] * sin(t); - - pl2[0] * v[4] + pl2[1] * v[5] + pl2[2] * v[3] + pl2[4] * v[0] + pl2[5] * v[1] + pl2[3] * v[2] = 0; - - 0 = pl2[0] * pl1[4] + - pl2[1] * (pl1[5] * cos(t) + pl1[2] * sin(t)) + - pl2[2] * (pl1[3] * cos(t) - pl1[1] * sin(t)) + - pl2[4] * pl1[0] + - pl2[5] * (pl1[1] * cos(t) + pl1[3] * sin(t)) + - pl2[3] * (pl1[2] * cos(t) - pl1[5] * sin(t)); - - v2 * cos(t) + v1 * sin(t) + v0 = 0; - - // rotation about the z-axis - v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0]; - v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5]; - v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2]; - - // rotation about the x-axis - //v0 = pl2[3] * pl1[2] + pl2[2] * pl1[3]; - //v1 = -pl2[5] * pl1[0] + pl2[4] * pl1[1] - pl2[1] * pl1[4] + pl2[0] * pl1[5]; - //v2 = pl2[4] * pl1[0] + pl2[5] * pl1[1] + pl2[0] * pl1[4] + pl2[1] * pl1[5]; - - r = tan(t / 2); - sin(t) = 2*r/(1+r*r); - cos(t) = (1-r*r)/(1+r*r); - - v1 * 2 * r / (1 + r*r) + v2 * (1 - r*r) / (1 + r*r) + v0 = 0 - (v1 * 2 * r + v2 * (1 - r*r)) / (1 + r*r) = -v0 - (v1 * 2 * r + v2 - v2 * r*r) / (1 + r*r) = -v0 - v1 * 2 * r + v2 - v2 * r*r = -v0 * (1 + r*r) - v1 * 2 * r + v2 - v2 * r*r = -v0 + -v0 * r*r - (v0 - v2) * r * r + (2 * v1) * r + (v0 + v2) = 0; - - MrE gives Pluecker a banana.. good monkey - - */ - - tanHalfAngle = tw->maxTan; - - // transform rotation axis to z-axis - ct = (vc - tw->origin) * tw->matrix; - dt = (vd - tw->origin) * tw->matrix; - - pl2.FromLine( ct, dt ); - - v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0]; - v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5]; - v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2]; - - a = v0 - v2; - b = v1; - c = v0 + v2; - if ( a == 0.0f ) { - if ( b == 0.0f ) { - return false; - } - frac1 = -c / ( 2.0f * b ); - frac2 = 1e10; // = tan( idMath::HALF_PI ) - } - else { - d = b * b - c * a; - if ( d <= 0.0f ) { - return false; - } - sqrtd = sqrt( d ); - if ( b > 0.0f ) { - q = - b + sqrtd; - } - else { - q = - b - sqrtd; - } - frac1 = q / a; - if (q == 0.0f) { //HUMANHEAD rww - CUFPF - frac2 = 0.0f; - } - else { - frac2 = c / q; - } - } - - if ( tw->angle < 0.0f ) { - frac1 = -frac1; - frac2 = -frac2; - } - - // get smallest tangent for which a collision occurs - if ( frac1 >= minTan && frac1 < tanHalfAngle ) { - tanHalfAngle = frac1; - } - if ( frac2 >= minTan && frac2 < tanHalfAngle ) { - tanHalfAngle = frac2; - } - - if ( tw->angle < 0.0f ) { - tanHalfAngle = -tanHalfAngle; - } - - return true; -} - -/* -================ -idCollisionModelManagerLocal::EdgeFurthestFromEdge - - calculates the direction of motion at the initial position, where dir < 0 means the edges move towards each other - if the edges move away from each other the tangent of half the rotation angle at which - the edges are furthest apart is also calculated -================ -*/ -int idCollisionModelManagerLocal::EdgeFurthestFromEdge( cm_traceWork_t *tw, const idPluecker &pl1, - const idVec3 &vc, const idVec3 &vd, - float &tanHalfAngle, float &dir ) { - double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2; - idVec3 ct, dt; - idPluecker pl2; - - /* - - v2 * cos(t) + v1 * sin(t) + v0 = 0; - - // rotation about the z-axis - v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0]; - v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5]; - v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2]; - - derivative: - v1 * cos(t) - v2 * sin(t) = 0; - - r = tan(t / 2); - sin(t) = 2*r/(1+r*r); - cos(t) = (1-r*r)/(1+r*r); - - -v2 * 2 * r / (1 + r*r) + v1 * (1 - r*r)/(1+r*r); - -v2 * 2 * r + v1 * (1 - r*r) / (1 + r*r) = 0; - -v2 * 2 * r + v1 * (1 - r*r) = 0; - (-v1) * r * r + (-2 * v2) * r + (v1) = 0; - - */ - - tanHalfAngle = 0.0f; - - // transform rotation axis to z-axis - ct = (vc - tw->origin) * tw->matrix; - dt = (vd - tw->origin) * tw->matrix; - - pl2.FromLine( ct, dt ); - - v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0]; - v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5]; - v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2]; - - // get the direction of motion at the initial position - c = v0 + v2; - if ( tw->angle > 0.0f ) { - if ( c > 0.0f ) { - dir = v1; - } - else { - dir = -v1; - } - } - else { - if ( c > 0.0f ) { - dir = -v1; - } - else { - dir = v1; - } - } - // negative direction means the edges move towards each other at the initial position - if ( dir <= 0.0f ) { - return true; - } - - a = -v1; - b = -v2; - c = v1; - if ( a == 0.0f ) { - if ( b == 0.0f ) { - return false; - } - frac1 = -c / ( 2.0f * b ); - frac2 = 1e10; // = tan( idMath::HALF_PI ) - } - else { - d = b * b - c * a; - if ( d <= 0.0f ) { - return false; - } - sqrtd = sqrt( d ); - if ( b > 0.0f ) { - q = - b + sqrtd; - } - else { - q = - b - sqrtd; - } - frac1 = q / a; - frac2 = c / q; - } - - if ( tw->angle < 0.0f ) { - frac1 = -frac1; - frac2 = -frac2; - } - - if ( frac1 < 0.0f && frac2 < 0.0f ) { - return false; - } - - if ( frac1 > frac2 ) { - tanHalfAngle = frac1; - } - else { - tanHalfAngle = frac2; - } - - if ( tw->angle < 0.0f ) { - tanHalfAngle = -tanHalfAngle; - } - - return true; -} - -/* -================ -idCollisionModelManagerLocal::RotateTrmEdgeThroughPolygon -================ -*/ -void idCollisionModelManagerLocal::RotateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge ) { - int i, j, edgeNum; - float f1, f2, startTan, dir, tanHalfAngle; - cm_edge_t *edge; - cm_vertex_t *v1, *v2; - idVec3 collisionPoint, collisionNormal, origin, epsDir; - idPluecker epsPl; - idBounds bounds; - - // if the trm is convex and the rotation axis intersects the trm - if ( tw->isConvex && tw->axisIntersectsTrm ) { - // if both points are behind the polygon the edge cannot collide within a 180 degrees rotation - if ( tw->vertices[trmEdge->vertexNum[0]].polygonSide & tw->vertices[trmEdge->vertexNum[1]].polygonSide ) { - return; - } - } - - // if the trace model edge rotation bounds do not intersect the polygon bounds - if ( !trmEdge->rotationBounds.IntersectsBounds( poly->bounds ) ) { - return; - } - - // edge rotation bounds should cross polygon plane - if ( trmEdge->rotationBounds.PlaneSide( poly->plane ) != SIDE_CROSS ) { - return; - } - - // check edges for a collision - for ( i = 0; i < poly->numEdges; i++ ) { - edgeNum = poly->edges[i]; - edge = tw->model->edges + abs(edgeNum); - - // if this edge is already checked - if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - - // can never collide with internal edges - if ( edge->internal ) { - continue; - } - - v1 = tw->model->vertices + edge->vertexNum[INTSIGNBITSET(edgeNum)]; - v2 = tw->model->vertices + edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]; - - // edge bounds - for ( j = 0; j < 3; j++ ) { - if ( v1->p[j] > v2->p[j] ) { - bounds[0][j] = v2->p[j]; - bounds[1][j] = v1->p[j]; - } - else { - bounds[0][j] = v1->p[j]; - bounds[1][j] = v2->p[j]; - } - } - - // if the trace model edge rotation bounds do not intersect the polygon edge bounds - if ( !trmEdge->rotationBounds.IntersectsBounds( bounds ) ) { - continue; - } - - f1 = trmEdge->pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ); - - // pluecker coordinate for epsilon expanded edge - epsDir = edge->normal * (CM_CLIP_EPSILON+CM_PL_RANGE_EPSILON); - epsPl.FromLine( tw->model->vertices[edge->vertexNum[0]].p + epsDir, - tw->model->vertices[edge->vertexNum[1]].p + epsDir ); - - f2 = trmEdge->pl.PermutedInnerProduct( epsPl ); - - // if the rotating edge is inbetween the polygon edge and the epsilon expanded edge - if ( ( f1 < 0.0f && f2 > 0.0f ) || ( f1 > 0.0f && f2 < 0.0f ) ) { - - if ( !EdgeFurthestFromEdge( tw, trmEdge->plzaxis, v1->p, v2->p, startTan, dir ) ) { - continue; - } - - if ( dir <= 0.0f ) { - // moving towards the polygon edge so stop immediately - tanHalfAngle = 0.0f; - } - else if ( idMath::Fabs( startTan ) >= tw->maxTan ) { - // never going to get beyond the start tangent during the current rotation - continue; - } - else { - // collide with the epsilon expanded edge - if ( !RotateEdgeThroughEdge(tw, trmEdge->plzaxis, v1->p + epsDir, v2->p + epsDir, idMath::Fabs( startTan ), tanHalfAngle ) ) { - tanHalfAngle = startTan; - } - } - } - else { - // collide with the epsilon expanded edge - epsDir = edge->normal * CM_CLIP_EPSILON; - if ( !RotateEdgeThroughEdge(tw, trmEdge->plzaxis, v1->p + epsDir, v2->p + epsDir, 0.0f, tanHalfAngle ) ) { - continue; - } - } - - if ( idMath::Fabs( tanHalfAngle ) >= tw->maxTan ) { - continue; - } - - // check if the collision is between the edge bounds - if ( !CollisionBetweenEdgeBounds( tw, trmEdge->start, trmEdge->end, v1->p, v2->p, - tanHalfAngle, collisionPoint, collisionNormal ) ) { - continue; - } - - // allow rotation if the rotation axis goes through the collisionPoint - origin = tw->origin + tw->axis * ( tw->axis * ( collisionPoint - tw->origin ) ); - if ( ( collisionPoint - origin ).LengthSqr() < ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) { - continue; - } - - // fill in trace structure - tw->maxTan = idMath::Fabs( tanHalfAngle ); - tw->trace.c.normal = collisionNormal; - tw->trace.c.normal.Normalize(); - tw->trace.c.dist = tw->trace.c.normal * v1->p; - // make sure the collision plane faces the trace model - if ( (tw->trace.c.normal * trmEdge->start) - tw->trace.c.dist < 0 ) { - tw->trace.c.normal = -tw->trace.c.normal; - tw->trace.c.dist = -tw->trace.c.dist; - } - tw->trace.c.contents = poly->contents; - tw->trace.c.material = poly->material; - tw->trace.c.type = CONTACT_EDGE; - tw->trace.c.modelFeature = edgeNum; - tw->trace.c.trmFeature = trmEdge - tw->edges; - tw->trace.c.point = collisionPoint; - // if no collision can be closer - if ( tw->maxTan == 0.0f ) { - break; - } - } -} - -/* -================ -idCollisionModelManagerLocal::RotatePointThroughPlane - - calculates the tangent of half the rotation angle at which the point collides with the plane -================ -*/ -int idCollisionModelManagerLocal::RotatePointThroughPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane, - const float angle, const float minTan, float &tanHalfAngle ) { - double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2; - idVec3 p, normal; - - /* - - p[0] = point[0] * cos(t) + point[1] * sin(t) - p[1] = point[0] * -sin(t) + point[1] * cos(t) - p[2] = point[2]; - - normal[0] * (p[0] * cos(t) + p[1] * sin(t)) + - normal[1] * (p[0] * -sin(t) + p[1] * cos(t)) + - normal[2] * p[2] + dist = 0 - - normal[0] * p[0] * cos(t) + normal[0] * p[1] * sin(t) + - -normal[1] * p[0] * sin(t) + normal[1] * p[1] * cos(t) + - normal[2] * p[2] + dist = 0 - - v2 * cos(t) + v1 * sin(t) + v0 - - // rotation about the z-axis - v0 = normal[2] * p[2] + dist - v1 = normal[0] * p[1] - normal[1] * p[0] - v2 = normal[0] * p[0] + normal[1] * p[1] - - r = tan(t / 2); - sin(t) = 2*r/(1+r*r); - cos(t) = (1-r*r)/(1+r*r); - - v1 * 2 * r / (1 + r*r) + v2 * (1 - r*r) / (1 + r*r) + v0 = 0 - (v1 * 2 * r + v2 * (1 - r*r)) / (1 + r*r) = -v0 - (v1 * 2 * r + v2 - v2 * r*r) / (1 + r*r) = -v0 - v1 * 2 * r + v2 - v2 * r*r = -v0 * (1 + r*r) - v1 * 2 * r + v2 - v2 * r*r = -v0 + -v0 * r*r - (v0 - v2) * r * r + (2 * v1) * r + (v0 + v2) = 0; - - */ - - tanHalfAngle = tw->maxTan; - - // transform rotation axis to z-axis - p = (point - tw->origin) * tw->matrix; - d = plane[3] + plane.Normal() * tw->origin; - normal = plane.Normal() * tw->matrix; - - v0 = normal[2] * p[2] + d; - v1 = normal[0] * p[1] - normal[1] * p[0]; - v2 = normal[0] * p[0] + normal[1] * p[1]; - - a = v0 - v2; - b = v1; - c = v0 + v2; - if ( a == 0.0f ) { - if ( b == 0.0f ) { - return false; - } - frac1 = -c / ( 2.0f * b ); - frac2 = 1e10; // = tan( idMath::HALF_PI ) - } - else { - d = b * b - c * a; - if ( d <= 0.0f ) { - return false; - } - sqrtd = sqrt( d ); - if ( b > 0.0f ) { - q = - b + sqrtd; - } - else { - q = - b - sqrtd; - } - frac1 = q / a; - frac2 = c / q; - } - - if ( angle < 0.0f ) { - frac1 = -frac1; - frac2 = -frac2; - } - - // get smallest tangent for which a collision occurs - if ( frac1 >= minTan && frac1 < tanHalfAngle ) { - tanHalfAngle = frac1; - } - if ( frac2 >= minTan && frac2 < tanHalfAngle ) { - tanHalfAngle = frac2; - } - - if ( angle < 0.0f ) { - tanHalfAngle = -tanHalfAngle; - } - - return true; -} - -/* -================ -idCollisionModelManagerLocal::PointFurthestFromPlane - - calculates the direction of motion at the initial position, where dir < 0 means the point moves towards the plane - if the point moves away from the plane the tangent of half the rotation angle at which - the point is furthest away from the plane is also calculated -================ -*/ -int idCollisionModelManagerLocal::PointFurthestFromPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane, - const float angle, float &tanHalfAngle, float &dir ) { - - double v1, v2, a, b, c, d, sqrtd, q, frac1, frac2; - idVec3 p, normal; - - /* - - v2 * cos(t) + v1 * sin(t) + v0 = 0; - - // rotation about the z-axis - v0 = normal[2] * p[2] + dist - v1 = normal[0] * p[1] - normal[1] * p[0] - v2 = normal[0] * p[0] + normal[1] * p[1] - - derivative: - v1 * cos(t) - v2 * sin(t) = 0; - - r = tan(t / 2); - sin(t) = 2*r/(1+r*r); - cos(t) = (1-r*r)/(1+r*r); - - -v2 * 2 * r / (1 + r*r) + v1 * (1 - r*r)/(1+r*r); - -v2 * 2 * r + v1 * (1 - r*r) / (1 + r*r) = 0; - -v2 * 2 * r + v1 * (1 - r*r) = 0; - (-v1) * r * r + (-2 * v2) * r + (v1) = 0; - - */ - - tanHalfAngle = 0.0f; - - // transform rotation axis to z-axis - p = (point - tw->origin) * tw->matrix; - normal = plane.Normal() * tw->matrix; - - v1 = normal[0] * p[1] - normal[1] * p[0]; - v2 = normal[0] * p[0] + normal[1] * p[1]; - - // the point will always start at the front of the plane, therefore v0 + v2 > 0 is always true - if ( angle < 0.0f ) { - dir = -v1; - } - else { - dir = v1; - } - // negative direction means the point moves towards the plane at the initial position - if ( dir <= 0.0f ) { - return true; - } - - a = -v1; - b = -v2; - c = v1; - if ( a == 0.0f ) { - if ( b == 0.0f ) { - return false; - } - frac1 = -c / ( 2.0f * b ); - frac2 = 1e10; // = tan( idMath::HALF_PI ) - } - else { - d = b * b - c * a; - if ( d <= 0.0f ) { - return false; - } - sqrtd = sqrt( d ); - if ( b > 0.0f ) { - q = - b + sqrtd; - } - else { - q = - b - sqrtd; - } - frac1 = q / a; - frac2 = c / q; - } - - if ( angle < 0.0f ) { - frac1 = -frac1; - frac2 = -frac2; - } - - if ( frac1 < 0.0f && frac2 < 0.0f ) { - return false; - } - - if ( frac1 > frac2 ) { - tanHalfAngle = frac1; - } - else { - tanHalfAngle = frac2; - } - - if ( angle < 0.0f ) { - tanHalfAngle = -tanHalfAngle; - } - - return true; -} - -/* -================ -idCollisionModelManagerLocal::RotatePointThroughEpsilonPlane -================ -*/ -int idCollisionModelManagerLocal::RotatePointThroughEpsilonPlane( const cm_traceWork_t *tw, const idVec3 &point, const idVec3 &endPoint, - const idPlane &plane, const float angle, const idVec3 &origin, - float &tanHalfAngle, idVec3 &collisionPoint, idVec3 &endDir ) { - float d, dir, startTan; - idVec3 vec, startDir; - idPlane epsPlane; - - // epsilon expanded plane - epsPlane = plane; - epsPlane.SetDist( epsPlane.Dist() + CM_CLIP_EPSILON ); - - // if the rotation sphere at the rotation origin is too far away from the polygon plane - d = epsPlane.Distance( origin ); - vec = point - origin; - if ( d * d > vec * vec ) { - return false; - } - - // calculate direction of motion at vertex start position - startDir = ( point - origin ).Cross( tw->axis ); - if ( angle < 0.0f ) { - startDir = -startDir; - } - // if moving away from plane at start position - if ( startDir * epsPlane.Normal() >= 0.0f ) { - // if end position is outside epsilon range - d = epsPlane.Distance( endPoint ); - if ( d >= 0.0f ) { - return false; // no collision - } - // calculate direction of motion at vertex end position - endDir = ( endPoint - origin ).Cross( tw->axis ); - if ( angle < 0.0f ) { - endDir = -endDir; - } - // if also moving away from plane at end position - if ( endDir * epsPlane.Normal() > 0.0f ) { - return false; // no collision - } - } - - // if the start position is in the epsilon range - d = epsPlane.Distance( point ); - if ( d <= CM_PL_RANGE_EPSILON ) { - - // calculate tangent of half the rotation for which the vertex is furthest away from the plane - if ( !PointFurthestFromPlane( tw, point, plane, angle, startTan, dir ) ) { - return false; - } - - if ( dir <= 0.0f ) { - // moving towards the polygon plane so stop immediately - tanHalfAngle = 0.0f; - } - else if ( idMath::Fabs( startTan ) >= tw->maxTan ) { - // never going to get beyond the start tangent during the current rotation - return false; - } - else { - // calculate collision with epsilon expanded plane - if ( !RotatePointThroughPlane( tw, point, epsPlane, angle, idMath::Fabs( startTan ), tanHalfAngle ) ) { - tanHalfAngle = startTan; - } - } - } - else { - // calculate collision with epsilon expanded plane - if ( !RotatePointThroughPlane( tw, point, epsPlane, angle, 0.0f, tanHalfAngle ) ) { - return false; - } - } - - // calculate collision point - collisionPoint = point; - if ( tanHalfAngle != 0.0f ) { - CM_RotatePoint( collisionPoint, tw->origin, tw->axis, tanHalfAngle ); - } - // calculate direction of motion at collision point - endDir = ( collisionPoint - origin ).Cross( tw->axis ); - if ( angle < 0.0f ) { - endDir = -endDir; - } - return true; -} - -/* -================ -idCollisionModelManagerLocal::RotateTrmVertexThroughPolygon -================ -*/ -void idCollisionModelManagerLocal::RotateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int vertexNum ) { - int i; - float tanHalfAngle; - idVec3 endDir, collisionPoint; - idPluecker pl; - - // if the trm vertex is behind the polygon plane it cannot collide with the polygon within a 180 degrees rotation - if ( tw->isConvex && tw->axisIntersectsTrm && v->polygonSide ) { - return; - } - - // if the trace model vertex rotation bounds do not intersect the polygon bounds - if ( !v->rotationBounds.IntersectsBounds( poly->bounds ) ) { - return; - } - - // vertex rotation bounds should cross polygon plane - if ( v->rotationBounds.PlaneSide( poly->plane ) != SIDE_CROSS ) { - return; - } - - // rotate the vertex through the epsilon plane - if ( !RotatePointThroughEpsilonPlane( tw, v->p, v->endp, poly->plane, tw->angle, v->rotationOrigin, - tanHalfAngle, collisionPoint, endDir ) ) { - return; - } - - if ( idMath::Fabs( tanHalfAngle ) < tw->maxTan ) { - // verify if 'collisionPoint' moving along 'endDir' moves between polygon edges - pl.FromRay( collisionPoint, endDir ); - for ( i = 0; i < poly->numEdges; i++ ) { - if ( poly->edges[i] < 0 ) { - if ( pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ) > 0.0f ) { - return; - } - } - else { - if ( pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ) < 0.0f ) { - return; - } - } - } - tw->maxTan = idMath::Fabs( tanHalfAngle ); - // collision plane is the polygon plane - tw->trace.c.normal = poly->plane.Normal(); - tw->trace.c.dist = poly->plane.Dist(); - tw->trace.c.contents = poly->contents; - tw->trace.c.material = poly->material; - tw->trace.c.type = CONTACT_TRMVERTEX; - tw->trace.c.modelFeature = *reinterpret_cast(&poly); - tw->trace.c.trmFeature = v - tw->vertices; - tw->trace.c.point = collisionPoint; - } -} - -/* -================ -idCollisionModelManagerLocal::RotateVertexThroughTrmPolygon -================ -*/ -void idCollisionModelManagerLocal::RotateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly, cm_polygon_t *poly, cm_vertex_t *v, idVec3 &rotationOrigin ) { - int i, edgeNum; - float tanHalfAngle; - idVec3 dir, endp, endDir, collisionPoint; - idPluecker pl; - cm_trmEdge_t *edge; - - // if the polygon vertex is behind the trm plane it cannot collide with the trm polygon within a 180 degrees rotation - if ( tw->isConvex && tw->axisIntersectsTrm && trmpoly->plane.Distance( v->p ) < 0.0f ) { - return; - } - - // if the model vertex is outside the trm polygon rotation bounds - if ( !trmpoly->rotationBounds.ContainsPoint( v->p ) ) { - return; - } - - // if the rotation axis goes through the polygon vertex - dir = v->p - rotationOrigin; - if ( dir * dir < ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) { - return; - } - - // calculate vertex end position - endp = v->p; - tw->modelVertexRotation.RotatePoint( endp ); - - // rotate the vertex through the epsilon plane - if ( !RotatePointThroughEpsilonPlane( tw, v->p, endp, trmpoly->plane, -tw->angle, rotationOrigin, - tanHalfAngle, collisionPoint, endDir ) ) { - return; - } - - if ( idMath::Fabs( tanHalfAngle ) < tw->maxTan ) { - // verify if 'collisionPoint' moving along 'endDir' moves between polygon edges - pl.FromRay( collisionPoint, endDir ); - for ( i = 0; i < trmpoly->numEdges; i++ ) { - edgeNum = trmpoly->edges[i]; - edge = tw->edges + abs(edgeNum); - if ( edgeNum < 0 ) { - if ( pl.PermutedInnerProduct( edge->pl ) > 0.0f ) { - return; - } - } - else { - if ( pl.PermutedInnerProduct( edge->pl ) < 0.0f ) { - return; - } - } - } - tw->maxTan = idMath::Fabs( tanHalfAngle ); - // collision plane is the flipped trm polygon plane - tw->trace.c.normal = -trmpoly->plane.Normal(); - tw->trace.c.dist = tw->trace.c.normal * v->p; - tw->trace.c.contents = poly->contents; - tw->trace.c.material = poly->material; - tw->trace.c.type = CONTACT_MODELVERTEX; - tw->trace.c.modelFeature = v - tw->model->vertices; - tw->trace.c.trmFeature = trmpoly - tw->polys; - tw->trace.c.point = v->p; - } -} - -/* -================ -idCollisionModelManagerLocal::RotateTrmThroughPolygon - - returns true if the polygon blocks the complete rotation -================ -*/ -bool idCollisionModelManagerLocal::RotateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *p ) { - int i, j, k, edgeNum; - float d; - cm_trmVertex_t *bv; - cm_trmEdge_t *be; - cm_trmPolygon_t *bp; - cm_vertex_t *v; - cm_edge_t *e; - idVec3 *rotationOrigin; - - // if already checked this polygon - if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) { - return false; - } - p->checkcount = idCollisionModelManagerLocal::checkCount; - - // if this polygon does not have the right contents behind it - if ( !(p->contents & tw->contents) ) { - return false; - } - - // if the the trace bounds do not intersect the polygon bounds - if ( !tw->bounds.IntersectsBounds( p->bounds ) ) { - return false; - } - - // back face culling - if ( tw->isConvex ) { - // if the center of the convex trm is behind the polygon plane - if ( p->plane.Distance( tw->start ) < 0.0f ) { - // if the rotation axis intersects the trace model - if ( tw->axisIntersectsTrm ) { - return false; - } - else { - // if the direction of motion at the start and end position of the - // center of the trm both go towards or away from the polygon plane - // or if the intersections of the rotation axis with the expanded heart planes - // are both in front of the polygon plane - } - } - } - - // if the polygon is too far from the first heart plane - d = p->bounds.PlaneDistance( tw->heartPlane1 ); - if ( idMath::Fabs(d) > tw->maxDistFromHeartPlane1 ) { - return false; - } - - // rotation bounds should cross polygon plane - switch( tw->bounds.PlaneSide( p->plane ) ) { - case PLANESIDE_CROSS: - break; - case PLANESIDE_FRONT: - if ( tw->model->isConvex ) { - tw->quickExit = true; - return true; - } - default: - return false; - } - - for ( i = 0; i < tw->numVerts; i++ ) { - bv = tw->vertices + i; - // calculate polygon side this vertex is on - d = p->plane.Distance( bv->p ); - bv->polygonSide = FLOATSIGNBITSET( d ); - } - - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - e = tw->model->edges + abs(edgeNum); - v = tw->model->vertices + e->vertexNum[INTSIGNBITSET(edgeNum)]; - - // pluecker coordinate for edge - tw->polygonEdgePlueckerCache[i].FromLine( tw->model->vertices[e->vertexNum[0]].p, - tw->model->vertices[e->vertexNum[1]].p ); - - // calculate rotation origin projected into rotation plane through the vertex - tw->polygonRotationOriginCache[i] = tw->origin + tw->axis * ( tw->axis * ( v->p - tw->origin ) ); - } - // copy first to last so we can easily cycle through - tw->polygonRotationOriginCache[p->numEdges] = tw->polygonRotationOriginCache[0]; - - // fast point rotation - if ( tw->pointTrace ) { - RotateTrmVertexThroughPolygon( tw, p, &tw->vertices[0], 0 ); - } - else { - // rotate trm vertices through polygon - for ( i = 0; i < tw->numVerts; i++ ) { - bv = tw->vertices + i; - if ( bv->used ) { - RotateTrmVertexThroughPolygon( tw, p, bv, i ); - } - } - - // rotate trm edges through polygon - for ( i = 1; i <= tw->numEdges; i++ ) { - be = tw->edges + i; - if ( be->used ) { - RotateTrmEdgeThroughPolygon( tw, p, be ); - } - } - - // rotate all polygon vertices through the trm - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - e = tw->model->edges + abs(edgeNum); - - if ( e->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - // set edge check count - e->checkcount = idCollisionModelManagerLocal::checkCount; - // can never collide with internal edges - if ( e->internal ) { - continue; - } - // got to check both vertices because we skip internal edges - for ( k = 0; k < 2; k++ ) { - - v = tw->model->vertices + e->vertexNum[k ^ INTSIGNBITSET(edgeNum)]; - - // if this vertex is already checked - if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - // set vertex check count - v->checkcount = idCollisionModelManagerLocal::checkCount; - - // if the vertex is outside the trm rotation bounds - if ( !tw->bounds.ContainsPoint( v->p ) ) { - continue; - } - - rotationOrigin = &tw->polygonRotationOriginCache[i+k]; - - for ( j = 0; j < tw->numPolys; j++ ) { - bp = tw->polys + j; - if ( bp->used ) { - RotateVertexThroughTrmPolygon( tw, bp, p, v, *rotationOrigin ); - } - } - } - } - } - - return ( tw->maxTan == 0.0f ); -} - -/* -================ -idCollisionModelManagerLocal::BoundsForRotation - - only for rotations < 180 degrees -================ -*/ -void idCollisionModelManagerLocal::BoundsForRotation( const idVec3 &origin, const idVec3 &axis, const idVec3 &start, const idVec3 &end, idBounds &bounds ) { - int i; - float radiusSqr; - idVec3 v1, v2; - - radiusSqr = ( start - origin ).LengthSqr(); - v1 = ( start - origin ).Cross( axis ); - v2 = ( end - origin ).Cross( axis ); - - for ( i = 0; i < 3; i++ ) { - // if the derivative changes sign along this axis during the rotation from start to end - if ( ( v1[i] > 0.0f && v2[i] < 0.0f ) || ( v1[i] < 0.0f && v2[i] > 0.0f ) ) { - if ( ( 0.5f * (start[i] + end[i]) - origin[i] ) > 0.0f ) { - bounds[0][i] = Min( start[i], end[i] ); - bounds[1][i] = origin[i] + idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) ); - } - else { - bounds[0][i] = origin[i] - idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) ); - bounds[1][i] = Max( start[i], end[i] ); - } - } - else if ( start[i] > end[i] ) { - bounds[0][i] = end[i]; - bounds[1][i] = start[i]; - } - else { - bounds[0][i] = start[i]; - bounds[1][i] = end[i]; - } - // expand for epsilons - bounds[0][i] -= CM_BOX_EPSILON; - bounds[1][i] += CM_BOX_EPSILON; - } -} - -/* -================ -idCollisionModelManagerLocal::Rotation180 -================ -*/ -void idCollisionModelManagerLocal::Rotation180( trace_t *results, const idVec3 &rorg, const idVec3 &axis, - const float startAngle, const float endAngle, const idVec3 &start, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { - int i, j, edgeNum; - float d, maxErr, initialTan; - bool model_rotated, trm_rotated; - idVec3 dir, dir1, dir2, tmp, vr, vup, org, at, bt; - idMat3 invModelAxis, endAxis, tmpAxis; - idRotation startRotation, endRotation; - idPluecker plaxis; - cm_trmPolygon_t *poly; - cm_trmEdge_t *edge; - cm_trmVertex_t *vert; - ALIGN16( static cm_traceWork_t tw; ) - - if ( model < 0 || model > MAX_SUBMODELS || model > idCollisionModelManagerLocal::maxModels ) { - common->Printf("idCollisionModelManagerLocal::Rotation180: invalid model handle\n"); - return; - } - if ( !idCollisionModelManagerLocal::models[model] ) { - common->Printf("idCollisionModelManagerLocal::Rotation180: invalid model\n"); - return; - } - - idCollisionModelManagerLocal::checkCount++; - - tw.trace.fraction = 1.0f; - tw.trace.c.contents = 0; - tw.trace.c.type = CONTACT_NONE; - tw.contents = contentMask; - tw.isConvex = true; - tw.rotation = true; - tw.positionTest = false; - tw.axisIntersectsTrm = false; - tw.quickExit = false; - tw.angle = endAngle - startAngle; - assert( tw.angle > -180.0f && tw.angle < 180.0f ); - tw.maxTan = initialTan = idMath::Fabs( tan( ( idMath::PI / 360.0f ) * tw.angle ) ); - tw.model = idCollisionModelManagerLocal::models[model]; - tw.start = start - modelOrigin; - // rotation axis, axis is assumed to be normalized - tw.axis = axis; - assert( tw.axis[0] * tw.axis[0] + tw.axis[1] * tw.axis[1] + tw.axis[2] * tw.axis[2] > 0.99f ); - // rotation origin projected into rotation plane through tw.start - tw.origin = rorg - modelOrigin; - d = (tw.axis * tw.origin) - ( tw.axis * tw.start ); - tw.origin = tw.origin - d * tw.axis; - // radius of rotation - tw.radius = ( tw.start - tw.origin ).Length(); - // maximum error of the circle approximation traced through the axial BSP tree - d = tw.radius * tw.radius - (CIRCLE_APPROXIMATION_LENGTH*CIRCLE_APPROXIMATION_LENGTH*0.25f); - if ( d > 0.0f ) { - maxErr = tw.radius - idMath::Sqrt( d ); - } else { - maxErr = tw.radius; - } - - model_rotated = modelAxis.IsRotated(); - if ( model_rotated ) { - invModelAxis = modelAxis.Transpose(); - tw.axis *= invModelAxis; - tw.origin *= invModelAxis; - } - - startRotation.Set( tw.origin, tw.axis, startAngle ); - endRotation.Set( tw.origin, tw.axis, endAngle ); - - // create matrix which rotates the rotation axis to the z-axis - tw.axis.NormalVectors( vr, vup ); - tw.matrix[0][0] = vr[0]; - tw.matrix[1][0] = vr[1]; - tw.matrix[2][0] = vr[2]; - tw.matrix[0][1] = -vup[0]; - tw.matrix[1][1] = -vup[1]; - tw.matrix[2][1] = -vup[2]; - tw.matrix[0][2] = tw.axis[0]; - tw.matrix[1][2] = tw.axis[1]; - tw.matrix[2][2] = tw.axis[2]; - - // if optimized point trace - if ( !trm || ( trm->bounds[1][0] - trm->bounds[0][0] <= 0.0f && - trm->bounds[1][1] - trm->bounds[0][1] <= 0.0f && - trm->bounds[1][2] - trm->bounds[0][2] <= 0.0f ) ) { - - if ( model_rotated ) { - // rotate trace instead of model - tw.start *= invModelAxis; - } - tw.end = tw.start; - // if we start at a specific angle - if ( startAngle != 0.0f ) { - startRotation.RotatePoint( tw.start ); - } - // calculate end position of rotation - endRotation.RotatePoint( tw.end ); - - // calculate rotation origin projected into rotation plane through the vertex - tw.numVerts = 1; - tw.vertices[0].p = tw.start; - tw.vertices[0].endp = tw.end; - tw.vertices[0].used = true; - tw.vertices[0].rotationOrigin = tw.origin + tw.axis * ( tw.axis * ( tw.vertices[0].p - tw.origin ) ); - BoundsForRotation( tw.vertices[0].rotationOrigin, tw.axis, tw.start, tw.end, tw.vertices[0].rotationBounds ); - // rotation bounds - tw.bounds = tw.vertices[0].rotationBounds; - tw.numEdges = tw.numPolys = 0; - - // collision with single point - tw.pointTrace = true; - - // extents is set to maximum error of the circle approximation traced through the axial BSP tree - tw.extents[0] = tw.extents[1] = tw.extents[2] = maxErr + CM_BOX_EPSILON; - - // setup rotation heart plane - tw.heartPlane1.SetNormal( tw.axis ); - tw.heartPlane1.FitThroughPoint( tw.start ); - tw.maxDistFromHeartPlane1 = CM_BOX_EPSILON; - - // trace through the model - idCollisionModelManagerLocal::TraceThroughModel( &tw ); - - // store results - *results = tw.trace; - results->endpos = start; - if ( tw.maxTan == initialTan ) { - results->fraction = 1.0f; - } else { - results->fraction = idMath::Fabs( atan( tw.maxTan ) * ( 2.0f * 180.0f / idMath::PI ) / tw.angle ); - } - assert( results->fraction <= 1.0f ); - endRotation.Set( rorg, axis, startAngle + (endAngle-startAngle) * results->fraction ); - endRotation.RotatePoint( results->endpos ); - results->endAxis.Identity(); - - if ( results->fraction < 1.0f ) { - // rotate trace plane normal if there was a collision with a rotated model - if ( model_rotated ) { - results->c.normal *= modelAxis; - results->c.point *= modelAxis; - } - results->c.point += modelOrigin; - results->c.dist += modelOrigin * results->c.normal; - } - return; - } - - tw.pointTrace = false; - - // setup trm structure - idCollisionModelManagerLocal::SetupTrm( &tw, trm ); - - trm_rotated = trmAxis.IsRotated(); - - // calculate vertex positions - if ( trm_rotated ) { - for ( i = 0; i < tw.numVerts; i++ ) { - // rotate trm around the start position - tw.vertices[i].p *= trmAxis; - } - } - for ( i = 0; i < tw.numVerts; i++ ) { - // set trm at start position - tw.vertices[i].p += tw.start; - } - if ( model_rotated ) { - for ( i = 0; i < tw.numVerts; i++ ) { - tw.vertices[i].p *= invModelAxis; - } - } - for ( i = 0; i < tw.numVerts; i++ ) { - tw.vertices[i].endp = tw.vertices[i].p; - } - // if we start at a specific angle - if ( startAngle != 0.0f ) { - for ( i = 0; i < tw.numVerts; i++ ) { - startRotation.RotatePoint( tw.vertices[i].p ); - } - } - for ( i = 0; i < tw.numVerts; i++ ) { - // end position of vertex - endRotation.RotatePoint( tw.vertices[i].endp ); - } - - // add offset to start point - if ( trm_rotated ) { - tw.start += trm->offset * trmAxis; - } else { - tw.start += trm->offset; - } - // if the model is rotated - if ( model_rotated ) { - // rotate trace instead of model - tw.start *= invModelAxis; - } - tw.end = tw.start; - // if we start at a specific angle - if ( startAngle != 0.0f ) { - startRotation.RotatePoint( tw.start ); - } - // calculate end position of rotation - endRotation.RotatePoint( tw.end ); - - // setup trm vertices - for ( vert = tw.vertices, i = 0; i < tw.numVerts; i++, vert++ ) { - // calculate rotation origin projected into rotation plane through the vertex - vert->rotationOrigin = tw.origin + tw.axis * ( tw.axis * ( vert->p - tw.origin ) ); - // calculate rotation bounds for this vertex - BoundsForRotation( vert->rotationOrigin, tw.axis, vert->p, vert->endp, vert->rotationBounds ); - // if the rotation axis goes through the vertex then the vertex is not used - d = ( vert->p - vert->rotationOrigin ).LengthSqr(); - if ( d > ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) { - vert->used = true; - } - } - - // setup trm edges - for ( edge = tw.edges + 1, i = 1; i <= tw.numEdges; i++, edge++ ) { - // if the rotation axis goes through both the edge vertices then the edge is not used - if ( tw.vertices[edge->vertexNum[0]].used | tw.vertices[edge->vertexNum[1]].used ) { - edge->used = true; - } - // edge start, end and pluecker coordinate - edge->start = tw.vertices[edge->vertexNum[0]].p; - edge->end = tw.vertices[edge->vertexNum[1]].p; - edge->pl.FromLine( edge->start, edge->end ); - // pluecker coordinate for edge being rotated about the z-axis - at = ( edge->start - tw.origin ) * tw.matrix; - bt = ( edge->end - tw.origin ) * tw.matrix; - edge->plzaxis.FromLine( at, bt ); - // get edge rotation bounds from the rotation bounds of both vertices - edge->rotationBounds = tw.vertices[edge->vertexNum[0]].rotationBounds; - edge->rotationBounds.AddBounds( tw.vertices[edge->vertexNum[1]].rotationBounds ); - // used to calculate if the rotation axis intersects the trm - edge->bitNum = 0; - } - - tw.bounds.Clear(); - - // rotate trm polygon planes - if ( trm_rotated & model_rotated ) { - tmpAxis = trmAxis * invModelAxis; - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - poly->plane *= tmpAxis; - } - } else if ( trm_rotated ) { - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - poly->plane *= trmAxis; - } - } else if ( model_rotated ) { - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - poly->plane *= invModelAxis; - } - } - - // setup trm polygons - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - poly->used = true; - // set trm polygon plane distance - poly->plane.FitThroughPoint( tw.edges[abs(poly->edges[0])].start ); - // get polygon bounds from edge bounds - poly->rotationBounds.Clear(); - for ( j = 0; j < poly->numEdges; j++ ) { - // add edge rotation bounds to polygon rotation bounds - edge = &tw.edges[abs( poly->edges[j] )]; - poly->rotationBounds.AddBounds( edge->rotationBounds ); - } - // get trace bounds from polygon bounds - tw.bounds.AddBounds( poly->rotationBounds ); - } - - // extents including the maximum error of the circle approximation traced through the axial BSP tree - for ( i = 0; i < 3; i++ ) { - tw.size[0][i] = tw.bounds[0][i] - tw.start[i]; - tw.size[1][i] = tw.bounds[1][i] - tw.start[i]; - if ( idMath::Fabs( tw.size[0][i] ) > idMath::Fabs( tw.size[1][i] ) ) { - tw.extents[i] = idMath::Fabs( tw.size[0][i] ) + maxErr + CM_BOX_EPSILON; - } else { - tw.extents[i] = idMath::Fabs( tw.size[1][i] ) + maxErr + CM_BOX_EPSILON; - } - } - - // for back-face culling - if ( tw.isConvex ) { - if ( tw.start == tw.origin ) { - tw.axisIntersectsTrm = true; - } else { - // determine if the rotation axis intersects the trm - plaxis.FromRay( tw.origin, tw.axis ); - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - // back face cull polygons - if ( poly->plane.Normal() * tw.axis > 0.0f ) { - continue; - } - // test if the axis goes between the polygon edges - for ( j = 0; j < poly->numEdges; j++ ) { - edgeNum = poly->edges[j]; - edge = tw.edges + abs(edgeNum); - if ( !(edge->bitNum & 2) ) { - d = plaxis.PermutedInnerProduct( edge->pl ); - edge->bitNum = FLOATSIGNBITSET( d ) | 2; - } - if ( ( edge->bitNum ^ INTSIGNBITSET( edgeNum ) ) & 1 ) { - break; - } - } - if ( j >= poly->numEdges ) { - tw.axisIntersectsTrm = true; - break; - } - } - } - } - - // setup rotation heart plane - tw.heartPlane1.SetNormal( tw.axis ); - tw.heartPlane1.FitThroughPoint( tw.start ); - tw.maxDistFromHeartPlane1 = 0.0f; - for ( i = 0; i < tw.numVerts; i++ ) { - d = idMath::Fabs( tw.heartPlane1.Distance( tw.vertices[i].p ) ); - if ( d > tw.maxDistFromHeartPlane1 ) { - tw.maxDistFromHeartPlane1 = d; - } - } - tw.maxDistFromHeartPlane1 += CM_BOX_EPSILON; - - // inverse rotation to rotate model vertices towards trace model - tw.modelVertexRotation.Set( tw.origin, tw.axis, -tw.angle ); - - // trace through the model - idCollisionModelManagerLocal::TraceThroughModel( &tw ); - - // store results - *results = tw.trace; - results->endpos = start; - if ( tw.maxTan == initialTan ) { - results->fraction = 1.0f; - } else { - results->fraction = idMath::Fabs( atan( tw.maxTan ) * ( 2.0f * 180.0f / idMath::PI ) / tw.angle ); - } - assert( results->fraction <= 1.0f ); - endRotation.Set( rorg, axis, startAngle + (endAngle-startAngle) * results->fraction ); - endRotation.RotatePoint( results->endpos ); - results->endAxis = trmAxis * endRotation.ToMat3(); - - if ( results->fraction < 1.0f ) { - // rotate trace plane normal if there was a collision with a rotated model - if ( model_rotated ) { - results->c.normal *= modelAxis; - results->c.point *= modelAxis; - } - results->c.point += modelOrigin; - results->c.dist += modelOrigin * results->c.normal; - } -} - -/* -================ -idCollisionModelManagerLocal::Rotation -================ -*/ -#ifdef _DEBUG -static int entered = 0; -#endif - -void idCollisionModelManagerLocal::Rotation( trace_t *results, const idVec3 &start, const idRotation &rotation, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { - idVec3 tmp; - float maxa, stepa, a, lasta; - - assert( ((byte *)&start) < ((byte *)results) || ((byte *)&start) > (((byte *)results) + sizeof( trace_t )) ); - assert( ((byte *)&trmAxis) < ((byte *)results) || ((byte *)&trmAxis) > (((byte *)results) + sizeof( trace_t )) ); - - memset( results, 0, sizeof( *results ) ); - - // if special position test - if ( rotation.GetAngle() == 0.0f ) { - idCollisionModelManagerLocal::ContentsTrm( results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); - return; - } - -#ifdef _DEBUG - bool startsolid = false; - // test whether or not stuck to begin with - if ( cm_debugCollision.GetBool() ) { - if ( !entered ) { - entered = 1; - // if already fucked up to begin with - if ( idCollisionModelManagerLocal::Contents( start, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) { - startsolid = true; - } - entered = 0; - } - } -#endif - - if ( rotation.GetAngle() >= 180.0f || rotation.GetAngle() <= -180.0f) { - if ( rotation.GetAngle() >= 360.0f ) { - maxa = 360.0f; - stepa = 120.0f; // three steps strictly < 180 degrees - } else if ( rotation.GetAngle() <= -360.0f ) { - maxa = -360.0f; - stepa = -120.0f; // three steps strictly < 180 degrees - } else { - maxa = rotation.GetAngle(); - stepa = rotation.GetAngle() * 0.5f; // two steps strictly < 180 degrees - } - for ( lasta = 0.0f, a = stepa; fabs( a ) < fabs( maxa ) + 1.0f; lasta = a, a += stepa ) { - // partial rotation - idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), lasta, a, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); - // if there is a collision - if ( results->fraction < 1.0f ) { - // fraction of total rotation - results->fraction = (lasta + stepa * results->fraction) / rotation.GetAngle(); - return; - } - } - results->fraction = 1.0f; - return; - } - - idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), 0.0f, rotation.GetAngle(), start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); - -#ifdef _DEBUG - // test for missed collisions - if ( cm_debugCollision.GetBool() ) { - if ( !entered ) { - entered = 1; - // if the trm is stuck in the model - if ( idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) { - trace_t tr; - - // test where the trm is stuck in the model - idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis ); - // re-run collision detection to find out where it failed - idCollisionModelManagerLocal::Rotation( &tr, start, rotation, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); - } - entered = 0; - } - } -#endif -} diff --git a/src/cm/CollisionModel_trace.cpp b/src/cm/CollisionModel_trace.cpp deleted file mode 100644 index d57caee..0000000 --- a/src/cm/CollisionModel_trace.cpp +++ /dev/null @@ -1,229 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - -/* -=============================================================================== - -Trace through the spatial subdivision - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::TraceTrmThroughNode -================ -*/ -void idCollisionModelManagerLocal::TraceTrmThroughNode( cm_traceWork_t *tw, cm_node_t *node ) { - cm_polygonRef_t *pref; - cm_brushRef_t *bref; - - // position test - if ( tw->positionTest ) { - // if already stuck in solid - if ( tw->trace.fraction == 0.0f ) { - return; - } - // test if any of the trm vertices is inside a brush - for ( bref = node->brushes; bref; bref = bref->next ) { - if ( idCollisionModelManagerLocal::TestTrmVertsInBrush( tw, bref->b ) ) { - return; - } - } - // if just testing a point we're done - if ( tw->pointTrace ) { - return; - } - // test if the trm is stuck in any polygons - for ( pref = node->polygons; pref; pref = pref->next ) { - if ( idCollisionModelManagerLocal::TestTrmInPolygon( tw, pref->p ) ) { - return; - } - } - } - else if ( tw->rotation ) { - // rotate through all polygons in this leaf - for ( pref = node->polygons; pref; pref = pref->next ) { - if ( idCollisionModelManagerLocal::RotateTrmThroughPolygon( tw, pref->p ) ) { - return; - } - } - } - else { - // trace through all polygons in this leaf - for ( pref = node->polygons; pref; pref = pref->next ) { - if ( idCollisionModelManagerLocal::TranslateTrmThroughPolygon( tw, pref->p ) ) { - return; - } - } - } -} - -/* -================ -idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r -================ -*/ -//#define NO_SPATIAL_SUBDIVISION - -void idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( cm_traceWork_t *tw, cm_node_t *node, float p1f, float p2f, idVec3 &p1, idVec3 &p2) { - float t1, t2, offset; - float frac, frac2; - float idist; - idVec3 mid; - int side; - float midf; - - if ( !node ) { - return; - } - - if ( tw->quickExit ) { - return; // stop immediately - } - - if ( tw->trace.fraction <= p1f ) { - return; // already hit something nearer - } - - // if we need to test this node for collisions - if ( node->polygons || (tw->positionTest && node->brushes) ) { - // trace through node with collision data - idCollisionModelManagerLocal::TraceTrmThroughNode( tw, node ); - } - // if already stuck in solid - if ( tw->positionTest && tw->trace.fraction == 0.0f ) { - return; - } - // if this is a leaf node - if ( node->planeType == -1 ) { - return; - } -#ifdef NO_SPATIAL_SUBDIVISION - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[0], p1f, p2f, p1, p2 ); - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[1], p1f, p2f, p1, p2 ); - return; -#endif - // distance from plane for trace start and end - t1 = p1[node->planeType] - node->planeDist; - t2 = p2[node->planeType] - node->planeDist; - // adjust the plane distance appropriately for mins/maxs - offset = tw->extents[node->planeType]; - // see which sides we need to consider - if ( t1 >= offset && t2 >= offset ) { - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[0], p1f, p2f, p1, p2 ); - return; - } - - if ( t1 < -offset && t2 < -offset ) { - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[1], p1f, p2f, p1, p2 ); - return; - } - - if ( t1 < t2 ) { - idist = 1.0f / (t1-t2); - side = 1; - frac2 = (t1 + offset) * idist; - frac = (t1 - offset) * idist; - } else if (t1 > t2) { - idist = 1.0f / (t1-t2); - side = 0; - frac2 = (t1 - offset) * idist; - frac = (t1 + offset) * idist; - } else { - side = 0; - frac = 1.0f; - frac2 = 0.0f; - } - - // move up to the node - if ( frac < 0.0f ) { - frac = 0.0f; - } - else if ( frac > 1.0f ) { - frac = 1.0f; - } - - 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]); - - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[side], p1f, midf, p1, mid ); - - - // go past the node - if ( frac2 < 0.0f ) { - frac2 = 0.0f; - } - else if ( frac2 > 1.0f ) { - frac2 = 1.0f; - } - - 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]); - - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, node->children[side^1], midf, p2f, mid, p2 ); -} - -/* -================ -idCollisionModelManagerLocal::TraceThroughModel -================ -*/ -void idCollisionModelManagerLocal::TraceThroughModel( cm_traceWork_t *tw ) { - float d; - int i, numSteps; - idVec3 start, end; - idRotation rot; - - if ( !tw->rotation ) { - // trace through spatial subdivision and then through leafs - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, tw->model->node, 0, 1, tw->start, tw->end ); - } - else { - // approximate the rotation with a series of straight line movements - // total length covered along circle - d = tw->radius * DEG2RAD( tw->angle ); - // if more than one step - if ( d > CIRCLE_APPROXIMATION_LENGTH ) { - // number of steps for the approximation - numSteps = (int) (CIRCLE_APPROXIMATION_LENGTH / d); - // start of approximation - start = tw->start; - // trace circle approximation steps through the BSP tree - for ( i = 0; i < numSteps; i++ ) { - // calculate next point on approximated circle - rot.Set( tw->origin, tw->axis, tw->angle * ((float) (i+1) / numSteps) ); - end = start * rot; - // trace through spatial subdivision and then through leafs - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, tw->model->node, 0, 1, start, end ); - // no need to continue if something was hit already - if ( tw->trace.fraction < 1.0f ) { - return; - } - start = end; - } - } - else { - start = tw->start; - } - // last step of the approximation - idCollisionModelManagerLocal::TraceThroughAxialBSPTree_r( tw, tw->model->node, 0, 1, start, tw->end ); - } -} diff --git a/src/cm/CollisionModel_translate.cpp b/src/cm/CollisionModel_translate.cpp deleted file mode 100644 index 1c7f838..0000000 --- a/src/cm/CollisionModel_translate.cpp +++ /dev/null @@ -1,1100 +0,0 @@ - -/* -=============================================================================== - - Trace model vs. polygonal model collision detection. - -=============================================================================== -*/ - -#include "../idlib/precompiled.h" -#pragma hdrstop - -#include "CollisionModel_local.h" - -/* -=============================================================================== - -Collision detection for translational motion - -=============================================================================== -*/ - -/* -================ -idCollisionModelManagerLocal::TranslateEdgeThroughEdge - - calculates fraction of the translation completed at which the edges collide -================ -*/ -ID_INLINE int idCollisionModelManagerLocal::TranslateEdgeThroughEdge( idVec3 &cross, idPluecker &l1, idPluecker &l2, float *fraction ) { - - float d, t; - - /* - - a = start of line - b = end of line - dir = movement direction - l1 = pluecker coordinate for line - l2 = pluecker coordinate for edge we might collide with - a+dir = start of line after movement - b+dir = end of line after movement - t = scale factor - solve pluecker inner product for t of line (a+t*dir : b+t*dir) and line l2 - - v[0] = (a[0]+t*dir[0]) * (b[1]+t*dir[1]) - (b[0]+t*dir[0]) * (a[1]+t*dir[1]); - v[1] = (a[0]+t*dir[0]) * (b[2]+t*dir[2]) - (b[0]+t*dir[0]) * (a[2]+t*dir[2]); - v[2] = (a[0]+t*dir[0]) - (b[0]+t*dir[0]); - v[3] = (a[1]+t*dir[1]) * (b[2]+t*dir[2]) - (b[1]+t*dir[1]) * (a[2]+t*dir[2]); - v[4] = (a[2]+t*dir[2]) - (b[2]+t*dir[2]); - v[5] = (b[1]+t*dir[1]) - (a[1]+t*dir[1]); - - l2[0] * v[4] + l2[1] * v[5] + l2[2] * v[3] + l2[4] * v[0] + l2[5] * v[1] + l2[3] * v[2] = 0; - - solve t - - v[0] = (a[0]+t*dir[0]) * (b[1]+t*dir[1]) - (b[0]+t*dir[0]) * (a[1]+t*dir[1]); - v[0] = (a[0]*b[1]) + a[0]*t*dir[1] + b[1]*t*dir[0] + (t*t*dir[0]*dir[1]) - - ((b[0]*a[1]) + b[0]*t*dir[1] + a[1]*t*dir[0] + (t*t*dir[0]*dir[1])); - v[0] = a[0]*b[1] + a[0]*t*dir[1] + b[1]*t*dir[0] - b[0]*a[1] - b[0]*t*dir[1] - a[1]*t*dir[0]; - - v[1] = (a[0]+t*dir[0]) * (b[2]+t*dir[2]) - (b[0]+t*dir[0]) * (a[2]+t*dir[2]); - v[1] = (a[0]*b[2]) + a[0]*t*dir[2] + b[2]*t*dir[0] + (t*t*dir[0]*dir[2]) - - ((b[0]*a[2]) + b[0]*t*dir[2] + a[2]*t*dir[0] + (t*t*dir[0]*dir[2])); - v[1] = a[0]*b[2] + a[0]*t*dir[2] + b[2]*t*dir[0] - b[0]*a[2] - b[0]*t*dir[2] - a[2]*t*dir[0]; - - v[2] = (a[0]+t*dir[0]) - (b[0]+t*dir[0]); - v[2] = a[0] - b[0]; - - v[3] = (a[1]+t*dir[1]) * (b[2]+t*dir[2]) - (b[1]+t*dir[1]) * (a[2]+t*dir[2]); - v[3] = (a[1]*b[2]) + a[1]*t*dir[2] + b[2]*t*dir[1] + (t*t*dir[1]*dir[2]) - - ((b[1]*a[2]) + b[1]*t*dir[2] + a[2]*t*dir[1] + (t*t*dir[1]*dir[2])); - v[3] = a[1]*b[2] + a[1]*t*dir[2] + b[2]*t*dir[1] - b[1]*a[2] - b[1]*t*dir[2] - a[2]*t*dir[1]; - - v[4] = (a[2]+t*dir[2]) - (b[2]+t*dir[2]); - v[4] = a[2] - b[2]; - - v[5] = (b[1]+t*dir[1]) - (a[1]+t*dir[1]); - v[5] = b[1] - a[1]; - - - v[0] = a[0]*b[1] + a[0]*t*dir[1] + b[1]*t*dir[0] - b[0]*a[1] - b[0]*t*dir[1] - a[1]*t*dir[0]; - v[1] = a[0]*b[2] + a[0]*t*dir[2] + b[2]*t*dir[0] - b[0]*a[2] - b[0]*t*dir[2] - a[2]*t*dir[0]; - v[2] = a[0] - b[0]; - v[3] = a[1]*b[2] + a[1]*t*dir[2] + b[2]*t*dir[1] - b[1]*a[2] - b[1]*t*dir[2] - a[2]*t*dir[1]; - v[4] = a[2] - b[2]; - v[5] = b[1] - a[1]; - - v[0] = (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) * t + a[0]*b[1] - b[0]*a[1]; - v[1] = (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) * t + a[0]*b[2] - b[0]*a[2]; - v[2] = a[0] - b[0]; - v[3] = (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1]) * t + a[1]*b[2] - b[1]*a[2]; - v[4] = a[2] - b[2]; - v[5] = b[1] - a[1]; - - l2[4] * (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) * t + l2[4] * (a[0]*b[1] - b[0]*a[1]) - + l2[5] * (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) * t + l2[5] * (a[0]*b[2] - b[0]*a[2]) - + l2[3] * (a[0] - b[0]) - + l2[2] * (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1]) * t + l2[2] * (a[1]*b[2] - b[1]*a[2]) - + l2[0] * (a[2] - b[2]) - + l2[1] * (b[1] - a[1]) = 0 - - t = (- l2[4] * (a[0]*b[1] - b[0]*a[1]) - - l2[5] * (a[0]*b[2] - b[0]*a[2]) - - l2[3] * (a[0] - b[0]) - - l2[2] * (a[1]*b[2] - b[1]*a[2]) - - l2[0] * (a[2] - b[2]) - - l2[1] * (b[1] - a[1])) / - (l2[4] * (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) + - l2[5] * (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) + - l2[2] * (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1])); - - d = l2[4] * (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) + - l2[5] * (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) + - l2[2] * (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1]); - - t = - ( l2[4] * (a[0]*b[1] - b[0]*a[1]) + - l2[5] * (a[0]*b[2] - b[0]*a[2]) + - l2[3] * (a[0] - b[0]) + - l2[2] * (a[1]*b[2] - b[1]*a[2]) + - l2[0] * (a[2] - b[2]) + - l2[1] * (b[1] - a[1])); - t /= d; - - MrE pats Pluecker on the head.. good monkey - - edgeDir = a - b; - d = l2[4] * (edgeDir[0]*dir[1] - edgeDir[1]*dir[0]) + - l2[5] * (edgeDir[0]*dir[2] - edgeDir[2]*dir[0]) + - l2[2] * (edgeDir[1]*dir[2] - edgeDir[2]*dir[1]); - */ - - d = l2[4] * cross[0] + l2[5] * cross[1] + l2[2] * cross[2]; - - if ( d == 0.0f ) { - *fraction = 1.0f; - // no collision ever - return false; - } - - t = -l1.PermutedInnerProduct( l2 ); - // if the lines cross each other to begin with - if ( t == 0.0f ) { - *fraction = 0.0f; - return true; - } - // fraction of movement at the time the lines cross each other - *fraction = t / d; - return true; -} - -/* -================ -CM_AddContact -================ -*/ -ID_INLINE void CM_AddContact( cm_traceWork_t *tw ) { - - if ( tw->numContacts >= tw->maxContacts ) { - return; - } - // copy contact information from trace_t - tw->contacts[tw->numContacts] = tw->trace.c; - tw->numContacts++; - // set fraction back to 1 to find all other contacts - tw->trace.fraction = 1.0f; -} - -/* -================ -CM_SetVertexSidedness - - stores for the given model vertex at which side of one of the trm edges it passes -================ -*/ -ID_INLINE void CM_SetVertexSidedness( cm_vertex_t *v, const idPluecker &vpl, const idPluecker &epl, const int bitNum ) { - if ( !(v->sideSet & (1<side = (v->side & ~(1<sideSet |= (1 << bitNum); - } -} - -/* -================ -CM_SetEdgeSidedness - - stores for the given model edge at which side one of the trm vertices -================ -*/ -ID_INLINE void CM_SetEdgeSidedness( cm_edge_t *edge, const idPluecker &vpl, const idPluecker &epl, const int bitNum ) { - if ( !(edge->sideSet & (1<side = (edge->side & ~(1<sideSet |= (1 << bitNum); - } -} - -/* -================ -idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon -================ -*/ -void idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge ) { - int i, edgeNum; - float f1, f2, dist, d1, d2; - idVec3 start, end, normal; - cm_edge_t *edge; - cm_vertex_t *v1, *v2; - idPluecker *pl, epsPl; - - // check edges for a collision - for ( i = 0; i < poly->numEdges; i++) { - edgeNum = poly->edges[i]; - edge = tw->model->edges + abs(edgeNum); - // if this edge is already checked - if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - // can never collide with internal edges - if ( edge->internal ) { - continue; - } - pl = &tw->polygonEdgePlueckerCache[i]; - // get the sides at which the trm edge vertices pass the polygon edge - CM_SetEdgeSidedness( edge, *pl, tw->vertices[trmEdge->vertexNum[0]].pl, trmEdge->vertexNum[0] ); - CM_SetEdgeSidedness( edge, *pl, tw->vertices[trmEdge->vertexNum[1]].pl, trmEdge->vertexNum[1] ); - // if the trm edge start and end vertex do not pass the polygon edge at different sides - if ( !(((edge->side >> trmEdge->vertexNum[0]) ^ (edge->side >> trmEdge->vertexNum[1])) & 1) ) { - continue; - } - // get the sides at which the polygon edge vertices pass the trm edge - v1 = tw->model->vertices + edge->vertexNum[INTSIGNBITSET(edgeNum)]; - CM_SetVertexSidedness( v1, tw->polygonVertexPlueckerCache[i], trmEdge->pl, trmEdge->bitNum ); - v2 = tw->model->vertices + edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]; - CM_SetVertexSidedness( v2, tw->polygonVertexPlueckerCache[i+1], trmEdge->pl, trmEdge->bitNum ); - // if the polygon edge start and end vertex do not pass the trm edge at different sides - if ( !((v1->side ^ v2->side) & (1<bitNum)) ) { - continue; - } - // if there is no possible collision between the trm edge and the polygon edge - if ( !idCollisionModelManagerLocal::TranslateEdgeThroughEdge( trmEdge->cross, trmEdge->pl, *pl, &f1 ) ) { - continue; - } - // if moving away from edge - if ( f1 < 0.0f ) { - continue; - } - - // pluecker coordinate for epsilon expanded edge - epsPl.FromLine( tw->model->vertices[edge->vertexNum[0]].p + edge->normal * CM_CLIP_EPSILON, - tw->model->vertices[edge->vertexNum[1]].p + edge->normal * CM_CLIP_EPSILON ); - // calculate collision fraction with epsilon expanded edge - if ( !idCollisionModelManagerLocal::TranslateEdgeThroughEdge( trmEdge->cross, trmEdge->pl, epsPl, &f2 ) ) { - continue; - } - // if no collision with epsilon edge or moving away from edge - if ( f2 > 1.0f || f1 < f2 ) { - continue; - } - - if ( f2 < 0.0f ) { - f2 = 0.0f; - } - - if ( f2 < tw->trace.fraction ) { - tw->trace.fraction = f2; - // create plane with normal vector orthogonal to both the polygon edge and the trm edge - start = tw->model->vertices[edge->vertexNum[0]].p; - end = tw->model->vertices[edge->vertexNum[1]].p; - tw->trace.c.normal = ( end - start ).Cross( trmEdge->end - trmEdge->start ); - // FIXME: do this normalize when we know the first collision - tw->trace.c.normal.Normalize(); - tw->trace.c.dist = tw->trace.c.normal * start; - // make sure the collision plane faces the trace model - if ( tw->trace.c.normal * trmEdge->start - tw->trace.c.dist < 0.0f ) { - tw->trace.c.normal = -tw->trace.c.normal; - tw->trace.c.dist = -tw->trace.c.dist; - } - tw->trace.c.contents = poly->contents; - tw->trace.c.material = poly->material; - tw->trace.c.type = CONTACT_EDGE; - tw->trace.c.modelFeature = edgeNum; - tw->trace.c.trmFeature = trmEdge - tw->edges; - // calculate collision point - normal[0] = trmEdge->cross[2]; - normal[1] = -trmEdge->cross[1]; - normal[2] = trmEdge->cross[0]; - dist = normal * trmEdge->start; - d1 = normal * start - dist; - d2 = normal * end - dist; - if (d1 == d2) { //HUMANHEAD rww - CUFPF - f1 = 0.0f; - } - else { - f1 = d1 / ( d1 - d2 ); - } - //assert( f1 >= 0.0f && f1 <= 1.0f ); - tw->trace.c.point = start + f1 * ( end - start ); - // if retrieving contacts - if ( tw->getContacts ) { - CM_AddContact( tw ); - } - } - } -} - -/* -================ -CM_TranslationPlaneFraction -================ -*/ - -#if 0 - -float CM_TranslationPlaneFraction( idPlane &plane, idVec3 &start, idVec3 &end ) { - float d1, d2; - - d2 = plane.Distance( end ); - // if the end point is closer to the plane than an epsilon we still take it for a collision - if ( d2 >= CM_CLIP_EPSILON ) { - return 1.0f; - } - d1 = plane.Distance( start ); - - // if completely behind the polygon - if ( d1 <= 0.0f ) { - return 1.0f; - } - // leaves polygon - if ( d1 <= d2 ) { - return 1.0f; - } - return (d1-CM_CLIP_EPSILON) / (d1-d2); -} - -#else - -float CM_TranslationPlaneFraction( idPlane &plane, idVec3 &start, idVec3 &end ) { - float d1, d2, d2eps; - - d2 = plane.Distance( end ); - // if the end point is closer to the plane than an epsilon we still take it for a collision - // if ( d2 >= CM_CLIP_EPSILON ) { - d2eps = d2 - CM_CLIP_EPSILON; - if ( FLOATSIGNBITNOTSET(d2eps) ) { - return 1.0f; - } - d1 = plane.Distance( start ); - - // if completely behind the polygon - if ( FLOATSIGNBITSET(d1) ) { - return 1.0f; - } - // if going towards the front of the plane and - // the start and end point are not at equal distance from the plane - // if ( d1 > d2 ) - d2 = d1 - d2; - if ( d2 <= 0.0f ) { - return 1.0f; - } - return (d1-CM_CLIP_EPSILON) / d2; -} - -#endif - -/* -================ -idCollisionModelManagerLocal::TranslateTrmVertexThroughPolygon -================ -*/ -void idCollisionModelManagerLocal::TranslateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int bitNum ) { - int i, edgeNum; - float f; - cm_edge_t *edge; - - f = CM_TranslationPlaneFraction( poly->plane, v->p, v->endp ); - if ( f < tw->trace.fraction ) { - - for ( i = 0; i < poly->numEdges; i++ ) { - edgeNum = poly->edges[i]; - edge = tw->model->edges + abs(edgeNum); - CM_SetEdgeSidedness( edge, tw->polygonEdgePlueckerCache[i], v->pl, bitNum ); - if ( INTSIGNBITSET(edgeNum) ^ ((edge->side >> bitNum) & 1) ) { - return; - } - } - if ( f < 0.0f ) { - f = 0.0f; - } - tw->trace.fraction = f; - // collision plane is the polygon plane - tw->trace.c.normal = poly->plane.Normal(); - tw->trace.c.dist = poly->plane.Dist(); - tw->trace.c.contents = poly->contents; - tw->trace.c.material = poly->material; - tw->trace.c.type = CONTACT_TRMVERTEX; - tw->trace.c.modelFeature = *reinterpret_cast(&poly); - tw->trace.c.trmFeature = v - tw->vertices; - tw->trace.c.point = v->p + tw->trace.fraction * ( v->endp - v->p ); - // if retrieving contacts - if ( tw->getContacts ) { - CM_AddContact( tw ); - // no need to store the trm vertex more than once as a contact - v->used = false; - } - } -} - -/* -================ -idCollisionModelManagerLocal::TranslatePointThroughPolygon -================ -*/ -void idCollisionModelManagerLocal::TranslatePointThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v ) { - int i, edgeNum; - float f; - cm_edge_t *edge; - idPluecker pl; - - f = CM_TranslationPlaneFraction( poly->plane, v->p, v->endp ); - if ( f < tw->trace.fraction ) { - - for ( i = 0; i < poly->numEdges; i++ ) { - edgeNum = poly->edges[i]; - edge = tw->model->edges + abs(edgeNum); - // if we didn't yet calculate the sidedness for this edge - if ( edge->checkcount != idCollisionModelManagerLocal::checkCount ) { - float fl; - edge->checkcount = idCollisionModelManagerLocal::checkCount; - pl.FromLine(tw->model->vertices[edge->vertexNum[0]].p, tw->model->vertices[edge->vertexNum[1]].p); - fl = v->pl.PermutedInnerProduct( pl ); - edge->side = FLOATSIGNBITSET(fl); - } - // if the point passes the edge at the wrong side - //if ( (edgeNum > 0) == edge->side ) { - if ( INTSIGNBITSET(edgeNum) ^ edge->side ) { - return; - } - } - if ( f < 0.0f ) { - f = 0.0f; - } - tw->trace.fraction = f; - // collision plane is the polygon plane - tw->trace.c.normal = poly->plane.Normal(); - tw->trace.c.dist = poly->plane.Dist(); - tw->trace.c.contents = poly->contents; - tw->trace.c.material = poly->material; - tw->trace.c.type = CONTACT_TRMVERTEX; - tw->trace.c.modelFeature = *reinterpret_cast(&poly); - tw->trace.c.trmFeature = v - tw->vertices; - tw->trace.c.point = v->p + tw->trace.fraction * ( v->endp - v->p ); - // if retrieving contacts - if ( tw->getContacts ) { - CM_AddContact( tw ); - // no need to store the trm vertex more than once as a contact - v->used = false; - } - } -} - -/* -================ -idCollisionModelManagerLocal::TranslateVertexThroughTrmPolygon -================ -*/ -void idCollisionModelManagerLocal::TranslateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly, cm_polygon_t *poly, cm_vertex_t *v, idVec3 &endp, idPluecker &pl ) { - int i, edgeNum; - float f; - cm_trmEdge_t *edge; - - f = CM_TranslationPlaneFraction( trmpoly->plane, v->p, endp ); - if ( f < tw->trace.fraction ) { - - for ( i = 0; i < trmpoly->numEdges; i++ ) { - edgeNum = trmpoly->edges[i]; - edge = tw->edges + abs(edgeNum); - - CM_SetVertexSidedness( v, pl, edge->pl, edge->bitNum ); - if ( INTSIGNBITSET(edgeNum) ^ ((v->side >> edge->bitNum) & 1) ) { - return; - } - } - if ( f < 0.0f ) { - f = 0.0f; - } - tw->trace.fraction = f; - // collision plane is the inverse trm polygon plane - tw->trace.c.normal = -trmpoly->plane.Normal(); - tw->trace.c.dist = -trmpoly->plane.Dist(); - tw->trace.c.contents = poly->contents; - tw->trace.c.material = poly->material; - tw->trace.c.type = CONTACT_MODELVERTEX; - tw->trace.c.modelFeature = v - tw->model->vertices; - tw->trace.c.trmFeature = trmpoly - tw->polys; - tw->trace.c.point = v->p + tw->trace.fraction * ( endp - v->p ); - // if retrieving contacts - if ( tw->getContacts ) { - CM_AddContact( tw ); - } - } -} - -/* -================ -idCollisionModelManagerLocal::TranslateTrmThroughPolygon - - returns true if the polygon blocks the complete translation -================ -*/ -bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *p ) { - int i, j, k, edgeNum; - float fraction, d; - idVec3 endp; - idPluecker *pl; - cm_trmVertex_t *bv; - cm_trmEdge_t *be; - cm_trmPolygon_t *bp; - cm_vertex_t *v; - cm_edge_t *e; - - // if already checked this polygon - if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) { - return false; - } - p->checkcount = idCollisionModelManagerLocal::checkCount; - - // if this polygon does not have the right contents behind it - if ( !(p->contents & tw->contents) ) { - return false; - } - - // if the the trace bounds do not intersect the polygon bounds - if ( !tw->bounds.IntersectsBounds( p->bounds ) ) { - return false; - } - - // only collide with the polygon if approaching at the front - if ( ( p->plane.Normal() * tw->dir ) > 0.0f ) { - return false; - } - - // if the polygon is too far from the first heart plane - d = p->bounds.PlaneDistance( tw->heartPlane1 ); - if ( idMath::Fabs(d) > tw->maxDistFromHeartPlane1 ) { - return false; - } - - // if the polygon is too far from the second heart plane - d = p->bounds.PlaneDistance( tw->heartPlane2 ); - if ( idMath::Fabs(d) > tw->maxDistFromHeartPlane2 ) { - return false; - } - fraction = tw->trace.fraction; - - // fast point trace - if ( tw->pointTrace ) { - idCollisionModelManagerLocal::TranslatePointThroughPolygon( tw, p, &tw->vertices[0] ); - } - else { - - // trace bounds should cross polygon plane - switch ( tw->bounds.PlaneSide( p->plane ) ) { - case PLANESIDE_CROSS: - break; - case PLANESIDE_FRONT: - if ( tw->model->isConvex ) { - tw->quickExit = true; - return true; - } - default: - return false; - } - - // calculate pluecker coordinates for the polygon edges and polygon vertices - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - e = tw->model->edges + abs(edgeNum); - // reset sidedness cache if this is the first time we encounter this edge during this trace - if ( e->checkcount != idCollisionModelManagerLocal::checkCount ) { - e->sideSet = 0; - } - // pluecker coordinate for edge - tw->polygonEdgePlueckerCache[i].FromLine( tw->model->vertices[e->vertexNum[0]].p, - tw->model->vertices[e->vertexNum[1]].p ); - - v = &tw->model->vertices[e->vertexNum[INTSIGNBITSET(edgeNum)]]; - // reset sidedness cache if this is the first time we encounter this vertex during this trace - if ( v->checkcount != idCollisionModelManagerLocal::checkCount ) { - v->sideSet = 0; - } - // pluecker coordinate for vertex movement vector - tw->polygonVertexPlueckerCache[i].FromRay( v->p, -tw->dir ); - } - // copy first to last so we can easily cycle through for the edges - tw->polygonVertexPlueckerCache[p->numEdges] = tw->polygonVertexPlueckerCache[0]; - - // trace trm vertices through polygon - for ( i = 0; i < tw->numVerts; i++ ) { - bv = tw->vertices + i; - if ( bv->used ) { - idCollisionModelManagerLocal::TranslateTrmVertexThroughPolygon( tw, p, bv, i ); - } - } - - // trace trm edges through polygon - for ( i = 1; i <= tw->numEdges; i++ ) { - be = tw->edges + i; - if ( be->used ) { - idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon( tw, p, be); - } - } - - // trace all polygon vertices through the trm - for ( i = 0; i < p->numEdges; i++ ) { - edgeNum = p->edges[i]; - e = tw->model->edges + abs(edgeNum); - - if ( e->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - // set edge check count - e->checkcount = idCollisionModelManagerLocal::checkCount; - // can never collide with internal edges - if ( e->internal ) { - continue; - } - // got to check both vertices because we skip internal edges - for ( k = 0; k < 2; k++ ) { - - v = tw->model->vertices + e->vertexNum[k ^ INTSIGNBITSET(edgeNum)]; - // if this vertex is already checked - if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) { - continue; - } - // set vertex check count - v->checkcount = idCollisionModelManagerLocal::checkCount; - - // if the vertex is outside the trace bounds - if ( !tw->bounds.ContainsPoint( v->p ) ) { - continue; - } - - // vertex end point after movement - endp = v->p - tw->dir; - // pluecker coordinate for vertex movement vector - pl = &tw->polygonVertexPlueckerCache[i+k]; - - for ( j = 0; j < tw->numPolys; j++ ) { - bp = tw->polys + j; - if ( bp->used ) { - idCollisionModelManagerLocal::TranslateVertexThroughTrmPolygon( tw, bp, p, v, endp, *pl ); - } - } - } - } - } - - // if there was a collision with this polygon and we are not retrieving contacts - if ( tw->trace.fraction < fraction && !tw->getContacts ) { - fraction = tw->trace.fraction; - endp = tw->start + fraction * tw->dir; - // decrease bounds - for ( i = 0; i < 3; i++ ) { - if ( tw->start[i] < endp[i] ) { - tw->bounds[0][i] = tw->start[i] + tw->size[0][i] - CM_BOX_EPSILON; - tw->bounds[1][i] = endp[i] + tw->size[1][i] + CM_BOX_EPSILON; - } - else { - tw->bounds[0][i] = endp[i] + tw->size[0][i] - CM_BOX_EPSILON; - tw->bounds[1][i] = tw->start[i] + tw->size[1][i] + CM_BOX_EPSILON; - } - } - } - - return ( tw->trace.fraction == 0.0f ); -} - -/* -================ -idCollisionModelManagerLocal::SetupTrm -================ -*/ -void idCollisionModelManagerLocal::SetupTrm( cm_traceWork_t *tw, const idTraceModel *trm ) { - int i, j; - - // vertices - tw->numVerts = trm->numVerts; - for ( i = 0; i < trm->numVerts; i++ ) { - tw->vertices[i].p = trm->verts[i]; - tw->vertices[i].used = false; - } - // edges - tw->numEdges = trm->numEdges; - for ( i = 1; i <= trm->numEdges; i++ ) { - tw->edges[i].vertexNum[0] = trm->edges[i].v[0]; - tw->edges[i].vertexNum[1] = trm->edges[i].v[1]; - tw->edges[i].used = false; - } - // polygons - tw->numPolys = trm->numPolys; - for ( i = 0; i < trm->numPolys; i++ ) { - tw->polys[i].numEdges = trm->polys[i].numEdges; - for ( j = 0; j < trm->polys[i].numEdges; j++ ) { - tw->polys[i].edges[j] = trm->polys[i].edges[j]; - } - tw->polys[i].plane.SetNormal( trm->polys[i].normal ); - tw->polys[i].used = false; - } - // is the trace model convex or not - tw->isConvex = trm->isConvex; -} - -/* -================ -idCollisionModelManagerLocal::SetupTranslationHeartPlanes -================ -*/ -void idCollisionModelManagerLocal::SetupTranslationHeartPlanes( cm_traceWork_t *tw ) { - idVec3 dir, normal1, normal2; - - // calculate trace heart planes - dir = tw->dir; - dir.Normalize(); - dir.NormalVectors( normal1, normal2 ); - tw->heartPlane1.SetNormal( normal1 ); - tw->heartPlane1.FitThroughPoint( tw->start ); - tw->heartPlane2.SetNormal( normal2 ); - tw->heartPlane2.FitThroughPoint( tw->start ); -} - -/* -================ -idCollisionModelManagerLocal::Translation -================ -*/ -#ifdef _DEBUG -static int entered = 0; -#endif - -void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &start, const idVec3 &end, - const idTraceModel *trm, const idMat3 &trmAxis, int contentMask, - cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { - - int i, j; - float dist; - bool model_rotated, trm_rotated; - idVec3 dir1, dir2, dir; - idMat3 invModelAxis, tmpAxis; - cm_trmPolygon_t *poly; - cm_trmEdge_t *edge; - cm_trmVertex_t *vert; - ALIGN16( static cm_traceWork_t tw; ) - - assert( ((byte *)&start) < ((byte *)results) || ((byte *)&start) > (((byte *)results) + sizeof( trace_t )) ); - assert( ((byte *)&end) < ((byte *)results) || ((byte *)&end) > (((byte *)results) + sizeof( trace_t )) ); - assert( ((byte *)&trmAxis) < ((byte *)results) || ((byte *)&trmAxis) > (((byte *)results) + sizeof( trace_t )) ); - - memset( results, 0, sizeof( *results ) ); - - if ( model < 0 || model > MAX_SUBMODELS || model > idCollisionModelManagerLocal::maxModels ) { - common->Printf("idCollisionModelManagerLocal::Translation: invalid model handle\n"); - return; - } - if ( !idCollisionModelManagerLocal::models[model] ) { - common->Printf("idCollisionModelManagerLocal::Translation: invalid model\n"); - return; - } - - // if case special position test - if ( start[0] == end[0] && start[1] == end[1] && start[2] == end[2] ) { - idCollisionModelManagerLocal::ContentsTrm( results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); - return; - } - -#ifdef _DEBUG - bool startsolid = false; - // test whether or not stuck to begin with - if ( cm_debugCollision.GetBool() ) { - if ( !entered && !idCollisionModelManagerLocal::getContacts ) { - entered = 1; - // if already fucked up to begin with - if ( idCollisionModelManagerLocal::Contents( start, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) { - startsolid = true; - } - entered = 0; - } - } -#endif - - idCollisionModelManagerLocal::checkCount++; - - tw.trace.fraction = 1.0f; - tw.trace.c.contents = 0; - tw.trace.c.type = CONTACT_NONE; - tw.trace.c.id = 0; // HUMANHEAD pdm: initialize so we don't get bogus values back - tw.contents = contentMask; - tw.isConvex = true; - tw.rotation = false; - tw.positionTest = false; - tw.quickExit = false; - tw.getContacts = idCollisionModelManagerLocal::getContacts; - tw.contacts = idCollisionModelManagerLocal::contacts; - tw.maxContacts = idCollisionModelManagerLocal::maxContacts; - tw.numContacts = 0; - tw.model = idCollisionModelManagerLocal::models[model]; - tw.start = start - modelOrigin; - tw.end = end - modelOrigin; - tw.dir = end - start; - - model_rotated = modelAxis.IsRotated(); - if ( model_rotated ) { - invModelAxis = modelAxis.Transpose(); - } - - // if optimized point trace - if ( !trm || ( trm->bounds[1][0] - trm->bounds[0][0] <= 0.0f && - trm->bounds[1][1] - trm->bounds[0][1] <= 0.0f && - trm->bounds[1][2] - trm->bounds[0][2] <= 0.0f ) ) { - - if ( model_rotated ) { - // rotate trace instead of model - tw.start *= invModelAxis; - tw.end *= invModelAxis; - tw.dir *= invModelAxis; - } - - // trace bounds - for ( i = 0; i < 3; i++ ) { - if ( tw.start[i] < tw.end[i] ) { - tw.bounds[0][i] = tw.start[i] - CM_BOX_EPSILON; - tw.bounds[1][i] = tw.end[i] + CM_BOX_EPSILON; - } - else { - tw.bounds[0][i] = tw.end[i] - CM_BOX_EPSILON; - tw.bounds[1][i] = tw.start[i] + CM_BOX_EPSILON; - } - } - tw.extents[0] = tw.extents[1] = tw.extents[2] = CM_BOX_EPSILON; - tw.size.Zero(); - - // setup trace heart planes - idCollisionModelManagerLocal::SetupTranslationHeartPlanes( &tw ); - tw.maxDistFromHeartPlane1 = CM_BOX_EPSILON; - tw.maxDistFromHeartPlane2 = CM_BOX_EPSILON; - // collision with single point - tw.numVerts = 1; - tw.vertices[0].p = tw.start; - tw.vertices[0].endp = tw.vertices[0].p + tw.dir; - tw.vertices[0].pl.FromRay( tw.vertices[0].p, tw.dir ); - tw.numEdges = tw.numPolys = 0; - tw.pointTrace = true; - // trace through the model - idCollisionModelManagerLocal::TraceThroughModel( &tw ); - // store results - *results = tw.trace; - results->endpos = start + results->fraction * (end - start); - results->endAxis = mat3_identity; - - if ( results->fraction < 1.0f ) { - // rotate trace plane normal if there was a collision with a rotated model - if ( model_rotated ) { - results->c.normal *= modelAxis; - results->c.point *= modelAxis; - } - results->c.point += modelOrigin; - results->c.dist += modelOrigin * results->c.normal; - } - idCollisionModelManagerLocal::numContacts = tw.numContacts; - return; - } - - // the trace fraction is too inaccurate to describe translations over huge distances - if ( tw.dir.LengthSqr() > Square( CM_MAX_TRACE_DIST ) ) { - results->fraction = 0.0f; - results->endpos = start; - results->endAxis = trmAxis; - results->c.normal = vec3_origin; - results->c.material = NULL; - results->c.point = start; - if ( session->rw ) { - session->rw->DebugArrow( colorRed, start, end, 1 ); - } - common->Printf( "idCollisionModelManagerLocal::Translation: huge translation\n" ); - common->Printf( " of entity: %s\n", tw.model->name.c_str() ); // HUMANHEAD - return; - } - - tw.pointTrace = false; - tw.size.Clear(); - - // setup trm structure - idCollisionModelManagerLocal::SetupTrm( &tw, trm ); - - trm_rotated = trmAxis.IsRotated(); - - // calculate vertex positions - if ( trm_rotated ) { - for ( i = 0; i < tw.numVerts; i++ ) { - // rotate trm around the start position - tw.vertices[i].p *= trmAxis; - } - } - for ( i = 0; i < tw.numVerts; i++ ) { - // set trm at start position - tw.vertices[i].p += tw.start; - } - if ( model_rotated ) { - for ( i = 0; i < tw.numVerts; i++ ) { - // rotate trm around model instead of rotating the model - tw.vertices[i].p *= invModelAxis; - } - } - - // add offset to start point - if ( trm_rotated ) { - dir = trm->offset * trmAxis; - tw.start += dir; - tw.end += dir; - } else { - tw.start += trm->offset; - tw.end += trm->offset; - } - if ( model_rotated ) { - // rotate trace instead of model - tw.start *= invModelAxis; - tw.end *= invModelAxis; - tw.dir *= invModelAxis; - } - - // rotate trm polygon planes - if ( trm_rotated & model_rotated ) { - tmpAxis = trmAxis * invModelAxis; - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - poly->plane *= tmpAxis; - } - } else if ( trm_rotated ) { - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - poly->plane *= trmAxis; - } - } else if ( model_rotated ) { - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - poly->plane *= invModelAxis; - } - } - - // setup trm polygons - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - // if the trm poly plane is facing in the movement direction - dist = poly->plane.Normal() * tw.dir; - if ( dist > 0.0f || ( !trm->isConvex && dist == 0.0f ) ) { - // this trm poly and it's edges and vertices need to be used for collision - poly->used = true; - for ( j = 0; j < poly->numEdges; j++ ) { - edge = &tw.edges[abs( poly->edges[j] )]; - edge->used = true; - tw.vertices[edge->vertexNum[0]].used = true; - tw.vertices[edge->vertexNum[1]].used = true; - } - } - } - - // setup trm vertices - for ( vert = tw.vertices, i = 0; i < tw.numVerts; i++, vert++ ) { - if ( !vert->used ) { - continue; - } - // get axial trm size after rotations - tw.size.AddPoint( vert->p - tw.start ); - // calculate the end position of each vertex for a full trace - vert->endp = vert->p + tw.dir; - // pluecker coordinate for vertex movement line - vert->pl.FromRay( vert->p, tw.dir ); - } - - // setup trm edges - for ( edge = tw.edges + 1, i = 1; i <= tw.numEdges; i++, edge++ ) { - if ( !edge->used ) { - continue; - } - // edge start, end and pluecker coordinate - edge->start = tw.vertices[edge->vertexNum[0]].p; - edge->end = tw.vertices[edge->vertexNum[1]].p; - edge->pl.FromLine( edge->start, edge->end ); - // calculate normal of plane through movement plane created by the edge - dir = edge->start - edge->end; - edge->cross[0] = dir[0] * tw.dir[1] - dir[1] * tw.dir[0]; - edge->cross[1] = dir[0] * tw.dir[2] - dir[2] * tw.dir[0]; - edge->cross[2] = dir[1] * tw.dir[2] - dir[2] * tw.dir[1]; - // bit for vertex sidedness bit cache - edge->bitNum = i; - } - - // set trm plane distances - for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) { - if ( poly->used ) { - poly->plane.FitThroughPoint( tw.edges[abs(poly->edges[0])].start ); - } - } - - // bounds for full trace, a little bit larger for epsilons - for ( i = 0; i < 3; i++ ) { - if ( tw.start[i] < tw.end[i] ) { - tw.bounds[0][i] = tw.start[i] + tw.size[0][i] - CM_BOX_EPSILON; - tw.bounds[1][i] = tw.end[i] + tw.size[1][i] + CM_BOX_EPSILON; - } else { - tw.bounds[0][i] = tw.end[i] + tw.size[0][i] - CM_BOX_EPSILON; - tw.bounds[1][i] = tw.start[i] + tw.size[1][i] + CM_BOX_EPSILON; - } - if ( idMath::Fabs( tw.size[0][i] ) > idMath::Fabs( tw.size[1][i] ) ) { - tw.extents[i] = idMath::Fabs( tw.size[0][i] ) + CM_BOX_EPSILON; - } else { - tw.extents[i] = idMath::Fabs( tw.size[1][i] ) + CM_BOX_EPSILON; - } - } - - // setup trace heart planes - idCollisionModelManagerLocal::SetupTranslationHeartPlanes( &tw ); - tw.maxDistFromHeartPlane1 = 0; - tw.maxDistFromHeartPlane2 = 0; - // calculate maximum trm vertex distance from both heart planes - for ( vert = tw.vertices, i = 0; i < tw.numVerts; i++, vert++ ) { - if ( !vert->used ) { - continue; - } - dist = idMath::Fabs( tw.heartPlane1.Distance( vert->p ) ); - if ( dist > tw.maxDistFromHeartPlane1 ) { - tw.maxDistFromHeartPlane1 = dist; - } - dist = idMath::Fabs( tw.heartPlane2.Distance( vert->p ) ); - if ( dist > tw.maxDistFromHeartPlane2 ) { - tw.maxDistFromHeartPlane2 = dist; - } - } - // for epsilons - tw.maxDistFromHeartPlane1 += CM_BOX_EPSILON; - tw.maxDistFromHeartPlane2 += CM_BOX_EPSILON; - - // trace through the model - idCollisionModelManagerLocal::TraceThroughModel( &tw ); - - // if we're getting contacts - if ( tw.getContacts ) { - // move all contacts to world space - if ( model_rotated ) { - for ( i = 0; i < tw.numContacts; i++ ) { - tw.contacts[i].normal *= modelAxis; - tw.contacts[i].point *= modelAxis; - } - } - if ( modelOrigin != vec3_origin ) { - for ( i = 0; i < tw.numContacts; i++ ) { - tw.contacts[i].point += modelOrigin; - tw.contacts[i].dist += modelOrigin * tw.contacts[i].normal; - } - } - idCollisionModelManagerLocal::numContacts = tw.numContacts; - } else { - // store results - *results = tw.trace; - results->endpos = start + results->fraction * ( end - start ); - results->endAxis = trmAxis; - - if ( results->fraction < 1.0f ) { - // if the fraction is tiny the actual movement could end up zero - if ( results->fraction > 0.0f && results->endpos.Compare( start ) ) { - results->fraction = 0.0f; - } - // rotate trace plane normal if there was a collision with a rotated model - if ( model_rotated ) { - results->c.normal *= modelAxis; - results->c.point *= modelAxis; - } - results->c.point += modelOrigin; - results->c.dist += modelOrigin * results->c.normal; - } - } - -#ifdef _DEBUG - // test for missed collisions - if ( cm_debugCollision.GetBool() ) { - if ( !entered && !idCollisionModelManagerLocal::getContacts ) { - entered = 1; - // if the trm is stuck in the model - if ( idCollisionModelManagerLocal::Contents( results->endpos, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) { - trace_t tr; - - // test where the trm is stuck in the model - idCollisionModelManagerLocal::Contents( results->endpos, trm, trmAxis, -1, model, modelOrigin, modelAxis ); - // re-run collision detection to find out where it failed - idCollisionModelManagerLocal::Translation( &tr, start, end, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); - } - entered = 0; - } - } -#endif -}