/* ================ R_AddInGameGuis ================ */ void R_AddInGameGuis( const drawSurf_t* const drawSurfs[], const int numDrawSurfs ) { SCOPED_PROFILE_EVENT( "R_AddInGameGuis" ); // check for gui surfaces for( int i = 0; i < numDrawSurfs; i++ ) { const drawSurf_t* drawSurf = drawSurfs[i]; idUserInterface* gui = drawSurf->material->GlobalGui(); int guiNum = drawSurf->material->GetEntityGui() - 1; if( guiNum >= 0 && guiNum < MAX_RENDERENTITY_GUI ) { if( drawSurf->space->entityDef != NULL ) { gui = drawSurf->space->entityDef->parms.gui[ guiNum ]; } } if( gui == NULL ) { continue; } idBounds ndcBounds; if( !R_PreciseCullSurface( drawSurf, ndcBounds ) ) { // did we ever use this to forward an entity color to a gui that didn't set color? // memcpy( tr.guiShaderParms, shaderParms, sizeof( tr.guiShaderParms ) ); R_RenderGuiSurf( gui, drawSurf ); } } }
/* ================== R_GenerateSurfaceSubview ================== */ bool R_GenerateSurfaceSubview( drawSurf_t *drawSurf ) { idBounds ndcBounds; viewDef_t *parms; const idMaterial *shader; // for testing the performance hit if ( r_skipSubviews.GetBool() ) { return false; } if ( R_PreciseCullSurface( drawSurf, ndcBounds ) ) { return false; } shader = drawSurf->material; // never recurse through a subview surface that we are // already seeing through for ( parms = tr.viewDef ; parms ; parms = parms->superView ) { if ( parms->subviewSurface && parms->subviewSurface->geo == drawSurf->geo && parms->subviewSurface->space->entityDef == drawSurf->space->entityDef ) { break; } } if ( parms ) { return false; } // crop the scissor bounds based on the precise cull idScreenRect scissor; idScreenRect *v = &tr.viewDef->viewport; scissor.x1 = v->x1 + (int)( (v->x2 - v->x1 + 1 ) * 0.5f * ( ndcBounds[0][0] + 1.0f )); scissor.y1 = v->y1 + (int)( (v->y2 - v->y1 + 1 ) * 0.5f * ( ndcBounds[0][1] + 1.0f )); scissor.x2 = v->x1 + (int)( (v->x2 - v->x1 + 1 ) * 0.5f * ( ndcBounds[1][0] + 1.0f )); scissor.y2 = v->y1 + (int)( (v->y2 - v->y1 + 1 ) * 0.5f * ( ndcBounds[1][1] + 1.0f )); // nudge a bit for safety scissor.Expand(); scissor.Intersect( tr.viewDef->scissor ); if ( scissor.IsEmpty() ) { // cropped out return false; } // see what kind of subview we are making if ( shader->GetSort() != SS_SUBVIEW ) { for ( int i = 0 ; i < shader->GetNumStages() ; i++ ) { const shaderStage_t *stage = shader->GetStage( i ); switch ( stage->texture.dynamic ) { case DI_REMOTE_RENDER: R_RemoteRender( drawSurf, const_cast<textureStage_t *>(&stage->texture) ); break; case DI_MIRROR_RENDER: R_MirrorRender( drawSurf, const_cast<textureStage_t *>(&stage->texture), scissor ); break; case DI_XRAY_RENDER: R_XrayRender( drawSurf, const_cast<textureStage_t *>(&stage->texture), scissor ); break; } } return true; } // issue a new view command parms = R_MirrorViewBySurface( drawSurf ); if ( !parms ) { return false; } parms->scissor = scissor; parms->superView = tr.viewDef; parms->subviewSurface = drawSurf; // triangle culling order changes with mirroring parms->isMirror = ( ( (int)parms->isMirror ^ (int)tr.viewDef->isMirror ) != 0 ); // generate render commands for it R_RenderView( parms ); return true; }
/* ================= R_AddDrawSurf ================= */ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity, const idMaterial *shader, const idScreenRect &scissor ) { drawSurf_t *drawSurf; const float *shaderParms; static float refRegs[MAX_EXPRESSION_REGISTERS]; // don't put on stack, or VC++ will do a page touch float generatedShaderParms[MAX_ENTITY_SHADER_PARMS]; drawSurf = ( drawSurf_t * ) R_FrameAlloc( sizeof( *drawSurf ) ); drawSurf->geo = tri; drawSurf->space = space; drawSurf->material = shader; drawSurf->scissorRect = scissor; drawSurf->sort = shader->GetSort() + tr.sortOffset; drawSurf->dsFlags = 0; // bumping this offset each time causes surfaces with equal sort orders to still // deterministically draw in the order they are added tr.sortOffset += 0.000001f; // if it doesn't fit, resize the list if( tr.viewDef->numDrawSurfs == tr.viewDef->maxDrawSurfs ) { drawSurf_t **old = tr.viewDef->drawSurfs; int count; if( tr.viewDef->maxDrawSurfs == 0 ) { tr.viewDef->maxDrawSurfs = INITIAL_DRAWSURFS; count = 0; } else { count = tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ); tr.viewDef->maxDrawSurfs *= 2; } tr.viewDef->drawSurfs = ( drawSurf_t ** ) R_FrameAlloc( tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ) ); memcpy( tr.viewDef->drawSurfs, old, count ); } tr.viewDef->drawSurfs[tr.viewDef->numDrawSurfs] = drawSurf; tr.viewDef->numDrawSurfs++; // 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 ) ); drawSurf->shaderRegisters = regs; // a reference shader will take the calculated stage color value from another shader // and use that for the parm0-parm3 of the current shader, which allows a stage of // a light model and light flares to pick up different flashing tables from // different light shaders if( renderEntity->referenceShader ) { // evaluate the reference shader to find our shader parms const shaderStage_t *pStage; renderEntity->referenceShader->EvaluateRegisters( refRegs, renderEntity->shaderParms, tr.viewDef, renderEntity->referenceSound ); pStage = renderEntity->referenceShader->GetStage( 0 ); memcpy( generatedShaderParms, renderEntity->shaderParms, sizeof( generatedShaderParms ) ); generatedShaderParms[0] = refRegs[pStage->color.registers[0]]; generatedShaderParms[1] = refRegs[pStage->color.registers[1]]; generatedShaderParms[2] = refRegs[pStage->color.registers[2]]; shaderParms = generatedShaderParms; } else { // evaluate with the entityDef's shader parms shaderParms = renderEntity->shaderParms; } float oldFloatTime; int oldTime; if( space->entityDef && space->entityDef->parms.timeGroup ) { oldFloatTime = tr.viewDef->floatTime; oldTime = tr.viewDef->renderView.time; tr.viewDef->floatTime = game->GetTimeGroupTime( space->entityDef->parms.timeGroup ) * 0.001; tr.viewDef->renderView.time = game->GetTimeGroupTime( space->entityDef->parms.timeGroup ); } shader->EvaluateRegisters( regs, shaderParms, tr.viewDef, renderEntity->referenceSound ); if( space->entityDef && space->entityDef->parms.timeGroup ) { tr.viewDef->floatTime = oldFloatTime; tr.viewDef->renderView.time = oldTime; } } // check for deformations R_DeformDrawSurf( drawSurf ); // skybox surfaces need a dynamic texgen switch( shader->Texgen() ) { case TG_SKYBOX_CUBE: R_SkyboxTexGen( drawSurf, tr.viewDef->renderView.vieworg ); break; case TG_WOBBLESKY_CUBE: R_WobbleskyTexGen( drawSurf, tr.viewDef->renderView.vieworg ); break; default: break; } // check for gui surfaces idUserInterface *gui = NULL; if( !space->entityDef ) { gui = shader->GlobalGui(); } else { int guiNum = shader->GetEntityGui() - 1; if( guiNum >= 0 && guiNum < MAX_RENDERENTITY_GUI ) { gui = renderEntity->gui[guiNum]; } if( gui == NULL ) { gui = shader->GlobalGui(); } } if( gui ) { // force guis on the fast time float oldFloatTime; int oldTime; oldFloatTime = tr.viewDef->floatTime; oldTime = tr.viewDef->renderView.time; tr.viewDef->floatTime = game->GetTimeGroupTime( 1 ) * 0.001; tr.viewDef->renderView.time = game->GetTimeGroupTime( 1 ); idBounds ndcBounds; if( !R_PreciseCullSurface( drawSurf, ndcBounds ) ) { R_RenderGuiSurf( gui, drawSurf ); } tr.viewDef->floatTime = oldFloatTime; tr.viewDef->renderView.time = oldTime; } // we can't add subviews at this point, because that would // increment tr.viewCount, messing up the rest of the surface // adds for this view }