/* ** SurfIsOffscreen ** ** Determines if a surface is completely offscreen. */ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128] ) { float shortest = 100000000; int entityNum; int numTriangles; shader_t *shader; int fogNum; int dlighted; vec4_t clip, eye; int i; unsigned int pointOr = 0; unsigned int pointAnd = (unsigned int)~0; R_RotateForViewer(); R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); RB_BeginSurface( shader, fogNum ); rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); assert( tess.numVertexes < 128 ); for ( i = 0; i < tess.numVertexes; i++ ) { int j; unsigned int pointFlags = 0; R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); for ( j = 0; j < 3; j++ ) { if ( clip[j] >= clip[3] ) { pointFlags |= (1 << (j*2)); } else if ( clip[j] <= -clip[3] ) { pointFlags |= ( 1 << (j*2+1)); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if ( pointAnd ) { return qtrue; } // determine if this surface is backfaced and also determine the distance // to the nearest vertex so we can cull based on portal range. Culling // based on vertex distance isn't 100% correct (we should be checking for // range to the surface), but it's good enough for the types of portals // we have in the game right now. numTriangles = tess.numIndexes / 3; for ( i = 0; i < tess.numIndexes; i += 3 ) { vec3_t normal; float len; VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal ); len = VectorLengthSquared( normal ); // lose the sqrt if ( len < shortest ) { shortest = len; } if ( DotProduct( normal, tess.normal[tess.indexes[i]] ) >= 0 ) { numTriangles--; } } if ( !numTriangles ) { return qtrue; } // mirrors can early out at this point, since we don't do a fade over distance // with them (although we could) if ( IsMirror( drawSurf, entityNum ) ) { return qfalse; } if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) ) { return qtrue; } return qfalse; }
/* ================== RB_AddFlare This is called at surface tesselation time ================== */ void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal, float lightScale) { int i; flare_t *f, *oldest; vec3_t local; float d; vec4_t eye, clip, normalized, window; backEnd.pc.c_flareAdds++; // if the point is off the screen, don't bother adding it // calculate screen coordinates and depth R_TransformModelToClip( point, backEnd.or.modelMatrix, backEnd.viewParms.projectionMatrix, eye, clip ); // check to see if the point is completely off screen for ( i = 0 ; i < 3 ; i++ ) { if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) { return; } } R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window ); if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) { return; // shouldn't happen, since we check the clip[] above, except for FP rounding } // see if a flare with a matching surface, scene, and view exists oldest = r_flareStructs; for ( f = r_activeFlares ; f ; f = f->next ) { if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum && f->inPortal == backEnd.viewParms.isPortal ) { break; } } // allocate a new one if (!f ) { if ( !r_inactiveFlares ) { // the list is completely full return; } f = r_inactiveFlares; r_inactiveFlares = r_inactiveFlares->next; f->next = r_activeFlares; r_activeFlares = f; f->surface = surface; f->frameSceneNum = backEnd.viewParms.frameSceneNum; f->inPortal = backEnd.viewParms.isPortal; f->addedFrame = -1; } if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) { f->visible = qfalse; f->fadeTime = backEnd.refdef.time - 2000; } f->addedFrame = backEnd.viewParms.frameCount; f->fogNum = fogNum; f->lightScale = lightScale; VectorCopy( color, f->color ); // fade the intensity of the flare down as the // light surface turns away from the viewer if ( normal ) { VectorSubtract( backEnd.viewParms.or.origin, point, local ); VectorNormalizeFast( local ); d = DotProduct( local, normal ); VectorScale( f->color, d, f->color ); } // save info needed to test f->windowX = backEnd.viewParms.viewportX + window[0]; f->windowY = backEnd.viewParms.viewportY + window[1]; f->eyeZ = eye[2]; }
/* ========================= R_PreciseCullSurface Check the surface for visibility on a per-triangle basis for cases when it is going to be VERY expensive to draw (subviews) If not culled, also returns the bounding box of the surface in Normalized Device Coordinates, so it can be used to crop the scissor rect. OPTIMIZE: we could also take exact portal passing into consideration ========================= */ bool R_PreciseCullSurface( const drawSurf_t *drawSurf, idBounds &ndcBounds ) { const srfTriangles_t *tri; int numTriangles; idPlane clip, eye; int i, j, k; unsigned int pointOr; unsigned int pointAnd; unsigned int pointFlags; idVec3 localView; idFixedWinding w; tri = drawSurf->geo; pointOr = 0; pointAnd = ( unsigned int )~0; // get an exact bounds of the triangles for scissor cropping ndcBounds.Clear(); for( i = 0; i < tri->numVerts; i++ ) { R_TransformModelToClip( tri->verts[i].xyz, drawSurf->space->modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); pointFlags = 0; for( k = 0; k < 3; k++ ) { if( clip[k] >= clip[3] ) { pointFlags |= ( 1 << ( k * 2 ) ); } else if( clip[k] <= -clip[3] ) { pointFlags |= ( 1 << ( k * 2 + 1 ) ); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if( pointAnd ) { return true; } // backface and frustum cull numTriangles = tri->numIndexes / 3; R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localView ); for( i = 0; i < tri->numIndexes; i += 3 ) { idVec3 dir, normal; float dot; idVec3 d1, d2; const idVec3 &v1 = tri->verts[tri->indexes[i]].xyz; const idVec3 &v2 = tri->verts[tri->indexes[i + 1]].xyz; const idVec3 &v3 = tri->verts[tri->indexes[i + 2]].xyz; // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized // axis that we get from the gui view transform. It doesn't hurt anything, because // we know that all gui generated surfaces are front facing if( tr.guiRecursionLevel == 0 ) { // we don't care that it isn't normalized, // all we want is the sign d1 = v2 - v1; d2 = v3 - v1; normal = d2.Cross( d1 ); dir = v1 - localView; dot = normal * dir; if( dot >= 0.0f ) { return true; } } // now find the exact screen bounds of the clipped triangle w.SetNumPoints( 3 ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() ); w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f; for( j = 0; j < 4; j++ ) { if( !w.ClipInPlace( -tr.viewDef->frustum[j], 0.1f ) ) { break; } } for( j = 0; j < w.GetNumPoints(); j++ ) { idVec3 screen; R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen ); ndcBounds.AddPoint( screen ); } } // if we don't enclose any area, return if( ndcBounds.IsCleared() ) { return true; } return false; }
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; }
/* ======================================================================================================================================= RB_AddFlare This is called at surface tesselation time ======================================================================================================================================= */ void RB_AddFlare(void *surface, int fogNum, vec3_t point, vec3_t color, float scale, vec3_t normal, int id, int flags) { //----(SA) added scale. added id. added visible int i; flare_t *f; vec3_t local; float d = 1; vec4_t eye, clip, normalized, window; backEnd.pc.c_flareAdds++; if(normal && (normal[0] || normal[1] || normal[2])) { VectorSubtract(backEnd.viewParms.or.origin, point, local); VectorNormalizeFast(local); d = DotProduct(local, normal); // If the viewer is behind the flare don't add it. if(d < 0) return; } // if the point is off the screen, don't bother adding it // calculate screen coordinates and depth R_TransformModelToClip(point, backEnd.or.modelMatrix, backEnd.viewParms.projectionMatrix, eye, clip); //ri.Printf(PRINT_ALL, "src: %f %f %f \n", point[0], point[1], point[2]); //ri.Printf(PRINT_ALL, "eye: %f %f %f %f\n", eye[0], eye[1], eye[2], eye[3]); // check to see if the point is completely off screen for (i = 0 ; i < 3 ; i++) { if (clip[i] >= clip[3] || clip[i] <= -clip[3]) { return; } } R_TransformClipToWindow(clip, &backEnd.viewParms, normalized, window); //ri.Printf(PRINT_ALL, "window: %f %f %f \n", window[0], window[1], window[2]); if (window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight) { return; // shouldn't happen, since we check the clip[] above, except for FP rounding } // see if a flare with a matching surface, scene, and view exists for (f = r_activeFlares ; f ; f = f->next) { // if (f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum && f->inPortal == backEnd.viewParms.isPortal) { // (SA) added back in more checks for different scenes if (f->id == id && f->frameSceneNum == backEnd.viewParms.frameSceneNum && f->inPortal == backEnd.viewParms.isPortal) { break; } } // allocate a new one if (!f) { if (!r_inactiveFlares) { // the list is completely full return; } f = r_inactiveFlares; r_inactiveFlares = r_inactiveFlares->next; f->next = r_activeFlares; r_activeFlares = f; f->surface = surface; f->frameSceneNum = backEnd.viewParms.frameSceneNum; f->inPortal = backEnd.viewParms.isPortal; f->addedFrame = -1; f->id = id; } f->flags = flags; if (f->addedFrame != backEnd.viewParms.frameCount - 1) { f->visible = qfalse; f->fadeTime = backEnd.refdef.time - 2000; } f->addedFrame = backEnd.viewParms.frameCount; f->fogNum = fogNum; VectorCopy(point, f->origin); VectorCopy(color, f->color); f->scale = scale; //----(SA) // fade the intensity of the flare down as the // light surface turns away from the viewer VectorScale(f->color, d, f->color); // save info needed to test f->windowX = backEnd.viewParms.viewportX + window[0]; f->windowY = backEnd.viewParms.viewportY + window[1]; f->eyeZ = eye[2]; }
/* ====================== 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 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_PreciseCullSurface Check the surface for visibility on a per-triangle basis for cases when it is going to be VERY expensive to draw (subviews) If not culled, also returns the bounding box of the surface in Normalized Device Coordinates, so it can be used to crop the scissor rect. OPTIMIZE: we could also take exact portal passing into consideration ========================= */ bool R_PreciseCullSurface( const drawSurf_t* drawSurf, idBounds& ndcBounds ) { const srfTriangles_t* tri = drawSurf->frontEndGeo; unsigned int pointOr = 0; unsigned int pointAnd = ( unsigned int )~0; // get an exact bounds of the triangles for scissor cropping ndcBounds.Clear(); const idJointMat* joints = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? tri->staticModelWithJoints->jointsInverted : NULL; for( int i = 0; i < tri->numVerts; i++ ) { const idVec3 vXYZ = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[i], joints ); idPlane eye, clip; R_TransformModelToClip( vXYZ, drawSurf->space->modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); unsigned int pointFlags = 0; for( int j = 0; j < 3; j++ ) { if( clip[j] >= clip[3] ) { pointFlags |= ( 1 << ( j * 2 + 0 ) ); } else if( clip[j] <= -clip[3] ) // FIXME: the D3D near clip plane is at zero instead of -1 { pointFlags |= ( 1 << ( j * 2 + 1 ) ); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if( pointAnd != 0 ) { return true; } // backface and frustum cull idVec3 localViewOrigin; R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewOrigin ); for( int i = 0; i < tri->numIndexes; i += 3 ) { const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 0 ] ], joints ); const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 1 ] ], joints ); const idVec3 v3 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 2 ] ], joints ); // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized // axis that we get from the gui view transform. It doesn't hurt anything, because // we know that all gui generated surfaces are front facing if( tr.guiRecursionLevel == 0 ) { // we don't care that it isn't normalized, // all we want is the sign const idVec3 d1 = v2 - v1; const idVec3 d2 = v3 - v1; const idVec3 normal = d2.Cross( d1 ); const idVec3 dir = v1 - localViewOrigin; const float dot = normal * dir; if( dot >= 0.0f ) { return true; } } // now find the exact screen bounds of the clipped triangle idFixedWinding w; w.SetNumPoints( 3 ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() ); w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f; for( int j = 0; j < 4; j++ ) { if( !w.ClipInPlace( -tr.viewDef->frustums[FRUSTUM_PRIMARY][j], 0.1f ) ) { break; } } for( int j = 0; j < w.GetNumPoints(); j++ ) { idVec3 screen; R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen ); ndcBounds.AddPoint( screen ); } } // if we don't enclose any area, return if( ndcBounds.IsCleared() ) { return true; } return false; }
/* =================== 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; }
void RB_AddFlare(srfFlare_t *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal, int radii, int efftype, float scaled, int type) { int i; flare_t *f, *oldest; vec3_t local; float d = 1; vec4_t eye, clip, normalized, window; backEnd.pc.c_flareAdds++; // fade the intensity of the flare down as the // light surface turns away from the viewer if(normal && (normal[0] || normal[1] || normal[2]) ) { VectorSubtract( backEnd.viewParms.or.origin, point, local ); VectorNormalizeFast(local); d = DotProduct(local, normal); // If the viewer is behind the flare don't add it. if(d < 0) return; } flaredsize = backEnd.viewParms.viewportHeight; R_TransformModelToClip( point, backEnd.or.modelMatrix, backEnd.viewParms.projectionMatrix, eye, clip ); // check to see if the point is completely off screen for ( i = 0 ; i < 3 ; i++ ) { if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) { return; } } R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window ); if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) { return; // shouldn't happen, since we check the clip[] above, except for FP rounding } // see if a flare with a matching surface, scene, and view exists oldest = r_flareStructs; for ( f = r_activeFlares ; f ; f = f->next ) { if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum && f->inPortal == backEnd.viewParms.isPortal ) { break; } } // allocate a new one if (!f ) { if ( !r_inactiveFlares ) { // the list is completely full return; } f = r_inactiveFlares; r_inactiveFlares = r_inactiveFlares->next; f->next = r_activeFlares; r_activeFlares = f; f->surface = surface; f->frameSceneNum = backEnd.viewParms.frameSceneNum; f->inPortal = backEnd.viewParms.isPortal; f->addedFrame = -1; } if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) { f->visible = qfalse; f->fadeTime = backEnd.refdef.time - 2000; } f->addedFrame = backEnd.viewParms.frameCount; f->fogNum = fogNum; f->ftype = efftype; VectorCopy(point, f->origin); VectorCopy( color, f->color ); if ( (r_flaresDlightFade->integer) && (type == 1) ) { // leilei - dynamic light flares fading instantly f->fadeTime = -666; } if (!pvrhack) // leilei - don't do this on powervr VectorScale( f->color, d, f->color ); // save info needed to test f->windowX = backEnd.viewParms.viewportX + window[0]; f->windowY = backEnd.viewParms.viewportY + window[1]; f->radius = radii * scaled * 0.17; f->eyeZ = eye[2]; f->theshader = tr.flareShader; f->type = type; if (f->type == 0) f->theshader = surface->shadder; else f->theshader = tr.flareShader; if ( (type == 1) && (r_flaresDlightScale->value) ) { // leilei - dynamic light flare scale float ef = r_flaresDlightScale->value; if (ef > 1.0f) ef = 1.0f; if (ef < 0.01f) ef = 0.01f; f->radius *= ef; } if ( (type == 1) && (r_flaresDlightOpacity->value) ) { // leilei - dynamic light flare scale float ef = r_flaresDlightOpacity->value; if (ef > 1.0f) ef = 1.0f; if (ef < 0.1f) ef = 0.1f; f->color[0] *= ef; f->color[1] *= ef; f->color[2] *= ef; } // if ( (r_flaresDlightShrink->integer) && (type == 1) ) // leilei - dynamic light flares shrinking when close // { // } }