Example #1
0
/*
============
idBox::GetParallelProjectionSilhouetteVerts
============
*/
int idBox::GetParallelProjectionSilhouetteVerts( const idVec3 &projectionDir, idVec3 silVerts[6] ) const {
	float f;
	int i, planeBits, *index;
	idVec3 points[8];

	ToPoints( points );

	planeBits = 0;
	f = projectionDir * axis[0];
	if ( FLOATNOTZERO( f ) ) {
		planeBits = 1 << FLOATSIGNBITSET( f );
	}
	f = projectionDir * axis[1];
	if ( FLOATNOTZERO( f ) ) {
		planeBits |= 4 << FLOATSIGNBITSET( f );
	}
	f = projectionDir * axis[2];
	if ( FLOATNOTZERO( f ) ) {
		planeBits |= 16 << FLOATSIGNBITSET( f );
	}

	index = boxPlaneBitsSilVerts[planeBits];
	for ( i = 0; i < index[0]; i++ ) {
		silVerts[i] = points[index[i+1]];
	}

	return index[0];
}
Example #2
0
/*
=============
idWinding2D::PlaneDistance
=============
*/
float idWinding2D::PlaneDistance( const idVec3 &plane ) const {
	int		i;
	float	d, min, max;

	min = idMath::INFINITY;
	max = -min;
	for ( i = 0; i < numPoints; i++ ) {
		d = plane.x * p[i].x + plane.y * p[i].y + plane.z;
		if ( d < min ) {
			min = d;
			if ( FLOATSIGNBITSET( min ) & FLOATSIGNBITNOTSET( max ) ) {
				return 0.0f;
			}
		}
		if ( d > max ) {
			max = d;
			if ( FLOATSIGNBITSET( min ) & FLOATSIGNBITNOTSET( max ) ) {
				return 0.0f;
			}
		}
	}
	if ( FLOATSIGNBITNOTSET( min ) ) {
		return min;
	}
	if ( FLOATSIGNBITSET( max ) ) {
		return max;
	}
	return 0.0f;
}
Example #3
0
/*
============
LineIntersectsPath
============
*/
bool LineIntersectsPath( const idVec2 &start, const idVec2 &end, const pathNode_t *node )
{
	float d0, d1, d2, d3;
	idVec3 plane1, plane2;
	
	plane1 = idWinding2D::Plane2DFromPoints( start, end );
	d0 = plane1.x * node->pos.x + plane1.y * node->pos.y + plane1.z;
	while( node->parent )
	{
		d1 = plane1.x * node->parent->pos.x + plane1.y * node->parent->pos.y + plane1.z;
		if( FLOATSIGNBITSET( d0 ) ^ FLOATSIGNBITSET( d1 ) )
		{
			plane2 = idWinding2D::Plane2DFromPoints( node->pos, node->parent->pos );
			d2 = plane2.x * start.x + plane2.y * start.y + plane2.z;
			d3 = plane2.x * end.x + plane2.y * end.y + plane2.z;
			if( FLOATSIGNBITSET( d2 ) ^ FLOATSIGNBITSET( d3 ) )
			{
				return true;
			}
		}
		d0 = d1;
		node = node->parent;
	}
	return false;
}
Example #4
0
/*
============
idBox::GetProjectionSilhouetteVerts
============
*/
int idBox::GetProjectionSilhouetteVerts( const idVec3 &projectionOrigin, idVec3 silVerts[6] ) const {
	float f;
	int i, planeBits, *index;
	idVec3 points[8], dir1, dir2;

	ToPoints( points );

	dir1 = points[0] - projectionOrigin;
	dir2 = points[6] - projectionOrigin;
	f = dir1 * axis[0];
	planeBits = FLOATSIGNBITNOTSET( f );
	f = dir2 * axis[0];
	planeBits |= FLOATSIGNBITSET( f ) << 1;
	f = dir1 * axis[1];
	planeBits |= FLOATSIGNBITNOTSET( f ) << 2;
	f = dir2 * axis[1];
	planeBits |= FLOATSIGNBITSET( f ) << 3;
	f = dir1 * axis[2];
	planeBits |= FLOATSIGNBITNOTSET( f ) << 4;
	f = dir2 * axis[2];
	planeBits |= FLOATSIGNBITSET( f ) << 5;

	index = boxPlaneBitsSilVerts[planeBits];
	for ( i = 0; i < index[0]; i++ ) {
		silVerts[i] = points[index[i+1]];
	}

	return index[0];
}
Example #5
0
/*
============
idAASLocal::EdgeSplitPoint

  calculates split point of the edge with the plane
  returns true if the split point is between the edge vertices
============
*/
bool idAASLocal::EdgeSplitPoint( idVec3 &split, int edgeNum, const idPlane &plane ) const {
	const aasEdge_t *edge;
	idVec3 v1, v2;
	float d1, d2;
	edge = &file->GetEdge( edgeNum );
	v1 = file->GetVertex( edge->vertexNum[0] );
	v2 = file->GetVertex( edge->vertexNum[1] );
	d1 = v1 * plane.Normal() - plane.Dist();
	d2 = v2 * plane.Normal() - plane.Dist();
	//if ( (d1 < CM_CLIP_EPSILON && d2 < CM_CLIP_EPSILON) || (d1 > -CM_CLIP_EPSILON && d2 > -CM_CLIP_EPSILON) ) {
	if( FLOATSIGNBITSET( d1 ) == FLOATSIGNBITSET( d2 ) ) {
		return false;
	}
	split = v1 + ( d1 / ( d1 - d2 ) ) * ( v2 - v1 );
	return true;
}
Example #6
0
/*
============
GetAxialBevel
============
*/
bool GetAxialBevel( const idVec3 &plane1, const idVec3 &plane2, const idVec2 &point, idVec3 &bevel ) {
	if ( FLOATSIGNBITSET( plane1.x ) ^ FLOATSIGNBITSET( plane2.x ) ) {
		if ( idMath::Fabs( plane1.x ) > 0.1f && idMath::Fabs( plane2.x ) > 0.1f ) {
			bevel.x = 0.0f;
			if ( FLOATSIGNBITSET( plane1.y ) ) {
				bevel.y = -1.0f;
			}
			else {
				bevel.y = 1.0f;
			}
			bevel.z = - ( point.x * bevel.x + point.y * bevel.y );
			return true;
		}
	}
	if ( FLOATSIGNBITSET( plane1.y ) ^ FLOATSIGNBITSET( plane2.y ) ) {
		if ( idMath::Fabs( plane1.y ) > 0.1f && idMath::Fabs( plane2.y ) > 0.1f ) {
			bevel.y = 0.0f;
			if ( FLOATSIGNBITSET( plane1.x ) ) {
				bevel.x = -1.0f;
			}
			else {
				bevel.x = 1.0f;
			}
			bevel.z = - ( point.x * bevel.x + point.y * bevel.y );
			return true;
		}
	}
	return false;
}
Example #7
0
/*
============
idWinding2D::ExpandForAxialBox
============
*/
void idWinding2D::ExpandForAxialBox( const idVec2 bounds[2] ) {
	int i, j, numPlanes;
	idVec2 v;
	idVec3 planes[MAX_POINTS_ON_WINDING_2D], plane, bevel;

	// get planes for the edges and add bevels
	for ( numPlanes = i = 0; i < numPoints; i++ ) {
		j = (i+1) % numPoints;
		if ( ( p[j] - p[i] ).LengthSqr() < 0.01f ) {
			continue;
		}
		plane = Plane2DFromPoints( p[i], p[j], true );
		if ( i ) {
			if ( GetAxialBevel( planes[numPlanes-1], plane, p[i], bevel ) ) {
				planes[numPlanes++] = bevel;
			}
		}
		assert( numPlanes < MAX_POINTS_ON_WINDING_2D );
		planes[numPlanes++] = plane;
	}
	if ( GetAxialBevel( planes[numPlanes-1], planes[0], p[0], bevel ) ) {
		planes[numPlanes++] = bevel;
	}

	// expand the planes
	for ( i = 0; i < numPlanes; i++ ) {
		v.x = bounds[ FLOATSIGNBITSET( planes[i].x ) ].x;
		v.y = bounds[ FLOATSIGNBITSET( planes[i].y ) ].y;
		planes[i].z += v.x * planes[i].x + v.y * planes[i].y;
	}

	// get intersection points of the planes
	for ( numPoints = i = 0; i < numPlanes; i++ ) {
		if ( Plane2DIntersection( planes[(i+numPlanes-1) % numPlanes], planes[i], p[numPoints] ) ) {
			numPoints++;
		}
	}
}
Example #8
0
/*
====================
sdTraceSurface::GetNextHashBin
====================
*/
ID_INLINE void sdTraceSurface::GetNextHashBin( const idVec3 &point, const idVec3 &normal, int hashBin[3], int &separatorAxis, float &separatorDist ) const {
	int		n[3];
	int		b[3];
	int		axis;
	float	d[3];
	idVec3	dir;

	// get the normal sign bits
	n[0] = FLOATSIGNBITSET( normal.x );
	n[1] = FLOATSIGNBITSET( normal.y );
	n[2] = FLOATSIGNBITSET( normal.z );

	// get the direction from the ray start point to the hash bin corner the normal is pointing at
	dir.x = _bounds[0][0] + ( hashBin[0] + ( n[0] ^ 1 ) ) * binSize[0] - point[0];
	dir.y = _bounds[0][1] + ( hashBin[1] + ( n[1] ^ 1 ) ) * binSize[1] - point[1];
	dir.z = _bounds[0][2] + ( hashBin[2] + ( n[2] ^ 1 ) ) * binSize[2] - point[2];

	// determine at which side the ray passes each edge coming from the has bin corner
	d[0] = dir.y * normal.x - dir.x * normal.y;		// x-y plane: edge parallel to z-axis
	d[1] = dir.z * normal.x - dir.x * normal.z;		// x-z plane: edge parallel to y-axis
	d[2] = dir.z * normal.y - dir.y * normal.z;		// y-z plane: edge parallel to x-axis

	b[0] = FLOATSIGNBITSET( d[0] ) ^ n[0] ^ n[1];
	b[1] = FLOATSIGNBITSET( d[1] ) ^ n[0] ^ n[2];
	b[2] = FLOATSIGNBITSET( d[2] ) ^ n[1] ^ n[2];

	// determine the hash bin boundary plane the ray goes through based on which side the ray passes each of the edges
	axis = ( b[0] & ( b[2] ^ 1 ) ) | ( ( b[1] & b[2] ) << 1 );	// 0 = x, 1 = y, 2 = z

	// increment/decrement the axis which separates this hash bin from the next
	hashBin[axis] += 1 - ( n[axis] << 1 );

	// save the separator axis and separator plane distance relative to ray start point
	separatorAxis = axis;
	separatorDist = dir[axis];
}
Example #9
0
/*
=================
idSurface::Split
=================
*/
int idSurface::Split( const idPlane &plane, const float epsilon, idSurface **front, idSurface **back, int *frontOnPlaneEdges, int *backOnPlaneEdges ) const {
	float *			dists;
	float			f;
	byte *			sides;
	int				counts[3];
	int *			edgeSplitVertex;
	int				numEdgeSplitVertexes;
	int *			vertexRemap[2];
	int				vertexIndexNum[2][2];
	int *			vertexCopyIndex[2];
	int *			indexPtr[2];
	int				indexNum[2];
	int *			index;
	int *			onPlaneEdges[2];
	int				numOnPlaneEdges[2];
	int				maxOnPlaneEdges;
	int				i;
	idSurface *		surface[2];
	idDrawVert		v;

	dists = (float *) _alloca( verts.Num() * sizeof( float ) );
	sides = (byte *) _alloca( verts.Num() * sizeof( byte ) );

	counts[0] = counts[1] = counts[2] = 0;

	// determine side for each vertex
	for ( i = 0; i < verts.Num(); i++ ) {
		dists[i] = f = plane.Distance( verts[i].xyz );
		if ( f > epsilon ) {
			sides[i] = SIDE_FRONT;
		} else if ( f < -epsilon ) {
			sides[i] = SIDE_BACK;
		} else {
			sides[i] = SIDE_ON;
		}
		counts[sides[i]]++;
	}

	*front = *back = NULL;

	// if coplanar, put on the front side if the normals match
	if ( !counts[SIDE_FRONT] && !counts[SIDE_BACK] ) {

		f = ( verts[indexes[1]].xyz - verts[indexes[0]].xyz ).Cross( verts[indexes[0]].xyz - verts[indexes[2]].xyz ) * plane.Normal();
		if ( FLOATSIGNBITSET( f ) ) {
			*back = new idSurface( *this );
			return SIDE_BACK;
		} else {
			*front = new idSurface( *this );
			return SIDE_FRONT;
		}
	}
	// if nothing at the front of the clipping plane
	if ( !counts[SIDE_FRONT] ) {
		*back = new idSurface( *this );
		return SIDE_BACK;
	}
	// if nothing at the back of the clipping plane
	if ( !counts[SIDE_BACK] ) {
		*front = new idSurface( *this );
		return SIDE_FRONT;
	}

	// allocate front and back surface
	*front = surface[0] = new idSurface();
	*back = surface[1] = new idSurface();

	edgeSplitVertex = (int *) _alloca( edges.Num() * sizeof( int ) );
	numEdgeSplitVertexes = 0;

	maxOnPlaneEdges = 4 * counts[SIDE_ON];
	counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;

	// split edges
	for ( i = 0; i < edges.Num(); i++ ) {
		int v0 = edges[i].verts[0];
		int v1 = edges[i].verts[1];
		int sidesOr = ( sides[v0] | sides[v1] );

		// if both vertexes are on the same side or one is on the clipping plane
		if ( !( sides[v0] ^ sides[v1] ) || ( sidesOr & SIDE_ON ) ) {
			edgeSplitVertex[i] = -1;
			counts[sidesOr & SIDE_BACK]++;
			counts[SIDE_ON] += ( sidesOr & SIDE_ON ) >> 1;
		} else {
Example #10
0
void CL_MouseMove( usercmd_t *cmd ) {
	float	mx, my;

	// allow mouse smoothing
	if ( m_filter->integer ) {
		mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5f;
		my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5f;
	} else {
		mx = cl.mouseDx[cl.mouseIndex];
		my = cl.mouseDy[cl.mouseIndex];
	}
	cl.mouseIndex ^= 1;
	cl.mouseDx[cl.mouseIndex] = 0;
	cl.mouseDy[cl.mouseIndex] = 0;

	if ( mx == 0.0f && my == 0.0f ) {
		return;
	}

	if ( cl_mouseAccel->value != 0.0f ) {
		if ( cl_mouseAccelStyle->integer == 0 ) {
			float accelSensitivity;
			float rate;
			rate = sqrt( mx * mx + my * my ) / (float)frame_msec;

			accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value;
			mx *= accelSensitivity;
			my *= accelSensitivity;
		} else {
			// sensitivity remains pretty much unchanged at low speeds
			// cl_mouseAccel is a power value to how the acceleration is shaped
			// cl_mouseAccelOffset is the rate for which the acceleration will have doubled the non accelerated amplificatio
			// NOTE: decouple the config cvars for independent acceleration setup along X and Y?

			float  rate[2];
			float  power[2];

			rate[0] = fabs( mx ) / (float)frame_msec;
			rate[1] = fabs( my ) / (float)frame_msec;
			power[0] = powf( rate[0] / cl_mouseAccelOffset->value, cl_mouseAccel->value );
			power[1] = powf( rate[1] / cl_mouseAccelOffset->value, cl_mouseAccel->value );

			mx = cl_sensitivity->value * ( mx + ( 1.0f - 2.0f * FLOATSIGNBITSET( mx ) ) * power[0] * cl_mouseAccelOffset->value );
			my = cl_sensitivity->value * ( my + ( 1.0f - 2.0f * FLOATSIGNBITSET( my ) ) * power[1] * cl_mouseAccelOffset->value );
		}
	} else {
		mx *= cl_sensitivity->value;
		my *= cl_sensitivity->value;
	}

	// ingame FOV
	mx *= cl.cgameSensitivity;
	my *= cl.cgameSensitivity;

	// add mouse X/Y movement to cmd
	if ( in_strafe.active ) {
		cmd->rightmove = ClampChar( cmd->rightmove + m_side->value * mx );
	} else {
		cl.viewangles[YAW] -= m_yaw->value * mx;
	}

	if ( (in_mlooking || cl_freelook->integer) && !in_strafe.active ) {
		cl.viewangles[PITCH] += m_pitch->value * my;
	} else {
		cmd->forwardmove = ClampChar( cmd->forwardmove - m_forward->value * my );
	}
}
Example #11
0
/*
================
idBrittleFracture::ProjectDecal
================
*/
void idBrittleFracture::ProjectDecal( const idVec3 &point, const idVec3 &dir, const int time, const char *damageDefName ) {
	int i, j, bits, clipBits;
	float a, c, s;
	idVec2 st[MAX_POINTS_ON_WINDING];
	idVec3 origin;
	idMat3 axis, axistemp;
	idPlane textureAxis[2];

	if ( gameLocal.isServer ) {
		idBitMsg	msg;
		byte		msgBuf[MAX_EVENT_PARAM_SIZE];

		msg.Init( msgBuf, sizeof( msgBuf ) );
		msg.BeginWriting();
		msg.WriteFloat( point[0] );
		msg.WriteFloat( point[1] );
		msg.WriteFloat( point[2] );
		msg.WriteFloat( dir[0] );
		msg.WriteFloat( dir[1] );
		msg.WriteFloat( dir[2] );
		ServerSendEvent( EVENT_PROJECT_DECAL, &msg, true, -1 );
	}

	if ( time >= gameLocal.time ) {
		// try to get the sound from the damage def
		const idDeclEntityDef *damageDef = NULL;
		const idSoundShader *sndShader = NULL;
		if ( damageDefName ) {
			damageDef = gameLocal.FindEntityDef( damageDefName, false );
			if ( damageDef ) {
				sndShader = declManager->FindSound( damageDef->dict.GetString( "snd_shatter", "" ) );
			}
		}

		if ( sndShader ) {
			StartSoundShader( sndShader, SND_CHANNEL_ANY, 0, false, NULL );
		} else {
			StartSound( "snd_bullethole", SND_CHANNEL_ANY, 0, false, NULL );
		}
	}

	a = gameLocal.random.RandomFloat() * idMath::TWO_PI;
	c = cos( a );
	s = -sin( a );

	axis[2] = -dir;
	axis[2].Normalize();
	axis[2].NormalVectors( axistemp[0], axistemp[1] );
	axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * s;
	axis[1] = axistemp[ 0 ] * s + axistemp[ 1 ] * -c;

	textureAxis[0] = axis[0] * ( 1.0f / decalSize );
	textureAxis[0][3] = -( point * textureAxis[0].Normal() ) + 0.5f;

	textureAxis[1] = axis[1] * ( 1.0f / decalSize );
	textureAxis[1][3] = -( point * textureAxis[1].Normal() ) + 0.5f;

	for ( i = 0; i < shards.Num(); i++ ) {
		idFixedWinding &winding = shards[i]->winding;
		origin = shards[i]->clipModel->GetOrigin();
		axis = shards[i]->clipModel->GetAxis();
		float d0, d1;

		clipBits = -1;
		for ( j = 0; j < winding.GetNumPoints(); j++ ) {
			idVec3 p = origin + winding[j].ToVec3() * axis;

			st[j].x = d0 = textureAxis[0].Distance( p );
			st[j].y = d1 = textureAxis[1].Distance( p );

			bits = FLOATSIGNBITSET( d0 );
			d0 = 1.0f - d0;
			bits |= FLOATSIGNBITSET( d1 ) << 2;
			d1 = 1.0f - d1;
			bits |= FLOATSIGNBITSET( d0 ) << 1;
			bits |= FLOATSIGNBITSET( d1 ) << 3;

			clipBits &= bits;
		}

		if ( clipBits ) {
			continue;
		}

		idFixedWinding *decal = new idFixedWinding;
		shards[i]->decals.Append( decal );

		decal->SetNumPoints( winding.GetNumPoints() );
		for ( j = 0; j < winding.GetNumPoints(); j++ ) {
			(*decal)[j].ToVec3() = winding[j].ToVec3();
			(*decal)[j].s = st[j].x;
			(*decal)[j].t = st[j].y;
		}
	}

	BecomeActive( TH_UPDATEVISUALS );
}
Example #12
0
/*
================
ColorFloatToByte
================
*/
ID_INLINE static byte ColorFloatToByte( float c ) {
	return (byte) ( ( (dword) ( c * 255.0f ) ) & colorMask[FLOATSIGNBITSET(c)] );
}
Example #13
0
/*
============
idAASLocal::FloorEdgeSplitPoint

  calculates either the closest or furthest point on the floor of the area which also lies on the pathPlane
  the point has to be on the front side of the frontPlane to be valid
============
*/
bool idAASLocal::FloorEdgeSplitPoint( idVec3 &bestSplit, int areaNum, const idPlane &pathPlane, const idPlane &frontPlane, bool closest ) const {
	int i, j, faceNum, edgeNum;
	const aasArea_t *area;
	const aasFace_t *face;
	idVec3 split;
	float dist, bestDist;
	const aasEdge_t *edge;
	idVec3 v1, v2;
	float d1, d2;

	area = &file->GetArea( areaNum );
	if ( closest ) {
		bestDist = maxWalkPathDistance;

		for ( i = area->numFaces-1; i >= 0; i-- ) {
			faceNum = file->GetFaceIndex( area->firstFace + i );
			face = &file->GetFace( abs(faceNum) );

			if ( !(face->flags & FACE_FLOOR ) ) {
				continue;
			}

			for ( j = face->numEdges-1; j >= 0; j-- ) {
				edgeNum = file->GetEdgeIndex( face->firstEdge + j );

				edge = &file->GetEdge( abs( edgeNum ) );
				v1 = file->GetVertex( edge->vertexNum[0] );
				v2 = file->GetVertex( edge->vertexNum[1] );
				d1 = v1 * pathPlane.Normal() - pathPlane.Dist();
				d2 = v2 * pathPlane.Normal() - pathPlane.Dist();

				//if ( (d1 < CM_CLIP_EPSILON && d2 < CM_CLIP_EPSILON) || (d1 > -CM_CLIP_EPSILON && d2 > -CM_CLIP_EPSILON) ) {
				if ( FLOATSIGNBITSET( d1 ) == FLOATSIGNBITSET( d2 ) ) {
					continue;
				}

				split = v1 + (d1 / (d1 - d2)) * (v2 - v1);
				dist = frontPlane.Distance( split );
				if ( dist >= -0.1f && dist < bestDist ) {
					bestDist = dist;
					bestSplit = split;
				}
			}
		}

		return ( bestDist < maxWalkPathDistance );

	} else {
		bestDist = -0.1f;

		for ( i = area->numFaces-1; i >= 0; i-- ) {
			faceNum = file->GetFaceIndex( area->firstFace + i );
			face = &file->GetFace( abs(faceNum) );

			if ( !(face->flags & FACE_FLOOR ) ) {
				continue;
			}

			for ( j = face->numEdges-1; j >= 0; j-- ) {
				edgeNum = file->GetEdgeIndex( face->firstEdge + j );

				edge = &file->GetEdge( abs( edgeNum ) );
				v1 = file->GetVertex( edge->vertexNum[0] );
				v2 = file->GetVertex( edge->vertexNum[1] );
				d1 = v1 * pathPlane.Normal() - pathPlane.Dist();
				d2 = v2 * pathPlane.Normal() - pathPlane.Dist();

				//if ( (d1 < CM_CLIP_EPSILON && d2 < CM_CLIP_EPSILON) || (d1 > -CM_CLIP_EPSILON && d2 > -CM_CLIP_EPSILON) ) {
				if ( FLOATSIGNBITSET( d1 ) == FLOATSIGNBITSET( d2 ) ) {
					continue;
				}

				split = v1 + (d1 / (d1 - d2)) * (v2 - v1);
				dist = frontPlane.Distance( split );
				if ( dist > bestDist ) {
					bestDist = dist;
					bestSplit = split;
				}
			}
		}

		return ( bestDist > -0.1f );
	}
}
/*
================
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::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 );
}