示例#1
0
/*
============
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)];
}
示例#2
0
文件: AAS.cpp 项目: Kaan88/doom3.gpl
/*
============
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)] );
}
示例#3
0
/*
============
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();
}
示例#4
0
/*
================
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
}
示例#5
0
/*
================
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 );
	}
}
示例#6
0
/*
============
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;
}
示例#7
0
/*
================
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
}
示例#8
0
/*
================
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;
}
示例#9
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 );
		}
	}
}
示例#10
0
/*
============
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();
}
示例#11
0
/*
============
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();
}
示例#12
0
/*
================
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;
}
示例#13
0
/*
====================
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;
		}
	}
}