idScreenRect R_CalcLightScissorRectangle( viewLight_t *vLight ) { idScreenRect r; srfTriangles_t *tri; idPlane eye, clip; idVec3 ndc; if( vLight->lightDef->parms.pointLight ) { idBounds bounds; idRenderLightLocal *lightDef = vLight->lightDef; tr.viewDef->viewFrustum.ProjectionBounds( idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ), bounds ); return R_ScreenRectFromViewFrustumBounds( bounds ); } if( r_useClippedLightScissors.GetInteger() == 2 ) { return R_ClippedLightScissorRectangle( vLight ); } r.Clear(); tri = vLight->lightDef->frustumTris; for( int i = 0; i < tri->numVerts; i++ ) { R_TransformModelToClip( tri->verts[i].xyz, tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); // if it is near clipped, clip the winding polygons to the view frustum if( clip[3] <= 1 ) { c_clippedLight++; if( r_useClippedLightScissors.GetInteger() ) { return R_ClippedLightScissorRectangle( vLight ); } else { r.x1 = r.y1 = 0; r.x2 = ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ) - 1; r.y2 = ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ) - 1; return r; } } R_TransformClipToDevice( clip, ndc ); float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ); float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ); if( windowX > tr.viewDef->scissor.x2 ) { windowX = tr.viewDef->scissor.x2; } else if( windowX < tr.viewDef->scissor.x1 ) { windowX = tr.viewDef->scissor.x1; } if( windowY > tr.viewDef->scissor.y2 ) { windowY = tr.viewDef->scissor.y2; } else if( windowY < tr.viewDef->scissor.y1 ) { windowY = tr.viewDef->scissor.y1; } r.AddPoint( windowX, windowY ); } // add the fudge boundary r.Expand(); c_unclippedLight++; return r; }
/* =================== R_EntityDefDynamicModel Issues a deferred entity callback if necessary. If the model isn't dynamic, it returns the original. Returns the cached dynamic model if present, otherwise creates it and any necessary overlays =================== */ idRenderModel *R_EntityDefDynamicModel( idRenderEntityLocal *def ) { bool callbackUpdate; // allow deferred entities to construct themselves if( def->parms.callback ) { callbackUpdate = R_IssueEntityDefCallback( def ); } else { callbackUpdate = false; } idRenderModel *model = def->parms.hModel; if( !model ) { common->Error( "R_EntityDefDynamicModel: NULL model" ); } if( model->IsDynamicModel() == DM_STATIC ) { def->dynamicModel = NULL; def->dynamicModelFrameCount = 0; return model; } // continously animating models (particle systems, etc) will have their snapshot updated every single view if( callbackUpdate || ( model->IsDynamicModel() == DM_CONTINUOUS && def->dynamicModelFrameCount != tr.frameCount ) ) { R_ClearEntityDefDynamicModel( def ); } // if we don't have a snapshot of the dynamic model, generate it now if( !def->dynamicModel ) { // instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot def->cachedDynamicModel = model->InstantiateDynamicModel( &def->parms, tr.viewDef, def->cachedDynamicModel ); if( def->cachedDynamicModel ) { // add any overlays to the snapshot of the dynamic model if( def->overlay && !r_skipOverlays.GetBool() ) { def->overlay->AddOverlaySurfacesToModel( def->cachedDynamicModel ); } else { idRenderModelOverlay::RemoveOverlaySurfacesFromModel( def->cachedDynamicModel ); } if( r_checkBounds.GetBool() ) { idBounds b = def->cachedDynamicModel->Bounds(); if( b[0][0] < def->referenceBounds[0][0] - CHECK_BOUNDS_EPSILON || b[0][1] < def->referenceBounds[0][1] - CHECK_BOUNDS_EPSILON || b[0][2] < def->referenceBounds[0][2] - CHECK_BOUNDS_EPSILON || b[1][0] > def->referenceBounds[1][0] + CHECK_BOUNDS_EPSILON || b[1][1] > def->referenceBounds[1][1] + CHECK_BOUNDS_EPSILON || b[1][2] > def->referenceBounds[1][2] + CHECK_BOUNDS_EPSILON ) { common->Printf( "entity %i dynamic model exceeded reference bounds\n", def->index ); } } } def->dynamicModel = def->cachedDynamicModel; def->dynamicModelFrameCount = tr.frameCount; } // set model depth hack value if( def->dynamicModel && model->DepthHack() != 0.0f && tr.viewDef ) { idPlane eye, clip; idVec3 ndc; R_TransformModelToClip( def->parms.origin, tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); R_TransformClipToDevice( clip, ndc ); def->parms.modelDepthHack = model->DepthHack() * ( 1.0f - ndc.z ); } // FIXME: if any of the surfaces have deforms, create a frame-temporary model with references to the // undeformed surfaces. This would allow deforms to be light interacting. return def->dynamicModel; }
/* ====================== R_ClippedLightScissorRectangle ====================== */ idScreenRect R_ClippedLightScissorRectangle( viewLight_t *vLight ) { int i, j; const idRenderLightLocal *light = vLight->lightDef; idScreenRect r; idFixedWinding w; r.Clear(); for( i = 0; i < 6; i++ ) { const idWinding *ow = light->frustumWindings[i]; // projected lights may have one of the frustums degenerated if( !ow ) { continue; } // the light frustum planes face out from the light, // so the planes that have the view origin on the negative // side will be the "back" faces of the light, which must have // some fragment inside the portalStack to be visible if( light->frustum[i].Distance( tr.viewDef->renderView.vieworg ) >= 0 ) { continue; } w = *ow; // now check the winding against each of the frustum planes for( j = 0; j < 5; j++ ) { if( !w.ClipInPlace( -tr.viewDef->frustum[j] ) ) { break; } } // project these points to the screen and add to bounds for( j = 0; j < w.GetNumPoints(); j++ ) { idPlane eye, clip; idVec3 ndc; R_TransformModelToClip( w[j].ToVec3(), tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); if( clip[3] <= 0.01f ) { clip[3] = 0.01f; } R_TransformClipToDevice( clip, ndc ); float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ); float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ); if( windowX > tr.viewDef->scissor.x2 ) { windowX = tr.viewDef->scissor.x2; } else if( windowX < tr.viewDef->scissor.x1 ) { windowX = tr.viewDef->scissor.x1; } if( windowY > tr.viewDef->scissor.y2 ) { windowY = tr.viewDef->scissor.y2; } else if( windowY < tr.viewDef->scissor.y1 ) { windowY = tr.viewDef->scissor.y1; } r.AddPoint( windowX, windowY ); } } // add the fudge boundary r.Expand(); return r; }
/* =================== R_EntityDefDynamicModel This is also called by the game code for idRenderWorldLocal::ModelTrace(), and idRenderWorldLocal::Trace() which is bad for performance... Issues a deferred entity callback if necessary. If the model isn't dynamic, it returns the original. Returns the cached dynamic model if present, otherwise creates it. =================== */ idRenderModel* R_EntityDefDynamicModel( idRenderEntityLocal* def ) { if( def->dynamicModelFrameCount == tr.frameCount ) { return def->dynamicModel; } // allow deferred entities to construct themselves bool callbackUpdate; if( def->parms.callback != NULL ) { SCOPED_PROFILE_EVENT( "R_IssueEntityDefCallback" ); callbackUpdate = R_IssueEntityDefCallback( def ); } else { callbackUpdate = false; } idRenderModel* model = def->parms.hModel; if( model == NULL ) { common->Error( "R_EntityDefDynamicModel: NULL model" ); return NULL; } if( model->IsDynamicModel() == DM_STATIC ) { def->dynamicModel = NULL; def->dynamicModelFrameCount = 0; return model; } // continously animating models (particle systems, etc) will have their snapshot updated every single view if( callbackUpdate || ( model->IsDynamicModel() == DM_CONTINUOUS && def->dynamicModelFrameCount != tr.frameCount ) ) { R_ClearEntityDefDynamicModel( def ); } // if we don't have a snapshot of the dynamic model, generate it now if( def->dynamicModel == NULL ) { SCOPED_PROFILE_EVENT( "InstantiateDynamicModel" ); // instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot def->cachedDynamicModel = model->InstantiateDynamicModel( &def->parms, tr.viewDef, def->cachedDynamicModel ); if( def->cachedDynamicModel != NULL && r_checkBounds.GetBool() ) { idBounds b = def->cachedDynamicModel->Bounds(); if( b[0][0] < def->localReferenceBounds[0][0] - CHECK_BOUNDS_EPSILON || b[0][1] < def->localReferenceBounds[0][1] - CHECK_BOUNDS_EPSILON || b[0][2] < def->localReferenceBounds[0][2] - CHECK_BOUNDS_EPSILON || b[1][0] > def->localReferenceBounds[1][0] + CHECK_BOUNDS_EPSILON || b[1][1] > def->localReferenceBounds[1][1] + CHECK_BOUNDS_EPSILON || b[1][2] > def->localReferenceBounds[1][2] + CHECK_BOUNDS_EPSILON ) { common->Printf( "entity %i dynamic model exceeded reference bounds\n", def->index ); } } def->dynamicModel = def->cachedDynamicModel; def->dynamicModelFrameCount = tr.frameCount; } // set model depth hack value if( def->dynamicModel != NULL && model->DepthHack() != 0.0f && tr.viewDef != NULL ) { idPlane eye, clip; idVec3 ndc; R_TransformModelToClip( def->parms.origin, tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); R_TransformClipToDevice( clip, ndc ); def->parms.modelDepthHack = model->DepthHack() * ( 1.0f - ndc.z ); } else { def->parms.modelDepthHack = 0.0f; } return def->dynamicModel; }