/* ============ idAASLocal::GetEdgeVertexNumbers ============ */ void idAASLocal::GetEdgeVertexNumbers( int edgeNum, int verts[2] ) const { if ( !file ) { verts[0] = verts[1] = 0; return; } const int *v = file->GetEdge( abs(edgeNum) ).vertexNum; verts[0] = v[INTSIGNBITSET(edgeNum)]; verts[1] = v[INTSIGNBITNOTSET(edgeNum)]; }
/* ============ idAASLocal::GetEdge ============ */ void idAASLocal::GetEdge( int edgeNum, idVec3 &start, idVec3 &end ) const { if ( !file ) { start.Zero(); end.Zero(); return; } const int *v = file->GetEdge( abs(edgeNum) ).vertexNum; start = file->GetVertex( v[INTSIGNBITSET(edgeNum)] ); end = file->GetVertex( v[INTSIGNBITNOTSET(edgeNum)] ); }
/* ============ idTraceModel::SetupOctahedron ============ */ void idTraceModel::SetupOctahedron( const idBounds &octBounds ) { int i, e0, e1, v0, v1, v2; idVec3 v; if ( type != TRM_OCTAHEDRON ) { InitOctahedron(); } offset = ( octBounds[0] + octBounds[1] ) * 0.5f; v[0] = octBounds[1][0] - offset[0]; v[1] = octBounds[1][1] - offset[1]; v[2] = octBounds[1][2] - offset[2]; // set vertices verts[0].Set( offset.x + v[0], offset.y, offset.z ); verts[1].Set( offset.x - v[0], offset.y, offset.z ); verts[2].Set( offset.x, offset.y + v[1], offset.z ); verts[3].Set( offset.x, offset.y - v[1], offset.z ); verts[4].Set( offset.x, offset.y, offset.z + v[2] ); verts[5].Set( offset.x, offset.y, offset.z - v[2] ); // set polygons for ( i = 0; i < numPolys; i++ ) { e0 = polys[i].edges[0]; e1 = polys[i].edges[1]; v0 = edges[abs(e0)].v[INTSIGNBITSET(e0)]; v1 = edges[abs(e0)].v[INTSIGNBITNOTSET(e0)]; v2 = edges[abs(e1)].v[INTSIGNBITNOTSET(e1)]; // polygon plane polys[i].normal = ( verts[v1] - verts[v0] ).Cross( verts[v2] - verts[v0] ); polys[i].normal.Normalize(); polys[i].dist = polys[i].normal * verts[v0]; // polygon bounds polys[i].bounds[0] = polys[i].bounds[1] = verts[v0]; polys[i].bounds.AddPoint( verts[v1] ); polys[i].bounds.AddPoint( verts[v2] ); } // trm bounds bounds = octBounds; GenerateEdgeNormals(); }
/* ================ idStr::Cmp ================ */ int idStr::Cmp( const char *s1, const char *s2 ) { int c1, c2, d; do { c1 = *s1++; c2 = *s2++; d = c1 - c2; if ( d ) { return ( INTSIGNBITNOTSET( d ) << 1 ) - 1; } } while( c1 ); return 0; // strings are equal }
/* ================ idAASReach::Reachability_Swim ================ */ void idAASReach::Reachability_Swim( int areaNum ) { int i, faceNum, otherAreaNum; aasArea_t *area; aasFace_t *face; idReachability_Swim *reach; if ( !CanSwimInArea( areaNum ) ) { return; } area = &file->areas[areaNum]; for ( i = 0; i < area->numFaces; i++ ) { faceNum = file->faceIndex[area->firstFace + i]; face = &file->faces[abs(faceNum)]; otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)]; if ( otherAreaNum == 0 ) { continue; } if ( !CanSwimInArea( otherAreaNum ) ) { continue; } if ( ReachabilityExists( areaNum, otherAreaNum ) ) { continue; } // create reachability going through this face reach = new idReachability_Swim(); reach->travelType = TFL_SWIM; reach->toAreaNum = otherAreaNum; reach->fromAreaNum = areaNum; reach->edgeNum = 0; reach->travelTime = 1; reach->start = file->FaceCenter( abs(faceNum) ); if ( faceNum < 0 ) { reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND; } else { reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_SWIMEND; } AddReachabilityToArea( reach, areaNum ); } }
/* ============ idTraceModel::GetPolygonArea ============ */ float idTraceModel::GetPolygonArea( int polyNum ) const { int i; idVec3 base, v1, v2, cross; float total; const traceModelPoly_t *poly; if ( polyNum < 0 || polyNum >= numPolys ) { return 0.0f; } poly = &polys[polyNum]; total = 0.0f; base = verts[ edges[ abs(poly->edges[0]) ].v[ INTSIGNBITSET( poly->edges[0] ) ] ]; for ( i = 0; i < poly->numEdges; i++ ) { v1 = verts[ edges[ abs(poly->edges[i]) ].v[ INTSIGNBITSET( poly->edges[i] ) ] ] - base; v2 = verts[ edges[ abs(poly->edges[i]) ].v[ INTSIGNBITNOTSET( poly->edges[i] ) ] ] - base; cross = v1.Cross( v2 ); total += cross.Length(); } return total * 0.5f; }
/* ================ idStr::Cmpn ================ */ int idStr::Cmpn( const char *s1, const char *s2, int n ) { int c1, c2, d; assert( n >= 0 ); do { c1 = *s1++; c2 = *s2++; if ( !n-- ) { return 0; // strings are equal until end point } d = c1 - c2; if ( d ) { return ( INTSIGNBITNOTSET( d ) << 1 ) - 1; } } while( c1 ); return 0; // strings are equal }
/* ================ idStr::IcmpnPath ================ */ int idStr::IcmpnPath( const char *s1, const char *s2, int n ) { int c1, c2, d; #if 0 //#if !defined( _WIN32 ) idLib::common->Printf( "WARNING: IcmpPath used on a case-sensitive filesystem?\n" ); #endif assert( n >= 0 ); do { c1 = *s1++; c2 = *s2++; if ( !n-- ) { return 0; // strings are equal until end point } d = c1 - c2; while( d ) { if ( c1 <= 'Z' && c1 >= 'A' ) { d += ('a' - 'A'); if ( !d ) { break; } } if ( c1 == '\\' ) { d += ('/' - '\\'); if ( !d ) { break; } } if ( c2 <= 'Z' && c2 >= 'A' ) { d -= ('a' - 'A'); if ( !d ) { break; } } if ( c2 == '\\' ) { d -= ('/' - '\\'); if ( !d ) { break; } } // make sure folders come first while( c1 ) { if ( c1 == '/' || c1 == '\\' ) { break; } c1 = *s1++; } while( c2 ) { if ( c2 == '/' || c2 == '\\' ) { break; } c2 = *s2++; } if ( c1 && !c2 ) { return -1; } else if ( !c1 && c2 ) { return 1; } // same folder depth so use the regular compare return ( INTSIGNBITNOTSET( d ) << 1 ) - 1; } } while( c1 ); return 0; }
/* ================ idAASReach::Reachability_EqualFloorHeight ================ */ void idAASReach::Reachability_EqualFloorHeight( int areaNum ) { int i, k, l, m, n, faceNum, face1Num, face2Num, otherAreaNum, edge1Num, edge2Num; aasArea_t *area, *otherArea; aasFace_t *face, *face1, *face2; idReachability_Walk *reach; if ( !AreaHasFloor( areaNum ) ) { return; } area = &file->areas[areaNum]; for ( i = 0; i < area->numFaces; i++ ) { faceNum = file->faceIndex[area->firstFace + i]; face = &file->faces[abs(faceNum)]; otherAreaNum = face->areas[INTSIGNBITNOTSET(faceNum)]; if ( !AreaHasFloor( otherAreaNum ) ) { continue; } otherArea = &file->areas[otherAreaNum]; for ( k = 0; k < area->numFaces; k++ ) { face1Num = file->faceIndex[area->firstFace + k]; face1 = &file->faces[abs(face1Num)]; if ( !( face1->flags & FACE_FLOOR ) ) { continue; } for ( l = 0; l < otherArea->numFaces; l++ ) { face2Num = file->faceIndex[otherArea->firstFace + l]; face2 = &file->faces[abs(face2Num)]; if ( !( face2->flags & FACE_FLOOR ) ) { continue; } for ( m = 0; m < face1->numEdges; m++ ) { edge1Num = abs(file->edgeIndex[face1->firstEdge + m]); for ( n = 0; n < face2->numEdges; n++ ) { edge2Num = abs(file->edgeIndex[face2->firstEdge + n]); if ( edge1Num == edge2Num ) { break; } } if ( n < face2->numEdges ) { break; } } if ( m < face1->numEdges ) { break; } } if ( l < otherArea->numFaces ) { break; } } if ( k < area->numFaces ) { // create reachability reach = new idReachability_Walk(); reach->travelType = TFL_WALK; reach->toAreaNum = otherAreaNum; reach->fromAreaNum = areaNum; reach->edgeNum = abs( edge1Num ); reach->travelTime = 1; reach->start = file->EdgeCenter( edge1Num ); if ( faceNum < 0 ) { reach->end = reach->start + file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND; } else { reach->end = reach->start - file->planeList[face->planeNum].Normal() * INSIDEUNITS_WALKEND; } AddReachabilityToArea( reach, areaNum ); } } }
/* ============ idTraceModel::SetupDodecahedron ============ */ void idTraceModel::SetupDodecahedron( const idBounds &dodBounds ) { int i, e0, e1, e2, e3, v0, v1, v2, v3, v4; float s, d; idVec3 a, b, c; if ( type != TRM_DODECAHEDRON ) { InitDodecahedron(); } a[0] = a[1] = a[2] = 0.5773502691896257f; // 1.0f / ( 3.0f ) ^ 0.5f; b[0] = b[1] = b[2] = 0.3568220897730899f; // ( ( 3.0f - ( 5.0f ) ^ 0.5f ) / 6.0f ) ^ 0.5f; c[0] = c[1] = c[2] = 0.9341723589627156f; // ( ( 3.0f + ( 5.0f ) ^ 0.5f ) / 6.0f ) ^ 0.5f; d = 0.5f / c[0]; s = ( dodBounds[1][0] - dodBounds[0][0] ) * d; a[0] *= s; b[0] *= s; c[0] *= s; s = ( dodBounds[1][1] - dodBounds[0][1] ) * d; a[1] *= s; b[1] *= s; c[1] *= s; s = ( dodBounds[1][2] - dodBounds[0][2] ) * d; a[2] *= s; b[2] *= s; c[2] *= s; offset = ( dodBounds[0] + dodBounds[1] ) * 0.5f; // set vertices verts[ 0].Set( offset.x + a[0], offset.y + a[1], offset.z + a[2] ); verts[ 1].Set( offset.x + a[0], offset.y + a[1], offset.z - a[2] ); verts[ 2].Set( offset.x + a[0], offset.y - a[1], offset.z + a[2] ); verts[ 3].Set( offset.x + a[0], offset.y - a[1], offset.z - a[2] ); verts[ 4].Set( offset.x - a[0], offset.y + a[1], offset.z + a[2] ); verts[ 5].Set( offset.x - a[0], offset.y + a[1], offset.z - a[2] ); verts[ 6].Set( offset.x - a[0], offset.y - a[1], offset.z + a[2] ); verts[ 7].Set( offset.x - a[0], offset.y - a[1], offset.z - a[2] ); verts[ 8].Set( offset.x + b[0], offset.y + c[1], offset.z ); verts[ 9].Set( offset.x - b[0], offset.y + c[1], offset.z ); verts[10].Set( offset.x + b[0], offset.y - c[1], offset.z ); verts[11].Set( offset.x - b[0], offset.y - c[1], offset.z ); verts[12].Set( offset.x + c[0], offset.y , offset.z + b[2] ); verts[13].Set( offset.x + c[0], offset.y , offset.z - b[2] ); verts[14].Set( offset.x - c[0], offset.y , offset.z + b[2] ); verts[15].Set( offset.x - c[0], offset.y , offset.z - b[2] ); verts[16].Set( offset.x , offset.y + b[1], offset.z + c[2] ); verts[17].Set( offset.x , offset.y - b[1], offset.z + c[2] ); verts[18].Set( offset.x , offset.y + b[1], offset.z - c[2] ); verts[19].Set( offset.x , offset.y - b[1], offset.z - c[2] ); // set polygons for ( i = 0; i < numPolys; i++ ) { e0 = polys[i].edges[0]; e1 = polys[i].edges[1]; e2 = polys[i].edges[2]; e3 = polys[i].edges[3]; v0 = edges[abs(e0)].v[INTSIGNBITSET(e0)]; v1 = edges[abs(e0)].v[INTSIGNBITNOTSET(e0)]; v2 = edges[abs(e1)].v[INTSIGNBITNOTSET(e1)]; v3 = edges[abs(e2)].v[INTSIGNBITNOTSET(e2)]; v4 = edges[abs(e3)].v[INTSIGNBITNOTSET(e3)]; // polygon plane polys[i].normal = ( verts[v1] - verts[v0] ).Cross( verts[v2] - verts[v0] ); polys[i].normal.Normalize(); polys[i].dist = polys[i].normal * verts[v0]; // polygon bounds polys[i].bounds[0] = polys[i].bounds[1] = verts[v0]; polys[i].bounds.AddPoint( verts[v1] ); polys[i].bounds.AddPoint( verts[v2] ); polys[i].bounds.AddPoint( verts[v3] ); polys[i].bounds.AddPoint( verts[v4] ); } // trm bounds bounds = dodBounds; GenerateEdgeNormals(); }
/* ============ idTraceModel::Shrink ============ */ void idTraceModel::Shrink( const float m ) { int i, j, edgeNum, vertexNum; float d, bestd, n, invDet, f0, f1; traceModelPoly_t *poly, *poly1, *poly2; traceModelEdge_t *edge; idVec3 start, dir; int vertexPolys[MAX_TRACEMODEL_VERTS][MAX_TRACEMODEL_POLYS]; int vertexNumPolys[MAX_TRACEMODEL_VERTS]; // special case for single polygon if ( type == TRM_POLYGON ) { for ( i = 0; i < numEdges; i++ ) { edgeNum = polys[0].edges[i]; edge = &edges[abs(edgeNum)]; dir = verts[ edge->v[ INTSIGNBITSET(edgeNum) ] ] - verts[ edge->v[ INTSIGNBITNOTSET(edgeNum) ] ]; if ( dir.Normalize() < 2.0f * m ) { continue; } dir *= m; verts[ edge->v[ 0 ] ] -= dir; verts[ edge->v[ 1 ] ] += dir; } return; } // the trace model should be a closed surface assert( IsClosedSurface() ); memset( vertexPolys, 0, sizeof( vertexPolys ) ); memset( vertexNumPolys, 0, sizeof( vertexNumPolys ) ); // move polygon planes and find the vertex polygons for ( i = 0; i < numPolys; i++ ) { poly = &polys[i]; poly->dist -= m; for ( j = 0; j < poly->numEdges; j++ ) { edgeNum = poly->edges[j]; edge = &edges[abs(edgeNum)]; vertexNum = edge->v[ INTSIGNBITSET( edgeNum ) ]; vertexPolys[vertexNum][vertexNumPolys[vertexNum]] = i; vertexNumPolys[vertexNum]++; } } // move vertices for ( i = 0; i < numVerts; i++ ) { assert( vertexNumPolys[i] >= 3 ); poly1 = &polys[vertexPolys[i][0]]; poly2 = NULL; // find the polygon that is most orthogonal to the first polygon bestd = 1.0f; for ( j = 1; j < vertexNumPolys[i]; j++ ) { d = fabs( poly1->normal * polys[vertexPolys[i][j]].normal ); if ( d < bestd ) { bestd = d; poly2 = &polys[vertexPolys[i][j]]; } } // calculate intersection line between planes n = poly1->normal * poly2->normal; invDet = 1.0f / ( 1.0f - n * n ); f0 = ( poly1->dist - n * poly2->dist ) * invDet; f1 = ( poly2->dist - n * poly1->dist ) * invDet; start = f0 * poly1->normal + f1 * poly2->normal; dir = poly1->normal.Cross( poly2->normal ); // find the polygon that is most orthogonal to the plane intersection ray bestd = 0.0f; for ( j = 1; j < vertexNumPolys[i]; j++ ) { d = fabs( dir * polys[vertexPolys[i][j]].normal ); if ( d > bestd ) { bestd = d; poly2 = &polys[vertexPolys[i][j]]; } } // calculate intersection with plane intersection ray f0 = poly2->normal * start - poly2->dist; f1 = poly2->normal * dir; verts[i] = start - dir * ( f0 / f1 ); } Verify(); }
/* ================ idAASBuild::GetFaceForPortal ================ */ bool idAASBuild::GetFaceForPortal( idBrushBSPPortal *portal, int side, int *faceNum ) { int i, j, v1num; int numFaceEdges, faceEdges[MAX_POINTS_ON_WINDING]; idWinding *w; aasFace_t face; if ( portal->GetFaceNum() > 0 ) { if ( side ) { *faceNum = -portal->GetFaceNum(); } else { *faceNum = portal->GetFaceNum(); } return true; } w = portal->GetWinding(); // turn the winding into a sequence of edges numFaceEdges = 0; v1num = -1; // first vertex unknown for ( i = 0; i < w->GetNumPoints(); i++ ) { GetEdge( (*w)[i].ToVec3(), (*w)[(i+1)%w->GetNumPoints()].ToVec3(), &faceEdges[numFaceEdges], v1num ); if ( faceEdges[numFaceEdges] ) { // last vertex of this edge is the first vertex of the next edge v1num = file->edges[ abs(faceEdges[numFaceEdges]) ].vertexNum[ INTSIGNBITNOTSET(faceEdges[numFaceEdges]) ]; // this edge is valid so keep it numFaceEdges++; } } // should have at least 3 edges if ( numFaceEdges < 3 ) { return false; } // the polygon is invalid if some edge is found twice for ( i = 0; i < numFaceEdges; i++ ) { for ( j = i+1; j < numFaceEdges; j++ ) { if ( faceEdges[i] == faceEdges[j] || faceEdges[i] == -faceEdges[j] ) { return false; } } } portal->SetFaceNum( file->faces.Num() ); face.planeNum = file->planeList.FindPlane( portal->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON ); face.flags = portal->GetFlags(); face.areas[0] = face.areas[1] = 0; face.firstEdge = file->edgeIndex.Num(); face.numEdges = numFaceEdges; for ( i = 0; i < numFaceEdges; i++ ) { file->edgeIndex.Append( faceEdges[i] ); } if ( side ) { *faceNum = -file->faces.Num(); } else { *faceNum = file->faces.Num(); } file->faces.Append( face ); return true; }
/* ==================== idSurface_Polytope::SplitPolytope ==================== */ int idSurface_Polytope::SplitPolytope( const idPlane &plane, const float epsilon, idSurface_Polytope **front, idSurface_Polytope **back ) const { int side, i, j, s, v0, v1, v2, edgeNum; idSurface *surface[2]; idSurface_Polytope *polytopeSurfaces[2], *surf; int *onPlaneEdges[2]; onPlaneEdges[0] = (int *) _alloca( indexes.Num() / 3 * sizeof( int ) ); onPlaneEdges[1] = (int *) _alloca( indexes.Num() / 3 * sizeof( int ) ); side = Split( plane, epsilon, &surface[0], &surface[1], onPlaneEdges[0], onPlaneEdges[1] ); *front = polytopeSurfaces[0] = new idSurface_Polytope; *back = polytopeSurfaces[1] = new idSurface_Polytope; for ( s = 0; s < 2; s++ ) { if ( surface[s] ) { polytopeSurfaces[s] = new idSurface_Polytope; polytopeSurfaces[s]->SwapTriangles( *surface[s] ); delete surface[s]; surface[s] = NULL; } } *front = polytopeSurfaces[0]; *back = polytopeSurfaces[1]; if ( side != SIDE_CROSS ) { return side; } // add triangles to close off the front and back polytope for ( s = 0; s < 2; s++ ) { surf = polytopeSurfaces[s]; edgeNum = surf->edgeIndexes[onPlaneEdges[s][0]]; v0 = surf->edges[abs(edgeNum)].verts[INTSIGNBITSET(edgeNum)]; v1 = surf->edges[abs(edgeNum)].verts[INTSIGNBITNOTSET(edgeNum)]; for ( i = 1; onPlaneEdges[s][i] >= 0; i++ ) { for ( j = i+1; onPlaneEdges[s][j] >= 0; j++ ) { edgeNum = surf->edgeIndexes[onPlaneEdges[s][j]]; if ( v1 == surf->edges[abs(edgeNum)].verts[INTSIGNBITSET(edgeNum)] ) { v1 = surf->edges[abs(edgeNum)].verts[INTSIGNBITNOTSET(edgeNum)]; idSwap( onPlaneEdges[s][i], onPlaneEdges[s][j] ); break; } } } for ( i = 2; onPlaneEdges[s][i] >= 0; i++ ) { edgeNum = surf->edgeIndexes[onPlaneEdges[s][i]]; v1 = surf->edges[abs(edgeNum)].verts[INTSIGNBITNOTSET(edgeNum)]; v2 = surf->edges[abs(edgeNum)].verts[INTSIGNBITSET(edgeNum)]; surf->indexes.Append( v0 ); surf->indexes.Append( v1 ); surf->indexes.Append( v2 ); } surf->GenerateEdgeIndexes(); } return side; }
/* ================ 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; } } }