//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "render_pch.h" #include "client.h" #include "bitmap/imageformat.h" #include "bitmap/tgawriter.h" #include #include "collisionutils.h" #include "cl_main.h" #include "tier0/vprof.h" #include "debugoverlay.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- extern ConVar r_avglight; extern int r_surfacevisframe; static model_t* s_pLightVecModel = 0; ConVar r_visualizetraces( "r_visualizetraces", "0", FCVAR_CHEAT ); ConVar r_visualizelighttraces( "r_visualizelighttraces", "0", FCVAR_CHEAT ); ConVar r_visualizelighttracesshowfulltrace( "r_visualizelighttracesshowfulltrace", "0", FCVAR_CHEAT ); //----------------------------------------------------------------------------- // State associated with R_LightVec //----------------------------------------------------------------------------- struct LightVecState_t { LightVecState_t() { } Ray_t m_Ray; float m_HitFrac; float* m_pTextureS; float* m_pTextureT; float* m_pLightmapS; float* m_pLightmapT; SurfaceHandle_t m_nSkySurfID; bool m_bUseLightStyles; CUtlVector m_LightTestDisps; }; //----------------------------------------------------------------------------- // Globals associated with dynamic lighting //----------------------------------------------------------------------------- int r_dlightchanged; int r_dlightactive; //----------------------------------------------------------------------------- // Displacements to test against for R_LightVec //----------------------------------------------------------------------------- /* ================== R_AnimateLight ================== */ void R_AnimateLight (void) { INetworkStringTable *table = cl.m_pLightStyleTable; if ( !table ) return; // light animations // 'm' is normal light, 'a' is no light, 'z' is double bright int i = (int)(cl.GetTime()*10); for (int j=0 ; jGetStringUserData( j, &length ); length--; if (!lightstyle || !lightstyle[0]) { d_lightstylevalue[j] = 256; d_lightstylenumframes[j] = 0; continue; } d_lightstylenumframes[j] = length; int k = i % length; k = lightstyle[k] - 'a'; k = k*22; if (d_lightstylevalue[j] != k) { d_lightstylevalue[j] = k; d_lightstyleframe[j] = r_framecount; } } } /* ============================================================================= DYNAMIC LIGHTS ============================================================================= */ // Returns true if the surface has the specified dlight already set on it for this frame. inline bool R_IsDLightAlreadyMarked( msurfacelighting_t *pLighting, int bit ) { return (pLighting->m_nDLightFrame == r_framecount) && (pLighting->m_fDLightBits & bit); } // Mark the surface as changed by the specified dlight (so its texture gets updated when // it comes time to render). inline void R_MarkSurfaceDLight( SurfaceHandle_t surfID, msurfacelighting_t *pLighting, int bit) { pLighting->m_nDLightFrame = r_framecount; pLighting->m_fDLightBits |= bit; MSurf_Flags( surfID ) |= SURFDRAW_HASDLIGHT; } int R_TryLightMarkSurface( dlight_t *light, msurfacelighting_t *pLighting, SurfaceHandle_t surfID, int bit ) { // Make sure this light actually intersects the surface cache of the surfaces it hits mtexinfo_t *tex; // FIXME: No worky for brush models // Find the perpendicular distance to the surface we're lighting // NOTE: Allow some stuff that's slightly behind it because view models can get behind walls // FIXME: We should figure out a better way to deal with view models float perpDistSq = DotProduct (light->origin, MSurf_Plane( surfID ).normal) - MSurf_Plane( surfID ).dist; if (perpDistSq < DLIGHT_BEHIND_PLANE_DIST) return 0; perpDistSq *= perpDistSq; float flInPlaneRadiusSq = light->GetRadiusSquared() - perpDistSq; if (flInPlaneRadiusSq <= 0) return 0; tex = MSurf_TexInfo( surfID ); Vector2D mins, maxs; mins.Init( pLighting->m_LightmapMins[0], pLighting->m_LightmapMins[1] ); maxs.Init( mins.x + pLighting->m_LightmapExtents[0], mins.y + pLighting->m_LightmapExtents[1] ); // Project light center into texture coordinates Vector2D vecCircleCenter; vecCircleCenter.x = DotProduct (light->origin, tex->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) + tex->lightmapVecsLuxelsPerWorldUnits[0][3]; vecCircleCenter.y = DotProduct (light->origin, tex->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) + tex->lightmapVecsLuxelsPerWorldUnits[1][3]; // convert from world space to luxel space and convert to int float flInPlaneLuxelRadius = sqrtf( flInPlaneRadiusSq * tex->luxelsPerWorldUnit * tex->luxelsPerWorldUnit ); // Does the circle intersect the square? if ( !IsCircleIntersectingRectangle( mins, maxs, vecCircleCenter, flInPlaneLuxelRadius ) ) return 0; // Ok, mark the surface as using this light. R_MarkSurfaceDLight( surfID, pLighting, bit); return 1; } int R_MarkLightsLeaf( dlight_t *light, int bit, mleaf_t *pLeaf ) { int countMarked = 0; for ( int i = 0; i < pLeaf->dispCount; i++ ) { IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i ); SurfaceHandle_t parentSurfID = pDispInfo->GetParent(); if ( parentSurfID ) { // Don't redo all this work if we already hit this surface and decided it's lit by this light. msurfacelighting_t *pLighting = SurfaceLighting( parentSurfID ); if( !R_IsDLightAlreadyMarked( pLighting, bit) ) { // Do a different test for displacement surfaces. Vector bmin, bmax; MSurf_DispInfo( parentSurfID )->GetBoundingBox( bmin, bmax ); if ( IsBoxIntersectingSphere(bmin, bmax, light->origin, light->GetRadius()) ) { R_MarkSurfaceDLight( parentSurfID, pLighting, bit ); countMarked++; } } } } SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface]; for ( int i = 0; i < pLeaf->nummarksurfaces; i++ ) { SurfaceHandle_t surfID = pHandle[i]; ASSERT_SURF_VALID( surfID ); // only process leaf surfaces if ( MSurf_Flags( surfID ) & SURFDRAW_NODE ) continue; // Don't redo all this work if we already hit this surface and decided it's lit by this light. msurfacelighting_t *pLighting = SurfaceLighting( surfID ); if(R_IsDLightAlreadyMarked(pLighting, bit)) continue; float dist = DotProduct( light->origin, MSurf_Plane( surfID ).normal) - MSurf_Plane( surfID ).dist; if ( dist > light->GetRadius() || dist < -light->GetRadius() ) continue; countMarked += R_TryLightMarkSurface( light, pLighting, surfID, bit ); } return countMarked; } /* ============= R_MarkLights ============= */ int R_MarkLights (dlight_t *light, int bit, mnode_t *node) { cplane_t *splitplane; float dist; int i; if (node->contents >= 0) { // This is a leaf, so check displacement surfaces and leaf faces return R_MarkLightsLeaf( light, bit, (mleaf_t*)node ); } splitplane = node->plane; dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; if (dist > light->GetRadius()) { return R_MarkLights (light, bit, node->children[0]); } if (dist < -light->GetRadius()) { return R_MarkLights (light, bit, node->children[1]); } // mark the polygons int countMarked = 0; SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface ); for (i=0 ; inumsurfaces ; i++, surfID++) { // Don't redo all this work if we already hit this surface and decided it's lit by this light. msurfacelighting_t *pLighting = SurfaceLighting( surfID ); if(R_IsDLightAlreadyMarked( pLighting, bit)) continue; countMarked += R_TryLightMarkSurface( light, pLighting, surfID, bit ); } countMarked += R_MarkLights( light, bit, node->children[0] ); return countMarked + R_MarkLights( light, bit, node->children[1] ); } void R_MarkDLightsOnSurface( mnode_t* pNode ) { if (!pNode || !g_bActiveDlights) return; dlight_t *l = cl_dlights; for (int i=0 ; idie < cl.GetTime() || !l->IsRadiusGreaterThanZero() ) continue; if (l->flags & DLIGHT_NO_WORLD_ILLUMINATION) continue; R_MarkLights ( l, 1<nodes ); MarkDLightsOnStaticProps(); } //----------------------------------------------------------------------------- // Computes s and t coords of texture at intersection pt //----------------------------------------------------------------------------- static void ComputeTextureCoordsAtIntersection( mtexinfo_t* pTex, Vector const& pt, float *textureS, float *textureT ) { if( pTex->material && textureS && textureT ) { *textureS = DotProduct( pt, pTex->textureVecsTexelsPerWorldUnits[0].AsVector3D() ) + pTex->textureVecsTexelsPerWorldUnits[0][3]; *textureT = DotProduct( pt, pTex->textureVecsTexelsPerWorldUnits[1].AsVector3D() ) + pTex->textureVecsTexelsPerWorldUnits[1][3]; *textureS /= pTex->material->GetMappingWidth(); *textureT /= pTex->material->GetMappingHeight(); } } //----------------------------------------------------------------------------- // Computes s and t coords of texture at intersection pt //----------------------------------------------------------------------------- static void ComputeLightmapCoordsAtIntersection( msurfacelighting_t *pLighting, float ds, float dt, float *lightmapS, float *lightmapT ) { if( lightmapS && lightmapT ) { if( pLighting->m_LightmapExtents[0] != 0 ) *lightmapS = (ds + 0.5f) / ( float )pLighting->m_LightmapExtents[0]; else *lightmapS = 0.5f; if( pLighting->m_LightmapExtents[1] != 0 ) *lightmapT = (dt + 0.5f) / ( float )pLighting->m_LightmapExtents[1]; else *lightmapT = 0.5f; } } //----------------------------------------------------------------------------- // Computes the lightmap color at a particular point //----------------------------------------------------------------------------- static void ComputeLightmapColor( SurfaceHandle_t surfID, int ds, int dt, bool bUseLightStyles, Vector& c ) { msurfacelighting_t *pLighting = SurfaceLighting( surfID ); ColorRGBExp32* pLightmap = pLighting->m_pSamples; if( !pLightmap ) { static int messagecount = 0; if ( ++messagecount < 10 ) { // Stop spamming. I heard you already!!! ConMsg( "hit surface has no samples\n" ); } return; } int smax = ( pLighting->m_LightmapExtents[0] ) + 1; int tmax = ( pLighting->m_LightmapExtents[1] ) + 1; int offset = smax * tmax; if ( SurfHasBumpedLightmaps( surfID ) ) { offset *= ( NUM_BUMP_VECTS + 1 ); } pLightmap += dt * smax + ds; int nMaxMaps = bUseLightStyles ? MAXLIGHTMAPS : 1; for (int maps = 0 ; maps < nMaxMaps && pLighting->m_nStyles[maps] != 255 ; ++maps) { float scale = LightStyleValue( pLighting->m_nStyles[maps] ); c[0] += TexLightToLinear( pLightmap->r, pLightmap->exponent ) * scale; c[1] += TexLightToLinear( pLightmap->g, pLightmap->exponent ) * scale; c[2] += TexLightToLinear( pLightmap->b, pLightmap->exponent ) * scale; // Check version 32 in source safe for some debugging crap pLightmap += offset; } } //----------------------------------------------------------------------------- // Computes the lightmap color at a particular point //----------------------------------------------------------------------------- static void ComputeLightmapColorFromAverage( msurfacelighting_t *pLighting, bool bUseLightStyles, Vector& c ) { int nMaxMaps = bUseLightStyles ? MAXLIGHTMAPS : 1; for (int maps = 0 ; maps < nMaxMaps && pLighting->m_nStyles[maps] != 255 ; ++maps) { float scale = LightStyleValue( pLighting->m_nStyles[maps] ); ColorRGBExp32* pAvgColor = pLighting->AvgLightColor(maps); c[0] += TexLightToLinear( pAvgColor->r, pAvgColor->exponent ) * scale; c[1] += TexLightToLinear( pAvgColor->g, pAvgColor->exponent ) * scale; c[2] += TexLightToLinear( pAvgColor->b, pAvgColor->exponent ) * scale; } } //----------------------------------------------------------------------------- // Tests a particular surface //----------------------------------------------------------------------------- static bool FASTCALL FindIntersectionAtSurface( SurfaceHandle_t surfID, float f, Vector& c, LightVecState_t& state ) { // no lightmaps on this surface? punt... // FIXME: should be water surface? if (MSurf_Flags( surfID ) & SURFDRAW_NOLIGHT) return false; // Compute the actual point Vector pt; VectorMA( state.m_Ray.m_Start, f, state.m_Ray.m_Delta, pt ); mtexinfo_t* pTex = MSurf_TexInfo( surfID ); // See where in lightmap space our intersection point is float s, t; s = DotProduct (pt, pTex->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) + pTex->lightmapVecsLuxelsPerWorldUnits[0][3]; t = DotProduct (pt, pTex->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) + pTex->lightmapVecsLuxelsPerWorldUnits[1][3]; // Not in the bounds of our lightmap? punt... msurfacelighting_t *pLighting = SurfaceLighting( surfID ); if( s < pLighting->m_LightmapMins[0] || t < pLighting->m_LightmapMins[1] ) return false; // assuming a square lightmap (FIXME: which ain't always the case), // lets see if it lies in that rectangle. If not, punt... float ds = s - pLighting->m_LightmapMins[0]; float dt = t - pLighting->m_LightmapMins[1]; if ( !pLighting->m_LightmapExtents[0] && !pLighting->m_LightmapExtents[1] ) { worldbrushdata_t *pBrushData = host_state.worldbrush; // float lightMaxs[2]; lightMaxs[ 0 ] = pLighting->m_LightmapMins[0]; lightMaxs[ 1 ] = pLighting->m_LightmapMins[1]; int i; for (i=0 ; ivertindices[MSurf_FirstVertIndex( surfID )+i]; mvertex_t *v = &pBrushData->vertexes[e]; int j; for ( j=0 ; j<2 ; j++) { float sextent, textent; sextent = DotProduct (v->position, pTex->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) + pTex->lightmapVecsLuxelsPerWorldUnits[0][3] - pLighting->m_LightmapMins[0]; textent = DotProduct (v->position, pTex->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) + pTex->lightmapVecsLuxelsPerWorldUnits[1][3] - pLighting->m_LightmapMins[1]; if ( sextent > lightMaxs[ 0 ] ) { lightMaxs[ 0 ] = sextent; } if ( textent > lightMaxs[ 1 ] ) { lightMaxs[ 1 ] = textent; } } } if( ds > lightMaxs[0] || dt > lightMaxs[1] ) return false; } else { if( ds > pLighting->m_LightmapExtents[0] || dt > pLighting->m_LightmapExtents[1] ) return false; } // Store off the hit distance... state.m_HitFrac = f; // You heard the man! ComputeTextureCoordsAtIntersection( pTex, pt, state.m_pTextureS, state.m_pTextureT ); #ifdef USE_CONVARS if ( r_avglight.GetInt() ) #else if ( 1 ) #endif { // This is the faster path; it looks slightly different though ComputeLightmapColorFromAverage( pLighting, state.m_bUseLightStyles, c ); } else { // Compute lightmap coords ComputeLightmapCoordsAtIntersection( pLighting, ds, dt, state.m_pLightmapS, state.m_pLightmapT ); // Check out the value of the lightmap at the intersection point ComputeLightmapColor( surfID, (int)ds, (int)dt, state.m_bUseLightStyles, c ); } return true; } //----------------------------------------------------------------------------- // Tests a particular node //----------------------------------------------------------------------------- // returns a surfID static SurfaceHandle_t FindIntersectionSurfaceAtNode( mnode_t *node, float t, Vector& c, LightVecState_t& state ) { SurfaceHandle_t surfID = SurfaceHandleFromIndex( node->firstsurface ); for (int i=0 ; inumsurfaces ; ++i, ++surfID) { // Don't immediately return when we hit sky; // we may actually hit another surface if (MSurf_Flags( surfID ) & SURFDRAW_SKY) { state.m_nSkySurfID = surfID; continue; } // Don't let water surfaces affect us if (MSurf_Flags( surfID ) & SURFDRAW_WATERSURFACE) continue; // Check this surface to see if there's an intersection if (FindIntersectionAtSurface( surfID, t, c, state )) { return surfID; } } return SURFACE_HANDLE_INVALID; } //----------------------------------------------------------------------------- // Tests a ray against displacements //----------------------------------------------------------------------------- // returns surfID static SurfaceHandle_t R_LightVecDisplacementChain( LightVecState_t& state, bool bUseLightStyles, Vector& c ) { // test the ray against displacements SurfaceHandle_t surfID = SURFACE_HANDLE_INVALID; for ( int i = 0; i < state.m_LightTestDisps.Count(); i++ ) { float dist; Vector2D luv, tuv; IDispInfo *pDispInfo = state.m_LightTestDisps[i]; if (pDispInfo->TestRay( state.m_Ray, 0.0f, state.m_HitFrac, dist, &luv, &tuv )) { // It hit it, and at a point closer than the previously computed // nearest intersection point state.m_HitFrac = dist; surfID = pDispInfo->GetParent(); ComputeLightmapColor( surfID, (int)luv.x, (int)luv.y, bUseLightStyles, c ); if (state.m_pLightmapS && state.m_pLightmapT) { ComputeLightmapCoordsAtIntersection( SurfaceLighting(surfID), (int)luv.x, (int)luv.y, state.m_pLightmapS, state.m_pLightmapT ); } if (state.m_pTextureS && state.m_pTextureT) { *state.m_pTextureS = tuv.x; *state.m_pTextureT = tuv.y; } } } return surfID; } //----------------------------------------------------------------------------- // Adds displacements in a leaf to a list to be tested against //----------------------------------------------------------------------------- static void AddDisplacementsInLeafToTestList( mleaf_t* pLeaf, LightVecState_t& state ) { // add displacement surfaces for ( int i = 0; i < pLeaf->dispCount; i++ ) { // NOTE: We're not using the displacement's touched method here // because we're just using the parent surface's visframe in the // surface add methods below IDispInfo *pDispInfo = MLeaf_Disaplcement( pLeaf, i ); SurfaceHandle_t parentSurfID = pDispInfo->GetParent(); // already processed this frame? Then don't do it again! if (MSurf_VisFrame( parentSurfID ) != r_surfacevisframe) { MSurf_VisFrame( parentSurfID ) = r_surfacevisframe; state.m_LightTestDisps.AddToTail( pDispInfo ); } } } //----------------------------------------------------------------------------- // Tests a particular leaf //----------------------------------------------------------------------------- // returns surfID static SurfaceHandle_t FASTCALL FindIntersectionSurfaceAtLeaf( mleaf_t *pLeaf, float start, float end, Vector& c, LightVecState_t& state ) { Vector pt; SurfaceHandle_t closestSurfID = SURFACE_HANDLE_INVALID; // Adds displacements in the leaf to a list of displacements to test at the end AddDisplacementsInLeafToTestList( pLeaf, state ); // Add non-displacement surfaces // Since there's no BSP tree here, we gotta test *all* surfaces! (blech) SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface]; // NOTE: Skip all marknodesurfaces, only check detail/leaf faces for ( int i = pLeaf->nummarknodesurfaces; i < pLeaf->nummarksurfaces; i++ ) { SurfaceHandle_t surfID = pHandle[i]; ASSERT_SURF_VALID( surfID ); // Don't add surfaces that have displacement; they are handled above // In fact, don't even set the vis frame; we need it unset for translucent // displacement code if ( SurfaceHasDispInfo(surfID) ) continue; Assert(!(MSurf_Flags( surfID ) & SURFDRAW_NODE)); if ( MSurf_Flags( surfID ) & (SURFDRAW_NODE|SURFDRAW_NODRAW | SURFDRAW_WATERSURFACE) ) continue; cplane_t* pPlane = &MSurf_Plane( surfID ); // Backface cull... if (DotProduct( pPlane->normal, state.m_Ray.m_Delta ) > 0.f) continue; float startDotN = DotProduct( state.m_Ray.m_Start, pPlane->normal ); float deltaDotN = DotProduct( state.m_Ray.m_Delta, pPlane->normal ); float front = startDotN + start * deltaDotN - pPlane->dist; float back = startDotN + end * deltaDotN - pPlane->dist; int side = front < 0.f; // Blow it off if it doesn't split the plane... if ( (back < 0.f) == side ) continue; // Don't test a surface that is farther away from the closest found intersection float frac = front / (front-back); if (frac >= state.m_HitFrac) continue; float mid = start * (1.0f - frac) + end * frac; // Check this surface to see if there's an intersection if (FindIntersectionAtSurface( surfID, mid, c, state )) { closestSurfID = surfID; } } // Return the closest surface hit return closestSurfID; } //----------------------------------------------------------------------------- // LIGHT SAMPLING //----------------------------------------------------------------------------- // returns surfID SurfaceHandle_t RecursiveLightPoint (mnode_t *node, float start, float end, Vector& c, LightVecState_t& state ) { // didn't hit anything if (node->contents >= 0) { // FIXME: Should we always do this? It could get expensive... // Check all the faces at the leaves return FindIntersectionSurfaceAtLeaf( (mleaf_t*)node, start, end, c, state ); } // Determine which side of the node plane our points are on // FIXME: optimize for axial cplane_t* plane = node->plane; float startDotN = DotProduct( state.m_Ray.m_Start, plane->normal ); float deltaDotN = DotProduct( state.m_Ray.m_Delta, plane->normal ); float front = startDotN + start * deltaDotN - plane->dist; float back = startDotN + end * deltaDotN - plane->dist; int side = front < 0; // If they're both on the same side of the plane, don't bother to split // just check the appropriate child SurfaceHandle_t surfID; if ( (back < 0) == side ) { surfID = RecursiveLightPoint (node->children[side], start, end, c, state); return surfID; } // calculate mid point float frac = front / (front-back); float mid = start * (1.0f - frac) + end * frac; // go down front side surfID = RecursiveLightPoint (node->children[side], start, mid, c, state ); if ( IS_SURF_VALID( surfID ) ) return surfID; // hit something // check for impact on this node surfID = FindIntersectionSurfaceAtNode( node, mid, c, state ); if ( IS_SURF_VALID( surfID ) ) return surfID; // go down back side surfID = RecursiveLightPoint (node->children[!side], mid, end, c, state ); return surfID; } //----------------------------------------------------------------------------- // Allows us to use a different model for R_LightVec //----------------------------------------------------------------------------- void R_LightVecUseModel( model_t* pModel ) { s_pLightVecModel = pModel; } //----------------------------------------------------------------------------- // returns light in range from 0 to 1. // lightmapS/T is in [0,1] within the space of the surface. // returns surfID //----------------------------------------------------------------------------- SurfaceHandle_t R_LightVec (const Vector& start, const Vector& end, bool bUseLightStyles, Vector& c, float *textureS, float *textureT, float *lightmapS, float *lightmapT ) { VPROF_INCREMENT_COUNTER( "R_LightVec", 1 ); SurfaceHandle_t retSurfID; SurfaceHandle_t dispSurfID; // We're using the vis frame here for lightvec tests // to make sure we test each displacement only once ++r_surfacevisframe; LightVecState_t state; state.m_HitFrac = 1.0f; state.m_Ray.Init( start, end ); state.m_pTextureS = textureS; state.m_pTextureT = textureT; state.m_pLightmapS = lightmapS; state.m_pLightmapT = lightmapT; state.m_nSkySurfID = SURFACE_HANDLE_INVALID; state.m_bUseLightStyles = bUseLightStyles; c[0] = c[1] = c[2] = 0.0f; model_t* model = s_pLightVecModel ? s_pLightVecModel : host_state.worldmodel; retSurfID = RecursiveLightPoint(&model->brush.pShared->nodes[model->brush.firstnode], 0.0f, 1.0f, c, state ); // While doing recursive light point, we built a list of all // displacement surfaces which we need to test, so let's test them dispSurfID = R_LightVecDisplacementChain( state, bUseLightStyles, c ); if( r_visualizelighttraces.GetBool() ) { if( r_visualizelighttracesshowfulltrace.GetBool() ) { CDebugOverlay::AddLineOverlay( start, end, 0, 255, 0, 255, true, -1.0f ); } else { CDebugOverlay::AddLineOverlay( start, start + ( end - start ) * state.m_HitFrac, 0, 255, 0, 255, true, -1.0f ); } } if ( IS_SURF_VALID( dispSurfID ) ) retSurfID = dispSurfID; // ConMsg( "R_LightVec: %f %f %f\n", c[0], c[1], c[2] ); // If we didn't hit anything else, but we hit a sky surface at // some point along the ray cast, return the sky id. if ( ( retSurfID == SURFACE_HANDLE_INVALID ) && ( state.m_nSkySurfID != SURFACE_HANDLE_INVALID ) ) return state.m_nSkySurfID; return retSurfID; } // returns light in range from 0 to 1. colorVec R_LightPoint (Vector& p) { SurfaceHandle_t surfID; Vector end; colorVec c; Vector color; end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; surfID = R_LightVec( p, end, true, color ); if( IS_SURF_VALID( surfID ) ) { c.r = LinearToScreenGamma( color[0] ) * 255; c.g = LinearToScreenGamma( color[1] ) * 255; c.b = LinearToScreenGamma( color[2] ) * 255; c.a = 1; } else { c.r = c.g = c.b = c.a = 0; } return c; }