/* ============ 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]; }
/* ============= 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; }
/* ============ 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; }
/* ============ 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]; }
/* ============ 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; }
/* ============ 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; }
/* ============ 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++; } } }
/* ==================== 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]; }
/* ================= 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 {
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 ); } }
/* ================ 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 ); }
/* ================ ColorFloatToByte ================ */ ID_INLINE static byte ColorFloatToByte( float c ) { return (byte) ( ( (dword) ( c * 255.0f ) ) & colorMask[FLOATSIGNBITSET(c)] ); }
/* ============ 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 ); }