/* ================ EmitSurface ================ */ void idGuiModel::EmitSurface( guiModelSurface_t *surf, float modelMatrix[16], float modelViewMatrix[16], bool depthHack ) { srfTriangles_t *tri; if( surf->numVerts == 0 ) { return; // nothing in the surface } // copy verts and indexes tri = ( srfTriangles_t * )R_ClearedFrameAlloc( sizeof( *tri ) ); tri->numIndexes = surf->numIndexes; tri->numVerts = surf->numVerts; tri->indexes = ( glIndex_t * )R_FrameAlloc( tri->numIndexes * sizeof( tri->indexes[0] ) ); memcpy( tri->indexes, &indexes[surf->firstIndex], tri->numIndexes * sizeof( tri->indexes[0] ) ); // we might be able to avoid copying these and just let them reference the list vars // but some things, like deforms and recursive // guis, need to access the verts in cpu space, not just through the vertex range tri->verts = ( idDrawVert * )R_FrameAlloc( tri->numVerts * sizeof( tri->verts[0] ) ); memcpy( tri->verts, &verts[surf->firstVert], tri->numVerts * sizeof( tri->verts[0] ) ); // move the verts to the vertex cache tri->ambientCache = vertexCache.AllocFrameTemp( tri->verts, tri->numVerts * sizeof( tri->verts[0] ) ); // if we are out of vertex cache, don't create the surface if( !tri->ambientCache ) { return; } renderEntity_t renderEntity; memset( &renderEntity, 0, sizeof( renderEntity ) ); memcpy( renderEntity.shaderParms, surf->color, sizeof( surf->color ) ); viewEntity_t *guiSpace = ( viewEntity_t * )R_ClearedFrameAlloc( sizeof( *guiSpace ) ); memcpy( guiSpace->modelMatrix, modelMatrix, sizeof( guiSpace->modelMatrix ) ); memcpy( guiSpace->modelViewMatrix, modelViewMatrix, sizeof( guiSpace->modelViewMatrix ) ); guiSpace->weaponDepthHack = depthHack; // add the surface, which might recursively create another gui R_AddDrawSurf( tri, guiSpace, &renderEntity, surf->material, tr.viewDef->scissor ); }
/* ============= R_SetEntityDefViewEntity If the entityDef isn't already on the viewEntity list, create a viewEntity and add it to the list with an empty scissor rect. This does not instantiate dynamic models for the entity yet. ============= */ viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def ) { viewEntity_t *vModel; if( def->viewCount == tr.viewCount ) { return def->viewEntity; } def->viewCount = tr.viewCount; // set the model and modelview matricies vModel = ( viewEntity_t * ) R_ClearedFrameAlloc( sizeof( *vModel ) ); vModel->entityDef = def; // the scissorRect will be expanded as the model bounds is accepted into visible portal chains vModel->scissorRect.Clear(); // copy the model and weapon depth hack for back-end use vModel->modelDepthHack = def->parms.modelDepthHack; vModel->weaponDepthHack = def->parms.weaponDepthHack; R_AxisToModelMatrix( def->parms.axis, def->parms.origin, vModel->modelMatrix ); // we may not have a viewDef if we are just creating shadows at entity creation time if( tr.viewDef ) { R_MatrixMultiply( vModel->modelMatrix, tr.viewDef->worldSpace.modelViewMatrix, vModel->modelViewMatrix ); vModel->next = tr.viewDef->viewEntitys; tr.viewDef->viewEntitys = vModel; } def->viewEntity = vModel; return vModel; }
/* ============= R_SetLightDefViewLight If the lightDef is not already on the viewLight list, create a viewLight and add it to the list with an empty scissor rect. ============= */ viewLight_t* R_SetLightDefViewLight( idRenderLightLocal* light ) { if( light->viewCount == tr.viewCount ) { // already set up for this frame return light->viewLight; } light->viewCount = tr.viewCount; // add to the view light chain viewLight_t* vLight = ( viewLight_t* )R_ClearedFrameAlloc( sizeof( *vLight ), FRAME_ALLOC_VIEW_LIGHT ); vLight->lightDef = light; // the scissorRect will be expanded as the light bounds is accepted into visible portal chains // and the scissor will be reduced in R_AddSingleLight based on the screen space projection vLight->scissorRect.Clear(); // link the view light vLight->next = tr.viewDef->viewLights; tr.viewDef->viewLights = vLight; light->viewLight = vLight; return vLight; }
/* ============= R_SetLightDefViewLight If the lightDef isn't already on the viewLight list, create a viewLight and add it to the list with an empty scissor rect. ============= */ viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *light ) { viewLight_t *vLight; if( light->viewCount == tr.viewCount ) { return light->viewLight; } light->viewCount = tr.viewCount; // add to the view light chain vLight = ( viewLight_t * ) R_ClearedFrameAlloc( sizeof( *vLight ) ); vLight->lightDef = light; // the scissorRect will be expanded as the light bounds is accepted into visible portal chains vLight->scissorRect.Clear(); // calculate the shadow cap optimization states vLight->viewInsideLight = R_TestPointInViewLight( tr.viewDef->renderView.vieworg, light ); if( !vLight->viewInsideLight ) { vLight->viewSeesShadowPlaneBits = 0; for( int i = 0; i < light->numShadowFrustums; i++ ) { float d = light->shadowFrustums[i].planes[5].Distance( tr.viewDef->renderView.vieworg ); if( d < INSIDE_LIGHT_FRUSTUM_SLOP ) { vLight->viewSeesShadowPlaneBits |= 1 << i; } } } else { // this should not be referenced in this case vLight->viewSeesShadowPlaneBits = 63; } // copy data used by backend vLight->globalLightOrigin = light->globalLightOrigin; vLight->lightProject[0] = light->lightProject[0]; vLight->lightProject[1] = light->lightProject[1]; vLight->lightProject[2] = light->lightProject[2]; vLight->lightProject[3] = light->lightProject[3]; vLight->fogPlane = light->frustum[5]; vLight->frustumTris = light->frustumTris; vLight->falloffImage = light->falloffImage; vLight->lightShader = light->lightShader; vLight->shaderRegisters = NULL; // allocated and evaluated in R_AddLightSurfaces // link the view light vLight->next = tr.viewDef->viewLights; tr.viewDef->viewLights = vLight; light->viewLight = vLight; return vLight; }
/* ================ idGuiModel::EmitFullScreen Creates a view that covers the screen and emit the surfaces ================ */ void idGuiModel::EmitFullScreen( void ) { viewDef_t *viewDef; if( surfaces[0].numVerts == 0 ) { return; } viewDef = ( viewDef_t * )R_ClearedFrameAlloc( sizeof( *viewDef ) ); // for gui editor if( !tr.viewDef || !tr.viewDef->isEditor ) { viewDef->renderView.x = 0; viewDef->renderView.y = 0; viewDef->renderView.width = SCREEN_WIDTH; viewDef->renderView.height = SCREEN_HEIGHT; tr.RenderViewToViewport( &viewDef->renderView, &viewDef->viewport ); viewDef->scissor.x1 = 0; viewDef->scissor.y1 = 0; viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1; viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1; } else { viewDef->renderView.x = tr.viewDef->renderView.x; viewDef->renderView.y = tr.viewDef->renderView.y; viewDef->renderView.width = tr.viewDef->renderView.width; viewDef->renderView.height = tr.viewDef->renderView.height; viewDef->viewport.x1 = tr.viewDef->renderView.x; viewDef->viewport.x2 = tr.viewDef->renderView.x + tr.viewDef->renderView.width; viewDef->viewport.y1 = tr.viewDef->renderView.y; viewDef->viewport.y2 = tr.viewDef->renderView.y + tr.viewDef->renderView.height; viewDef->scissor.x1 = tr.viewDef->scissor.x1; viewDef->scissor.y1 = tr.viewDef->scissor.y1; viewDef->scissor.x2 = tr.viewDef->scissor.x2; viewDef->scissor.y2 = tr.viewDef->scissor.y2; } viewDef->floatTime = tr.frameShaderTime; // glOrtho( 0, 640, 480, 0, 0, 1 ); // always assume 640x480 virtual coordinates viewDef->projectionMatrix[0] = 2.0f / 640.0f; viewDef->projectionMatrix[5] = -2.0f / 480.0f; viewDef->projectionMatrix[10] = -2.0f / 1.0f; viewDef->projectionMatrix[12] = -1.0f; viewDef->projectionMatrix[13] = 1.0f; viewDef->projectionMatrix[14] = -1.0f; viewDef->projectionMatrix[15] = 1.0f; viewDef->worldSpace.modelViewMatrix[0] = 1.0f; viewDef->worldSpace.modelViewMatrix[5] = 1.0f; viewDef->worldSpace.modelViewMatrix[10] = 1.0f; viewDef->worldSpace.modelViewMatrix[15] = 1.0f; viewDef->maxDrawSurfs = surfaces.Num(); viewDef->drawSurfs = ( drawSurf_t ** )R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ) ); viewDef->numDrawSurfs = 0; viewDef_t *oldViewDef = tr.viewDef; tr.viewDef = viewDef; // add the surfaces to this view for( int i = 0 ; i < surfaces.Num() ; i++ ) { EmitSurface( &surfaces[i], viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix, false ); } tr.viewDef = oldViewDef; // add the command to draw this view R_AddDrawViewCmd( viewDef ); }
/* ============= R_SetEntityDefViewEntity If the entityDef is not already on the viewEntity list, create a viewEntity and add it to the list with an empty scissor rect. ============= */ viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def ) { if ( def->viewCount == tr.viewCount ) { // already set up for this frame return def->viewEntity; } def->viewCount = tr.viewCount; viewEntity_t * vModel = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *vModel ), FRAME_ALLOC_VIEW_ENTITY ); vModel->entityDef = def; // the scissorRect will be expanded as the model bounds is accepted into visible portal chains // It will remain clear if the model is only needed for shadows. vModel->scissorRect.Clear(); vModel->next = tr.viewDef->viewEntitys; tr.viewDef->viewEntitys = vModel; def->viewEntity = vModel; return vModel; }
/* ================ idGuiModel::EmitFullScreen Creates a view that covers the screen and emit the surfaces ================ */ void idGuiModel::EmitFullScreen() { if( surfaces[0].numIndexes == 0 ) { return; } SCOPED_PROFILE_EVENT( "Gui::EmitFullScreen" ); viewDef_t* viewDef = ( viewDef_t* )R_ClearedFrameAlloc( sizeof( *viewDef ), FRAME_ALLOC_VIEW_DEF ); viewDef->is2Dgui = true; tr.GetCroppedViewport( &viewDef->viewport ); bool stereoEnabled = ( renderSystem->GetStereo3DMode() != STEREO3D_OFF ); if( stereoEnabled ) { const float screenSeparation = GetScreenSeparationForGuis(); // this will be negated on the alternate eyes, both rendered each frame viewDef->renderView.stereoScreenSeparation = screenSeparation; extern idCVar stereoRender_swapEyes; viewDef->renderView.viewEyeBuffer = 0; // render to both buffers if( stereoRender_swapEyes.GetBool() ) { viewDef->renderView.stereoScreenSeparation = -screenSeparation; } } viewDef->scissor.x1 = 0; viewDef->scissor.y1 = 0; viewDef->scissor.x2 = viewDef->viewport.x2 - viewDef->viewport.x1; viewDef->scissor.y2 = viewDef->viewport.y2 - viewDef->viewport.y1; viewDef->projectionMatrix[0 * 4 + 0] = 2.0f / renderSystem->GetVirtualWidth(); viewDef->projectionMatrix[0 * 4 + 1] = 0.0f; viewDef->projectionMatrix[0 * 4 + 2] = 0.0f; viewDef->projectionMatrix[0 * 4 + 3] = 0.0f; viewDef->projectionMatrix[1 * 4 + 0] = 0.0f; viewDef->projectionMatrix[1 * 4 + 1] = -2.0f / renderSystem->GetVirtualHeight(); viewDef->projectionMatrix[1 * 4 + 2] = 0.0f; viewDef->projectionMatrix[1 * 4 + 3] = 0.0f; viewDef->projectionMatrix[2 * 4 + 0] = 0.0f; viewDef->projectionMatrix[2 * 4 + 1] = 0.0f; viewDef->projectionMatrix[2 * 4 + 2] = -2.0f; viewDef->projectionMatrix[2 * 4 + 3] = 0.0f; viewDef->projectionMatrix[3 * 4 + 0] = -1.0f; viewDef->projectionMatrix[3 * 4 + 1] = 1.0f; viewDef->projectionMatrix[3 * 4 + 2] = -1.0f; viewDef->projectionMatrix[3 * 4 + 3] = 1.0f; // make a tech5 renderMatrix for faster culling idRenderMatrix::Transpose( *( idRenderMatrix* )viewDef->projectionMatrix, viewDef->projectionRenderMatrix ); viewDef->worldSpace.modelMatrix[0 * 4 + 0] = 1.0f; viewDef->worldSpace.modelMatrix[1 * 4 + 1] = 1.0f; viewDef->worldSpace.modelMatrix[2 * 4 + 2] = 1.0f; viewDef->worldSpace.modelMatrix[3 * 4 + 3] = 1.0f; viewDef->worldSpace.modelViewMatrix[0 * 4 + 0] = 1.0f; viewDef->worldSpace.modelViewMatrix[1 * 4 + 1] = 1.0f; viewDef->worldSpace.modelViewMatrix[2 * 4 + 2] = 1.0f; viewDef->worldSpace.modelViewMatrix[3 * 4 + 3] = 1.0f; viewDef->maxDrawSurfs = surfaces.Num(); viewDef->drawSurfs = ( drawSurf_t** )R_FrameAlloc( viewDef->maxDrawSurfs * sizeof( viewDef->drawSurfs[0] ), FRAME_ALLOC_DRAW_SURFACE_POINTER ); viewDef->numDrawSurfs = 0; #if 1 // RB: give renderView the current time to calculate 2D shader effects int shaderTime = tr.frameShaderTime * 1000; //Sys_Milliseconds(); viewDef->renderView.time[0] = shaderTime; viewDef->renderView.time[1] = shaderTime; // RB end #endif viewDef_t* oldViewDef = tr.viewDef; tr.viewDef = viewDef; EmitSurfaces( viewDef->worldSpace.modelMatrix, viewDef->worldSpace.modelViewMatrix, false /* depthHack */ , stereoEnabled /* stereoDepthSort */, false /* link as entity */ ); tr.viewDef = oldViewDef; // add the command to draw this view R_AddDrawViewCmd( viewDef, true ); }
/* ================ EmitSurfaces For full screen GUIs, we can add in per-surface stereoscopic depth effects ================ */ void idGuiModel::EmitSurfaces( float modelMatrix[16], float modelViewMatrix[16], bool depthHack, bool allowFullScreenStereoDepth, bool linkAsEntity ) { viewEntity_t* guiSpace = ( viewEntity_t* )R_ClearedFrameAlloc( sizeof( *guiSpace ), FRAME_ALLOC_VIEW_ENTITY ); memcpy( guiSpace->modelMatrix, modelMatrix, sizeof( guiSpace->modelMatrix ) ); memcpy( guiSpace->modelViewMatrix, modelViewMatrix, sizeof( guiSpace->modelViewMatrix ) ); guiSpace->weaponDepthHack = depthHack; guiSpace->isGuiSurface = true; // If this is an in-game gui, we need to be able to find the matrix again for head mounted // display bypass matrix fixup. if( linkAsEntity ) { guiSpace->next = tr.viewDef->viewEntitys; tr.viewDef->viewEntitys = guiSpace; } //--------------------------- // make a tech5 renderMatrix //--------------------------- idRenderMatrix viewMat; idRenderMatrix::Transpose( *( idRenderMatrix* )modelViewMatrix, viewMat ); idRenderMatrix::Multiply( tr.viewDef->projectionRenderMatrix, viewMat, guiSpace->mvp ); if( depthHack ) { idRenderMatrix::ApplyDepthHack( guiSpace->mvp ); } // to allow 3D-TV effects in the menu system, we define surface flags to set // depth fractions between 0=screen and 1=infinity, which directly modulate the // screenSeparation parameter for an X offset. // The value is stored in the drawSurf sort value, which adjusts the matrix in the // backend. float defaultStereoDepth = stereoRender_defaultGuiDepth.GetFloat(); // default to at-screen // add the surfaces to this view for( int i = 0; i < surfaces.Num(); i++ ) { const guiModelSurface_t& guiSurf = surfaces[i]; if( guiSurf.numIndexes == 0 ) { continue; } const idMaterial* shader = guiSurf.material; drawSurf_t* drawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE ); drawSurf->numIndexes = guiSurf.numIndexes; drawSurf->ambientCache = vertexBlock; // build a vertCacheHandle_t that points inside the allocated block drawSurf->indexCache = indexBlock + ( ( int64 )( guiSurf.firstIndex * sizeof( triIndex_t ) ) << VERTCACHE_OFFSET_SHIFT ); drawSurf->shadowCache = 0; drawSurf->jointCache = 0; drawSurf->frontEndGeo = NULL; drawSurf->space = guiSpace; drawSurf->material = shader; drawSurf->extraGLState = guiSurf.glState; drawSurf->scissorRect = tr.viewDef->scissor; drawSurf->sort = shader->GetSort(); drawSurf->renderZFail = 0; // process the shader expressions for conditionals / color / texcoords const float* constRegs = shader->ConstantRegisters(); if( constRegs ) { // shader only uses constant values drawSurf->shaderRegisters = constRegs; } else { float* regs = ( float* )R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER ); drawSurf->shaderRegisters = regs; shader->EvaluateRegisters( regs, shaderParms, tr.viewDef->renderView.shaderParms, tr.viewDef->renderView.time[1] * 0.001f, NULL ); } R_LinkDrawSurfToView( drawSurf, tr.viewDef ); if( allowFullScreenStereoDepth ) { // override sort with the stereoDepth //drawSurf->sort = stereoDepth; switch( guiSurf.stereoType ) { case STEREO_DEPTH_TYPE_NEAR: drawSurf->sort = STEREO_DEPTH_NEAR; break; case STEREO_DEPTH_TYPE_MID: drawSurf->sort = STEREO_DEPTH_MID; break; case STEREO_DEPTH_TYPE_FAR: drawSurf->sort = STEREO_DEPTH_FAR; break; case STEREO_DEPTH_TYPE_NONE: default: drawSurf->sort = defaultStereoDepth; break; } } } }
/* =================== R_AddSingleLight May be run in parallel. Sets vLight->removeFromList to true if the light should be removed from the list. Builds a chain of entities that need to be added for shadows only off vLight->shadowOnlyViewEntities. Allocates and fills in vLight->entityInteractionState. Calc the light shader values, removing any light from the viewLight list if it is determined to not have any visible effect due to being flashed off or turned off. Add any precomputed shadow volumes. =================== */ static void R_AddSingleLight( viewLight_t* vLight ) { // until proven otherwise vLight->removeFromList = true; vLight->shadowOnlyViewEntities = NULL; vLight->preLightShadowVolumes = NULL; // globals we really should pass in... const viewDef_t* viewDef = tr.viewDef; const idRenderLightLocal* light = vLight->lightDef; const idMaterial* lightShader = light->lightShader; if( lightShader == NULL ) { common->Error( "R_AddSingleLight: NULL lightShader" ); return; } SCOPED_PROFILE_EVENT( lightShader->GetName() ); // see if we are suppressing the light in this view if( !r_skipSuppress.GetBool() ) { if( light->parms.suppressLightInViewID && light->parms.suppressLightInViewID == viewDef->renderView.viewID ) { return; } if( light->parms.allowLightInViewID && light->parms.allowLightInViewID != viewDef->renderView.viewID ) { return; } } // evaluate the light shader registers float* lightRegs = ( float* )R_FrameAlloc( lightShader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER ); lightShader->EvaluateRegisters( lightRegs, light->parms.shaderParms, viewDef->renderView.shaderParms, tr.viewDef->renderView.time[0] * 0.001f, light->parms.referenceSound ); // if this is a purely additive light and no stage in the light shader evaluates // to a positive light value, we can completely skip the light if( !lightShader->IsFogLight() && !lightShader->IsBlendLight() ) { int lightStageNum; for( lightStageNum = 0; lightStageNum < lightShader->GetNumStages(); lightStageNum++ ) { const shaderStage_t* lightStage = lightShader->GetStage( lightStageNum ); // ignore stages that fail the condition if( !lightRegs[ lightStage->conditionRegister ] ) { continue; } const int* registers = lightStage->color.registers; // snap tiny values to zero if( lightRegs[ registers[0] ] < 0.001f ) { lightRegs[ registers[0] ] = 0.0f; } if( lightRegs[ registers[1] ] < 0.001f ) { lightRegs[ registers[1] ] = 0.0f; } if( lightRegs[ registers[2] ] < 0.001f ) { lightRegs[ registers[2] ] = 0.0f; } if( lightRegs[ registers[0] ] > 0.0f || lightRegs[ registers[1] ] > 0.0f || lightRegs[ registers[2] ] > 0.0f ) { break; } } if( lightStageNum == lightShader->GetNumStages() ) { // we went through all the stages and didn't find one that adds anything // remove the light from the viewLights list, and change its frame marker // so interaction generation doesn't think the light is visible and // create a shadow for it return; } } //-------------------------------------------- // copy data used by backend //-------------------------------------------- vLight->globalLightOrigin = light->globalLightOrigin; vLight->lightProject[0] = light->lightProject[0]; vLight->lightProject[1] = light->lightProject[1]; vLight->lightProject[2] = light->lightProject[2]; vLight->lightProject[3] = light->lightProject[3]; // the fog plane is the light far clip plane idPlane fogPlane( light->baseLightProject[2][0] - light->baseLightProject[3][0], light->baseLightProject[2][1] - light->baseLightProject[3][1], light->baseLightProject[2][2] - light->baseLightProject[3][2], light->baseLightProject[2][3] - light->baseLightProject[3][3] ); const float planeScale = idMath::InvSqrt( fogPlane.Normal().LengthSqr() ); vLight->fogPlane[0] = fogPlane[0] * planeScale; vLight->fogPlane[1] = fogPlane[1] * planeScale; vLight->fogPlane[2] = fogPlane[2] * planeScale; vLight->fogPlane[3] = fogPlane[3] * planeScale; // copy the matrix for deforming the 'zeroOneCubeModel' to exactly cover the light volume in world space vLight->inverseBaseLightProject = light->inverseBaseLightProject; // RB begin vLight->baseLightProject = light->baseLightProject; vLight->pointLight = light->parms.pointLight; vLight->parallel = light->parms.parallel; vLight->lightCenter = light->parms.lightCenter; // RB end vLight->falloffImage = light->falloffImage; vLight->lightShader = light->lightShader; vLight->shaderRegisters = lightRegs; const bool lightCastsShadows = light->LightCastsShadows(); if( r_useLightScissors.GetInteger() != 0 ) { // Calculate the matrix that projects the zero-to-one cube to exactly cover the // light frustum in clip space. idRenderMatrix invProjectMVPMatrix; idRenderMatrix::Multiply( viewDef->worldSpace.mvp, light->inverseBaseLightProject, invProjectMVPMatrix ); // Calculate the projected bounds, either not clipped at all, near clipped, or fully clipped. idBounds projected; if( r_useLightScissors.GetInteger() == 1 ) { idRenderMatrix::ProjectedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube ); } else if( r_useLightScissors.GetInteger() == 2 ) { idRenderMatrix::ProjectedNearClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube ); } else { idRenderMatrix::ProjectedFullyClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube ); } if( projected[0][2] >= projected[1][2] ) { // the light was culled to the view frustum return; } float screenWidth = ( float )viewDef->viewport.x2 - ( float )viewDef->viewport.x1; float screenHeight = ( float )viewDef->viewport.y2 - ( float )viewDef->viewport.y1; idScreenRect lightScissorRect; lightScissorRect.x1 = idMath::Ftoi( projected[0][0] * screenWidth ); lightScissorRect.x2 = idMath::Ftoi( projected[1][0] * screenWidth ); lightScissorRect.y1 = idMath::Ftoi( projected[0][1] * screenHeight ); lightScissorRect.y2 = idMath::Ftoi( projected[1][1] * screenHeight ); lightScissorRect.Expand(); vLight->scissorRect.Intersect( lightScissorRect ); vLight->scissorRect.zmin = projected[0][2]; vLight->scissorRect.zmax = projected[1][2]; // RB: calculate shadow LOD similar to Q3A .md3 LOD code vLight->shadowLOD = 0; if( r_useShadowMapping.GetBool() && lightCastsShadows ) { float flod, lodscale; float projectedRadius; int lod; int numLods; numLods = MAX_SHADOWMAP_RESOLUTIONS; // compute projected bounding sphere // and use that as a criteria for selecting LOD idVec3 center = projected.GetCenter(); projectedRadius = projected.GetRadius( center ); if( projectedRadius > 1.0f ) { projectedRadius = 1.0f; } if( projectedRadius != 0 ) { lodscale = r_shadowMapLodScale.GetFloat(); if( lodscale > 20 ) lodscale = 20; flod = 1.0f - projectedRadius * lodscale; } else { // object intersects near view plane, e.g. view weapon flod = 0; } flod *= numLods; if( flod < 0 ) { flod = 0; } lod = idMath::Ftoi( flod ); if( lod >= numLods ) { //lod = numLods - 1; } lod += r_shadowMapLodBias.GetInteger(); if( lod < 0 ) { lod = 0; } if( lod >= numLods ) { // don't draw any shadow //lod = -1; lod = numLods - 1; } // 2048^2 ultra quality is only for cascaded shadow mapping with sun lights if( lod == 0 && !light->parms.parallel ) { lod = 1; } vLight->shadowLOD = lod; } // RB end } // this one stays on the list vLight->removeFromList = false; //-------------------------------------------- // create interactions with all entities the light may touch, and add viewEntities // that may cast shadows, even if they aren't directly visible. Any real work // will be deferred until we walk through the viewEntities //-------------------------------------------- const int renderViewID = viewDef->renderView.viewID; // this bool array will be set true whenever the entity will visibly interact with the light vLight->entityInteractionState = ( byte* )R_ClearedFrameAlloc( light->world->entityDefs.Num() * sizeof( vLight->entityInteractionState[0] ), FRAME_ALLOC_INTERACTION_STATE ); idInteraction** const interactionTableRow = light->world->interactionTable + light->index * light->world->interactionTableWidth; for( areaReference_t* lref = light->references; lref != NULL; lref = lref->ownerNext ) { portalArea_t* area = lref->area; // some lights have their center of projection outside the world, but otherwise // we want to ignore areas that are not connected to the light center due to a closed door if( light->areaNum != -1 && r_useAreasConnectedForShadowCulling.GetInteger() == 2 ) { if( !light->world->AreasAreConnected( light->areaNum, area->areaNum, PS_BLOCK_VIEW ) ) { // can't possibly be seen or shadowed continue; } } // check all the models in this area for( areaReference_t* eref = area->entityRefs.areaNext; eref != &area->entityRefs; eref = eref->areaNext ) { idRenderEntityLocal* edef = eref->entity; if( vLight->entityInteractionState[ edef->index ] != viewLight_t::INTERACTION_UNCHECKED ) { continue; } // until proven otherwise vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_NO; // The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree() const idInteraction* inter = interactionTableRow[ edef->index ]; const renderEntity_t& eParms = edef->parms; const idRenderModel* eModel = eParms.hModel; // a large fraction of static entity / light pairs will still have no interactions even though // they are both present in the same area(s) if( eModel != NULL && !eModel->IsDynamicModel() && inter == INTERACTION_EMPTY ) { // the interaction was statically checked, and it didn't generate any surfaces, // so there is no need to force the entity onto the view list if it isn't // already there continue; } // We don't want the lights on weapons to illuminate anything else. // There are two assumptions here -- that allowLightInViewID is only // used for weapon lights, and that all weapons will have weaponDepthHack. // A more general solution would be to have an allowLightOnEntityID field. // HACK: the armor-mounted flashlight is a private spot light, which is probably // wrong -- you would expect to see them in multiplayer. // if( light->parms.allowLightInViewID && light->parms.pointLight && !eParms.weaponDepthHack ) // { // continue; // } // non-shadow casting entities don't need to be added if they aren't // directly visible if( ( eParms.noShadow || ( eModel && !eModel->ModelHasShadowCastingSurfaces() ) ) && !edef->IsDirectlyVisible() ) { continue; } // if the model doesn't accept lighting or cast shadows, it doesn't need to be added if( eModel && !eModel->ModelHasInteractingSurfaces() && !eModel->ModelHasShadowCastingSurfaces() ) { continue; } // no interaction present, so either the light or entity has moved // assert( lightHasMoved || edef->entityHasMoved ); if( inter == NULL ) { // some big outdoor meshes are flagged to not create any dynamic interactions // when the level designer knows that nearby moving lights shouldn't actually hit them if( eParms.noDynamicInteractions ) { continue; } // do a check of the entity reference bounds against the light frustum to see if they can't // possibly interact, despite sharing one or more world areas if( R_CullModelBoundsToLight( light, edef->localReferenceBounds, edef->modelRenderMatrix ) ) { continue; } } // we now know that the entity and light do overlap if( edef->IsDirectlyVisible() ) { // entity is directly visible, so the interaction is definitely needed vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES; continue; } // the entity is not directly visible, but if we can tell that it may cast // shadows onto visible surfaces, we must make a viewEntity for it if( !lightCastsShadows ) { // surfaces are never shadowed in this light continue; } // if we are suppressing its shadow in this view (player shadows, etc), skip if( !r_skipSuppress.GetBool() ) { if( eParms.suppressShadowInViewID && eParms.suppressShadowInViewID == renderViewID ) { continue; } if( eParms.suppressShadowInLightID && eParms.suppressShadowInLightID == light->parms.lightId ) { continue; } } // should we use the shadow bounds from pre-calculated interactions? idBounds shadowBounds; R_ShadowBounds( edef->globalReferenceBounds, light->globalLightBounds, light->globalLightOrigin, shadowBounds ); // this test is pointless if we knew the light was completely contained // in the view frustum, but the entity would also be directly visible in most // of those cases. // this doesn't say that the shadow can't effect anything, only that it can't // effect anything in the view, so we shouldn't set up a view entity if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) ) { continue; } // debug tool to allow viewing of only one entity at a time if( r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != edef->index ) { continue; } // we do need it for shadows vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES; // we will need to create a viewEntity_t for it in the serial code section shadowOnlyEntity_t* shadEnt = ( shadowOnlyEntity_t* )R_FrameAlloc( sizeof( shadowOnlyEntity_t ), FRAME_ALLOC_SHADOW_ONLY_ENTITY ); shadEnt->next = vLight->shadowOnlyViewEntities; shadEnt->edef = edef; vLight->shadowOnlyViewEntities = shadEnt; } } //-------------------------------------------- // add the prelight shadows for the static world geometry //-------------------------------------------- if( light->parms.prelightModel != NULL && !r_useShadowMapping.GetBool() ) { srfTriangles_t* tri = light->parms.prelightModel->Surface( 0 )->geometry; // these shadows will have valid bounds, and can be culled normally, // but they will typically cover most of the light's bounds if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, tri->bounds ) ) { return; } // prelight models should always have static data that never gets purged assert( vertexCache.CacheIsCurrent( tri->shadowCache ) ); assert( vertexCache.CacheIsCurrent( tri->indexCache ) ); drawSurf_t* shadowDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE ); shadowDrawSurf->frontEndGeo = tri; shadowDrawSurf->ambientCache = 0; shadowDrawSurf->indexCache = tri->indexCache; shadowDrawSurf->shadowCache = tri->shadowCache; shadowDrawSurf->jointCache = 0; shadowDrawSurf->numIndexes = 0; shadowDrawSurf->space = &viewDef->worldSpace; shadowDrawSurf->material = NULL; shadowDrawSurf->extraGLState = 0; shadowDrawSurf->shaderRegisters = NULL; shadowDrawSurf->scissorRect = vLight->scissorRect; // default to the light scissor and light depth bounds shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE; // assume the shadow volume is done in case r_skipPrelightShadows is set if( !r_skipPrelightShadows.GetBool() ) { preLightShadowVolumeParms_t* shadowParms = ( preLightShadowVolumeParms_t* )R_FrameAlloc( sizeof( shadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); shadowParms->verts = tri->preLightShadowVertexes; shadowParms->numVerts = tri->numVerts * 2; shadowParms->indexes = tri->indexes; shadowParms->numIndexes = tri->numIndexes; shadowParms->triangleBounds = tri->bounds; shadowParms->triangleMVP = viewDef->worldSpace.mvp; shadowParms->localLightOrigin = vLight->globalLightOrigin; shadowParms->localViewOrigin = viewDef->renderView.vieworg; shadowParms->zNear = r_znear.GetFloat(); shadowParms->lightZMin = vLight->scissorRect.zmin; shadowParms->lightZMax = vLight->scissorRect.zmax; shadowParms->forceShadowCaps = r_forceShadowCaps.GetBool(); shadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool(); shadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool(); shadowParms->numShadowIndices = & shadowDrawSurf->numIndexes; shadowParms->renderZFail = & shadowDrawSurf->renderZFail; shadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin; shadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax; shadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState; // the pre-light shadow volume "_prelight_light_3297" in "d3xpdm2" is malformed in that it contains the light origin so the precise inside test always fails if( tr.primaryWorld->mapName.IcmpPath( "maps/game/mp/d3xpdm2.map" ) == 0 && idStr::Icmp( light->parms.prelightModel->Name(), "_prelight_light_3297" ) == 0 ) { shadowParms->useShadowPreciseInsideTest = false; } shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED; shadowParms->next = vLight->preLightShadowVolumes; vLight->preLightShadowVolumes = shadowParms; } // actually link it in shadowDrawSurf->nextOnLight = vLight->globalShadows; vLight->globalShadows = shadowDrawSurf; } }
/* ===================== idRenderModelDecal::CreateDecalDrawSurf ===================== */ drawSurf_t * idRenderModelDecal::CreateDecalDrawSurf( const viewEntity_t *space, unsigned int index ) { if ( index < 0 || index >= numDecalMaterials ) { return NULL; } const idMaterial * material = decalMaterials[index]; int maxVerts = 0; int maxIndexes = 0; for ( unsigned int i = firstDecal; i < nextDecal; i++ ) { const decal_t & decal = decals[i & ( MAX_DECALS - 1 )]; if ( decal.material == material ) { maxVerts += decal.numVerts; maxIndexes += decal.numIndexes; } } if ( maxVerts == 0 || maxIndexes == 0 ) { return NULL; } // create a new triangle surface in frame memory so it gets automatically disposed of srfTriangles_t *newTri = (srfTriangles_t *)R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); newTri->numVerts = maxVerts; newTri->numIndexes = maxIndexes; newTri->ambientCache = vertexCache.AllocVertex( NULL, ALIGN( maxVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); newTri->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); idDrawVert * mappedVerts = (idDrawVert *)vertexCache.MappedVertexBuffer( newTri->ambientCache ); triIndex_t * mappedIndexes = (triIndex_t *)vertexCache.MappedIndexBuffer( newTri->indexCache ); const decalInfo_t decalInfo = material->GetDecalInfo(); const int maxTime = decalInfo.stayTime + decalInfo.fadeTime; const int time = tr.viewDef->renderView.time[0]; int numVerts = 0; int numIndexes = 0; for ( unsigned int i = firstDecal; i < nextDecal; i++ ) { const decal_t & decal = decals[i & ( MAX_DECALS - 1 )]; if ( decal.numVerts == 0 ) { if ( i == firstDecal ) { firstDecal++; } continue; } if ( decal.material != material ) { continue; } const int deltaTime = time - decal.startTime; const int fadeTime = deltaTime - decalInfo.stayTime; if ( deltaTime > maxTime ) { continue; // already completely faded away, but not yet removed } const float f = ( deltaTime > decalInfo.stayTime ) ? ( (float) fadeTime / decalInfo.fadeTime ) : 0.0f; ALIGNTYPE16 float fadeColor[4]; for ( int j = 0; j < 4; j++ ) { fadeColor[j] = 255.0f * ( decalInfo.start[j] + ( decalInfo.end[j] - decalInfo.start[j] ) * f ); } // use SIMD optimized routine to copy the vertices and indices directly to write-combined memory // this also applies any depth/time based fading while copying R_CopyDecalSurface( mappedVerts, numVerts, mappedIndexes, numIndexes, &decal, fadeColor ); numVerts += decal.numVerts; numIndexes += decal.numIndexes; } newTri->numVerts = numVerts; newTri->numIndexes = numIndexes; // create the drawsurf drawSurf_t * drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE ); drawSurf->frontEndGeo = newTri; drawSurf->numIndexes = newTri->numIndexes; drawSurf->ambientCache = newTri->ambientCache; drawSurf->indexCache = newTri->indexCache; drawSurf->shadowCache = 0; drawSurf->jointCache = 0; drawSurf->space = space; drawSurf->scissorRect = space->scissorRect; drawSurf->extraGLState = 0; drawSurf->renderZFail = 0; R_SetupDrawSurfShader( drawSurf, material, &space->entityDef->parms ); return drawSurf; }
/* ==================== idRenderModelOverlay::CreateOverlayDrawSurf ==================== */ drawSurf_t* idRenderModelOverlay::CreateOverlayDrawSurf( const viewEntity_t* space, const idRenderModel* baseModel, unsigned int index ) { if( index < 0 || index >= numOverlayMaterials ) { return NULL; } // md5 models won't have any surfaces when r_showSkel is set if( baseModel == NULL || baseModel->IsDefaultModel() || baseModel->NumSurfaces() == 0 ) { return NULL; } assert( baseModel->IsDynamicModel() == DM_STATIC ); const idRenderModelStatic* staticModel = static_cast< const idRenderModelStatic* >( baseModel ); const idMaterial* material = overlayMaterials[index]; int maxVerts = 0; int maxIndexes = 0; for( unsigned int i = firstOverlay; i < nextOverlay; i++ ) { const overlay_t& overlay = overlays[i & ( MAX_OVERLAYS - 1 )]; if( overlay.material == material ) { maxVerts += overlay.numVerts; maxIndexes += overlay.numIndexes; } } if( maxVerts == 0 || maxIndexes == 0 ) { return NULL; } // create a new triangle surface in frame memory so it gets automatically disposed of srfTriangles_t* newTri = ( srfTriangles_t* )R_ClearedFrameAlloc( sizeof( *newTri ), FRAME_ALLOC_SURFACE_TRIANGLES ); newTri->staticModelWithJoints = ( staticModel->jointsInverted != NULL ) ? const_cast< idRenderModelStatic* >( staticModel ) : NULL; // allow GPU skinning newTri->ambientCache = vertexCache.AllocVertex( NULL, ALIGN( maxVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); newTri->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); idDrawVert* mappedVerts = ( idDrawVert* )vertexCache.MappedVertexBuffer( newTri->ambientCache ); triIndex_t* mappedIndexes = ( triIndex_t* )vertexCache.MappedIndexBuffer( newTri->indexCache ); int numVerts = 0; int numIndexes = 0; for( unsigned int i = firstOverlay; i < nextOverlay; i++ ) { overlay_t& overlay = overlays[i & ( MAX_OVERLAYS - 1 )]; if( overlay.numVerts == 0 ) { if( i == firstOverlay ) { firstOverlay++; } continue; } if( overlay.material != material ) { continue; } // get the source model surface for this overlay surface const modelSurface_t* baseSurf = ( overlay.surfaceNum < staticModel->NumSurfaces() ) ? staticModel->Surface( overlay.surfaceNum ) : NULL; // if the surface ids no longer match if( baseSurf == NULL || baseSurf->id != overlay.surfaceId ) { // find the surface with the correct id if( staticModel->FindSurfaceWithId( overlay.surfaceId, overlay.surfaceNum ) ) { baseSurf = staticModel->Surface( overlay.surfaceNum ); } else { // the surface with this id no longer exists FreeOverlay( overlay ); if( i == firstOverlay ) { firstOverlay++; } continue; } } // check for out of range vertex references const srfTriangles_t* baseTri = baseSurf->geometry; if( overlay.maxReferencedVertex >= baseTri->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::CreateOverlayDrawSurf: overlay vertex out of range. Model has probably changed since generating the overlay." ); FreeOverlay( overlay ); if( i == firstOverlay ) { firstOverlay++; } continue; } // use SIMD optimized routine to copy the vertices and indices directly to write-combined memory R_CopyOverlaySurface( mappedVerts, numVerts, mappedIndexes, numIndexes, &overlay, baseTri->verts ); numIndexes += overlay.numIndexes; numVerts += overlay.numVerts; } newTri->numVerts = numVerts; newTri->numIndexes = numIndexes; // create the drawsurf drawSurf_t* drawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *drawSurf ), FRAME_ALLOC_DRAW_SURFACE ); drawSurf->frontEndGeo = newTri; drawSurf->numIndexes = newTri->numIndexes; drawSurf->ambientCache = newTri->ambientCache; drawSurf->indexCache = newTri->indexCache; drawSurf->shadowCache = 0; drawSurf->space = space; drawSurf->scissorRect = space->scissorRect; drawSurf->extraGLState = 0; drawSurf->renderZFail = 0; R_SetupDrawSurfShader( drawSurf, material, &space->entityDef->parms ); R_SetupDrawSurfJoints( drawSurf, newTri, NULL ); return drawSurf; }