/* ============ idTraceModel::Shrink ============ */ void idTraceModel::Shrink( const float m ) { int i, j, edgeNum; traceModelEdge_t *edge; idVec3 dir; 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; } for ( i = 0; i < numPolys; i++ ) { polys[i].dist -= m; for ( j = 0; j < polys[i].numEdges; j++ ) { edgeNum = polys[i].edges[j]; edge = &edges[abs(edgeNum)]; verts[ edge->v[ INTSIGNBITSET(edgeNum) ] ] -= polys[i].normal * m; } } }
/* ================= UpdateVertexIndex ================= */ ID_INLINE int UpdateVertexIndex( int vertexIndexNum[2], int *vertexRemap, int *vertexCopyIndex, int vertNum ) { int s = INTSIGNBITSET( vertexRemap[vertNum] ); vertexIndexNum[0] = vertexRemap[vertNum]; vertexRemap[vertNum] = vertexIndexNum[s]; vertexIndexNum[1] += s; vertexCopyIndex[vertexRemap[vertNum]] = vertNum; return vertexRemap[vertNum]; }
/* ============ 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::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; }
/* ================ 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 ); } } }
/* ============ idTraceModel::Rotate ============ */ void idTraceModel::Rotate( const idMat3 &rotation ) { int i, j, edgeNum; for ( i = 0; i < numVerts; i++ ) { verts[i] *= rotation; } bounds.Clear(); for ( i = 0; i < numPolys; i++ ) { polys[i].normal *= rotation; polys[i].bounds.Clear(); edgeNum = 0; for ( j = 0; j < polys[i].numEdges; j++ ) { edgeNum = polys[i].edges[j]; polys[i].bounds.AddPoint( verts[edges[abs(edgeNum)].v[INTSIGNBITSET(edgeNum)]] ); } polys[i].dist = polys[i].normal * verts[edges[abs(edgeNum)].v[INTSIGNBITSET(edgeNum)]]; bounds += polys[i].bounds; } GenerateEdgeNormals(); }
/* ================ idAASFileLocal::FaceBounds ================ */ idBounds idAASFileLocal::FaceBounds( int faceNum ) const { int i, edgeNum; const aasFace_t *face; const aasEdge_t *edge; idBounds bounds; face = &faces[faceNum]; bounds.Clear(); for ( i = 0; i < face->numEdges; i++ ) { edgeNum = edgeIndex[ face->firstEdge + i ]; edge = &edges[ abs( edgeNum ) ]; bounds.AddPoint( vertices[ edge->vertexNum[ INTSIGNBITSET(edgeNum) ] ] ); } return bounds; }
/* ============ 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(); }
/* ================ idAASFileLocal::FaceCenter ================ */ idVec3 idAASFileLocal::FaceCenter( int faceNum ) const { int i, edgeNum; const aasFace_t *face; const aasEdge_t *edge; idVec3 center; center = vec3_origin; face = &faces[faceNum]; if ( face->numEdges > 0 ) { for ( i = 0; i < face->numEdges; i++ ) { edgeNum = edgeIndex[ face->firstEdge + i ]; edge = &edges[ abs( edgeNum ) ]; center += vertices[ edge->vertexNum[ INTSIGNBITSET(edgeNum) ] ]; } center /= face->numEdges; } return center; }
/* ============ idAASFileLocal::PushPointIntoAreaNum ============ */ void idAASFileLocal::PushPointIntoAreaNum( int areaNum, idVec3 &point ) const { int i, faceNum; const aasArea_t *area; const aasFace_t *face; area = &areas[areaNum]; // push the point to the right side of all area face planes for ( i = 0; i < area->numFaces; i++ ) { faceNum = faceIndex[area->firstFace + i]; face = &faces[abs( faceNum )]; const idPlane &plane = planeList[face->planeNum ^ INTSIGNBITSET( faceNum )]; float dist = plane.Distance( point ); // project the point onto the face plane if it is on the wrong side if ( dist < 0.0f ) { point -= dist * plane.Normal(); } } }
/* ============ idTraceModel::GetProjectionSilhouetteEdges ============ */ int idTraceModel::GetProjectionSilhouetteEdges( const idVec3 &projectionOrigin, int silEdges[MAX_TRACEMODEL_EDGES] ) const { int i, j, edgeNum; int edgeIsSilEdge[MAX_TRACEMODEL_EDGES+1]; const traceModelPoly_t *poly; idVec3 dir; memset( edgeIsSilEdge, 0, sizeof( edgeIsSilEdge ) ); for ( i = 0; i < numPolys; i++ ) { poly = &polys[i]; edgeNum = poly->edges[0]; dir = verts[ edges[abs(edgeNum)].v[ INTSIGNBITSET(edgeNum) ] ] - projectionOrigin; if ( dir * poly->normal < 0.0f ) { for ( j = 0; j < poly->numEdges; j++ ) { edgeNum = poly->edges[j]; edgeIsSilEdge[abs(edgeNum)] ^= 1; } } } return GetOrderedSilhouetteEdges( edgeIsSilEdge, silEdges ); }
/* ============ idTraceModel::Verify ============ */ bool idTraceModel::Verify( void ) { int i, j, edgeNum, vertexNum; traceModelPoly_t *poly; traceModelEdge_t *edge; // test whether or not the vertices are on the polygon planes for ( i = 0; i < numPolys; i++ ) { poly = &polys[i]; for ( j = 0; j < polys[i].numEdges; j++ ) { edgeNum = poly->edges[j]; edge = &edges[abs(edgeNum)]; vertexNum = edge->v[ INTSIGNBITSET( edgeNum ) ]; float d = poly->normal * verts[vertexNum] - poly->dist; if ( fabs( d ) > 1e-4f ) { return false; } } } 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; } } }
/* ============ 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(); }
/* ================ 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; } }
/* ================ idAASReach::Reachability_WalkOffLedge ================ */ void idAASReach::Reachability_WalkOffLedge( int areaNum ) { int i, j, faceNum, edgeNum, side, reachAreaNum, p, areas[10]; aasArea_t *area; aasFace_t *face; aasEdge_t *edge; idPlane *plane; idVec3 v1, v2, mid, dir, testEnd; idReachability_WalkOffLedge *reach; aasTrace_t trace; if ( !AreaHasFloor( areaNum ) || 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)]; // face must be a floor face if ( !(face->flags & FACE_FLOOR) ) { continue; } for ( j = 0; j < face->numEdges; j++ ) { edgeNum = file->edgeIndex[face->firstEdge + j]; edge = &file->edges[abs(edgeNum)]; //if ( !(edge->flags & EDGE_LEDGE) ) { // continue; //} side = edgeNum < 0; v1 = file->vertices[edge->vertexNum[side]]; v2 = file->vertices[edge->vertexNum[!side]]; plane = &file->planeList[face->planeNum ^ INTSIGNBITSET(faceNum) ]; // get the direction into the other area dir = plane->Normal().Cross( v2 - v1 ); dir.Normalize(); mid = ( v1 + v2 ) * 0.5f; testEnd = mid + INSIDEUNITS_WALKEND * dir; testEnd[2] -= file->settings.maxFallHeight + 1.0f; trace.areas = areas; trace.maxAreas = sizeof(areas) / sizeof(int); file->Trace( trace, mid, testEnd ); reachAreaNum = trace.lastAreaNum; if ( !reachAreaNum || reachAreaNum == areaNum ) { continue; } if ( idMath::Fabs( mid[2] - trace.endpos[2] ) > file->settings.maxFallHeight ) { continue; } if ( !AreaHasFloor( reachAreaNum ) && !CanSwimInArea( reachAreaNum ) ) { continue; } if ( ReachabilityExists( areaNum, reachAreaNum) ) { continue; } // if not going through a cluster portal for ( p = 0; p < trace.numAreas; p++ ) { if ( AreaIsClusterPortal( trace.areas[p] ) ) { break; } } if ( p < trace.numAreas ) { continue; } reach = new idReachability_WalkOffLedge(); reach->travelType = TFL_WALKOFFLEDGE; reach->toAreaNum = reachAreaNum; reach->fromAreaNum = areaNum; reach->start = mid; reach->end = trace.endpos; reach->edgeNum = abs( edgeNum ); reach->travelTime = file->settings.tt_startWalkOffLedge + idMath::Fabs(mid[2] - trace.endpos[2]) * 50 / file->settings.gravityValue; AddReachabilityToArea( reach, areaNum ); } } }
/* ==================== 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; }
/* ============ 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(); }
/* ================ 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 ); }