diff options
Diffstat (limited to 'engine/gl_rlight.cpp')
| -rw-r--r-- | engine/gl_rlight.cpp | 830 |
1 files changed, 830 insertions, 0 deletions
diff --git a/engine/gl_rlight.cpp b/engine/gl_rlight.cpp new file mode 100644 index 0000000..7eff74d --- /dev/null +++ b/engine/gl_rlight.cpp @@ -0,0 +1,830 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + + +#include "render_pch.h" +#include "client.h" +#include "bitmap/imageformat.h" +#include "bitmap/tgawriter.h" +#include <float.h> +#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<IDispInfo *> 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 ; j<MAX_LIGHTSTYLES ; j++) + { + int length; + const char * lightstyle = (const char*) table->GetStringUserData( 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 ; i<node->numsurfaces ; 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 ; i<MAX_DLIGHTS ; i++, l++) + { + if (l->die < cl.GetTime() || !l->IsRadiusGreaterThanZero() ) + continue; + if (l->flags & DLIGHT_NO_WORLD_ILLUMINATION) + continue; + + R_MarkLights ( l, 1<<i, pNode ); + } +} + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights (void) +{ + R_MarkDLightsOnSurface( host_state.worldbrush->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 ; i<MSurf_VertCount( surfID ); i++) + { + int e = pBrushData->vertindices[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 ; i<node->numsurfaces ; ++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; +} |