/*
================
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::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<int *>(&poly);
		tw->trace.c.trmFeature = v - tw->vertices;
		tw->trace.c.point = collisionPoint;
	}
}