/* ==================== idMD5Mesh::CalcBounds ==================== */ idBounds idMD5Mesh::CalcBounds( const idJointMat *entJoints ) { idBounds bounds; idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) ); TransformVerts( verts, entJoints ); SIMDProcessor->MinMax( bounds[0], bounds[1], verts, texCoords.Num() ); return bounds; }
/* ============ idTraceModel::SetupPolygon ============ */ void idTraceModel::SetupPolygon( const idWinding &w ) { int i; idVec3 *verts; verts = (idVec3 *) _alloca16( w.GetNumPoints() * sizeof( idVec3 ) ); for ( i = 0; i < w.GetNumPoints(); i++ ) { verts[i] = w[i].ToVec3(); } SetupPolygon( verts, w.GetNumPoints() ); }
/* ============ idAASLocal::SortWallEdges ============ */ void idAASLocal::SortWallEdges( int *edges, int numEdges ) const { int i, j, k, numSequences; wallEdge_t **sequenceFirst, **sequenceLast, *wallEdges, *wallEdge; wallEdges = ( wallEdge_t * ) _alloca16( numEdges * sizeof( wallEdge_t ) ); sequenceFirst = ( wallEdge_t ** )_alloca16( numEdges * sizeof( wallEdge_t * ) ); sequenceLast = ( wallEdge_t ** )_alloca16( numEdges * sizeof( wallEdge_t * ) ); for( i = 0; i < numEdges; i++ ) { wallEdges[i].edgeNum = edges[i]; GetEdgeVertexNumbers( edges[i], wallEdges[i].verts ); wallEdges[i].next = NULL; sequenceFirst[i] = &wallEdges[i]; sequenceLast[i] = &wallEdges[i]; } numSequences = numEdges; for( i = 0; i < numSequences; i++ ) { for( j = i + 1; j < numSequences; j++ ) { if( sequenceFirst[i]->verts[0] == sequenceLast[j]->verts[1] ) { sequenceLast[j]->next = sequenceFirst[i]; sequenceFirst[i] = sequenceFirst[j]; break; } if( sequenceLast[i]->verts[1] == sequenceFirst[j]->verts[0] ) { sequenceLast[i]->next = sequenceFirst[j]; break; } } if( j < numSequences ) { numSequences--; for( k = j; k < numSequences; k++ ) { sequenceFirst[k] = sequenceFirst[k + 1]; sequenceLast[k] = sequenceLast[k + 1]; } i = -1; } } k = 0; for( i = 0; i < numSequences; i++ ) { for( wallEdge = sequenceFirst[i]; wallEdge; wallEdge = wallEdge->next ) { edges[k++] = wallEdge->edgeNum; } } }
/* ============= idPolynomial::GetRoots ============= */ int idPolynomial::GetRoots( idComplex* roots ) const { int i, j; idComplex x, b, c, *coef; coef = ( idComplex* ) _alloca16( ( degree + 1 ) * sizeof( idComplex ) ); for( i = 0; i <= degree; i++ ) { coef[i].Set( coefficient[i], 0.0f ); } for( i = degree - 1; i >= 0; i-- ) { x.Zero(); Laguer( coef, i + 1, x ); if( idMath::Fabs( x.i ) < 2.0f * EPSILON * idMath::Fabs( x.r ) ) { x.i = 0.0f; } roots[i] = x; b = coef[i + 1]; for( j = i; j >= 0; j-- ) { c = coef[j]; coef[j] = b; b = x * b + c; } } for( i = 0; i <= degree; i++ ) { coef[i].Set( coefficient[i], 0.0f ); } for( i = 0; i < degree; i++ ) { Laguer( coef, degree, roots[i] ); } for( i = 1; i < degree; i++ ) { x = roots[i]; for( j = i - 1; j >= 0; j-- ) { if( roots[j].r <= x.r ) { break; } roots[j + 1] = roots[j]; } roots[j + 1] = x; } return degree; }
/* ================ idBrittleFracture::DropFloatingIslands ================ */ void idBrittleFracture::DropFloatingIslands( const idVec3 &point, const idVec3 &impulse, const int time ) { int i, j, numIslands; int queueStart, queueEnd; shard_t *curShard, *nextShard, **queue; bool touchesEdge; idVec3 dir; dir = impulse; dir.Normalize(); numIslands = 0; queue = ( shard_t ** ) _alloca16( shards.Num() * sizeof( shard_t ** ) ); for( i = 0; i < shards.Num(); i++ ) { shards[i]->islandNum = 0; } for( i = 0; i < shards.Num(); i++ ) { if( shards[i]->droppedTime != -1 ) { continue; } if( shards[i]->islandNum ) { continue; } queueStart = 0; queueEnd = 1; queue[0] = shards[i]; shards[i]->islandNum = numIslands + 1; touchesEdge = false; if( shards[i]->atEdge ) { touchesEdge = true; } for( curShard = queue[queueStart]; queueStart < queueEnd; curShard = queue[++queueStart] ) { for( j = 0; j < curShard->neighbours.Num(); j++ ) { nextShard = curShard->neighbours[j]; if( nextShard->droppedTime != -1 ) { continue; } if( nextShard->islandNum ) { continue; } queue[queueEnd++] = nextShard; nextShard->islandNum = numIslands + 1; if( nextShard->atEdge ) { touchesEdge = true; } } } numIslands++; // if the island is not connected to the world at any edges if( !touchesEdge ) { for( j = 0; j < queueEnd; j++ ) { DropShard( queue[j], point, dir, 0.0f, time ); } } } }
/* ================== R_CreateVertexProgramShadowCache This is constant for any number of lights, the vertex program takes care of projecting the verts to infinity. ================== */ void R_CreateVertexProgramShadowCache( srfTriangles_t *tri ) { if( tri->verts == NULL ) { return; } shadowCache_t *temp = ( shadowCache_t * ) _alloca16( tri->numVerts * 2 * sizeof( shadowCache_t ) ); SIMDProcessor->CreateVertexProgramShadowCache( &temp->xyz, tri->verts, tri->numVerts ); vertexCache.Alloc( temp, tri->numVerts * 2 * sizeof( shadowCache_t ), &tri->shadowCache ); }
/* ================ R_CalcPointCull Also inits the remap[] array to all -1 ================ */ static void R_CalcPointCull(stencilRef_t *st, const srfTriangles_t *tri, const idPlane frustum[6], unsigned short *pointCull) { int i; int frontBits; float *planeSide; byte *side1, *side2; SIMDProcessor->Memset(st->remap, -1, tri->numVerts * sizeof(st->remap[0])); for (frontBits = 0, i = 0; i < 6; i++) { // get front bits for the whole surface if (tri->bounds.PlaneDistance(frustum[i]) >= LIGHT_CLIP_EPSILON) { frontBits |= 1 << (i + 6); } } // initialize point cull for (i = 0; i < tri->numVerts; i++) { pointCull[i] = frontBits; } // if the surface is not completely inside the light frustum if (frontBits == (((1 << 6) - 1)) << 6) { return; } planeSide = (float *)_alloca16(tri->numVerts * sizeof(float)); side1 = (byte *)_alloca16(tri->numVerts * sizeof(byte)); side2 = (byte *)_alloca16(tri->numVerts * sizeof(byte)); SIMDProcessor->Memset(side1, 0, tri->numVerts * sizeof(byte)); SIMDProcessor->Memset(side2, 0, tri->numVerts * sizeof(byte)); for (i = 0; i < 6; i++) { if (frontBits & (1 << (i + 6))) { continue; } SIMDProcessor->Dot(planeSide, frustum[i], tri->verts, tri->numVerts); SIMDProcessor->CmpLT(side1, i, planeSide, LIGHT_CLIP_EPSILON, tri->numVerts); SIMDProcessor->CmpGT(side2, i, planeSide, -LIGHT_CLIP_EPSILON, tri->numVerts); } for (i = 0; i < tri->numVerts; i++) { pointCull[i] |= side1[i] | (side2[i] << 6); } }
/* ================= R_CreateShadowVolumeInFrustum Adds new verts and indexes to the shadow volume. If the frustum completely defines the projected light, makeClippedPlanes should be true, which will cause sil quads to be added along all clipped edges. If the frustum is just part of a point light, clipped planes don't need to be added. ================= */ static void R_CreateShadowVolumeInFrustum( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, const idVec3 lightOrigin, const idPlane frustum[6], const idPlane &farPlane, bool makeClippedPlanes ) { int i; int numTris; unsigned short *pointCull; int numCapIndexes; int firstShadowIndex; int firstShadowVert; int cullBits; pointCull = (unsigned short *)_alloca16( tri->numVerts * sizeof( pointCull[0] ) ); // test the vertexes for inside the light frustum, which will allow // us to completely cull away some triangles from consideration. R_CalcPointCull( tri, frustum, pointCull ); // this may not be the first frustum added to the volume firstShadowIndex = numShadowIndexes; firstShadowVert = numShadowVerts; // decide which triangles front shadow volumes, clipping as needed numClipSilEdges = 0; numTris = tri->numIndexes / 3; for ( i = 0 ; i < numTris ; i++ ) { int i1, i2, i3; faceCastsShadow[i] = 0; // until shown otherwise // if it isn't facing the right way, don't add it // to the shadow volume if ( globalFacing[i] ) { continue; } i1 = tri->silIndexes[ i*3 + 0 ]; i2 = tri->silIndexes[ i*3 + 1 ]; i3 = tri->silIndexes[ i*3 + 2 ]; // if all the verts are off one side of the frustum, // don't add any of them if ( TRIANGLE_CULLED( i1, i2, i3 ) ) { continue; }
/* ============ idLCP_Symmetric::AddClamped ============ */ void idLCP_Symmetric::AddClamped(int r, bool useSolveCache) { float d, dot; assert(r >= numClamped); if (numClamped < clampedChangeStart) { clampedChangeStart = numClamped; } // add a row at the bottom and a column at the right of the factored // matrix for the clamped variables Swap(numClamped, r); // solve for v in L * v = rowPtr[numClamped] if (useSolveCache) { // the lower triangular solve was cached in SolveClamped called by CalcForceDelta memcpy(clamped[numClamped], solveCache2.ToFloatPtr(), numClamped * sizeof(float)); // calculate row dot product SIMDProcessor->Dot(dot, solveCache2.ToFloatPtr(), solveCache1.ToFloatPtr(), numClamped); } else { float *v = (float *) _alloca16(numClamped * sizeof(float)); SIMDProcessor->MatX_LowerTriangularSolve(clamped, v, rowPtrs[numClamped], numClamped); // add bottom row to L SIMDProcessor->Mul(clamped[numClamped], v, diagonal.ToFloatPtr(), numClamped); // calculate row dot product SIMDProcessor->Dot(dot, clamped[numClamped], v, numClamped); } // update diagonal[numClamped] d = rowPtrs[numClamped][numClamped] - dot; if (d == 0.0f) { idLib::common->Printf("idLCP_Symmetric::AddClamped: updating factorization failed\n"); numClamped++; return; } clamped[numClamped][numClamped] = d; diagonal[numClamped] = 1.0f / d; numClamped++; }
/* ================ idDict::Copy copy all key value pairs without removing existing key/value pairs not present in the other dict ================ */ void idDict::Copy( const idDict &other ) { int i, n, *found; idKeyValue kv; // check for assignment to self if ( this == &other ) { return; } n = other.args.Num(); if ( args.Num() ) { found = (int *) _alloca16( other.args.Num() * sizeof( int ) ); for ( i = 0; i < n; i++ ) { found[i] = FindKeyIndex( other.args[i].GetKey() ); } } else { found = NULL; } for ( i = 0; i < n; i++ ) { if ( found && found[i] != -1 ) { // first set the new value and then free the old value to allow proper self copying const idPoolStr *oldValue = args[found[i]].value; args[found[i]].value = globalValues.CopyString( other.args[i].value ); globalValues.FreeString( oldValue ); } else { kv.key = globalKeys.CopyString( other.args[i].key ); kv.value = globalValues.CopyString( other.args[i].value ); argHash.Add( argHash.GenerateKey( kv.GetKey(), false ), args.Append( kv ) ); } } }
/* ================== R_SkyboxTexGen ================== */ void R_SkyboxTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) { idVec3 localViewOrigin; R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin ); int numVerts = surf->geo->numVerts; int size = numVerts * sizeof( idVec3 ); idVec3 *texCoords = ( idVec3 * ) _alloca16( size ); const idDrawVert *verts = surf->geo->verts; for( int i = 0; i < numVerts; i++ ) { texCoords[i][0] = verts[i].xyz[0] - localViewOrigin[0]; texCoords[i][1] = verts[i].xyz[1] - localViewOrigin[1]; texCoords[i][2] = verts[i].xyz[2] - localViewOrigin[2]; } surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size ); }
/* ===================== R_CalcInteractionCullBits We want to cull a little on the sloppy side, because the pre-clipping of geometry to the lights in dmap will give many cases that are right at the border we throw things out on the border, because if any one vertex is clearly inside, the entire triangle will be accepted. ===================== */ void R_CalcInteractionCullBits( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) { int i, frontBits; if ( cullInfo.cullBits != NULL ) { return; } frontBits = 0; // cull the triangle surface bounding box for ( i = 0; i < 6; i++ ) { R_GlobalPlaneToLocal( ent->modelMatrix, -light->frustum[i], cullInfo.localClipPlanes[i] ); // get front bits for the whole surface if ( tri->bounds.PlaneDistance( cullInfo.localClipPlanes[i] ) >= LIGHT_CLIP_EPSILON ) { frontBits |= 1<<i; } } // if the surface is completely inside the light frustum if ( frontBits == ( ( 1 << 6 ) - 1 ) ) { cullInfo.cullBits = LIGHT_CULL_ALL_FRONT; return; } cullInfo.cullBits = (byte *) R_StaticAlloc( tri->numVerts * sizeof( cullInfo.cullBits[0] ) ); SIMDProcessor->Memset( cullInfo.cullBits, 0, tri->numVerts * sizeof( cullInfo.cullBits[0] ) ); float *planeSide = (float *) _alloca16( tri->numVerts * sizeof( float ) ); for ( i = 0; i < 6; i++ ) { // if completely infront of this clipping plane if ( frontBits & ( 1 << i ) ) { continue; } SIMDProcessor->Dot( planeSide, cullInfo.localClipPlanes[i], tri->verts, tri->numVerts ); SIMDProcessor->CmpLT( cullInfo.cullBits, i, planeSide, LIGHT_CLIP_EPSILON, tri->numVerts ); } }
/* ============= idPolynomial::GetRoots ============= */ int idPolynomial::GetRoots( float* roots ) const { int i, num; idComplex* complexRoots; switch( degree ) { case 0: return 0; case 1: return GetRoots1( coefficient[1], coefficient[0], roots ); case 2: return GetRoots2( coefficient[2], coefficient[1], coefficient[0], roots ); case 3: return GetRoots3( coefficient[3], coefficient[2], coefficient[1], coefficient[0], roots ); case 4: return GetRoots4( coefficient[4], coefficient[3], coefficient[2], coefficient[1], coefficient[0], roots ); } // The Abel-Ruffini theorem states that there is no general solution // in radicals to polynomial equations of degree five or higher. // A polynomial equation can be solved by radicals if and only if // its Galois group is a solvable group. complexRoots = ( idComplex* ) _alloca16( degree * sizeof( idComplex ) ); GetRoots( complexRoots ); for( num = i = 0; i < degree; i++ ) { if( complexRoots[i].i == 0.0f ) { roots[i] = complexRoots[i].r; num++; } } return num; }
/* ===================== idRenderModelOverlay::CreateOverlay This projects on both front and back sides to avoid seams The material should be clamped, because entire triangles are added, some of which may extend well past the 0.0 to 1.0 texture range ===================== */ void idRenderModelOverlay::CreateOverlay( const idRenderModel *model, const idPlane localTextureAxis[2], const idMaterial *mtr ) { int i, maxVerts, maxIndexes, surfNum; idRenderModelOverlay *overlay = NULL; // count up the maximum possible vertices and indexes per surface maxVerts = 0; maxIndexes = 0; for ( surfNum = 0; surfNum < model->NumSurfaces(); surfNum++ ) { const modelSurface_t *surf = model->Surface( surfNum ); if ( surf->geometry->numVerts > maxVerts ) { maxVerts = surf->geometry->numVerts; } if ( surf->geometry->numIndexes > maxIndexes ) { maxIndexes = surf->geometry->numIndexes; } } // make temporary buffers for the building process overlayVertex_t *overlayVerts = (overlayVertex_t *)_alloca( maxVerts * sizeof( *overlayVerts ) ); glIndex_t *overlayIndexes = (glIndex_t *)_alloca16( maxIndexes * sizeof( *overlayIndexes ) ); // pull out the triangles we need from the base surfaces for ( surfNum = 0; surfNum < model->NumBaseSurfaces(); surfNum++ ) { const modelSurface_t *surf = model->Surface( surfNum ); float d; if ( !surf->geometry || !surf->shader ) { continue; } // some surfaces can explicitly disallow overlays if ( !surf->shader->AllowOverlays() ) { continue; } const srfTriangles_t *stri = surf->geometry; // try to cull the whole surface along the first texture axis d = stri->bounds.PlaneDistance( localTextureAxis[0] ); if ( d < 0.0f || d > 1.0f ) { continue; } // try to cull the whole surface along the second texture axis d = stri->bounds.PlaneDistance( localTextureAxis[1] ); if ( d < 0.0f || d > 1.0f ) { continue; } byte *cullBits = (byte *)_alloca16( stri->numVerts * sizeof( cullBits[0] ) ); idVec2 *texCoords = (idVec2 *)_alloca16( stri->numVerts * sizeof( texCoords[0] ) ); SIMDProcessor->OverlayPointCull( cullBits, texCoords, localTextureAxis, stri->verts, stri->numVerts ); glIndex_t *vertexRemap = (glIndex_t *)_alloca16( sizeof( vertexRemap[0] ) * stri->numVerts ); SIMDProcessor->Memset( vertexRemap, -1, sizeof( vertexRemap[0] ) * stri->numVerts ); // find triangles that need the overlay int numVerts = 0; int numIndexes = 0; int triNum = 0; for ( int index = 0; index < stri->numIndexes; index += 3, triNum++ ) { int v1 = stri->indexes[index+0]; int v2 = stri->indexes[index+1]; int v3 = stri->indexes[index+2]; // skip triangles completely off one side if ( cullBits[v1] & cullBits[v2] & cullBits[v3] ) { continue; } // we could do more precise triangle culling, like the light interaction does, if desired // keep this triangle for ( int vnum = 0; vnum < 3; vnum++ ) { int ind = stri->indexes[index+vnum]; if ( vertexRemap[ind] == (glIndex_t)-1 ) { vertexRemap[ind] = numVerts; overlayVerts[numVerts].vertexNum = ind; overlayVerts[numVerts].st[0] = texCoords[ind][0]; overlayVerts[numVerts].st[1] = texCoords[ind][1]; numVerts++; } overlayIndexes[numIndexes++] = vertexRemap[ind]; } } if ( !numIndexes ) { continue; } overlaySurface_t *s = (overlaySurface_t *) Mem_Alloc( sizeof( overlaySurface_t ) ); s->surfaceNum = surfNum; s->surfaceId = surf->id; s->verts = (overlayVertex_t *)Mem_Alloc( numVerts * sizeof( s->verts[0] ) ); memcpy( s->verts, overlayVerts, numVerts * sizeof( s->verts[0] ) ); s->numVerts = numVerts; s->indexes = (glIndex_t *)Mem_Alloc( numIndexes * sizeof( s->indexes[0] ) ); memcpy( s->indexes, overlayIndexes, numIndexes * sizeof( s->indexes[0] ) ); s->numIndexes = numIndexes; for ( i = 0; i < materials.Num(); i++ ) { if ( materials[i]->material == mtr ) { break; } } if ( i < materials.Num() ) { materials[i]->surfaces.Append( s ); } else { overlayMaterial_t *mat = new overlayMaterial_t; mat->material = mtr; mat->surfaces.Append( s ); materials.Append( mat ); } } // remove the oldest overlay surfaces if there are too many per material for ( i = 0; i < materials.Num(); i++ ) { while( materials[i]->surfaces.Num() > MAX_OVERLAY_SURFACES ) { FreeSurface( materials[i]->surfaces[0] ); materials[i]->surfaces.RemoveIndex( 0 ); } } }
/* ============ idBrush::Split ============ */ int idBrush::Split( const idPlane& plane, int planeNum, idBrush** front, idBrush** back ) const { int res, i, j; idBrushSide* side, *frontSide, *backSide; float dist, maxBack, maxFront, *maxBackWinding, *maxFrontWinding; idWinding* w, *mid; assert( windingsValid ); if( front ) { *front = NULL; } if( back ) { *back = NULL; } res = bounds.PlaneSide( plane, -BRUSH_EPSILON ); if( res == PLANESIDE_FRONT ) { if( front ) { *front = Copy(); } return res; } if( res == PLANESIDE_BACK ) { if( back ) { *back = Copy(); } return res; } maxBackWinding = ( float* ) _alloca16( sides.Num() * sizeof( float ) ); maxFrontWinding = ( float* ) _alloca16( sides.Num() * sizeof( float ) ); maxFront = maxBack = 0.0f; for( i = 0; i < sides.Num(); i++ ) { side = sides[i]; w = side->winding; if( !w ) { continue; } maxBackWinding[i] = 10.0f; maxFrontWinding[i] = -10.0f; for( j = 0; j < w->GetNumPoints(); j++ ) { dist = plane.Distance( ( *w )[j].ToVec3() ); if( dist > maxFrontWinding[i] ) { maxFrontWinding[i] = dist; } if( dist < maxBackWinding[i] ) { maxBackWinding[i] = dist; } } if( maxFrontWinding[i] > maxFront ) { maxFront = maxFrontWinding[i]; } if( maxBackWinding[i] < maxBack ) { maxBack = maxBackWinding[i]; } } if( maxFront < BRUSH_EPSILON ) { if( back ) { *back = Copy(); } return PLANESIDE_BACK; } if( maxBack > -BRUSH_EPSILON ) { if( front ) { *front = Copy(); } return PLANESIDE_FRONT; } mid = new idWinding( plane.Normal(), plane.Dist() ); for( i = 0; i < sides.Num() && mid; i++ ) { mid = mid->Clip( -sides[i]->plane, BRUSH_EPSILON, false ); } if( mid ) { if( mid->IsTiny() ) { delete mid; mid = NULL; } else if( mid->IsHuge() ) { // if the winding is huge then the brush is unbounded common->Warning( "brush %d on entity %d is unbounded" "( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )", primitiveNum, entityNum, bounds[0][0], bounds[0][1], bounds[0][2], bounds[1][0], bounds[1][1], bounds[1][2], bounds[1][0] - bounds[0][0], bounds[1][1] - bounds[0][1], bounds[1][2] - bounds[0][2] ); delete mid; mid = NULL; } } if( !mid ) { if( maxFront > - maxBack ) { if( front ) { *front = Copy(); } return PLANESIDE_FRONT; } else { if( back ) { *back = Copy(); } return PLANESIDE_BACK; } } if( !front && !back ) { delete mid; return PLANESIDE_CROSS; } *front = new idBrush(); ( *front )->SetContents( contents ); ( *front )->SetEntityNum( entityNum ); ( *front )->SetPrimitiveNum( primitiveNum ); *back = new idBrush(); ( *back )->SetContents( contents ); ( *back )->SetEntityNum( entityNum ); ( *back )->SetPrimitiveNum( primitiveNum ); for( i = 0; i < sides.Num(); i++ ) { side = sides[i]; if( !side->winding ) { continue; } // if completely at the front if( maxBackWinding[i] >= BRUSH_EPSILON ) { ( *front )->sides.Append( side->Copy() ); } // if completely at the back else if( maxFrontWinding[i] <= -BRUSH_EPSILON ) { ( *back )->sides.Append( side->Copy() ); } else { // split the side side->Split( plane, &frontSide, &backSide ); if( frontSide ) { ( *front )->sides.Append( frontSide ); } else if( maxFrontWinding[i] > -BRUSH_EPSILON ) { // favor an overconstrained brush side = side->Copy(); side->winding = side->winding->Clip( idPlane( plane.Normal(), ( plane.Dist() - ( BRUSH_EPSILON + 0.02f ) ) ), 0.01f, true ); assert( side->winding ); ( *front )->sides.Append( side ); } if( backSide ) { ( *back )->sides.Append( backSide ); } else if( maxBackWinding[i] < BRUSH_EPSILON ) { // favor an overconstrained brush side = side->Copy(); side->winding = side->winding->Clip( idPlane( -plane.Normal(), -( plane.Dist() + ( BRUSH_EPSILON + 0.02f ) ) ), 0.01f, true ); assert( side->winding ); ( *back )->sides.Append( side ); } } } side = new idBrushSide( -plane, planeNum ^ 1 ); side->winding = mid->Reverse(); side->flags |= SFL_SPLIT; ( *front )->sides.Append( side ); ( *front )->windingsValid = true; ( *front )->BoundBrush( this ); side = new idBrushSide( plane, planeNum ); side->winding = mid; side->flags |= SFL_SPLIT; ( *back )->sides.Append( side ); ( *back )->windingsValid = true; ( *back )->BoundBrush( this ); return PLANESIDE_CROSS; }
/* ================ idIK_Reach::Init ================ */ bool idIK_Reach::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) { int i; const char *jointName; idTraceModel trm; idVec3 dir, handOrigin, elbowOrigin, shoulderOrigin, dirOrigin; idMat3 axis, handAxis, elbowAxis, shoulderAxis; if ( !self ) { return false; } numArms = Min( self->spawnArgs.GetInt( "ik_numArms", "0" ), MAX_ARMS ); if ( numArms == 0 ) { return true; } if ( !idIK::Init( self, anim, modelOffset ) ) { return false; } int numJoints = animator->NumJoints(); idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) ); // create the animation frame used to setup the IK gameEdit->ANIM_CreateAnimFrame( animator->ModelHandle(), animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset() + modelOffset, animator->RemoveOrigin() ); enabledArms = 0; // get all the joints for ( i = 0; i < numArms; i++ ) { jointName = self->spawnArgs.GetString( va( "ik_hand%d", i+1 ) ); handJoints[i] = animator->GetJointHandle( jointName ); if ( handJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Reach::Init: invalid hand joint '%s'", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_elbow%d", i+1 ) ); elbowJoints[i] = animator->GetJointHandle( jointName ); if ( elbowJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Reach::Init: invalid elbow joint '%s'\n", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_shoulder%d", i+1 ) ); shoulderJoints[i] = animator->GetJointHandle( jointName ); if ( shoulderJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Reach::Init: invalid shoulder joint '%s'\n", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_elbowDir%d", i+1 ) ); dirJoints[i] = animator->GetJointHandle( jointName ); enabledArms |= 1 << i; } // get the arm bone lengths and rotation matrices for ( i = 0; i < numArms; i++ ) { handAxis = joints[ handJoints[ i ] ].ToMat3(); handOrigin = joints[ handJoints[ i ] ].ToVec3(); elbowAxis = joints[ elbowJoints[ i ] ].ToMat3(); elbowOrigin = joints[ elbowJoints[ i ] ].ToVec3(); shoulderAxis = joints[ shoulderJoints[ i ] ].ToMat3(); shoulderOrigin = joints[ shoulderJoints[ i ] ].ToVec3(); // get the IK direction if ( dirJoints[i] != INVALID_JOINT ) { dirOrigin = joints[ dirJoints[ i ] ].ToVec3(); dir = dirOrigin - elbowOrigin; } else { dir.Set( -1.0f, 0.0f, 0.0f ); } shoulderForward[i] = dir * shoulderAxis.Transpose(); elbowForward[i] = dir * elbowAxis.Transpose(); // conversion from upper arm bone axis to should joint axis upperArmLength[i] = GetBoneAxis( shoulderOrigin, elbowOrigin, dir, axis ); upperArmToShoulderJoint[i] = shoulderAxis * axis.Transpose(); // conversion from lower arm bone axis to elbow joint axis lowerArmLength[i] = GetBoneAxis( elbowOrigin, handOrigin, dir, axis ); lowerArmToElbowJoint[i] = elbowAxis * axis.Transpose(); } initialized = true; return true; }
void OBJExporter::ConvertBrushToOBJ( OBJGroup& group, const idMapBrush* mapBrush, int entityNum, int primitiveNum, const idMat4& transform ) { OBJExporter::OBJObject& geometry = group.objects.Alloc(); geometry.name.Format( "Primitive.%i", primitiveNum ); // fix degenerate planes idPlane* planes = ( idPlane* ) _alloca16( mapBrush->GetNumSides() * sizeof( planes[0] ) ); for( int i = 0; i < mapBrush->GetNumSides(); i++ ) { planes[i] = mapBrush->GetSide( i )->GetPlane(); planes[i].FixDegeneracies( DEGENERATE_DIST_EPSILON ); } //idFixedWinding w; idList<idFixedWinding> planeWindings; idBounds bounds; bounds.Clear(); int numVerts = 0; int numIndexes = 0; bool badBrush = false; for( int i = 0; i < mapBrush->GetNumSides(); i++ ) { idMapBrushSide* mapSide = mapBrush->GetSide( i ); const idMaterial* material = declManager->FindMaterial( mapSide->GetMaterial() ); //contents |= ( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ); materials.AddUnique( material ); // chop base plane by other brush sides idFixedWinding& w = planeWindings.Alloc(); w.BaseForPlane( -planes[i] ); if( !w.GetNumPoints() ) { common->Printf( "Entity %i, Brush %i: base winding has no points\n", entityNum, primitiveNum ); badBrush = true; } for( int j = 0; j < mapBrush->GetNumSides() && w.GetNumPoints(); j++ ) { if( i == j ) { continue; } if( !w.ClipInPlace( -planes[j], 0 ) ) { // no intersection //badBrush = true; common->Printf( "Entity %i, Brush %i: no intersection with other brush plane\n", entityNum, primitiveNum ); //break; } } if( w.GetNumPoints() <= 2 ) { continue; } for( int j = 0; j < w.GetNumPoints(); j++ ) { const idVec3& v = w[j].ToVec3(); bounds.AddPoint( v ); } } // allocate the surface // copy the data from the windings and build polygons for( int i = 0; i < mapBrush->GetNumSides(); i++ ) { idMapBrushSide* mapSide = mapBrush->GetSide( i ); idFixedWinding& w = planeWindings[i]; if( !w.GetNumPoints() ) { continue; } OBJExporter::OBJFace& face = geometry.faces.Alloc(); face.material = declManager->FindMaterial( mapSide->GetMaterial() ); for( int j = 0; j < w.GetNumPoints(); j++ ) { idDrawVert& dv = face.verts.Alloc(); const idVec3& xyz = w[j].ToVec3(); dv.xyz = ( transform * idVec4( xyz.x, xyz.y, xyz.z, 1 ) ).ToVec3(); // calculate texture s/t from brush primitive texture matrix idVec4 texVec[2]; mapSide->GetTextureVectors( texVec ); idVec2 st; st.x = ( xyz * texVec[0].ToVec3() ) + texVec[0][3]; st.y = ( xyz * texVec[1].ToVec3() ) + texVec[1][3]; // flip y st.y = 1.0f - st.y; dv.SetTexCoord( st ); // copy normal dv.SetNormal( transform * mapSide->GetPlane().Normal() ); //if( dv->GetNormal().Length() < 0.9 || dv->GetNormal().Length() > 1.1 ) //{ // common->Error( "Bad normal in TriListForSide" ); //} } #if 0 // triangulate for( int j = 1; j < w.GetNumPoints() - 1; j++ ) { face.indexes.Append( numVerts ); face.indexes.Append( numVerts + j ); face.indexes.Append( numVerts + j + 1 ); } #else // export n-gon //for( int j = 0; j < w.GetNumPoints(); j++ ) // reverse order, so normal does not point inwards for( int j = w.GetNumPoints() - 1; j >= 0; j-- ) { face.indexes.Append( numVerts + j ); } #endif numVerts += w.GetNumPoints(); } }
/* ================= R_SortDrawSurfs ================= */ static void R_SortDrawSurfs( drawSurf_t ** drawSurfs, const int numDrawSurfs ) { #if 1 uint64 * indices = (uint64 *) _alloca16( numDrawSurfs * sizeof( indices[0] ) ); // sort the draw surfs based on: // 1. sort value (largest first) // 2. depth (smallest first) // 3. index (largest first) assert( numDrawSurfs <= 0xFFFF ); for ( int i = 0; i < numDrawSurfs; i++ ) { float sort = SS_POST_PROCESS - drawSurfs[i]->sort; assert( sort >= 0.0f ); uint64 dist = 0; if ( drawSurfs[i]->frontEndGeo != NULL ) { float min = 0.0f; float max = 1.0f; idRenderMatrix::DepthBoundsForBounds( min, max, drawSurfs[i]->space->mvp, drawSurfs[i]->frontEndGeo->bounds ); dist = idMath::Ftoui16( min * 0xFFFF ); } indices[i] = ( ( numDrawSurfs - i ) & 0xFFFF ) | ( dist << 16 ) | ( (uint64) ( *(uint32 *)&sort ) << 32 ); } const int64 MAX_LEVELS = 128; int64 lo[MAX_LEVELS]; int64 hi[MAX_LEVELS]; // Keep the top of the stack in registers to avoid load-hit-stores. register int64 st_lo = 0; register int64 st_hi = numDrawSurfs - 1; register int64 level = 0; for ( ; ; ) { register int64 i = st_lo; register int64 j = st_hi; if ( j - i >= 4 && level < MAX_LEVELS - 1 ) { register uint64 pivot = indices[( i + j ) / 2]; do { while ( indices[i] > pivot ) i++; while ( indices[j] < pivot ) j--; if ( i > j ) break; uint64 h = indices[i]; indices[i] = indices[j]; indices[j] = h; } while ( ++i <= --j ); // No need for these iterations because we are always sorting unique values. //while ( indices[j] == pivot && st_lo < j ) j--; //while ( indices[i] == pivot && i < st_hi ) i++; assert( level < MAX_LEVELS - 1 ); lo[level] = i; hi[level] = st_hi; st_hi = j; level++; } else { for( ; i < j; j-- ) { register int64 m = i; for ( int64 k = i + 1; k <= j; k++ ) { if ( indices[k] < indices[m] ) { m = k; } } uint64 h = indices[m]; indices[m] = indices[j]; indices[j] = h; } if ( --level < 0 ) { break; } st_lo = lo[level]; st_hi = hi[level]; } } drawSurf_t ** newDrawSurfs = (drawSurf_t **) indices; for ( int i = 0; i < numDrawSurfs; i++ ) { newDrawSurfs[i] = drawSurfs[numDrawSurfs - ( indices[i] & 0xFFFF )]; } memcpy( drawSurfs, newDrawSurfs, numDrawSurfs * sizeof( drawSurfs[0] ) ); #else struct local_t { static int R_QsortSurfaces( const void *a, const void *b ) { const drawSurf_t * ea = *(drawSurf_t **)a; const drawSurf_t * eb = *(drawSurf_t **)b; if ( ea->sort < eb->sort ) { return -1; } if ( ea->sort > eb->sort ) { return 1; } return 0; } }; // Add a sort offset so surfaces with equal sort orders still deterministically // draw in the order they were added, at least within a given model. float sorfOffset = 0.0f; for ( int i = 0; i < numDrawSurfs; i++ ) { drawSurf[i]->sort += sorfOffset; sorfOffset += 0.000001f; } // sort the drawsurfs qsort( drawSurfs, numDrawSurfs, sizeof( drawSurfs[0] ), local_t::R_QsortSurfaces ); #endif }
/* ==================== idMD5Anim::GetInterpolatedFrame ==================== */ void idMD5Anim::GetInterpolatedFrame( frameBlend_t &frame, idJointQuat *joints, const int *index, int numIndexes ) const { int i, numLerpJoints; const float *frame1; const float *frame2; const float *jointframe1; const float *jointframe2; const jointAnimInfo_t *infoPtr; int animBits; idJointQuat *blendJoints; idJointQuat *jointPtr; idJointQuat *blendPtr; int *lerpIndex; // copy the baseframe SIMDProcessor->Memcpy( joints, baseFrame.Ptr(), baseFrame.Num() * sizeof( baseFrame[ 0 ] ) ); if ( !numAnimatedComponents ) { // just use the base frame return; } blendJoints = (idJointQuat *)_alloca16( baseFrame.Num() * sizeof( blendPtr[ 0 ] ) ); lerpIndex = (int *)_alloca16( baseFrame.Num() * sizeof( lerpIndex[ 0 ] ) ); numLerpJoints = 0; frame1 = &componentFrames[ frame.frame1 * numAnimatedComponents ]; frame2 = &componentFrames[ frame.frame2 * numAnimatedComponents ]; for ( i = 0; i < numIndexes; i++ ) { int j = index[i]; jointPtr = &joints[j]; blendPtr = &blendJoints[j]; infoPtr = &jointInfo[j]; animBits = infoPtr->animBits; if ( animBits ) { lerpIndex[numLerpJoints++] = j; jointframe1 = frame1 + infoPtr->firstComponent; jointframe2 = frame2 + infoPtr->firstComponent; switch( animBits & (ANIM_TX|ANIM_TY|ANIM_TZ) ) { case 0: blendPtr->t = jointPtr->t; break; case ANIM_TX: jointPtr->t.x = jointframe1[0]; blendPtr->t.x = jointframe2[0]; blendPtr->t.y = jointPtr->t.y; blendPtr->t.z = jointPtr->t.z; jointframe1++; jointframe2++; break; case ANIM_TY: jointPtr->t.y = jointframe1[0]; blendPtr->t.y = jointframe2[0]; blendPtr->t.x = jointPtr->t.x; blendPtr->t.z = jointPtr->t.z; jointframe1++; jointframe2++; break; case ANIM_TZ: jointPtr->t.z = jointframe1[0]; blendPtr->t.z = jointframe2[0]; blendPtr->t.x = jointPtr->t.x; blendPtr->t.y = jointPtr->t.y; jointframe1++; jointframe2++; break; case ANIM_TX|ANIM_TY: jointPtr->t.x = jointframe1[0]; jointPtr->t.y = jointframe1[1]; blendPtr->t.x = jointframe2[0]; blendPtr->t.y = jointframe2[1]; blendPtr->t.z = jointPtr->t.z; jointframe1 += 2; jointframe2 += 2; break; case ANIM_TX|ANIM_TZ: jointPtr->t.x = jointframe1[0]; jointPtr->t.z = jointframe1[1]; blendPtr->t.x = jointframe2[0]; blendPtr->t.z = jointframe2[1]; blendPtr->t.y = jointPtr->t.y; jointframe1 += 2; jointframe2 += 2; break; case ANIM_TY|ANIM_TZ: jointPtr->t.y = jointframe1[0]; jointPtr->t.z = jointframe1[1]; blendPtr->t.y = jointframe2[0]; blendPtr->t.z = jointframe2[1]; blendPtr->t.x = jointPtr->t.x; jointframe1 += 2; jointframe2 += 2; break; case ANIM_TX|ANIM_TY|ANIM_TZ: jointPtr->t.x = jointframe1[0]; jointPtr->t.y = jointframe1[1]; jointPtr->t.z = jointframe1[2]; blendPtr->t.x = jointframe2[0]; blendPtr->t.y = jointframe2[1]; blendPtr->t.z = jointframe2[2]; jointframe1 += 3; jointframe2 += 3; break; } switch( animBits & (ANIM_QX|ANIM_QY|ANIM_QZ) ) { case 0: blendPtr->q = jointPtr->q; break; case ANIM_QX: jointPtr->q.x = jointframe1[0]; blendPtr->q.x = jointframe2[0]; blendPtr->q.y = jointPtr->q.y; blendPtr->q.z = jointPtr->q.z; jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); break; case ANIM_QY: jointPtr->q.y = jointframe1[0]; blendPtr->q.y = jointframe2[0]; blendPtr->q.x = jointPtr->q.x; blendPtr->q.z = jointPtr->q.z; jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); break; case ANIM_QZ: jointPtr->q.z = jointframe1[0]; blendPtr->q.z = jointframe2[0]; blendPtr->q.x = jointPtr->q.x; blendPtr->q.y = jointPtr->q.y; jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); break; case ANIM_QX|ANIM_QY: jointPtr->q.x = jointframe1[0]; jointPtr->q.y = jointframe1[1]; blendPtr->q.x = jointframe2[0]; blendPtr->q.y = jointframe2[1]; blendPtr->q.z = jointPtr->q.z; jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); break; case ANIM_QX|ANIM_QZ: jointPtr->q.x = jointframe1[0]; jointPtr->q.z = jointframe1[1]; blendPtr->q.x = jointframe2[0]; blendPtr->q.z = jointframe2[1]; blendPtr->q.y = jointPtr->q.y; jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); break; case ANIM_QY|ANIM_QZ: jointPtr->q.y = jointframe1[0]; jointPtr->q.z = jointframe1[1]; blendPtr->q.y = jointframe2[0]; blendPtr->q.z = jointframe2[1]; blendPtr->q.x = jointPtr->q.x; jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); break; case ANIM_QX|ANIM_QY|ANIM_QZ: jointPtr->q.x = jointframe1[0]; jointPtr->q.y = jointframe1[1]; jointPtr->q.z = jointframe1[2]; blendPtr->q.x = jointframe2[0]; blendPtr->q.y = jointframe2[1]; blendPtr->q.z = jointframe2[2]; jointPtr->q.w = jointPtr->q.CalcW(); blendPtr->q.w = blendPtr->q.CalcW(); break; } } } SIMDProcessor->BlendJoints( joints, blendJoints, frame.backlerp, lerpIndex, numLerpJoints ); if ( frame.cycleCount ) { joints[ 0 ].t += totaldelta * ( float )frame.cycleCount; } }
/* ================= idRenderModelDecal::CreateDecal ================= */ void idRenderModelDecal::CreateDecal( const idRenderModel *model, const decalProjectionInfo_t &localInfo ) { // check all model surfaces for( int surfNum = 0; surfNum < model->NumSurfaces(); surfNum++ ) { const modelSurface_t *surf = model->Surface( surfNum ); // if no geometry or no shader if( !surf->geometry || !surf->shader ) { continue; } // decals and overlays use the same rules if( !localInfo.force && !surf->shader->AllowOverlays() ) { continue; } srfTriangles_t *stri = surf->geometry; // if the triangle bounds do not overlap with projection bounds if( !localInfo.projectionBounds.IntersectsBounds( stri->bounds ) ) { continue; } // allocate memory for the cull bits byte *cullBits = ( byte * )_alloca16( stri->numVerts * sizeof( cullBits[0] ) ); // catagorize all points by the planes SIMDProcessor->DecalPointCull( cullBits, localInfo.boundingPlanes, stri->verts, stri->numVerts ); // find triangles inside the projection volume for( int triNum = 0, index = 0; index < stri->numIndexes; index += 3, triNum++ ) { int v1 = stri->indexes[index + 0]; int v2 = stri->indexes[index + 1]; int v3 = stri->indexes[index + 2]; // skip triangles completely off one side if( cullBits[v1] & cullBits[v2] & cullBits[v3] ) { continue; } // skip back facing triangles if( stri->facePlanes && stri->facePlanesCalculated && stri->facePlanes[triNum].Normal() * localInfo.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 2].Normal() < -0.1f ) { continue; } // create a winding with texture coordinates for the triangle idFixedWinding fw; fw.SetNumPoints( 3 ); if( localInfo.parallel ) { for( int j = 0; j < 3; j++ ) { fw[j] = stri->verts[stri->indexes[index + j]].xyz; fw[j].s = localInfo.textureAxis[0].Distance( fw[j].ToVec3() ); fw[j].t = localInfo.textureAxis[1].Distance( fw[j].ToVec3() ); } } else { for( int j = 0; j < 3; j++ ) { idVec3 dir; float scale; fw[j] = stri->verts[stri->indexes[index + j]].xyz; dir = fw[j].ToVec3() - localInfo.projectionOrigin; localInfo.boundingPlanes[NUM_DECAL_BOUNDING_PLANES - 1].RayIntersection( fw[j].ToVec3(), dir, scale ); dir = fw[j].ToVec3() + scale * dir; fw[j].s = localInfo.textureAxis[0].Distance( dir ); fw[j].t = localInfo.textureAxis[1].Distance( dir ); } } int orBits = cullBits[v1] | cullBits[v2] | cullBits[v3]; // clip the exact surface triangle to the projection volume for( int j = 0; j < NUM_DECAL_BOUNDING_PLANES; j++ ) { if( orBits & ( 1 << j ) ) { if( !fw.ClipInPlace( -localInfo.boundingPlanes[j] ) ) { break; } } } if( fw.GetNumPoints() == 0 ) { continue; } AddDepthFadedWinding( fw, localInfo.material, localInfo.fadePlanes, localInfo.fadeDepth, localInfo.startTime ); } } }
/* ==================== idMD5Mesh::ParseMesh ==================== */ void idMD5Mesh::ParseMesh( idLexer &parser, int numJoints, const idJointMat *joints ) { idToken token; idToken name; int num; int count; int jointnum; idStr shaderName; int i, j; idList<int> tris; idList<int> firstWeightForVertex; idList<int> numWeightsForVertex; int maxweight; idList<vertexWeight_t> tempWeights; parser.ExpectTokenString( "{" ); // // parse name // if ( parser.CheckTokenString( "name" ) ) { parser.ReadToken( &name ); } // // parse shader // parser.ExpectTokenString( "shader" ); parser.ReadToken( &token ); shaderName = token; shader = declManager->FindMaterial( shaderName ); // // parse texture coordinates // parser.ExpectTokenString( "numverts" ); count = parser.ParseInt(); if ( count < 0 ) { parser.Error( "Invalid size: %s", token.c_str() ); } texCoords.SetNum( count ); firstWeightForVertex.SetNum( count ); numWeightsForVertex.SetNum( count ); numWeights = 0; maxweight = 0; for( i = 0; i < texCoords.Num(); i++ ) { parser.ExpectTokenString( "vert" ); parser.ParseInt(); parser.Parse1DMatrix( 2, texCoords[ i ].ToFloatPtr() ); firstWeightForVertex[ i ] = parser.ParseInt(); numWeightsForVertex[ i ] = parser.ParseInt(); if ( !numWeightsForVertex[ i ] ) { parser.Error( "Vertex without any joint weights." ); } numWeights += numWeightsForVertex[ i ]; if ( numWeightsForVertex[ i ] + firstWeightForVertex[ i ] > maxweight ) { maxweight = numWeightsForVertex[ i ] + firstWeightForVertex[ i ]; } } // // parse tris // parser.ExpectTokenString( "numtris" ); count = parser.ParseInt(); if ( count < 0 ) { parser.Error( "Invalid size: %d", count ); } tris.SetNum( count * 3 ); numTris = count; for( i = 0; i < count; i++ ) { parser.ExpectTokenString( "tri" ); parser.ParseInt(); tris[ i * 3 + 0 ] = parser.ParseInt(); tris[ i * 3 + 1 ] = parser.ParseInt(); tris[ i * 3 + 2 ] = parser.ParseInt(); } // // parse weights // parser.ExpectTokenString( "numweights" ); count = parser.ParseInt(); if ( count < 0 ) { parser.Error( "Invalid size: %d", count ); } if ( maxweight > count ) { parser.Warning( "Vertices reference out of range weights in model (%d of %d weights).", maxweight, count ); } tempWeights.SetNum( count ); for( i = 0; i < count; i++ ) { parser.ExpectTokenString( "weight" ); parser.ParseInt(); jointnum = parser.ParseInt(); if ( ( jointnum < 0 ) || ( jointnum >= numJoints ) ) { parser.Error( "Joint Index out of range(%d): %d", numJoints, jointnum ); } tempWeights[ i ].joint = jointnum; tempWeights[ i ].jointWeight = parser.ParseFloat(); parser.Parse1DMatrix( 3, tempWeights[ i ].offset.ToFloatPtr() ); } // create pre-scaled weights and an index for the vertex/joint lookup scaledWeights = (idVec4 *) Mem_Alloc16( numWeights * sizeof( scaledWeights[0] ) ); weightIndex = (int *) Mem_Alloc16( numWeights * 2 * sizeof( weightIndex[0] ) ); memset( weightIndex, 0, numWeights * 2 * sizeof( weightIndex[0] ) ); count = 0; for( i = 0; i < texCoords.Num(); i++ ) { num = firstWeightForVertex[i]; for( j = 0; j < numWeightsForVertex[i]; j++, num++, count++ ) { scaledWeights[count].ToVec3() = tempWeights[num].offset * tempWeights[num].jointWeight; scaledWeights[count].w = tempWeights[num].jointWeight; weightIndex[count * 2 + 0] = tempWeights[num].joint * sizeof( idJointMat ); } weightIndex[count * 2 - 1] = 1; } tempWeights.Clear(); numWeightsForVertex.Clear(); firstWeightForVertex.Clear(); parser.ExpectTokenString( "}" ); // update counters c_numVerts += texCoords.Num(); c_numWeights += numWeights; c_numWeightJoints++; for ( i = 0; i < numWeights; i++ ) { c_numWeightJoints += weightIndex[i*2+1]; } // // build the information that will be common to all animations of this mesh: // silhouette edge connectivity and normal / tangent generation information // idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) ); for ( i = 0; i < texCoords.Num(); i++ ) { verts[i].Clear(); verts[i].st = texCoords[i]; } TransformVerts( verts, joints ); deformInfo = R_BuildDeformInfo( texCoords.Num(), verts, tris.Num(), tris.Ptr(), shader->UseUnsmoothedTangents() ); }
/* ==================== idRenderModelMD5::LoadModel used for initial loads, reloadModel, and reloading the data of purged models Upon exit, the model will absolutely be valid, but possibly as a default model ==================== */ void idRenderModelMD5::LoadModel() { int version; int i; int num; int parentNum; idToken token; idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS ); idJointQuat *pose; idMD5Joint *joint; idJointMat *poseMat3; if ( !purged ) { PurgeModel(); } purged = false; if ( !parser.LoadFile( name ) ) { MakeDefaultModel(); return; } parser.ExpectTokenString( MD5_VERSION_STRING ); version = parser.ParseInt(); if ( version != MD5_VERSION ) { parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION ); } // // skip commandline // parser.ExpectTokenString( "commandline" ); parser.ReadToken( &token ); // parse num joints parser.ExpectTokenString( "numJoints" ); num = parser.ParseInt(); joints.SetGranularity( 1 ); joints.SetNum( num ); defaultPose.SetGranularity( 1 ); defaultPose.SetNum( num ); poseMat3 = ( idJointMat * )_alloca16( num * sizeof( *poseMat3 ) ); // parse num meshes parser.ExpectTokenString( "numMeshes" ); num = parser.ParseInt(); if ( num < 0 ) { parser.Error( "Invalid size: %d", num ); } meshes.SetGranularity( 1 ); meshes.SetNum( num ); // // parse joints // parser.ExpectTokenString( "joints" ); parser.ExpectTokenString( "{" ); pose = defaultPose.Ptr(); joint = joints.Ptr(); for( i = 0; i < joints.Num(); i++, joint++, pose++ ) { ParseJoint( parser, joint, pose ); poseMat3[ i ].SetRotation( pose->q.ToMat3() ); poseMat3[ i ].SetTranslation( pose->t ); if ( joint->parent ) { parentNum = joint->parent - joints.Ptr(); pose->q = ( poseMat3[ i ].ToMat3() * poseMat3[ parentNum ].ToMat3().Transpose() ).ToQuat(); pose->t = ( poseMat3[ i ].ToVec3() - poseMat3[ parentNum ].ToVec3() ) * poseMat3[ parentNum ].ToMat3().Transpose(); } } parser.ExpectTokenString( "}" ); for( i = 0; i < meshes.Num(); i++ ) { parser.ExpectTokenString( "mesh" ); meshes[ i ].ParseMesh( parser, defaultPose.Num(), poseMat3 ); } // // calculate the bounds of the model // CalculateBounds( poseMat3 ); // set the timestamp for reloadmodels fileSystem->ReadFile( name, NULL, &timeStamp ); }
/* ================== R_WobbleskyTexGen ================== */ void R_WobbleskyTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) { idVec3 localViewOrigin; const int *parms = surf->material->GetTexGenRegisters(); float wobbleDegrees = surf->shaderRegisters[parms[0]]; float wobbleSpeed = surf->shaderRegisters[parms[1]]; float rotateSpeed = surf->shaderRegisters[parms[2]]; wobbleDegrees = wobbleDegrees * idMath::PI / 180; wobbleSpeed = wobbleSpeed * 2 * idMath::PI / 60; rotateSpeed = rotateSpeed * 2 * idMath::PI / 60; // very ad-hoc "wobble" transform float transform[16]; float a = tr.viewDef->floatTime * wobbleSpeed; float s = sin( a ) * sin( wobbleDegrees ); float c = cos( a ) * sin( wobbleDegrees ); float z = cos( wobbleDegrees ); idVec3 axis[3]; axis[2][0] = c; axis[2][1] = s; axis[2][2] = z; axis[1][0] = -sin( a * 2 ) * sin( wobbleDegrees ); axis[1][2] = -s * sin( wobbleDegrees ); axis[1][1] = sqrt( 1.0f - ( axis[1][0] * axis[1][0] + axis[1][2] * axis[1][2] ) ); // make the second vector exactly perpendicular to the first axis[1] -= ( axis[2] * axis[1] ) * axis[2]; axis[1].Normalize(); // construct the third with a cross axis[0].Cross( axis[1], axis[2] ); // add the rotate s = sin( rotateSpeed * tr.viewDef->floatTime ); c = cos( rotateSpeed * tr.viewDef->floatTime ); transform[0] = axis[0][0] * c + axis[1][0] * s; transform[4] = axis[0][1] * c + axis[1][1] * s; transform[8] = axis[0][2] * c + axis[1][2] * s; transform[1] = axis[1][0] * c - axis[0][0] * s; transform[5] = axis[1][1] * c - axis[0][1] * s; transform[9] = axis[1][2] * c - axis[0][2] * s; transform[2] = axis[2][0]; transform[6] = axis[2][1]; transform[10] = axis[2][2]; transform[3] = transform[7] = transform[11] = 0.0f; transform[12] = transform[13] = transform[14] = 0.0f; R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin ); int numVerts = surf->geo->numVerts; int size = numVerts * sizeof( idVec3 ); idVec3 *texCoords = ( idVec3 * ) _alloca16( size ); const idDrawVert *verts = surf->geo->verts; for( int i = 0; i < numVerts; i++ ) { idVec3 v; v[0] = verts[i].xyz[0] - localViewOrigin[0]; v[1] = verts[i].xyz[1] - localViewOrigin[1]; v[2] = verts[i].xyz[2] - localViewOrigin[2]; R_LocalPointToGlobal( transform, v, texCoords[i] ); } surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size ); }
/* ==================== idMD5Mesh::TransformScaledVerts Special transform to make the mesh seem fat or skinny. May be used for zombie deaths ==================== */ void idMD5Mesh::TransformScaledVerts( idDrawVert *verts, const idJointMat *entJoints, float scale ) { idVec4 *scaledWeights = (idVec4 *) _alloca16( numWeights * sizeof( scaledWeights[0] ) ); SIMDProcessor->Mul( scaledWeights[0].ToFloatPtr(), scale, scaledWeights[0].ToFloatPtr(), numWeights * 4 ); SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights ); }
/* ================ idAF::Load ================ */ bool idAF::Load( idEntity *ent, const char *fileName ) { int i, j; const idDeclAF *file; const idDeclModelDef *modelDef; idRenderModel *model; int numJoints; idJointMat *joints; assert( ent ); self = ent; physicsObj.SetSelf( self ); if( animator == NULL ) { gameLocal.DWarning( "Couldn't load af '%s' for entity '%s' at (%s): NULL animator\n", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString( 0 ) ); return false; } name = fileName; name.StripFileExtension(); file = static_cast<const idDeclAF *>( declManager->FindType( DECL_AF, name ) ); if( !file ) { gameLocal.DWarning( "Couldn't load af '%s' for entity '%s' at (%s)\n", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString( 0 ) ); return false; } if( file->bodies.Num() == 0 || file->bodies[0]->jointName != "origin" ) { gameLocal.DWarning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no body which modifies the origin joint.", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString( 0 ) ); return false; } modelDef = animator->ModelDef(); if( modelDef == NULL || modelDef->GetState() == DS_DEFAULTED ) { gameLocal.DWarning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no or defaulted modelDef '%s'", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString( 0 ), modelDef ? modelDef->GetName() : "" ); return false; } model = animator->ModelHandle(); if( model == NULL || model->IsDefaultModel() ) { gameLocal.DWarning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no or defaulted model '%s'", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString( 0 ), model ? model->Name() : "" ); return false; } // get the modified animation modifiedAnim = animator->GetAnim( ARTICULATED_FIGURE_ANIM ); if( !modifiedAnim ) { gameLocal.DWarning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) has no modified animation '%s'", name.c_str(), ent->name.c_str(), ent->GetPhysics()->GetOrigin().ToString( 0 ), ARTICULATED_FIGURE_ANIM ); return false; } // create the animation frame used to setup the articulated figure numJoints = animator->NumJoints(); joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) ); gameEdit->ANIM_CreateAnimFrame( model, animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset(), animator->RemoveOrigin() ); // set all vector positions from model joints file->Finish( GetJointTransform, joints, animator ); // initialize articulated figure physics physicsObj.SetGravity( gameLocal.GetGravity() ); physicsObj.SetClipMask( file->clipMask ); physicsObj.SetDefaultFriction( file->defaultLinearFriction, file->defaultAngularFriction, file->defaultContactFriction ); physicsObj.SetSuspendSpeed( file->suspendVelocity, file->suspendAcceleration ); physicsObj.SetSuspendTolerance( file->noMoveTime, file->noMoveTranslation, file->noMoveRotation ); physicsObj.SetSuspendTime( file->minMoveTime, file->maxMoveTime ); physicsObj.SetSelfCollision( file->selfCollision ); // clear the list with transforms from joints to bodies jointMods.SetNum( 0, false ); // clear the joint to body conversion list jointBody.AssureSize( animator->NumJoints() ); for( i = 0; i < jointBody.Num(); i++ ) { jointBody[i] = -1; } // delete any bodies in the physicsObj that are no longer in the idDeclAF for( i = 0; i < physicsObj.GetNumBodies(); i++ ) { idAFBody *body = physicsObj.GetBody( i ); for( j = 0; j < file->bodies.Num(); j++ ) { if( file->bodies[j]->name.Icmp( body->GetName() ) == 0 ) { break; } } if( j >= file->bodies.Num() ) { physicsObj.DeleteBody( i ); i--; } } // delete any constraints in the physicsObj that are no longer in the idDeclAF for( i = 0; i < physicsObj.GetNumConstraints(); i++ ) { idAFConstraint *constraint = physicsObj.GetConstraint( i ); for( j = 0; j < file->constraints.Num(); j++ ) { if( file->constraints[j]->name.Icmp( constraint->GetName() ) == 0 && file->constraints[j]->type == constraint->GetType() ) { break; } } if( j >= file->constraints.Num() ) { physicsObj.DeleteConstraint( i ); i--; } } // load bodies from the file for( i = 0; i < file->bodies.Num(); i++ ) { LoadBody( file->bodies[i], joints ); } // load constraints from the file for( i = 0; i < file->constraints.Num(); i++ ) { LoadConstraint( file->constraints[i] ); } physicsObj.UpdateClipModels(); // check if each joint is contained by a body for( i = 0; i < animator->NumJoints(); i++ ) { if( jointBody[i] == -1 ) { gameLocal.DWarning( "idAF::Load: articulated figure '%s' for entity '%s' at (%s) joint '%s' is not contained by a body", name.c_str(), self->name.c_str(), self->GetPhysics()->GetOrigin().ToString( 0 ), animator->GetJointName( ( jointHandle_t )i ) ); } } physicsObj.SetMass( file->totalMass ); physicsObj.SetChanged(); // disable the articulated figure for collision detection until activated physicsObj.DisableClip(); isLoaded = true; return true; }
/* ===================== R_CreateVertexProgramTurboShadowVolume are dangling edges that are outside the light frustum still making planes? ===================== */ srfTriangles_t *R_CreateVertexProgramTurboShadowVolume( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) { int i, j; srfTriangles_t *newTri; silEdge_t *sil; const glIndex_t *indexes; const byte *facing; R_CalcInteractionFacing( ent, tri, light, cullInfo ); if ( r_useShadowProjectedCull.GetBool() ) { R_CalcInteractionCullBits( ent, tri, light, cullInfo ); } int numFaces = tri->numIndexes / 3; int numShadowingFaces = 0; facing = cullInfo.facing; // if all the triangles are inside the light frustum if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT || !r_useShadowProjectedCull.GetBool() ) { // count the number of shadowing faces for ( i = 0; i < numFaces; i++ ) { numShadowingFaces += facing[i]; } numShadowingFaces = numFaces - numShadowingFaces; } else { // make all triangles that are outside the light frustum "facing", so they won't cast shadows indexes = tri->indexes; byte *modifyFacing = cullInfo.facing; const byte *cullBits = cullInfo.cullBits; for ( j = i = 0; i < tri->numIndexes; i += 3, j++ ) { if ( !modifyFacing[j] ) { int i1 = indexes[i+0]; int i2 = indexes[i+1]; int i3 = indexes[i+2]; if ( cullBits[i1] & cullBits[i2] & cullBits[i3] ) { modifyFacing[j] = 1; } else { numShadowingFaces++; } } } } if ( !numShadowingFaces ) { // no faces are inside the light frustum and still facing the right way return NULL; } // shadowVerts will be NULL on these surfaces, so the shadowVerts will be taken from the ambient surface newTri = R_AllocStaticTriSurf(); newTri->numVerts = tri->numVerts * 2; // alloc the max possible size #ifdef USE_TRI_DATA_ALLOCATOR R_AllocStaticTriSurfIndexes( newTri, ( numShadowingFaces + tri->numSilEdges ) * 6 ); glIndex_t *tempIndexes = newTri->indexes; glIndex_t *shadowIndexes = newTri->indexes; #else glIndex_t *tempIndexes = (glIndex_t *)_alloca16( tri->numSilEdges * 6 * sizeof( tempIndexes[0] ) ); glIndex_t *shadowIndexes = tempIndexes; #endif // create new triangles along sil planes for ( sil = tri->silEdges, i = tri->numSilEdges; i > 0; i--, sil++ ) { int f1 = facing[sil->p1]; int f2 = facing[sil->p2]; if ( !( f1 ^ f2 ) ) { continue; } int v1 = sil->v1 << 1; int v2 = sil->v2 << 1; // set the two triangle winding orders based on facing // without using a poorly-predictable branch shadowIndexes[0] = v1; shadowIndexes[1] = v2 ^ f1; shadowIndexes[2] = v2 ^ f2; shadowIndexes[3] = v1 ^ f2; shadowIndexes[4] = v1 ^ f1; shadowIndexes[5] = v2 ^ 1; shadowIndexes += 6; } int numShadowIndexes = shadowIndexes - tempIndexes; // we aren't bothering to separate front and back caps on these newTri->numIndexes = newTri->numShadowIndexesNoFrontCaps = numShadowIndexes + numShadowingFaces * 6; newTri->numShadowIndexesNoCaps = numShadowIndexes; newTri->shadowCapPlaneBits = SHADOW_CAP_INFINITE; #ifdef USE_TRI_DATA_ALLOCATOR // decrease the size of the memory block to only store the used indexes R_ResizeStaticTriSurfIndexes( newTri, newTri->numIndexes ); #else // allocate memory for the indexes R_AllocStaticTriSurfIndexes( newTri, newTri->numIndexes ); // copy the indexes we created for the sil planes SIMDProcessor->Memcpy( newTri->indexes, tempIndexes, numShadowIndexes * sizeof( tempIndexes[0] ) ); #endif // these have no effect, because they extend to infinity newTri->bounds.Clear(); // put some faces on the model and some on the distant projection indexes = tri->indexes; shadowIndexes = newTri->indexes + numShadowIndexes; for ( i = 0, j = 0; i < tri->numIndexes; i += 3, j++ ) { if ( facing[j] ) { continue; } int i0 = indexes[i+0] << 1; shadowIndexes[2] = i0; shadowIndexes[3] = i0 ^ 1; int i1 = indexes[i+1] << 1; shadowIndexes[1] = i1; shadowIndexes[4] = i1 ^ 1; int i2 = indexes[i+2] << 1; shadowIndexes[0] = i2; shadowIndexes[5] = i2 ^ 1; shadowIndexes += 6; } return newTri; }
/* ================ MMX_Memcpy2kB 240MB/sec ================ */ void MMX_Memcpy2kB( void *dest, const void *src, const int count ) { byte *tbuf = (byte *)_alloca16(2048); __asm { push ebx mov esi, src mov ebx, count shr ebx, 11 // 2048 bytes at a time mov edi, dest loop2k: push edi // copy 2k into temporary buffer mov edi, tbuf mov ecx, 32 loopMemToL1: prefetchnta 64[ESI] // Prefetch next loop, non-temporal prefetchnta 96[ESI] movq mm1, 0[ESI] // Read in source data movq mm2, 8[ESI] movq mm3, 16[ESI] movq mm4, 24[ESI] movq mm5, 32[ESI] movq mm6, 40[ESI] movq mm7, 48[ESI] movq mm0, 56[ESI] movq 0[EDI], mm1 // Store into L1 movq 8[EDI], mm2 movq 16[EDI], mm3 movq 24[EDI], mm4 movq 32[EDI], mm5 movq 40[EDI], mm6 movq 48[EDI], mm7 movq 56[EDI], mm0 add esi, 64 add edi, 64 dec ecx jnz loopMemToL1 pop edi // Now copy from L1 to system memory push esi mov esi, tbuf mov ecx, 32 loopL1ToMem: movq mm1, 0[ESI] // Read in source data from L1 movq mm2, 8[ESI] movq mm3, 16[ESI] movq mm4, 24[ESI] movq mm5, 32[ESI] movq mm6, 40[ESI] movq mm7, 48[ESI] movq mm0, 56[ESI] movntq 0[EDI], mm1 // Non-temporal stores movntq 8[EDI], mm2 movntq 16[EDI], mm3 movntq 24[EDI], mm4 movntq 32[EDI], mm5 movntq 40[EDI], mm6 movntq 48[EDI], mm7 movntq 56[EDI], mm0 add esi, 64 add edi, 64 dec ecx jnz loopL1ToMem pop esi // Do next 2k block dec ebx jnz loop2k pop ebx } EMMS_INSTRUCTION }
/* ==================== idRenderModelMD5::LoadModel used for initial loads, reloadModel, and reloading the data of purged models Upon exit, the model will absolutely be valid, but possibly as a default model ==================== */ void idRenderModelMD5::LoadModel() { int version; int num; int parentNum; idToken token; idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS ); if( !purged ) { PurgeModel(); } purged = false; if( !parser.LoadFile( name ) ) { MakeDefaultModel(); return; } parser.ExpectTokenString( MD5_VERSION_STRING ); version = parser.ParseInt(); if( version != MD5_VERSION ) { parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION ); } // // skip commandline // parser.ExpectTokenString( "commandline" ); parser.ReadToken( &token ); // parse num joints parser.ExpectTokenString( "numJoints" ); num = parser.ParseInt(); joints.SetGranularity( 1 ); joints.SetNum( num ); defaultPose.SetGranularity( 1 ); defaultPose.SetNum( num ); // parse num meshes parser.ExpectTokenString( "numMeshes" ); num = parser.ParseInt(); if( num < 0 ) { parser.Error( "Invalid size: %d", num ); } meshes.SetGranularity( 1 ); meshes.SetNum( num ); // // parse joints // parser.ExpectTokenString( "joints" ); parser.ExpectTokenString( "{" ); idJointMat* poseMat = ( idJointMat* )_alloca16( joints.Num() * sizeof( poseMat[0] ) ); for( int i = 0; i < joints.Num(); i++ ) { idMD5Joint* joint = &joints[i]; idJointQuat* pose = &defaultPose[i]; ParseJoint( parser, joint, pose ); poseMat[ i ].SetRotation( pose->q.ToMat3() ); poseMat[ i ].SetTranslation( pose->t ); if( joint->parent ) { parentNum = joint->parent - joints.Ptr(); pose->q = ( poseMat[ i ].ToMat3() * poseMat[ parentNum ].ToMat3().Transpose() ).ToQuat(); pose->t = ( poseMat[ i ].ToVec3() - poseMat[ parentNum ].ToVec3() ) * poseMat[ parentNum ].ToMat3().Transpose(); } } parser.ExpectTokenString( "}" ); //----------------------------------------- // create the inverse of the base pose joints to support tech6 style deformation // of base pose vertexes, normals, and tangents. // // vertex * joints * inverseJoints == vertex when joints is the base pose // When the joints are in another pose, it gives the animated vertex position //----------------------------------------- invertedDefaultPose.SetNum( SIMD_ROUND_JOINTS( joints.Num() ) ); for( int i = 0; i < joints.Num(); i++ ) { invertedDefaultPose[i] = poseMat[i]; invertedDefaultPose[i].Invert(); } SIMD_INIT_LAST_JOINT( invertedDefaultPose.Ptr(), joints.Num() ); for( int i = 0; i < meshes.Num(); i++ ) { parser.ExpectTokenString( "mesh" ); meshes[i].ParseMesh( parser, defaultPose.Num(), poseMat ); } // calculate the bounds of the model bounds.Clear(); for( int i = 0; i < meshes.Num(); i++ ) { idBounds meshBounds; meshes[i].CalculateBounds( poseMat, meshBounds ); bounds.AddBounds( meshBounds ); } // set the timestamp for reloadmodels fileSystem->ReadFile( name, NULL, &timeStamp ); common->UpdateLevelLoadPacifier(true); }
/* ================= R_CreateShadowVolumeInFrustum Adds new verts and indexes to the shadow volume. If the frustum completely defines the projected light, makeClippedPlanes should be true, which will cause sil quads to be added along all clipped edges. If the frustum is just part of a point light, clipped planes don't need to be added. ================= */ static void R_CreateShadowVolumeInFrustum( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, const idVec3 lightOrigin, const idPlane frustum[6], const idPlane &farPlane, bool makeClippedPlanes ) { int i; int numTris; unsigned short *pointCull; int numCapIndexes; int firstShadowIndex; int firstShadowVert; int cullBits; pointCull = (unsigned short *)_alloca16( tri->numVerts * sizeof( pointCull[0] ) ); // test the vertexes for inside the light frustum, which will allow // us to completely cull away some triangles from consideration. R_CalcPointCull( tri, frustum, pointCull ); // this may not be the first frustum added to the volume firstShadowIndex = numShadowIndexes; firstShadowVert = numShadowVerts; // decide which triangles front shadow volumes, clipping as needed int numFace = 0; numClipSilEdges = 0; numTris = tri->numIndexes / 3; for ( i = 0 ; i < numTris ; i++ ) { int i1, i2, i3; faceCastsShadow[i] = 0; // until shown otherwise // if it isn't facing the right way, don't add it // to the shadow volume if ( globalFacing[i] ) { continue; } ++numFace; i1 = tri->silIndexes[ i*3 + 0 ]; i2 = tri->silIndexes[ i*3 + 1 ]; i3 = tri->silIndexes[ i*3 + 2 ]; // if all the verts are off one side of the frustum, // don't add any of them if ( TRIANGLE_CULLED( i1, i2, i3 ) ) { continue; } // make sure the verts that are not on the negative sides // of the frustum are copied over. // we need to get the original verts even from clipped triangles // so the edges reference correctly, because an edge may be unclipped // even when a triangle is clipped. if ( numShadowVerts + 6 > MAX_SHADOW_VERTS ) { overflowed = true; return; } if ( !POINT_CULLED(i1) && remap[i1] == -1 ) { remap[i1] = numShadowVerts; shadowVerts[ numShadowVerts ].ToVec3() = tri->verts[i1].xyz; numShadowVerts+=2; } if ( !POINT_CULLED(i2) && remap[i2] == -1 ) { remap[i2] = numShadowVerts; shadowVerts[ numShadowVerts ].ToVec3() = tri->verts[i2].xyz; numShadowVerts+=2; } if ( !POINT_CULLED(i3) && remap[i3] == -1 ) { remap[i3] = numShadowVerts; shadowVerts[ numShadowVerts ].ToVec3() = tri->verts[i3].xyz; numShadowVerts+=2; } // clip the triangle if any points are on the negative sides if ( TRIANGLE_CLIPPED( i1, i2, i3 ) ) { cullBits = ( ( pointCull[ i1 ] ^ 0xfc0 ) | ( pointCull[ i2 ] ^ 0xfc0 ) | ( pointCull[ i3 ] ^ 0xfc0 ) ) >> 6; // this will also define clip edges that will become // silhouette planes if ( R_ClipTriangleToLight( tri->verts[i1].xyz, tri->verts[i2].xyz, tri->verts[i3].xyz, cullBits, frustum ) ) { faceCastsShadow[i] = 1; } } else { // instead of overflowing or drawing a streamer shadow, don't draw a shadow at all if ( numShadowIndexes + 3 > MAX_SHADOW_INDEXES ) { overflowed = true; return; } if ( remap[i1] == -1 || remap[i2] == -1 || remap[i3] == -1 ) { common->Error( "R_CreateShadowVolumeInFrustum: bad remap[]" ); } shadowIndexes[numShadowIndexes++] = remap[i3]; shadowIndexes[numShadowIndexes++] = remap[i2]; shadowIndexes[numShadowIndexes++] = remap[i1]; faceCastsShadow[i] = 1; } }
/* ================ idIK_Walk::Init ================ */ bool idIK_Walk::Init( idEntity *self, const char *anim, const idVec3 &modelOffset ) { int i; float footSize; idVec3 verts[4]; idTraceModel trm; const char *jointName; idVec3 dir, ankleOrigin, kneeOrigin, hipOrigin, dirOrigin; idMat3 axis, ankleAxis, kneeAxis, hipAxis; static idVec3 footWinding[4] = { idVec3( 1.0f, 1.0f, 0.0f ), idVec3( -1.0f, 1.0f, 0.0f ), idVec3( -1.0f, -1.0f, 0.0f ), idVec3( 1.0f, -1.0f, 0.0f ) }; if ( !self ) { return false; } numLegs = Min( self->spawnArgs.GetInt( "ik_numLegs", "0" ), MAX_LEGS ); if ( numLegs == 0 ) { return true; } if ( !idIK::Init( self, anim, modelOffset ) ) { return false; } int numJoints = animator->NumJoints(); idJointMat *joints = ( idJointMat * )_alloca16( numJoints * sizeof( joints[0] ) ); // create the animation frame used to setup the IK gameEdit->ANIM_CreateAnimFrame( animator->ModelHandle(), animator->GetAnim( modifiedAnim )->MD5Anim( 0 ), numJoints, joints, 1, animator->ModelDef()->GetVisualOffset() + modelOffset, animator->RemoveOrigin() ); enabledLegs = 0; // get all the joints for ( i = 0; i < numLegs; i++ ) { jointName = self->spawnArgs.GetString( va( "ik_foot%d", i+1 ) ); footJoints[i] = animator->GetJointHandle( jointName ); if ( footJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid foot joint '%s'", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_ankle%d", i+1 ) ); ankleJoints[i] = animator->GetJointHandle( jointName ); if ( ankleJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid ankle joint '%s'", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_knee%d", i+1 ) ); kneeJoints[i] = animator->GetJointHandle( jointName ); if ( kneeJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid knee joint '%s'\n", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_hip%d", i+1 ) ); hipJoints[i] = animator->GetJointHandle( jointName ); if ( hipJoints[i] == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid hip joint '%s'\n", jointName ); } jointName = self->spawnArgs.GetString( va( "ik_dir%d", i+1 ) ); dirJoints[i] = animator->GetJointHandle( jointName ); enabledLegs |= 1 << i; } jointName = self->spawnArgs.GetString( "ik_waist" ); waistJoint = animator->GetJointHandle( jointName ); if ( waistJoint == INVALID_JOINT ) { gameLocal.Error( "idIK_Walk::Init: invalid waist joint '%s'\n", jointName ); } // get the leg bone lengths and rotation matrices for ( i = 0; i < numLegs; i++ ) { oldAnkleHeights[i] = 0.0f; ankleAxis = joints[ ankleJoints[ i ] ].ToMat3(); ankleOrigin = joints[ ankleJoints[ i ] ].ToVec3(); kneeAxis = joints[ kneeJoints[ i ] ].ToMat3(); kneeOrigin = joints[ kneeJoints[ i ] ].ToVec3(); hipAxis = joints[ hipJoints[ i ] ].ToMat3(); hipOrigin = joints[ hipJoints[ i ] ].ToVec3(); // get the IK direction if ( dirJoints[i] != INVALID_JOINT ) { dirOrigin = joints[ dirJoints[ i ] ].ToVec3(); dir = dirOrigin - kneeOrigin; } else { dir.Set( 1.0f, 0.0f, 0.0f ); } hipForward[i] = dir * hipAxis.Transpose(); kneeForward[i] = dir * kneeAxis.Transpose(); // conversion from upper leg bone axis to hip joint axis upperLegLength[i] = GetBoneAxis( hipOrigin, kneeOrigin, dir, axis ); upperLegToHipJoint[i] = hipAxis * axis.Transpose(); // conversion from lower leg bone axis to knee joint axis lowerLegLength[i] = GetBoneAxis( kneeOrigin, ankleOrigin, dir, axis ); lowerLegToKneeJoint[i] = kneeAxis * axis.Transpose(); } smoothing = self->spawnArgs.GetFloat( "ik_smoothing", "0.75" ); waistSmoothing = self->spawnArgs.GetFloat( "ik_waistSmoothing", "0.75" ); footShift = self->spawnArgs.GetFloat( "ik_footShift", "0" ); waistShift = self->spawnArgs.GetFloat( "ik_waistShift", "0" ); minWaistFloorDist = self->spawnArgs.GetFloat( "ik_minWaistFloorDist", "0" ); minWaistAnkleDist = self->spawnArgs.GetFloat( "ik_minWaistAnkleDist", "0" ); footUpTrace = self->spawnArgs.GetFloat( "ik_footUpTrace", "32" ); footDownTrace = self->spawnArgs.GetFloat( "ik_footDownTrace", "32" ); tiltWaist = self->spawnArgs.GetBool( "ik_tiltWaist", "0" ); usePivot = self->spawnArgs.GetBool( "ik_usePivot", "0" ); // setup a clip model for the feet footSize = self->spawnArgs.GetFloat( "ik_footSize", "4" ) * 0.5f; if ( footSize > 0.0f ) { for ( i = 0; i < 4; i++ ) { verts[i] = footWinding[i] * footSize; } trm.SetupPolygon( verts, 4 ); footModel = new (TAG_PHYSICS_CLIP) idClipModel( trm ); } initialized = true; return true; }