/* ===================== R_PolytopeSurface Generate vertexes and indexes for a polytope, and optionally returns the polygon windings. The positive sides of the planes will be visible. ===================== */ srfTriangles_t *R_PolytopeSurface( int numPlanes, const idPlane *planes, idWinding **windings ) { int i, j; srfTriangles_t *tri; idFixedWinding planeWindings[MAX_POLYTOPE_PLANES]; int numVerts, numIndexes; if( numPlanes > MAX_POLYTOPE_PLANES ) { common->Error( "R_PolytopeSurface: more than %d planes", MAX_POLYTOPE_PLANES ); } numVerts = 0; numIndexes = 0; for( i = 0; i < numPlanes; i++ ) { const idPlane &plane = planes[i]; idFixedWinding &w = planeWindings[i]; w.BaseForPlane( plane ); for( j = 0; j < numPlanes; j++ ) { const idPlane &plane2 = planes[j]; if( j == i ) { continue; } else if( !w.ClipInPlace( -plane2, ON_EPSILON ) ) { break; } } if( w.GetNumPoints() <= 2 ) { continue; } numVerts += w.GetNumPoints(); numIndexes += ( w.GetNumPoints() - 2 ) * 3; } // allocate the surface tri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( tri, numVerts ); R_AllocStaticTriSurfIndexes( tri, numIndexes ); // copy the data from the windings for( i = 0; i < numPlanes; i++ ) { idFixedWinding &w = planeWindings[i]; if( !w.GetNumPoints() ) { continue; } for( j = 0 ; j < w.GetNumPoints() ; j++ ) { tri->verts[tri->numVerts + j ].Clear(); tri->verts[tri->numVerts + j ].xyz = w[j].ToVec3(); } for( j = 1 ; j < w.GetNumPoints() - 1 ; j++ ) { tri->indexes[ tri->numIndexes + 0 ] = tri->numVerts; tri->indexes[ tri->numIndexes + 1 ] = tri->numVerts + j; tri->indexes[ tri->numIndexes + 2 ] = tri->numVerts + j + 1; tri->numIndexes += 3; } tri->numVerts += w.GetNumPoints(); // optionally save the winding if( windings ) { windings[i] = new idWinding( w.GetNumPoints() ); *windings[i] = w; } } R_BoundTriSurf( tri ); return tri; }
/* ================ CombineModelSurfaces Frees the model and returns a new model with all triangles combined into one surface ================ */ static idRenderModel *CombineModelSurfaces( idRenderModel *model ) { int totalVerts; int totalIndexes; int numIndexes; int numVerts; int i, j; totalVerts = 0; totalIndexes = 0; for ( i = 0 ; i < model->NumSurfaces() ; i++ ) { const modelSurface_t *surf = model->Surface(i); totalVerts += surf->geometry->numVerts; totalIndexes += surf->geometry->numIndexes; } srfTriangles_t *newTri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( newTri, totalVerts ); R_AllocStaticTriSurfIndexes( newTri, totalIndexes ); newTri->numVerts = totalVerts; newTri->numIndexes = totalIndexes; newTri->bounds.Clear(); idDrawVert *verts = newTri->verts; glIndex_t *indexes = newTri->indexes; numIndexes = 0; numVerts = 0; for ( i = 0 ; i < model->NumSurfaces() ; i++ ) { const modelSurface_t *surf = model->Surface(i); const srfTriangles_t *tri = surf->geometry; memcpy( verts + numVerts, tri->verts, tri->numVerts * sizeof( tri->verts[0] ) ); for ( j = 0 ; j < tri->numIndexes ; j++ ) { indexes[numIndexes+j] = numVerts + tri->indexes[j]; } newTri->bounds.AddBounds( tri->bounds ); numIndexes += tri->numIndexes; numVerts += tri->numVerts; } modelSurface_t surf; surf.id = 0; surf.geometry = newTri; surf.shader = tr.defaultMaterial; idRenderModel *newModel = renderModelManager->AllocModel(); newModel->AddSurface( surf ); renderModelManager->FreeModel( model ); return newModel; }
/* ================ idRenderWorldLocal::ParseShadowModel ================ */ idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) { idRenderModel *model; idToken token; int j; srfTriangles_t *tri; modelSurface_t surf; src->ExpectTokenString( "{" ); // parse the name src->ExpectAnyToken( &token ); model = renderModelManager->AllocModel(); model->InitEmpty( token ); surf.shader = tr.defaultMaterial; tri = R_AllocStaticTriSurf(); surf.geometry = tri; tri->numVerts = src->ParseInt(); tri->numShadowIndexesNoCaps = src->ParseInt(); tri->numShadowIndexesNoFrontCaps = src->ParseInt(); tri->numIndexes = src->ParseInt(); tri->shadowCapPlaneBits = src->ParseInt(); R_AllocStaticTriSurfShadowVerts( tri, tri->numVerts ); tri->bounds.Clear(); for ( j = 0 ; j < tri->numVerts ; j++ ) { float vec[8]; src->Parse1DMatrix( 3, vec ); tri->shadowVertexes[j].xyz[0] = vec[0]; tri->shadowVertexes[j].xyz[1] = vec[1]; tri->shadowVertexes[j].xyz[2] = vec[2]; tri->shadowVertexes[j].xyz[3] = 1; // no homogenous value tri->bounds.AddPoint( tri->shadowVertexes[j].xyz.ToVec3() ); } R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for ( j = 0 ; j < tri->numIndexes ; j++ ) { tri->indexes[j] = src->ParseInt(); } // add the completed surface to the model model->AddSurface( surf ); src->ExpectTokenString( "}" ); // we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc. // model->FinishSurfaces(); return model; }
/* ==================== ShareMapTriVerts Converts independent triangles to shared vertex triangles ==================== */ srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris ) { const mapTri_t *step; int count; int i, j; int numVerts; int numIndexes; srfTriangles_t *uTri; // unique the vertexes count = CountTriList( tris ); uTri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( uTri, count * 3 ); R_AllocStaticTriSurfIndexes( uTri, count * 3 ); numVerts = 0; numIndexes = 0; for ( step = tris ; step ; step = step->next ) { for ( i = 0 ; i < 3 ; i++ ) { const idDrawVert *dv; dv = &step->v[i]; // search for a match for ( j = 0 ; j < numVerts ; j++ ) { if ( MatchVert( &uTri->verts[j], dv ) ) { break; } } if ( j == numVerts ) { numVerts++; uTri->verts[j].xyz = dv->xyz; uTri->verts[j].normal = dv->normal; uTri->verts[j].st[0] = dv->st[0]; uTri->verts[j].st[1] = dv->st[1]; } uTri->indexes[numIndexes++] = j; } } uTri->numVerts = numVerts; uTri->numIndexes = numIndexes; return uTri; }
/* ================ idRenderWorldLocal::ParseShadowModel ================ */ idRenderModel* idRenderWorldLocal::ParseShadowModel( idLexer* src, idFile* fileOut ) { idToken token; src->ExpectTokenString( "{" ); // parse the name src->ExpectAnyToken( &token ); idRenderModel* model = renderModelManager->AllocModel(); model->InitEmpty( token ); if( fileOut != NULL ) { // write out the type so the binary reader knows what to instantiate fileOut->WriteString( "shadowmodel" ); fileOut->WriteString( token ); } srfTriangles_t* tri = R_AllocStaticTriSurf(); tri->numVerts = src->ParseInt(); tri->numShadowIndexesNoCaps = src->ParseInt(); tri->numShadowIndexesNoFrontCaps = src->ParseInt(); tri->numIndexes = src->ParseInt(); tri->shadowCapPlaneBits = src->ParseInt(); assert( ( tri->numVerts & 1 ) == 0 ); R_AllocStaticTriSurfPreLightShadowVerts( tri, ALIGN( tri->numVerts, 2 ) ); tri->bounds.Clear(); for( int j = 0; j < tri->numVerts; j++ ) { float vec[8]; src->Parse1DMatrix( 3, vec ); tri->preLightShadowVertexes[j].xyzw[0] = vec[0]; tri->preLightShadowVertexes[j].xyzw[1] = vec[1]; tri->preLightShadowVertexes[j].xyzw[2] = vec[2]; tri->preLightShadowVertexes[j].xyzw[3] = 1.0f; // no homogenous value tri->bounds.AddPoint( tri->preLightShadowVertexes[j].xyzw.ToVec3() ); } // clear the last vertex if it wasn't stored if( ( tri->numVerts & 1 ) != 0 ) { tri->preLightShadowVertexes[ALIGN( tri->numVerts, 2 ) - 1].xyzw.Zero(); } // to be consistent set the number of vertices to half the number of shadow vertices tri->numVerts = ALIGN( tri->numVerts, 2 ) / 2; R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for( int j = 0; j < tri->numIndexes; j++ ) { tri->indexes[j] = src->ParseInt(); } // add the completed surface to the model modelSurface_t surf; surf.id = 0; surf.shader = tr.defaultMaterial; surf.geometry = tri; model->AddSurface( surf ); src->ExpectTokenString( "}" ); // NOTE: we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc. if( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) { model->WriteBinaryModel( fileOut, &mapTimeStamp ); } return model; }
/* ================ idRenderWorldLocal::ParseModel ================ */ idRenderModel* idRenderWorldLocal::ParseModel( idLexer* src, const char* mapName, ID_TIME_T mapTimeStamp, idFile* fileOut ) { idToken token; src->ExpectTokenString( "{" ); // parse the name src->ExpectAnyToken( &token ); idRenderModel* model = renderModelManager->AllocModel(); model->InitEmpty( token ); if( fileOut != NULL ) { // write out the type so the binary reader knows what to instantiate fileOut->WriteString( "shadowmodel" ); fileOut->WriteString( token ); } int numSurfaces = src->ParseInt(); if( numSurfaces < 0 ) { src->Error( "R_ParseModel: bad numSurfaces" ); } for( int i = 0; i < numSurfaces; i++ ) { src->ExpectTokenString( "{" ); src->ExpectAnyToken( &token ); modelSurface_t surf; surf.shader = declManager->FindMaterial( token ); ( ( idMaterial* )surf.shader )->AddReference(); srfTriangles_t* tri = R_AllocStaticTriSurf(); surf.geometry = tri; tri->numVerts = src->ParseInt(); tri->numIndexes = src->ParseInt(); // parse the vertices idTempArray<float> verts( tri->numVerts * 8 ); for( int j = 0; j < tri->numVerts; j++ ) { src->Parse1DMatrix( 8, &verts[j * 8] ); } // parse the indices idTempArray<triIndex_t> indexes( tri->numIndexes ); for( int j = 0; j < tri->numIndexes; j++ ) { indexes[j] = src->ParseInt(); } #if 1 // find the island that each vertex belongs to idTempArray<int> vertIslands( tri->numVerts ); idTempArray<bool> trisVisited( tri->numIndexes ); vertIslands.Zero(); trisVisited.Zero(); int numIslands = 0; for( int j = 0; j < tri->numIndexes; j += 3 ) { if( trisVisited[j] ) { continue; } int islandNum = ++numIslands; vertIslands[indexes[j + 0]] = islandNum; vertIslands[indexes[j + 1]] = islandNum; vertIslands[indexes[j + 2]] = islandNum; trisVisited[j] = true; idList<int> queue; queue.Append( j ); for( int n = 0; n < queue.Num(); n++ ) { int t = queue[n]; for( int k = 0; k < tri->numIndexes; k += 3 ) { if( trisVisited[k] ) { continue; } bool connected = indexes[t + 0] == indexes[k + 0] || indexes[t + 0] == indexes[k + 1] || indexes[t + 0] == indexes[k + 2] || indexes[t + 1] == indexes[k + 0] || indexes[t + 1] == indexes[k + 1] || indexes[t + 1] == indexes[k + 2] || indexes[t + 2] == indexes[k + 0] || indexes[t + 2] == indexes[k + 1] || indexes[t + 2] == indexes[k + 2]; if( connected ) { vertIslands[indexes[k + 0]] = islandNum; vertIslands[indexes[k + 1]] = islandNum; vertIslands[indexes[k + 2]] = islandNum; trisVisited[k] = true; queue.Append( k ); } } } } // center the texture coordinates for each island for maximum 16-bit precision for( int j = 1; j <= numIslands; j++ ) { float minS = idMath::INFINITY; float minT = idMath::INFINITY; float maxS = -idMath::INFINITY; float maxT = -idMath::INFINITY; for( int k = 0; k < tri->numVerts; k++ ) { if( vertIslands[k] == j ) { minS = Min( minS, verts[k * 8 + 3] ); maxS = Max( maxS, verts[k * 8 + 3] ); minT = Min( minT, verts[k * 8 + 4] ); maxT = Max( maxT, verts[k * 8 + 4] ); } } const float averageS = idMath::Ftoi( ( minS + maxS ) * 0.5f ); const float averageT = idMath::Ftoi( ( minT + maxT ) * 0.5f ); for( int k = 0; k < tri->numVerts; k++ ) { if( vertIslands[k] == j ) { verts[k * 8 + 3] -= averageS; verts[k * 8 + 4] -= averageT; } } } #endif R_AllocStaticTriSurfVerts( tri, tri->numVerts ); for( int j = 0; j < tri->numVerts; j++ ) { tri->verts[j].xyz[0] = verts[j * 8 + 0]; tri->verts[j].xyz[1] = verts[j * 8 + 1]; tri->verts[j].xyz[2] = verts[j * 8 + 2]; tri->verts[j].SetTexCoord( verts[j * 8 + 3], verts[j * 8 + 4] ); tri->verts[j].SetNormal( verts[j * 8 + 5], verts[j * 8 + 6], verts[j * 8 + 7] ); } R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for( int j = 0; j < tri->numIndexes; j++ ) { tri->indexes[j] = indexes[j]; } src->ExpectTokenString( "}" ); // add the completed surface to the model model->AddSurface( surf ); } src->ExpectTokenString( "}" ); model->FinishSurfaces(); if( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) { model->WriteBinaryModel( fileOut, &mapTimeStamp ); } return model; }
/* ==================== R_CreateLightTris The resulting surface will be a subset of the original triangles, it will never clip triangles, but it may cull on a per-triangle basis. ==================== */ static srfTriangles_t *R_CreateLightTris( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, const idMaterial *shader, srfCullInfo_t &cullInfo ) { int i; int numIndexes; glIndex_t *indexes; srfTriangles_t *newTri; int c_backfaced; int c_distance; idBounds bounds; bool includeBackFaces; int faceNum; tr.pc.c_createLightTris++; c_backfaced = 0; c_distance = 0; numIndexes = 0; indexes = NULL; // it is debatable if non-shadowing lights should light back faces. we aren't at the moment if ( r_lightAllBackFaces.GetBool() || light->lightShader->LightEffectsBackSides() || shader->ReceivesLightingOnBackSides() || ent->parms.noSelfShadow || ent->parms.noShadow ) { includeBackFaces = true; } else { includeBackFaces = false; } // allocate a new surface for the lit triangles newTri = R_AllocStaticTriSurf(); // save a reference to the original surface newTri->ambientSurface = const_cast<srfTriangles_t *>(tri); // the light surface references the verts of the ambient surface newTri->numVerts = tri->numVerts; R_ReferenceStaticTriSurfVerts( newTri, tri ); // calculate cull information if ( !includeBackFaces ) { R_CalcInteractionFacing( ent, tri, light, cullInfo ); } R_CalcInteractionCullBits( ent, tri, light, cullInfo ); // if the surface is completely inside the light frustum if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT ) { // if we aren't self shadowing, let back facing triangles get // through so the smooth shaded bump maps light all the way around if ( includeBackFaces ) { // the whole surface is lit so the light surface just references the indexes of the ambient surface R_ReferenceStaticTriSurfIndexes( newTri, tri ); numIndexes = tri->numIndexes; bounds = tri->bounds; } else { // the light tris indexes are going to be a subset of the original indexes so we generally // allocate too much memory here but we decrease the memory block when the number of indexes is known R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes ); // back face cull the individual triangles indexes = newTri->indexes; const byte *facing = cullInfo.facing; for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) { if ( !facing[ faceNum ] ) { c_backfaced++; continue; } indexes[numIndexes+0] = tri->indexes[i+0]; indexes[numIndexes+1] = tri->indexes[i+1]; indexes[numIndexes+2] = tri->indexes[i+2]; numIndexes += 3; } // get bounds for the surface SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes ); // decrease the size of the memory block to the size of the number of used indexes R_ResizeStaticTriSurfIndexes( newTri, numIndexes ); } } else { // the light tris indexes are going to be a subset of the original indexes so we generally // allocate too much memory here but we decrease the memory block when the number of indexes is known R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes ); // cull individual triangles indexes = newTri->indexes; const byte *facing = cullInfo.facing; const byte *cullBits = cullInfo.cullBits; for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) { int i1, i2, i3; // if we aren't self shadowing, let back facing triangles get // through so the smooth shaded bump maps light all the way around if ( !includeBackFaces ) { // back face cull if ( !facing[ faceNum ] ) { c_backfaced++; continue; } } i1 = tri->indexes[i+0]; i2 = tri->indexes[i+1]; i3 = tri->indexes[i+2]; // fast cull outside the frustum // if all three points are off one plane side, it definately isn't visible if ( cullBits[i1] & cullBits[i2] & cullBits[i3] ) { c_distance++; continue; } if ( r_usePreciseTriangleInteractions.GetBool() ) { // do a precise clipped cull if none of the points is completely inside the frustum // note that we do not actually use the clipped triangle, which would have Z fighting issues. if ( cullBits[i1] && cullBits[i2] && cullBits[i3] ) { int cull = cullBits[i1] | cullBits[i2] | cullBits[i3]; if ( !R_ClipTriangleToLight( tri->verts[i1].xyz, tri->verts[i2].xyz, tri->verts[i3].xyz, cull, cullInfo.localClipPlanes ) ) { continue; } } } // add to the list indexes[numIndexes+0] = i1; indexes[numIndexes+1] = i2; indexes[numIndexes+2] = i3; numIndexes += 3; } // get bounds for the surface SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes ); // decrease the size of the memory block to the size of the number of used indexes R_ResizeStaticTriSurfIndexes( newTri, numIndexes ); } if ( !numIndexes ) { R_ReallyFreeStaticTriSurf( newTri ); return NULL; } newTri->numIndexes = numIndexes; newTri->bounds = bounds; return newTri; }
/* ==================== idRenderModelOverlay::AddOverlaySurfacesToModel ==================== */ void idRenderModelOverlay::AddOverlaySurfacesToModel( idRenderModel *baseModel ) { int i, j, k, numVerts, numIndexes, surfaceNum; const modelSurface_t *baseSurf; idRenderModelStatic *staticModel; overlaySurface_t *surf; srfTriangles_t *newTri; modelSurface_t *newSurf; if ( baseModel == NULL || baseModel->IsDefaultModel() ) { return; } // md5 models won't have any surfaces when r_showSkel is set if ( !baseModel->NumSurfaces() ) { return; } if ( baseModel->IsDynamicModel() != DM_STATIC ) { common->Error( "idRenderModelOverlay::AddOverlaySurfacesToModel: baseModel is not a static model" ); } assert( dynamic_cast<idRenderModelStatic *>(baseModel) != NULL ); staticModel = static_cast<idRenderModelStatic *>(baseModel); staticModel->overlaysAdded = 0; if ( !materials.Num() ) { staticModel->DeleteSurfacesWithNegativeId(); return; } for ( k = 0; k < materials.Num(); k++ ) { numVerts = numIndexes = 0; for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) { numVerts += materials[k]->surfaces[i]->numVerts; numIndexes += materials[k]->surfaces[i]->numIndexes; } if ( staticModel->FindSurfaceWithId( -1 - k, surfaceNum ) ) { newSurf = &staticModel->surfaces[surfaceNum]; } else { newSurf = &staticModel->surfaces.Alloc(); newSurf->geometry = NULL; newSurf->shader = materials[k]->material; newSurf->id = -1 - k; } if ( newSurf->geometry == NULL || newSurf->geometry->numVerts < numVerts || newSurf->geometry->numIndexes < numIndexes ) { R_FreeStaticTriSurf( newSurf->geometry ); newSurf->geometry = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( newSurf->geometry, numVerts ); R_AllocStaticTriSurfIndexes( newSurf->geometry, numIndexes ); SIMDProcessor->Memset( newSurf->geometry->verts, 0, numVerts * sizeof( newTri->verts[0] ) ); } else { R_FreeStaticTriSurfVertexCaches( newSurf->geometry ); } newTri = newSurf->geometry; numVerts = numIndexes = 0; for ( i = 0; i < materials[k]->surfaces.Num(); i++ ) { surf = materials[k]->surfaces[i]; // get the model surface for this overlay surface if ( surf->surfaceNum < staticModel->NumSurfaces() ) { baseSurf = staticModel->Surface( surf->surfaceNum ); } else { baseSurf = NULL; } // if the surface ids no longer match if ( !baseSurf || baseSurf->id != surf->surfaceId ) { // find the surface with the correct id if ( staticModel->FindSurfaceWithId( surf->surfaceId, surf->surfaceNum ) ) { baseSurf = staticModel->Surface( surf->surfaceNum ); } else { // the surface with this id no longer exists FreeSurface( surf ); materials[k]->surfaces.RemoveIndex( i ); i--; continue; } } // copy indexes; for ( j = 0; j < surf->numIndexes; j++ ) { newTri->indexes[numIndexes + j] = numVerts + surf->indexes[j]; } numIndexes += surf->numIndexes; // copy vertices for ( j = 0; j < surf->numVerts; j++ ) { overlayVertex_t *overlayVert = &surf->verts[j]; newTri->verts[numVerts].st[0] = overlayVert->st[0]; newTri->verts[numVerts].st[1] = overlayVert->st[1]; if ( overlayVert->vertexNum >= baseSurf->geometry->numVerts ) { // This can happen when playing a demofile and a model has been changed since it was recorded, so just issue a warning and go on. common->Warning( "idRenderModelOverlay::AddOverlaySurfacesToModel: overlay vertex out of range. Model has probably changed since generating the overlay." ); FreeSurface( surf ); materials[k]->surfaces.RemoveIndex( i ); staticModel->DeleteSurfaceWithId( newSurf->id ); return; } newTri->verts[numVerts].xyz = baseSurf->geometry->verts[overlayVert->vertexNum].xyz; numVerts++; } } newTri->numVerts = numVerts; newTri->numIndexes = numIndexes; R_BoundTriSurf( newTri ); staticModel->overlaysAdded++; // so we don't create an overlay on an overlay surface } }
/* ==================== R_CreateInteractionLightTris This is only used for the static interaction case, dynamic interactions just draw everything and let the GPU deal with it. The resulting surface will be a subset of the original triangles, it will never clip triangles, but it may cull on a per-triangle basis. ==================== */ static srfTriangles_t* R_CreateInteractionLightTris( const idRenderEntityLocal* ent, const srfTriangles_t* tri, const idRenderLightLocal* light, const idMaterial* shader ) { SCOPED_PROFILE_EVENT( "R_CreateInteractionLightTris" ); int i; int numIndexes; triIndex_t* indexes; srfTriangles_t* newTri; int c_backfaced; int c_distance; idBounds bounds; bool includeBackFaces; int faceNum; c_backfaced = 0; c_distance = 0; numIndexes = 0; indexes = NULL; // it is debatable if non-shadowing lights should light back faces. we aren't at the moment // RB: now we do with r_useHalfLambert, so don't cull back faces if we have smooth shadowing enabled if( r_lightAllBackFaces.GetBool() || light->lightShader->LightEffectsBackSides() || shader->ReceivesLightingOnBackSides() || ent->parms.noSelfShadow || ent->parms.noShadow || ( r_useHalfLambertLighting.GetInteger() && r_useShadowMapping.GetBool() ) ) { includeBackFaces = true; } else { includeBackFaces = false; } // allocate a new surface for the lit triangles newTri = R_AllocStaticTriSurf(); // save a reference to the original surface newTri->ambientSurface = const_cast<srfTriangles_t*>( tri ); // the light surface references the verts of the ambient surface newTri->numVerts = tri->numVerts; R_ReferenceStaticTriSurfVerts( newTri, tri ); // calculate cull information srfCullInfo_t cullInfo = {}; if( !includeBackFaces ) { R_CalcInteractionFacing( ent, tri, light, cullInfo ); } R_CalcInteractionCullBits( ent, tri, light, cullInfo ); // if the surface is completely inside the light frustum if( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT ) { // if we aren't self shadowing, let back facing triangles get // through so the smooth shaded bump maps light all the way around if( includeBackFaces ) { // the whole surface is lit so the light surface just references the indexes of the ambient surface newTri->indexes = tri->indexes; newTri->indexCache = tri->indexCache; // R_ReferenceStaticTriSurfIndexes( newTri, tri ); numIndexes = tri->numIndexes; bounds = tri->bounds; } else { // the light tris indexes are going to be a subset of the original indexes so we generally // allocate too much memory here but we decrease the memory block when the number of indexes is known R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes ); // back face cull the individual triangles indexes = newTri->indexes; const byte* facing = cullInfo.facing; for( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) { if( !facing[ faceNum ] ) { c_backfaced++; continue; } indexes[numIndexes + 0] = tri->indexes[i + 0]; indexes[numIndexes + 1] = tri->indexes[i + 1]; indexes[numIndexes + 2] = tri->indexes[i + 2]; numIndexes += 3; } // get bounds for the surface SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes ); // decrease the size of the memory block to the size of the number of used indexes newTri->numIndexes = numIndexes; R_ResizeStaticTriSurfIndexes( newTri, numIndexes ); } } else { // the light tris indexes are going to be a subset of the original indexes so we generally // allocate too much memory here but we decrease the memory block when the number of indexes is known R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes ); // cull individual triangles indexes = newTri->indexes; const byte* facing = cullInfo.facing; const byte* cullBits = cullInfo.cullBits; for( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) { int i1, i2, i3; // if we aren't self shadowing, let back facing triangles get // through so the smooth shaded bump maps light all the way around if( !includeBackFaces ) { // back face cull if( !facing[ faceNum ] ) { c_backfaced++; continue; } } i1 = tri->indexes[i + 0]; i2 = tri->indexes[i + 1]; i3 = tri->indexes[i + 2]; // fast cull outside the frustum // if all three points are off one plane side, it definately isn't visible if( cullBits[i1] & cullBits[i2] & cullBits[i3] ) { c_distance++; continue; } // add to the list indexes[numIndexes + 0] = i1; indexes[numIndexes + 1] = i2; indexes[numIndexes + 2] = i3; numIndexes += 3; } // get bounds for the surface SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes ); // decrease the size of the memory block to the size of the number of used indexes newTri->numIndexes = numIndexes; R_ResizeStaticTriSurfIndexes( newTri, numIndexes ); } // free the cull information when it's no longer needed R_FreeInteractionCullInfo( cullInfo ); if( !numIndexes ) { R_FreeStaticTriSurf( newTri ); return NULL; } newTri->numIndexes = numIndexes; newTri->bounds = bounds; return newTri; }
/* ======================== idRenderModelMD5::LoadBinaryModel ======================== */ bool idRenderModelMD5::LoadBinaryModel( idFile* file, const ID_TIME_T sourceTimeStamp ) { if( !idRenderModelStatic::LoadBinaryModel( file, sourceTimeStamp ) ) { return false; } unsigned int magic = 0; file->ReadBig( magic ); if( magic != MD5B_MAGIC ) { return false; } int tempNum; file->ReadBig( tempNum ); joints.SetNum( tempNum ); for( int i = 0; i < joints.Num(); i++ ) { file->ReadString( joints[i].name ); int offset; file->ReadBig( offset ); if( offset >= 0 ) { joints[i].parent = joints.Ptr() + offset; } else { joints[i].parent = NULL; } } file->ReadBig( tempNum ); defaultPose.SetNum( tempNum ); for( int i = 0; i < defaultPose.Num(); i++ ) { file->ReadBig( defaultPose[i].q.x ); file->ReadBig( defaultPose[i].q.y ); file->ReadBig( defaultPose[i].q.z ); file->ReadBig( defaultPose[i].q.w ); file->ReadVec3( defaultPose[i].t ); } file->ReadBig( tempNum ); invertedDefaultPose.SetNum( tempNum ); for( int i = 0; i < invertedDefaultPose.Num(); i++ ) { file->ReadBigArray( invertedDefaultPose[ i ].ToFloatPtr(), JOINTMAT_TYPESIZE ); } SIMD_INIT_LAST_JOINT( invertedDefaultPose.Ptr(), joints.Num() ); file->ReadBig( tempNum ); meshes.SetNum( tempNum ); for( int i = 0; i < meshes.Num(); i++ ) { idStr materialName; file->ReadString( materialName ); if( materialName.IsEmpty() ) { meshes[i].shader = NULL; } else { meshes[i].shader = declManager->FindMaterial( materialName ); } file->ReadBig( meshes[i].numVerts ); file->ReadBig( meshes[i].numTris ); file->ReadBig( meshes[i].numMeshJoints ); meshes[i].meshJoints = ( byte* ) Mem_Alloc( meshes[i].numMeshJoints * sizeof( meshes[i].meshJoints[0] ), TAG_MODEL ); file->ReadBigArray( meshes[i].meshJoints, meshes[i].numMeshJoints ); file->ReadBig( meshes[i].maxJointVertDist ); meshes[i].deformInfo = ( deformInfo_t* )R_ClearedStaticAlloc( sizeof( deformInfo_t ) ); deformInfo_t& deform = *meshes[i].deformInfo; file->ReadBig( deform.numSourceVerts ); file->ReadBig( deform.numOutputVerts ); file->ReadBig( deform.numIndexes ); file->ReadBig( deform.numMirroredVerts ); file->ReadBig( deform.numDupVerts ); file->ReadBig( deform.numSilEdges ); srfTriangles_t tri; memset( &tri, 0, sizeof( srfTriangles_t ) ); if( deform.numOutputVerts > 0 ) { R_AllocStaticTriSurfVerts( &tri, deform.numOutputVerts ); deform.verts = tri.verts; file->ReadBigArray( deform.verts, deform.numOutputVerts ); } if( deform.numIndexes > 0 ) { R_AllocStaticTriSurfIndexes( &tri, deform.numIndexes ); R_AllocStaticTriSurfSilIndexes( &tri, deform.numIndexes ); deform.indexes = tri.indexes; deform.silIndexes = tri.silIndexes; file->ReadBigArray( deform.indexes, deform.numIndexes ); file->ReadBigArray( deform.silIndexes, deform.numIndexes ); } if( deform.numMirroredVerts > 0 ) { R_AllocStaticTriSurfMirroredVerts( &tri, deform.numMirroredVerts ); deform.mirroredVerts = tri.mirroredVerts; file->ReadBigArray( deform.mirroredVerts, deform.numMirroredVerts ); } if( deform.numDupVerts > 0 ) { R_AllocStaticTriSurfDupVerts( &tri, deform.numDupVerts ); deform.dupVerts = tri.dupVerts; file->ReadBigArray( deform.dupVerts, deform.numDupVerts * 2 ); } if( deform.numSilEdges > 0 ) { R_AllocStaticTriSurfSilEdges( &tri, deform.numSilEdges ); deform.silEdges = tri.silEdges; assert( deform.silEdges != NULL ); for( int j = 0; j < deform.numSilEdges; j++ ) { file->ReadBig( deform.silEdges[j].p1 ); file->ReadBig( deform.silEdges[j].p2 ); file->ReadBig( deform.silEdges[j].v1 ); file->ReadBig( deform.silEdges[j].v2 ); } } idShadowVertSkinned* shadowVerts = ( idShadowVertSkinned* ) Mem_Alloc( ALIGN( deform.numOutputVerts * 2 * sizeof( idShadowVertSkinned ), 16 ), TAG_MODEL ); idShadowVertSkinned::CreateShadowCache( shadowVerts, deform.verts, deform.numOutputVerts ); deform.staticAmbientCache = vertexCache.AllocStaticVertex( deform.verts, ALIGN( deform.numOutputVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); deform.staticIndexCache = vertexCache.AllocStaticIndex( deform.indexes, ALIGN( deform.numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); deform.staticShadowCache = vertexCache.AllocStaticVertex( shadowVerts, ALIGN( deform.numOutputVerts * 2 * sizeof( idShadowVertSkinned ), VERTEX_CACHE_ALIGN ) ); Mem_Free( shadowVerts ); file->ReadBig( meshes[i].surfaceNum ); } 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; }
/* =============== idRenderModelSprite::InstantiateDynamicModel =============== */ idRenderModel * idRenderModelSprite::InstantiateDynamicModel( const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel ) { idRenderModelStatic *staticModel; srfTriangles_t *tri; modelSurface_t surf; if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) { delete cachedModel; cachedModel = NULL; } if ( renderEntity == NULL || viewDef == NULL ) { delete cachedModel; return NULL; } if ( cachedModel != NULL ) { assert( dynamic_cast<idRenderModelStatic *>( cachedModel ) != NULL ); assert( idStr::Icmp( cachedModel->Name(), sprite_SnapshotName ) == 0 ); staticModel = static_cast<idRenderModelStatic *>( cachedModel ); surf = *staticModel->Surface( 0 ); tri = surf.geometry; } else { staticModel = new idRenderModelStatic; staticModel->InitEmpty( sprite_SnapshotName ); tri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( tri, 4 ); R_AllocStaticTriSurfIndexes( tri, 6 ); tri->verts[ 0 ].Clear(); tri->verts[ 0 ].normal.Set( 1.0f, 0.0f, 0.0f ); tri->verts[ 0 ].tangents[0].Set( 0.0f, 1.0f, 0.0f ); tri->verts[ 0 ].tangents[1].Set( 0.0f, 0.0f, 1.0f ); tri->verts[ 0 ].st[ 0 ] = 0.0f; tri->verts[ 0 ].st[ 1 ] = 0.0f; tri->verts[ 1 ].Clear(); tri->verts[ 1 ].normal.Set( 1.0f, 0.0f, 0.0f ); tri->verts[ 1 ].tangents[0].Set( 0.0f, 1.0f, 0.0f ); tri->verts[ 1 ].tangents[1].Set( 0.0f, 0.0f, 1.0f ); tri->verts[ 1 ].st[ 0 ] = 1.0f; tri->verts[ 1 ].st[ 1 ] = 0.0f; tri->verts[ 2 ].Clear(); tri->verts[ 2 ].normal.Set( 1.0f, 0.0f, 0.0f ); tri->verts[ 2 ].tangents[0].Set( 0.0f, 1.0f, 0.0f ); tri->verts[ 2 ].tangents[1].Set( 0.0f, 0.0f, 1.0f ); tri->verts[ 2 ].st[ 0 ] = 1.0f; tri->verts[ 2 ].st[ 1 ] = 1.0f; tri->verts[ 3 ].Clear(); tri->verts[ 3 ].normal.Set( 1.0f, 0.0f, 0.0f ); tri->verts[ 3 ].tangents[0].Set( 0.0f, 1.0f, 0.0f ); tri->verts[ 3 ].tangents[1].Set( 0.0f, 0.0f, 1.0f ); tri->verts[ 3 ].st[ 0 ] = 0.0f; tri->verts[ 3 ].st[ 1 ] = 1.0f; tri->indexes[ 0 ] = 0; tri->indexes[ 1 ] = 1; tri->indexes[ 2 ] = 3; tri->indexes[ 3 ] = 1; tri->indexes[ 4 ] = 2; tri->indexes[ 5 ] = 3; tri->numVerts = 4; tri->numIndexes = 6; surf.geometry = tri; surf.id = 0; surf.shader = tr.defaultMaterial; staticModel->AddSurface( surf ); } int red = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_RED ] * 255.0f ); int green = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_GREEN ] * 255.0f ); int blue = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_BLUE ] * 255.0f ); int alpha = idMath::FtoiFast( renderEntity->shaderParms[ SHADERPARM_ALPHA ] * 255.0f ); idVec3 right = idVec3( 0.0f, renderEntity->shaderParms[ SHADERPARM_SPRITE_WIDTH ] * 0.5f, 0.0f ); idVec3 up = idVec3( 0.0f, 0.0f, renderEntity->shaderParms[ SHADERPARM_SPRITE_HEIGHT ] * 0.5f ); tri->verts[ 0 ].xyz = up + right; tri->verts[ 0 ].color[ 0 ] = red; tri->verts[ 0 ].color[ 1 ] = green; tri->verts[ 0 ].color[ 2 ] = blue; tri->verts[ 0 ].color[ 3 ] = alpha; tri->verts[ 1 ].xyz = up - right; tri->verts[ 1 ].color[ 0 ] = red; tri->verts[ 1 ].color[ 1 ] = green; tri->verts[ 1 ].color[ 2 ] = blue; tri->verts[ 1 ].color[ 3 ] = alpha; tri->verts[ 2 ].xyz = - right - up; tri->verts[ 2 ].color[ 0 ] = red; tri->verts[ 2 ].color[ 1 ] = green; tri->verts[ 2 ].color[ 2 ] = blue; tri->verts[ 2 ].color[ 3 ] = alpha; tri->verts[ 3 ].xyz = right - up; tri->verts[ 3 ].color[ 0 ] = red; tri->verts[ 3 ].color[ 1 ] = green; tri->verts[ 3 ].color[ 2 ] = blue; tri->verts[ 3 ].color[ 3 ] = alpha; R_BoundTriSurf( tri ); staticModel->bounds = tri->bounds; return staticModel; }
/* ================ idRenderWorldLocal::ParseModel ================ */ idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) { idRenderModel *model; idToken token; int i, j; srfTriangles_t *tri; modelSurface_t surf; src->ExpectTokenString( "{" ); // parse the name src->ExpectAnyToken( &token ); model = renderModelManager->AllocModel(); model->InitEmpty( token ); int numSurfaces = src->ParseInt(); if ( numSurfaces < 0 ) { src->Error( "R_ParseModel: bad numSurfaces" ); } for ( i = 0 ; i < numSurfaces ; i++ ) { src->ExpectTokenString( "{" ); src->ExpectAnyToken( &token ); surf.shader = declManager->FindMaterial( token ); ((idMaterial*)surf.shader)->AddReference(); tri = R_AllocStaticTriSurf(); surf.geometry = tri; tri->numVerts = src->ParseInt(); tri->numIndexes = src->ParseInt(); R_AllocStaticTriSurfVerts( tri, tri->numVerts ); for ( j = 0 ; j < tri->numVerts ; j++ ) { float vec[8]; src->Parse1DMatrix( 8, vec ); tri->verts[j].xyz[0] = vec[0]; tri->verts[j].xyz[1] = vec[1]; tri->verts[j].xyz[2] = vec[2]; tri->verts[j].st[0] = vec[3]; tri->verts[j].st[1] = vec[4]; tri->verts[j].normal[0] = vec[5]; tri->verts[j].normal[1] = vec[6]; tri->verts[j].normal[2] = vec[7]; } R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for ( j = 0 ; j < tri->numIndexes ; j++ ) { tri->indexes[j] = src->ParseInt(); } src->ExpectTokenString( "}" ); // add the completed surface to the model model->AddSurface( surf ); } src->ExpectTokenString( "}" ); model->FinishSurfaces(); return model; }
/* ==================== idRenderModelPrt::InstantiateDynamicModel ==================== */ idRenderModel* idRenderModelPrt::InstantiateDynamicModel( const struct renderEntity_s* renderEntity, const viewDef_t* viewDef, idRenderModel* cachedModel ) { idRenderModelStatic* staticModel; if( cachedModel && !r_useCachedDynamicModels.GetBool() ) { delete cachedModel; cachedModel = NULL; } // this may be triggered by a model trace or other non-view related source, to which we should look like an empty model if( renderEntity == NULL || viewDef == NULL ) { delete cachedModel; return NULL; } if( r_skipParticles.GetBool() ) { delete cachedModel; return NULL; } /* // if the entire system has faded out if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] && viewDef->renderView.time * 0.001f >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] ) { delete cachedModel; return NULL; } */ if( cachedModel != NULL ) { assert( dynamic_cast<idRenderModelStatic*>( cachedModel ) != NULL ); assert( idStr::Icmp( cachedModel->Name(), parametricParticle_SnapshotName ) == 0 ); staticModel = static_cast<idRenderModelStatic*>( cachedModel ); } else { staticModel = new( TAG_MODEL ) idRenderModelStatic; staticModel->InitEmpty( parametricParticle_SnapshotName ); } particleGen_t g; g.renderEnt = renderEntity; g.renderView = &viewDef->renderView; g.origin.Zero(); g.axis.Identity(); for( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) { idParticleStage* stage = particleSystem->stages[stageNum]; if( !stage->material ) { continue; } if( !stage->cycleMsec ) { continue; } if( stage->hidden ) // just for gui particle editor use { staticModel->DeleteSurfaceWithId( stageNum ); continue; } idRandom steppingRandom, steppingRandom2; int stageAge = g.renderView->time[renderEntity->timeGroup] + renderEntity->shaderParms[SHADERPARM_TIMEOFFSET] * 1000 - stage->timeOffset * 1000; int stageCycle = stageAge / stage->cycleMsec; // some particles will be in this cycle, some will be in the previous cycle steppingRandom.SetSeed( ( ( stageCycle << 10 ) & idRandom::MAX_RAND ) ^ ( int )( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) ); steppingRandom2.SetSeed( ( ( ( stageCycle - 1 ) << 10 ) & idRandom::MAX_RAND ) ^ ( int )( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) ); int count = stage->totalParticles * stage->NumQuadsPerParticle(); int surfaceNum; modelSurface_t* surf; if( staticModel->FindSurfaceWithId( stageNum, surfaceNum ) ) { surf = &staticModel->surfaces[surfaceNum]; R_FreeStaticTriSurfVertexCaches( surf->geometry ); } else { surf = &staticModel->surfaces.Alloc(); surf->id = stageNum; surf->shader = stage->material; surf->geometry = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( surf->geometry, 4 * count ); R_AllocStaticTriSurfIndexes( surf->geometry, 6 * count ); } int numVerts = 0; idDrawVert* verts = surf->geometry->verts; for( int index = 0; index < stage->totalParticles; index++ ) { g.index = index; // bump the random steppingRandom.RandomInt(); steppingRandom2.RandomInt(); // calculate local age for this index int bunchOffset = stage->particleLife * 1000 * stage->spawnBunching * index / stage->totalParticles; int particleAge = stageAge - bunchOffset; int particleCycle = particleAge / stage->cycleMsec; if( particleCycle < 0 ) { // before the particleSystem spawned continue; } if( stage->cycles && particleCycle >= stage->cycles ) { // cycled systems will only run cycle times continue; } if( particleCycle == stageCycle ) { g.random = steppingRandom; } else { g.random = steppingRandom2; } int inCycleTime = particleAge - particleCycle * stage->cycleMsec; if( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] && g.renderView->time[renderEntity->timeGroup] - inCycleTime >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] * 1000 ) { // don't fire any more particles continue; } // supress particles before or after the age clamp g.frac = ( float )inCycleTime / ( stage->particleLife * 1000 ); if( g.frac < 0.0f ) { // yet to be spawned continue; } if( g.frac > 1.0f ) { // this particle is in the deadTime band continue; } // this is needed so aimed particles can calculate origins at different times g.originalRandom = g.random; g.age = g.frac * stage->particleLife; // if the particle doesn't get drawn because it is faded out or beyond a kill region, don't increment the verts numVerts += stage->CreateParticle( &g, verts + numVerts ); } // numVerts must be a multiple of 4 assert( ( numVerts & 3 ) == 0 && numVerts <= 4 * count ); // build the indexes int numIndexes = 0; triIndex_t* indexes = surf->geometry->indexes; for( int i = 0; i < numVerts; i += 4 ) { indexes[numIndexes + 0] = i + 0; indexes[numIndexes + 1] = i + 2; indexes[numIndexes + 2] = i + 3; indexes[numIndexes + 3] = i + 0; indexes[numIndexes + 4] = i + 3; indexes[numIndexes + 5] = i + 1; numIndexes += 6; } surf->geometry->tangentsCalculated = false; surf->geometry->numVerts = numVerts; surf->geometry->numIndexes = numIndexes; surf->geometry->bounds = stage->bounds; // just always draw the particles } return staticModel; }
/* =============== idRenderModelBeam::InstantiateDynamicModel =============== */ idRenderModel *idRenderModelBeam::InstantiateDynamicModel(const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel) { idRenderModelStatic *staticModel; srfTriangles_t *tri; modelSurface_t surf; if (cachedModel) { delete cachedModel; cachedModel = NULL; } if (renderEntity == NULL || viewDef == NULL) { delete cachedModel; return NULL; } if (cachedModel != NULL) { assert(dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL); assert(idStr::Icmp(cachedModel->Name(), beam_SnapshotName) == 0); staticModel = static_cast<idRenderModelStatic *>(cachedModel); surf = *staticModel->Surface(0); tri = surf.geometry; } else { staticModel = new idRenderModelStatic; staticModel->InitEmpty(beam_SnapshotName); tri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts(tri, 4); R_AllocStaticTriSurfIndexes(tri, 6); tri->verts[0].Clear(); tri->verts[0].st[0] = 0; tri->verts[0].st[1] = 0; tri->verts[1].Clear(); tri->verts[1].st[0] = 0; tri->verts[1].st[1] = 1; tri->verts[2].Clear(); tri->verts[2].st[0] = 1; tri->verts[2].st[1] = 0; tri->verts[3].Clear(); tri->verts[3].st[0] = 1; tri->verts[3].st[1] = 1; tri->indexes[0] = 0; tri->indexes[1] = 2; tri->indexes[2] = 1; tri->indexes[3] = 2; tri->indexes[4] = 3; tri->indexes[5] = 1; tri->numVerts = 4; tri->numIndexes = 6; surf.geometry = tri; surf.id = 0; surf.shader = tr.defaultMaterial; staticModel->AddSurface(surf); } idVec3 target = *reinterpret_cast<const idVec3 *>(&renderEntity->shaderParms[SHADERPARM_BEAM_END_X]); // we need the view direction to project the minor axis of the tube // as the view changes idVec3 localView, localTarget; float modelMatrix[16]; R_AxisToModelMatrix(renderEntity->axis, renderEntity->origin, modelMatrix); R_GlobalPointToLocal(modelMatrix, viewDef->renderView.vieworg, localView); R_GlobalPointToLocal(modelMatrix, target, localTarget); idVec3 major = localTarget; idVec3 minor; idVec3 mid = 0.5f * localTarget; idVec3 dir = mid - localView; minor.Cross(major, dir); minor.Normalize(); if (renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] != 0.0f) { minor *= renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] * 0.5f; } int red = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_RED] * 255.0f); int green = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_GREEN] * 255.0f); int blue = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_BLUE] * 255.0f); int alpha = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_ALPHA] * 255.0f); tri->verts[0].xyz = minor; tri->verts[0].color[0] = red; tri->verts[0].color[1] = green; tri->verts[0].color[2] = blue; tri->verts[0].color[3] = alpha; tri->verts[1].xyz = -minor; tri->verts[1].color[0] = red; tri->verts[1].color[1] = green; tri->verts[1].color[2] = blue; tri->verts[1].color[3] = alpha; tri->verts[2].xyz = localTarget + minor; tri->verts[2].color[0] = red; tri->verts[2].color[1] = green; tri->verts[2].color[2] = blue; tri->verts[2].color[3] = alpha; tri->verts[3].xyz = localTarget - minor; tri->verts[3].color[0] = red; tri->verts[3].color[1] = green; tri->verts[3].color[2] = blue; tri->verts[3].color[3] = alpha; R_BoundTriSurf(tri); staticModel->bounds = tri->bounds; return staticModel; }
/* ============= idRenderModelMD3::InstantiateDynamicModel ============= */ idRenderModel *idRenderModelMD3::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) { int i, j; float backlerp; int * triangles; float * texCoords; int indexes; int numVerts; md3Surface_t * surface; int frame, oldframe; idRenderModelStatic *staticModel; if ( cachedModel ) { delete cachedModel; cachedModel = NULL; } staticModel = new idRenderModelStatic; staticModel->bounds.Clear(); surface = (md3Surface_t *) ((byte *)md3 + md3->ofsSurfaces); // TODO: these need set by an entity frame = ent->shaderParms[SHADERPARM_MD3_FRAME]; // probably want to keep frames < 1000 or so oldframe = ent->shaderParms[SHADERPARM_MD3_LASTFRAME]; backlerp = ent->shaderParms[SHADERPARM_MD3_BACKLERP]; for( i = 0; i < md3->numSurfaces; i++ ) { srfTriangles_t *tri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( tri, surface->numVerts ); R_AllocStaticTriSurfIndexes( tri, surface->numTriangles * 3 ); tri->bounds.Clear(); modelSurface_t surf; surf.geometry = tri; md3Shader_t* shaders = (md3Shader_t *) ((byte *)surface + surface->ofsShaders); surf.shader = shaders->shader; LerpMeshVertexes( tri, surface, backlerp, frame, oldframe ); triangles = (int *) ((byte *)surface + surface->ofsTriangles); indexes = surface->numTriangles * 3; for (j = 0 ; j < indexes ; j++) { tri->indexes[j] = triangles[j]; } tri->numIndexes += indexes; texCoords = (float *) ((byte *)surface + surface->ofsSt); numVerts = surface->numVerts; for ( j = 0; j < numVerts; j++ ) { idDrawVert *stri = &tri->verts[j]; stri->st[0] = texCoords[j*2+0]; stri->st[1] = texCoords[j*2+1]; } R_BoundTriSurf( tri ); staticModel->AddSurface( surf ); staticModel->bounds.AddPoint( surf.geometry->bounds[0] ); staticModel->bounds.AddPoint( surf.geometry->bounds[1] ); // find the next surface surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd ); } return staticModel; }
/* ===================== R_CreateInteractionShadowVolume Note that dangling edges outside the light frustum don't make silhouette planes because a triangle outside the light frustum is considered facing and the "fake triangle" on the outside of the dangling edge is also set to facing: cullInfo.facing[numFaces] = 1; ===================== */ static srfTriangles_t *R_CreateInteractionShadowVolume( const idRenderEntityLocal * ent, const srfTriangles_t * tri, const idRenderLightLocal * light ) { SCOPED_PROFILE_EVENT( "R_CreateInteractionShadowVolume" ); srfCullInfo_t cullInfo = {}; R_CalcInteractionFacing( ent, tri, light, cullInfo ); R_CalcInteractionCullBits( ent, tri, light, cullInfo ); int numFaces = tri->numIndexes / 3; int numShadowingFaces = 0; const byte * facing = cullInfo.facing; // if all the triangles are inside the light frustum if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT ) { // count the number of shadowing faces for ( int 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 const triIndex_t * indexes = tri->indexes; byte *modifyFacing = cullInfo.facing; const byte *cullBits = cullInfo.cullBits; for ( int i = 0, j = 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 R_FreeInteractionCullInfo( cullInfo ); return NULL; } // shadowVerts will be NULL on these surfaces, so the shadowVerts will be taken from the ambient surface srfTriangles_t * newTri = R_AllocStaticTriSurf(); newTri->numVerts = tri->numVerts * 2; // alloc the max possible size R_AllocStaticTriSurfIndexes( newTri, ( numShadowingFaces + tri->numSilEdges ) * 6 ); triIndex_t * tempIndexes = newTri->indexes; triIndex_t * shadowIndexes = newTri->indexes; // create new triangles along sil planes const silEdge_t * sil = tri->silEdges; for ( int 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; // decrease the size of the memory block to only store the used indexes // R_ResizeStaticTriSurfIndexes( newTri, newTri->numIndexes ); // these have no effect, because they extend to infinity newTri->bounds.Clear(); // put some faces on the model and some on the distant projection const triIndex_t * indexes = tri->indexes; shadowIndexes = newTri->indexes + numShadowIndexes; for ( int i = 0, j = 0; i < tri->numIndexes; i += 3, j++ ) { if ( facing[j] ) { continue; } int i0 = indexes[i+0] << 1; int i1 = indexes[i+1] << 1; int i2 = indexes[i+2] << 1; shadowIndexes[0] = i2; shadowIndexes[1] = i1; shadowIndexes[2] = i0; shadowIndexes[3] = i0 ^ 1; shadowIndexes[4] = i1 ^ 1; shadowIndexes[5] = i2 ^ 1; shadowIndexes += 6; } R_FreeInteractionCullInfo( cullInfo ); return newTri; }