/* ================= idRenderModelDecal::CreateProjectionInfo ================= */ void idRenderModelDecal::GlobalProjectionInfoToLocal( decalProjectionInfo_t &localInfo, const decalProjectionInfo_t &info, const idVec3 &origin, const idMat3 &axis ) { float modelMatrix[16]; R_AxisToModelMatrix( axis, origin, modelMatrix ); for ( int j = 0; j < NUM_DECAL_BOUNDING_PLANES; j++ ) { R_GlobalPlaneToLocal( modelMatrix, info.boundingPlanes[j], localInfo.boundingPlanes[j] ); } R_GlobalPlaneToLocal( modelMatrix, info.fadePlanes[0], localInfo.fadePlanes[0] ); R_GlobalPlaneToLocal( modelMatrix, info.fadePlanes[1], localInfo.fadePlanes[1] ); R_GlobalPlaneToLocal( modelMatrix, info.textureAxis[0], localInfo.textureAxis[0] ); R_GlobalPlaneToLocal( modelMatrix, info.textureAxis[1], localInfo.textureAxis[1] ); R_GlobalPointToLocal( modelMatrix, info.projectionOrigin, localInfo.projectionOrigin ); localInfo.projectionBounds = info.projectionBounds; localInfo.projectionBounds.TranslateSelf( -origin ); localInfo.projectionBounds.RotateSelf( axis.Transpose() ); localInfo.material = info.material; localInfo.parallel = info.parallel; localInfo.fadeDepth = info.fadeDepth; localInfo.startTime = info.startTime; localInfo.force = info.force; }
/* ================ idCollisionModelManagerLocal::DrawModel ================ */ void idCollisionModelManagerLocal::DrawModel( cmHandle_t handle, const idVec3& modelOrigin, const idMat3& modelAxis, const idVec3& viewOrigin, const float radius ) { cm_model_t* model; idVec3 viewPos; if( handle < 0 && handle >= numModels ) { return; } if( cm_drawColor.IsModified() ) { sscanf( cm_drawColor.GetString(), "%f %f %f %f", &cm_color.x, &cm_color.y, &cm_color.z, &cm_color.w ); cm_drawColor.ClearModified(); } model = models[ handle ]; viewPos = ( viewOrigin - modelOrigin ) * modelAxis.Transpose(); checkCount++; DrawNodePolygons( model, model->node, modelOrigin, modelAxis, viewPos, radius ); }
/* ================ 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 ) == 0 ) { d = plaxis.PermutedInnerProduct( edge->pl ); edge->bitNum = ( ( d < 0.0f ) ? 1 : 0 ) | 2; } if ( ( edge->bitNum ^ INT32_SIGNBITSET( 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; } }
/* ============ idPush::ClipPush Try to push other entities by moving the given entity. ============ */ float idPush::ClipPush( trace_t& results, idEntity* pusher, const int flags, const idVec3& oldOrigin, const idMat3& oldAxis, idVec3& newOrigin, idMat3& newAxis ) { idVec3 translation; idRotation rotation; float mass; mass = 0.0f; results.fraction = 1.0f; results.endpos = newOrigin; results.endAxis = newAxis; memset( &results.c, 0, sizeof( results.c ) ); // translational push translation = newOrigin - oldOrigin; // if the pusher translates if( translation != vec3_origin ) { mass += ClipTranslationalPush( results, pusher, flags, newOrigin, translation ); if( results.fraction < 1.0f ) { newOrigin = oldOrigin; newAxis = oldAxis; return mass; } } else { newOrigin = oldOrigin; } // rotational push rotation = ( oldAxis.Transpose() * newAxis ).ToRotation(); rotation.SetOrigin( newOrigin ); rotation.Normalize180(); rotation.ReCalculateMatrix(); // recalculate the rotation matrix to avoid accumulating rounding errors // if the pusher rotates if( rotation.GetAngle() != 0.0f ) { // recalculate new axis to avoid floating point rounding problems newAxis = oldAxis * rotation.ToMat3(); newAxis.OrthoNormalizeSelf(); newAxis.FixDenormals(); newAxis.FixDegeneracies(); pusher->GetPhysics()->GetClipModel()->SetPosition( newOrigin, oldAxis ); mass += ClipRotationalPush( results, pusher, flags, newAxis, rotation ); if( results.fraction < 1.0f ) { newOrigin = oldOrigin; newAxis = oldAxis; return mass; } } else { newAxis = oldAxis; } return mass; }