/* ==================== idInteraction::CreateInteraction Called when a entityDef and a lightDef are both present in a portalArea, and might be visible. Performs cull checking before doing the expensive computations. References tr.viewCount so lighting surfaces will only be created if the ambient surface is visible, otherwise it will be marked as deferred. The results of this are cached and valid until the light or entity change. ==================== */ void idInteraction::CreateInteraction( const idRenderModel *model ) { const idMaterial * lightShader = lightDef->lightShader; const idMaterial* shader; bool interactionGenerated; idBounds bounds; tr.pc.c_createInteractions++; bounds = model->Bounds( &entityDef->parms ); // if it doesn't contact the light frustum, none of the surfaces will if ( R_CullLocalBox( bounds, entityDef->modelMatrix, 6, lightDef->frustum ) ) { MakeEmpty(); return; } // use the turbo shadow path shadowGen_t shadowGen = SG_DYNAMIC; // really large models, like outside terrain meshes, should use // the more exactly culled static shadow path instead of the turbo shadow path. // FIXME: this is a HACK, we should probably have a material flag. if ( bounds[1][0] - bounds[0][0] > 3000 ) { shadowGen = SG_STATIC; } // // create slots for each of the model's surfaces // numSurfaces = model->NumSurfaces(); surfaces = (surfaceInteraction_t *)R_ClearedStaticAlloc( sizeof( *surfaces ) * numSurfaces ); interactionGenerated = false; // check each surface in the model for ( int c = 0 ; c < model->NumSurfaces() ; c++ ) { const modelSurface_t *surf; srfTriangles_t *tri; surf = model->Surface( c ); tri = surf->geometry; if ( !tri ) { continue; } // determine the shader for this surface, possibly by skinning shader = surf->shader; shader = R_RemapShaderBySkin( shader, entityDef->parms.customSkin, entityDef->parms.customShader ); if ( !shader ) { continue; } // try to cull each surface if ( R_CullLocalBox( tri->bounds, entityDef->modelMatrix, 6, lightDef->frustum ) ) { continue; } surfaceInteraction_t *sint = &surfaces[c]; sint->shader = shader; // save the ambient tri pointer so we can reject lightTri interactions // when the ambient surface isn't in view, and we can get shared vertex // and shadow data from the source surface sint->ambientTris = tri; // "invisible ink" lights and shaders if ( shader->Spectrum() != lightShader->Spectrum() ) { continue; } // generate a lighted surface and add it if ( shader->ReceivesLighting() ) { if ( tri->ambientViewCount == tr.viewCount ) { sint->lightTris = R_CreateLightTris( entityDef, tri, lightDef, shader, sint->cullInfo ); } else { // this will be calculated when sint->ambientTris is actually in view sint->lightTris = LIGHT_TRIS_DEFERRED; } interactionGenerated = true; } // if the interaction has shadows and this surface casts a shadow if ( HasShadows() && shader->SurfaceCastsShadow() && tri->silEdges != NULL ) { // if the light has an optimized shadow volume, don't create shadows for any models that are part of the base areas if ( lightDef->parms.prelightModel == NULL || !model->IsStaticWorldModel() || !r_useOptimizedShadows.GetBool() ) { // this is the only place during gameplay (outside the utilities) that R_CreateShadowVolume() is called sint->shadowTris = R_CreateShadowVolume( entityDef, tri, lightDef, shadowGen, sint->cullInfo ); if ( sint->shadowTris ) { if ( shader->Coverage() != MC_OPAQUE || ( !r_skipSuppress.GetBool() && entityDef->parms.suppressSurfaceInViewID ) ) { // if any surface is a shadow-casting perforated or translucent surface, or the // base surface is suppressed in the view (world weapon shadows) we can't use // the external shadow optimizations because we can see through some of the faces sint->shadowTris->numShadowIndexesNoCaps = sint->shadowTris->numIndexes; sint->shadowTris->numShadowIndexesNoFrontCaps = sint->shadowTris->numIndexes; } } interactionGenerated = true; } } // free the cull information when it's no longer needed if ( sint->lightTris != LIGHT_TRIS_DEFERRED ) { R_FreeInteractionCullInfo( sint->cullInfo ); } } // if none of the surfaces generated anything, don't even bother checking? if ( !interactionGenerated ) { MakeEmpty(); } }
/* ====================== CreateStaticInteraction Called by idRenderWorldLocal::GenerateAllInteractions ====================== */ void idInteraction::CreateStaticInteraction() { // note that it is a static interaction staticInteraction = true; const idRenderModel *model = entityDef->parms.hModel; if ( model == NULL || model->NumSurfaces() <= 0 || model->IsDynamicModel() != DM_STATIC ) { MakeEmpty(); return; } const idBounds bounds = model->Bounds( &entityDef->parms ); // if it doesn't contact the light frustum, none of the surfaces will if ( R_CullModelBoundsToLight( lightDef, bounds, entityDef->modelRenderMatrix ) ) { MakeEmpty(); return; } // // create slots for each of the model's surfaces // numSurfaces = model->NumSurfaces(); surfaces = (surfaceInteraction_t *)R_ClearedStaticAlloc( sizeof( *surfaces ) * numSurfaces ); bool interactionGenerated = false; // check each surface in the model for ( int c = 0 ; c < model->NumSurfaces() ; c++ ) { const modelSurface_t * surf = model->Surface( c ); const srfTriangles_t * tri = surf->geometry; if ( tri == NULL ) { continue; } // determine the shader for this surface, possibly by skinning // Note that this will be wrong if customSkin/customShader are // changed after map load time without invalidating the interaction! const idMaterial * const shader = R_RemapShaderBySkin( surf->shader, entityDef->parms.customSkin, entityDef->parms.customShader ); if ( shader == NULL ) { continue; } // try to cull each surface if ( R_CullModelBoundsToLight( lightDef, tri->bounds, entityDef->modelRenderMatrix ) ) { continue; } surfaceInteraction_t *sint = &surfaces[c]; // generate a set of indexes for the lit surfaces, culling away triangles that are // not at least partially inside the light if ( shader->ReceivesLighting() ) { srfTriangles_t * lightTris = R_CreateInteractionLightTris( entityDef, tri, lightDef, shader ); if ( lightTris != NULL ) { // make a static index cache sint->numLightTrisIndexes = lightTris->numIndexes; sint->lightTrisIndexCache = vertexCache.AllocStaticIndex( lightTris->indexes, ALIGN( lightTris->numIndexes * sizeof( lightTris->indexes[0] ), INDEX_CACHE_ALIGN ) ); interactionGenerated = true; R_FreeStaticTriSurf( lightTris ); } } // if the interaction has shadows and this surface casts a shadow if ( HasShadows() && shader->SurfaceCastsShadow() && tri->silEdges != NULL ) { // if the light has an optimized shadow volume, don't create shadows for any models that are part of the base areas if ( lightDef->parms.prelightModel == NULL || !model->IsStaticWorldModel() || r_skipPrelightShadows.GetBool() ) { srfTriangles_t * shadowTris = R_CreateInteractionShadowVolume( entityDef, tri, lightDef ); if ( shadowTris != NULL ) { // make a static index cache sint->shadowIndexCache = vertexCache.AllocStaticIndex( shadowTris->indexes, ALIGN( shadowTris->numIndexes * sizeof( shadowTris->indexes[0] ), INDEX_CACHE_ALIGN ) ); sint->numShadowIndexes = shadowTris->numIndexes; #if defined( KEEP_INTERACTION_CPU_DATA ) sint->shadowIndexes = shadowTris->indexes; shadowTris->indexes = NULL; #endif if ( shader->Coverage() != MC_OPAQUE ) { // if any surface is a shadow-casting perforated or translucent surface, or the // base surface is suppressed in the view (world weapon shadows) we can't use // the external shadow optimizations because we can see through some of the faces sint->numShadowIndexesNoCaps = shadowTris->numIndexes; } else { sint->numShadowIndexesNoCaps = shadowTris->numShadowIndexesNoCaps; } R_FreeStaticTriSurf( shadowTris ); } interactionGenerated = true; } } } // if none of the surfaces generated anything, don't even bother checking? if ( !interactionGenerated ) { MakeEmpty(); } }
/* ==================== idRenderModelMD5::InstantiateDynamicModel ==================== */ idRenderModel *idRenderModelMD5::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) { int i, surfaceNum; idMD5Mesh *mesh; idRenderModelStatic *staticModel; if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) { delete cachedModel; cachedModel = NULL; } if ( purged ) { common->DWarning( "model %s instantiated while purged", Name() ); LoadModel(); } if ( !ent->joints ) { common->Printf( "idRenderModelMD5::InstantiateDynamicModel: NULL joints on renderEntity for '%s'\n", Name() ); delete cachedModel; return NULL; } else if ( ent->numJoints != joints.Num() ) { common->Printf( "idRenderModelMD5::InstantiateDynamicModel: renderEntity has different number of joints than model for '%s'\n", Name() ); delete cachedModel; return NULL; } tr.pc.c_generateMd5++; if ( cachedModel ) { assert( dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL ); assert( idStr::Icmp( cachedModel->Name(), MD5_SnapshotName ) == 0 ); staticModel = static_cast<idRenderModelStatic *>(cachedModel); } else { staticModel = new idRenderModelStatic; staticModel->InitEmpty( MD5_SnapshotName ); } staticModel->bounds.Clear(); if ( r_showSkel.GetInteger() ) { if ( ( view != NULL ) && ( !r_skipSuppress.GetBool() || !ent->suppressSurfaceInViewID || ( ent->suppressSurfaceInViewID != view->renderView.viewID ) ) ) { // only draw the skeleton DrawJoints( ent, view ); } if ( r_showSkel.GetInteger() > 1 ) { // turn off the model when showing the skeleton staticModel->InitEmpty( MD5_SnapshotName ); return staticModel; } } // create all the surfaces for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) { // avoid deforming the surface if it will be a nodraw due to a skin remapping // FIXME: may have to still deform clipping hulls const idMaterial *shader = mesh->shader; shader = R_RemapShaderBySkin( shader, ent->customSkin, ent->customShader ); if ( !shader || ( !shader->IsDrawn() && !shader->SurfaceCastsShadow() ) ) { staticModel->DeleteSurfaceWithId( i ); mesh->surfaceNum = -1; continue; } modelSurface_t *surf; if ( staticModel->FindSurfaceWithId( i, surfaceNum ) ) { mesh->surfaceNum = surfaceNum; surf = &staticModel->surfaces[surfaceNum]; } else { // Remove Overlays before adding new surfaces idRenderModelOverlay::RemoveOverlaySurfacesFromModel( staticModel ); mesh->surfaceNum = staticModel->NumSurfaces(); surf = &staticModel->surfaces.Alloc(); surf->geometry = NULL; surf->shader = NULL; surf->id = i; } mesh->UpdateSurface( ent, ent->joints, surf ); staticModel->bounds.AddPoint( surf->geometry->bounds[0] ); staticModel->bounds.AddPoint( surf->geometry->bounds[1] ); } return staticModel; }
/* =============== R_AddAmbientDrawsurfs Adds surfaces for the given viewEntity Walks through the viewEntitys list and creates drawSurf_t for each surface of each viewEntity that has a non-empty scissorRect =============== */ static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) { int i, total; idRenderEntityLocal *def; srfTriangles_t *tri; idRenderModel *model; const idMaterial *shader; def = vEntity->entityDef; if( def->dynamicModel ) { model = def->dynamicModel; } else { model = def->parms.hModel; } // add all the surfaces total = model->NumSurfaces(); for( i = 0; i < total; i++ ) { const modelSurface_t *surf = model->Surface( i ); // for debugging, only show a single surface at a time if( r_singleSurface.GetInteger() >= 0 && i != r_singleSurface.GetInteger() ) { continue; } tri = surf->geometry; if( !tri ) { continue; } if( !tri->numIndexes ) { continue; } shader = surf->shader; shader = R_RemapShaderBySkin( shader, def->parms.customSkin, def->parms.customShader ); R_GlobalShaderOverride( &shader ); if( !shader ) { continue; } if( !shader->IsDrawn() ) { continue; } // debugging tool to make sure we are have the correct pre-calculated bounds if( r_checkBounds.GetBool() ) { int j, k; for( j = 0; j < tri->numVerts; j++ ) { for( k = 0; k < 3; k++ ) { if( tri->verts[j].xyz[k] > tri->bounds[1][k] + CHECK_BOUNDS_EPSILON || tri->verts[j].xyz[k] < tri->bounds[0][k] - CHECK_BOUNDS_EPSILON ) { common->Printf( "bad tri->bounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() ); break; } if( tri->verts[j].xyz[k] > def->referenceBounds[1][k] + CHECK_BOUNDS_EPSILON || tri->verts[j].xyz[k] < def->referenceBounds[0][k] - CHECK_BOUNDS_EPSILON ) { common->Printf( "bad referenceBounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() ); break; } } if( k != 3 ) { break; } } } if( !R_CullLocalBox( tri->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) { def->visibleCount = tr.viewCount; // make sure we have an ambient cache if( !R_CreateAmbientCache( tri, shader->ReceivesLighting() ) ) { // don't add anything if the vertex cache was too full to give us an ambient cache return; } // touch it so it won't get purged vertexCache.Touch( tri->ambientCache ); if( !tri->indexCache ) { vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true ); } if( tri->indexCache ) { vertexCache.Touch( tri->indexCache ); } // add the surface for drawing R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect ); // ambientViewCount is used to allow light interactions to be rejected // if the ambient surface isn't visible at all tri->ambientViewCount = tr.viewCount; } } // add the lightweight decal surfaces for( idRenderModelDecal *decal = def->decals; decal; decal = decal->Next() ) { decal->AddDecalDrawSurf( vEntity ); } }