From 7877bcb26c2d136696f4b49276e92164c9b3affa Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Thu, 23 Apr 2026 17:05:49 -0700 Subject: [PATCH] Added spotlight support. --- neo/framework/Common.cpp | 2 +- neo/opengl/gl_d3d12raylight.cpp | 235 ++++++++++++++++++++++++++++++-- neo/opengl/opengl.h | 49 +++++-- neo/renderer/Image_load.cpp | 50 +------ neo/renderer/Image_program.cpp | 8 ++ neo/renderer/draw_dx.cpp | 220 +++++++++++++++++++++++++++--- 6 files changed, 467 insertions(+), 97 deletions(-) diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index d497ed29..46320e79 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -2465,7 +2465,7 @@ void idCommonLocal::Frame(void) { eventLoop->RunEventLoop(); - if (1) + if (Sys_IsWindowVisible()) { //-------------------------------------------- // Determine how many game tics we are going to run, diff --git a/neo/opengl/gl_d3d12raylight.cpp b/neo/opengl/gl_d3d12raylight.cpp index 7c2aa205..300a93da 100644 --- a/neo/opengl/gl_d3d12raylight.cpp +++ b/neo/opengl/gl_d3d12raylight.cpp @@ -1924,9 +1924,11 @@ struct Light float pad1; // For point lights, this is the axis-aligned XYZ attenuation radius. - // The scalar radius above is still kept as a max/fallback range and for rect lights. + // For spot lights, pointRadius.x stores the near clip plane. + // The scalar radius above is still kept as a max/fallback range for point lights, + // as the influence range for rect lights, and as the far clip distance for spot lights. float3 pointRadius; - float pointRadiusPad; + float pointRadiusPad; // non-zero disables specular for this light }; struct ShadowPayload @@ -1958,6 +1960,7 @@ RWTexture2D gOutputTex : register(u0); static const uint GL_RAYTRACING_LIGHT_TYPE_POINT = 0; static const uint GL_RAYTRACING_LIGHT_TYPE_RECT = 1; +static const uint GL_RAYTRACING_LIGHT_TYPE_SPOT = 2; static const uint GEOMETRY_FLAG_SKELETAL = 1; static const uint GEOMETRY_FLAG_UNLIT = 2; @@ -2097,7 +2100,55 @@ float ComputePointLightAttenuation(float3 worldPos, Light Lgt) // and radius.z controls Z reach in world space. float ellipsoidDistance = length(normalizedOffset); float atten = saturate(1.0 - ellipsoidDistance); - return atten * atten; + return atten; +} + +float ComputeSpotLightAttenuation(float3 worldPos, Light Lgt) +{ + float3 lightToSurface = worldPos - Lgt.position; + + float nearClip = max(Lgt.pointRadius.x, 0.0); + float farClip = max(Lgt.radius, nearClip + 1e-4); + + float depth = dot(lightToSurface, Lgt.normal); + if (depth <= nearClip || depth >= farClip) + return 0.0; + + float invDepth = 1.0 / max(depth, 1e-4); + + float projU = dot(lightToSurface, Lgt.axisU) * invDepth; + float projV = dot(lightToSurface, Lgt.axisV) * invDepth; + + float halfU = max(abs(Lgt.halfWidth), 1e-4); + float halfV = max(abs(Lgt.halfHeight), 1e-4); + + float edgeU = abs(projU) / halfU; + float edgeV = abs(projV) / halfV; + float edge = max(edgeU, edgeV); + + if (edge >= 1.0) + return 0.0; + + float coneAtten = saturate(1.0 - edge); + coneAtten = coneAtten; + + float rangeAtten = saturate((farClip - depth) / max(farClip - nearClip, 1e-4)); + rangeAtten = rangeAtten; + + return coneAtten * rangeAtten; +} + +float TraceSpotShadow(float3 worldPos, float3 N, float3 toLight, float dist) +{ + float3 L = toLight / max(dist, 1e-6); + + float NdotLRaw = saturate(dot(N, L)); + float normalBias = lerp(gShadowBias * 3.0, gShadowBias * 0.75, NdotLRaw); + + float3 shadowOrigin = worldPos + N * normalBias + L * (gShadowBias * 0.5); + float shadowTMax = max(dist - gShadowBias * 0.5, 0.001); + + return TraceShadow(shadowOrigin, L, shadowTMax); } float TraceSoftShadow(float3 worldPos, float3 N, Light Lgt, float3 toLight, float dist) @@ -2438,7 +2489,7 @@ void RayGen() float NdotLWrap = saturate((dot(N, L) + wrap) / (1.0 + wrap)); float shadow = 1.0; - if (NdotLWrap > 0.0001 && atten > 0.0 && dist > 0.01) + if (Lgt.samples != 0u && NdotLWrap > 0.0001 && atten > 0.0 && dist > 0.01) { shadow = TraceSoftShadow(worldPos, N, Lgt, toLight, dist); } @@ -2446,7 +2497,44 @@ void RayGen() float3 diffuse = Lgt.color * (Lgt.intensity * atten * NdotLWrap * shadow); lightingAccum += diffuse; - specularAccum += ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, baseAlbedo); + if (Lgt.pointRadiusPad <= 0.5) + { + specularAccum += ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, baseAlbedo); + } + } + else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT) + { + float3 toLight = Lgt.position - worldPos; + float distSq = dot(toLight, toLight); + float dist = sqrt(max(distSq, 1e-6)); + float3 L = toLight / dist; + + float atten = ComputeSpotLightAttenuation(worldPos, Lgt); + + float wrap = 0.35; + float NdotLWrap = saturate((dot(N, L) + wrap) / (1.0 + wrap)); + + float shadow = 1.0; + if (Lgt.samples != 0u && NdotLWrap > 0.0001 && atten > 0.0 && dist > 0.01) + { + shadow = TraceSpotShadow(worldPos, N, toLight, dist); + } + + float3 diffuse = Lgt.color * (Lgt.intensity * atten * NdotLWrap * shadow); + lightingAccum += diffuse; + + if (Lgt.pointRadiusPad <= 0.5) + { + specularAccum += ComputeSpecular( + N, + V, + L, + Lgt.color, + Lgt.intensity, + atten, + shadow, + baseAlbedo); + } } else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT) { @@ -2460,7 +2548,7 @@ void RayGen() atten = atten * atten * atten * atten; float shadow = 1.0; - if (atten > 0.0 && centerDist > 0.01) + if (Lgt.samples != 0u && atten > 0.0 && centerDist > 0.01) { shadow = RectLightShadow(worldPos, N, Lgt, pixel); } @@ -2498,15 +2586,18 @@ void RayGen() rectDiffuseAccum += Lgt.color * sampleWeight; - rectSpecAccum += ComputeSpecular( - N, - V, - L, - Lgt.color, - Lgt.intensity * faceTerm, - 1.0, - 1.0, - baseAlbedo); + if (Lgt.pointRadiusPad <= 0.5) + { + rectSpecAccum += ComputeSpecular( + N, + V, + L, + Lgt.color, + Lgt.intensity * faceTerm, + 1.0, + 1.0, + baseAlbedo); + } } rectDiffuseAccum /= (float)sampleCount; @@ -3329,6 +3420,120 @@ glRaytracingLight_t glRaytracingLightingMakePointLight( return l; } + +glRaytracingLight_t glRaytracingLightingMakeSpotLight( + float px, float py, float pz, + float dx, float dy, float dz, + float ux, float uy, float uz, + float vx, float vy, float vz, + float nearPlane, + float farPlane, + float tanHalfWidth, + float tanHalfHeight, + float r, float g, float b, + float intensity, + uint32_t samples) +{ + glRaytracingLight_t l = {}; + + glRaytracingNormalize3(dx, dy, dz); + + // Make U perpendicular to D. + { + const float du = dx * ux + dy * uy + dz * uz; + ux -= dx * du; + uy -= dy * du; + uz -= dz * du; + + const float uLenSq = ux * ux + uy * uy + uz * uz; + if (uLenSq <= 1e-20f) + { + const float absDz = (dz < 0.0f) ? -dz : dz; + if (absDz < 0.999f) + { + glRaytracingCross3(0.0f, 0.0f, 1.0f, dx, dy, dz, ux, uy, uz); + } + else + { + glRaytracingCross3(0.0f, 1.0f, 0.0f, dx, dy, dz, ux, uy, uz); + } + } + glRaytracingNormalize3(ux, uy, uz); + } + + // Rebuild V from D x U so the basis is orthonormal, while preserving the + // sign of the caller-provided V whenever possible. + { + float builtVx, builtVy, builtVz; + glRaytracingCross3(dx, dy, dz, ux, uy, uz, builtVx, builtVy, builtVz); + glRaytracingNormalize3(builtVx, builtVy, builtVz); + + const float sign = builtVx * vx + builtVy * vy + builtVz * vz; + if (sign < 0.0f) + { + builtVx = -builtVx; + builtVy = -builtVy; + builtVz = -builtVz; + } + + vx = builtVx; + vy = builtVy; + vz = builtVz; + } + + if (nearPlane < 0.0f) + nearPlane = 0.0f; + if (farPlane <= nearPlane) + farPlane = nearPlane + 1e-3f; + + if (tanHalfWidth < 0.0f) tanHalfWidth = -tanHalfWidth; + if (tanHalfHeight < 0.0f) tanHalfHeight = -tanHalfHeight; + + if (tanHalfWidth <= 1e-4f) + tanHalfWidth = 1e-4f; + if (tanHalfHeight <= 1e-4f) + tanHalfHeight = 1e-4f; + + l.position.x = px; + l.position.y = py; + l.position.z = pz; + + // For spot lights, radius stores the far clip distance while pointRadius.x + // stores the near clip distance. + l.radius = farPlane; + l.pointRadius.x = nearPlane; + l.pointRadius.y = 0.0f; + l.pointRadius.z = 0.0f; + l.pointRadiusPad = 0.0f; + + l.color.x = r; + l.color.y = g; + l.color.z = b; + l.intensity = intensity; + + l.normal.x = dx; + l.normal.y = dy; + l.normal.z = dz; + l.type = GL_RAYTRACING_LIGHT_TYPE_SPOT; + + l.axisU.x = ux; + l.axisU.y = uy; + l.axisU.z = uz; + l.halfWidth = tanHalfWidth; + + l.axisV.x = vx; + l.axisV.y = vy; + l.axisV.z = vz; + l.halfHeight = tanHalfHeight; + + l.samples = samples ? samples : 1u; + l.twoSided = 0; + l.persistant = 0.0f; + l.pad1 = 0.0f; + + return l; +} + glRaytracingLight_t glRaytracingLightingMakeRectLight( float px, float py, float pz, float nx, float ny, float nz, diff --git a/neo/opengl/opengl.h b/neo/opengl/opengl.h index 758a5ec1..9f5dbb9b 100644 --- a/neo/opengl/opengl.h +++ b/neo/opengl/opengl.h @@ -1636,27 +1636,38 @@ typedef struct glRaytracingVec3_s typedef struct glRaytracingLight_s { glRaytracingVec3_t position; - float radius; + float radius; // point: max XYZ radius / fallback range + // rect : influence range + // spot : far clip distance glRaytracingVec3_t color; float intensity; - glRaytracingVec3_t normal; - uint32_t type; + glRaytracingVec3_t normal; // rect: emitter normal + // spot: forward direction + // point: ignored + uint32_t type; // POINT / RECT / SPOT - glRaytracingVec3_t axisU; - float halfWidth; + glRaytracingVec3_t axisU; // rect: local X axis + // spot: right basis + float halfWidth; // rect: half extent along axisU + // spot: projected half-width slope - glRaytracingVec3_t axisV; - float halfHeight; + glRaytracingVec3_t axisV; // rect: local Y axis + // spot: up basis + float halfHeight; // rect: half extent along axisV + // spot: projected half-height slope - uint32_t samples; - uint32_t twoSided; + uint32_t samples; // rect: sample count + // point/spot: 0 disables shadows, non-zero enables them + uint32_t twoSided; // rect: 0/1, ignored for point / spot float persistant; float pad1; - glRaytracingVec3_t pointRadius; - float pointRadiusPad; + glRaytracingVec3_t pointRadius; // point: XYZ attenuation radii + // spot: x = near clip, y/z unused + // rect : scalar range copy + float pointRadiusPad; // non-zero disables specular for this light } glRaytracingLight_t; typedef struct glRaytracingLightingPassDesc_s @@ -1746,7 +1757,20 @@ glRaytracingLight_t glRaytracingLightingMakePointLight( float r, float g, float b, float intensity); -glRaytracingLight_t glRaytracingLightingMakeRectLight( +glRaytracingLight_t glRaytracingLightingMakeSpotLight( + float px, float py, float pz, + float dx, float dy, float dz, + float ux, float uy, float uz, + float vx, float vy, float vz, + float nearPlane, + float farPlane, + float tanHalfWidth, + float tanHalfHeight, + float r, float g, float b, + float intensity, + uint32_t samples); + +glRaytracingLight_t glRaytracingLightingMakeRectLight( float px, float py, float pz, float nx, float ny, float nz, float ux, float uy, float uz, @@ -1801,6 +1825,7 @@ static void glRaytracingCross3( static const int GL_RAYTRACING_LIGHT_TYPE_POINT = 0; static const int GL_RAYTRACING_LIGHT_TYPE_RECT = 1; +static const int GL_RAYTRACING_LIGHT_TYPE_SPOT = 2; void TessellatePolygon(const std::vector& src, std::vector& out); diff --git a/neo/renderer/Image_load.cpp b/neo/renderer/Image_load.cpp index 8ba36234..f6a5056b 100644 --- a/neo/renderer/Image_load.cpp +++ b/neo/renderer/Image_load.cpp @@ -417,55 +417,7 @@ helper function that takes the current width/height and might make them smaller ================ */ void idImage::GetDownsize( int &scaled_width, int &scaled_height ) const { - int size = 0; - - // perform optional picmip operation to save texture memory - if ( depth == TD_SPECULAR && globalImages->image_downSizeSpecular.GetInteger() ) { - size = globalImages->image_downSizeSpecularLimit.GetInteger(); - if ( size == 0 ) { - size = 64; - } - } else if ( depth == TD_BUMP && globalImages->image_downSizeBump.GetInteger() ) { - size = globalImages->image_downSizeBumpLimit.GetInteger(); - if ( size == 0 ) { - size = 64; - } - } else if ( ( allowDownSize || globalImages->image_forceDownSize.GetBool() ) && globalImages->image_downSize.GetInteger() ) { - size = globalImages->image_downSizeLimit.GetInteger(); - if ( size == 0 ) { - size = 256; - } - } - - if ( size > 0 ) { - while ( scaled_width > size || scaled_height > size ) { - if ( scaled_width > 1 ) { - scaled_width >>= 1; - } - if ( scaled_height > 1 ) { - scaled_height >>= 1; - } - } - } - - // clamp to minimum size - if ( scaled_width < 1 ) { - scaled_width = 1; - } - if ( scaled_height < 1 ) { - scaled_height = 1; - } - - // clamp size to the hardware specific upper limit - // scale both axis down equally so we don't have to - // deal with a half mip resampling - // This causes a 512*256 texture to sample down to - // 256*128 on a voodoo3, even though it could be 256*256 - while ( scaled_width > glConfig.maxTextureSize - || scaled_height > glConfig.maxTextureSize ) { - scaled_width >>= 1; - scaled_height >>= 1; - } + } /* diff --git a/neo/renderer/Image_program.cpp b/neo/renderer/Image_program.cpp index 1f0a39c2..b284beba 100644 --- a/neo/renderer/Image_program.cpp +++ b/neo/renderer/Image_program.cpp @@ -427,6 +427,14 @@ static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *he } return false; } + + if (width2 <= 0 || height2 <= 0) { + if (pic) { + R_StaticFree(*pic); + *pic = NULL; + } + return false; + } // process it if ( pic ) { diff --git a/neo/renderer/draw_dx.cpp b/neo/renderer/draw_dx.cpp index 14f67439..4221884b 100644 --- a/neo/renderer/draw_dx.cpp +++ b/neo/renderer/draw_dx.cpp @@ -2,54 +2,234 @@ #pragma hdrstop #include "tr_local.h" +#include + +static idVec3 RB_DXRMakeVec3(float x, float y, float z) +{ + idVec3 v; + v.x = x; + v.y = y; + v.z = z; + return v; +} + +static idVec3 RB_DXRSubVec3(const idVec3& a, const idVec3& b) +{ + return RB_DXRMakeVec3(a.x - b.x, a.y - b.y, a.z - b.z); +} + +static idVec3 RB_DXRScaleVec3(const idVec3& v, float s) +{ + return RB_DXRMakeVec3(v.x * s, v.y * s, v.z * s); +} + +static float RB_DXRDotVec3(const idVec3& a, const idVec3& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +static idVec3 RB_DXRCrossVec3(const idVec3& a, const idVec3& b) +{ + return RB_DXRMakeVec3( + a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x); +} + +static float RB_DXRLengthVec3(const idVec3& v) +{ + return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); +} + +static idVec3 RB_DXRNormalizeVec3Safe(const idVec3& v, const idVec3& fallback) +{ + const float len = RB_DXRLengthVec3(v); + if (len > 1e-20f) + { + const float invLen = 1.0f / len; + return RB_DXRMakeVec3(v.x * invLen, v.y * invLen, v.z * invLen); + } + return fallback; +} + +static idVec3 RB_DXRTransformLightVector(const idMat3& axis, const idVec3& v) +{ + return RB_DXRMakeVec3( + axis[0].x * v.x + axis[1].x * v.y + axis[2].x * v.z, + axis[0].y * v.x + axis[1].y * v.y + axis[2].y * v.z, + axis[0].z * v.x + axis[1].z * v.y + axis[2].z * v.z); +} + +static glRaytracingLight_t RB_DXRMakeSpotLightFromRenderLight( + const renderLight_t& srcLight, + const idVec3& worldOrigin, + float r, float g, float b, + float intensity) +{ + const idVec3 targetW = RB_DXRTransformLightVector(srcLight.axis, srcLight.target); + const idVec3 rightW = RB_DXRTransformLightVector(srcLight.axis, srcLight.right); + const idVec3 upW = RB_DXRTransformLightVector(srcLight.axis, srcLight.up); + const idVec3 startW = RB_DXRTransformLightVector(srcLight.axis, srcLight.start); + const idVec3 endW = RB_DXRTransformLightVector(srcLight.axis, srcLight.end); + + idVec3 fallbackDir = RB_DXRCrossVec3(rightW, upW); + fallbackDir = RB_DXRNormalizeVec3Safe(fallbackDir, RB_DXRMakeVec3(0.0f, 0.0f, -1.0f)); + + const idVec3 dir = RB_DXRNormalizeVec3Safe(targetW, fallbackDir); + + float centerDepth = RB_DXRDotVec3(targetW, dir); + if (centerDepth <= 1e-4f) + { + centerDepth = RB_DXRLengthVec3(targetW); + } + if (centerDepth <= 1e-4f) + { + centerDepth = RB_DXRDotVec3(endW, dir); + } + if (centerDepth <= 1e-4f) + { + centerDepth = 64.0f; + } + + const idVec3 rightPerp = RB_DXRSubVec3( + rightW, + RB_DXRScaleVec3(dir, RB_DXRDotVec3(rightW, dir))); + + const idVec3 upPerp = RB_DXRSubVec3( + upW, + RB_DXRScaleVec3(dir, RB_DXRDotVec3(upW, dir))); + + idVec3 axisU = RB_DXRCrossVec3(RB_DXRMakeVec3(0.0f, 0.0f, 1.0f), dir); + axisU = RB_DXRNormalizeVec3Safe(axisU, RB_DXRMakeVec3(1.0f, 0.0f, 0.0f)); + axisU = RB_DXRNormalizeVec3Safe(rightPerp, axisU); + + idVec3 axisV = RB_DXRCrossVec3(dir, axisU); + axisV = RB_DXRNormalizeVec3Safe(axisV, RB_DXRMakeVec3(0.0f, 1.0f, 0.0f)); + if (RB_DXRDotVec3(axisV, upPerp) < 0.0f) + { + axisV = RB_DXRScaleVec3(axisV, -1.0f); + } + + float tanHalfWidth = RB_DXRLengthVec3(rightPerp) / centerDepth; + float tanHalfHeight = RB_DXRLengthVec3(upPerp) / centerDepth; + + float nearPlane = RB_DXRDotVec3(startW, dir); + if (nearPlane < 0.0f) + { + nearPlane = 0.0f; + } + + float farPlane = RB_DXRDotVec3(endW, dir); + if (farPlane < centerDepth) + { + farPlane = centerDepth; + } + if (farPlane <= nearPlane) + { + farPlane = nearPlane + 64.0f; + } + + glRaytracingLight_t light = glRaytracingLightingMakeSpotLight( + worldOrigin.x, worldOrigin.y, worldOrigin.z, + dir.x, dir.y, dir.z, + axisU.x, axisU.y, axisU.z, + axisV.x, axisV.y, axisV.z, + nearPlane, + farPlane, + tanHalfWidth, + tanHalfHeight, + r, g, b, + intensity, + 1u); + + light.samples = srcLight.noShadows ? 0u : 1u; + light.pointRadiusPad = srcLight.noSpecular ? 1.0f : 0.0f; + + return light; +} /* ==================== RB_DXDrawInteractions ==================== */ -void RB_DXDrawInteractions(void) { +void RB_DXDrawInteractions(void) +{ viewLight_t* vLight; bool hasLight = false; - for (vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next) { + for (vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next) + { backEnd.vLight = vLight; // do fogging later - if (vLight->lightShader->IsFogLight()) { + if (vLight->lightShader->IsFogLight()) + { continue; } - if (vLight->lightShader->IsBlendLight()) { + if (vLight->lightShader->IsBlendLight()) + { continue; } if (!vLight->localInteractions && !vLight->globalInteractions - && !vLight->translucentInteractions) { + && !vLight->translucentInteractions) + { continue; } - hasLight = true; + const renderLight_t& srcLight = vLight->lightDef->parms; + const float r = srcLight.shaderParms[SHADERPARM_RED]; + const float g = srcLight.shaderParms[SHADERPARM_GREEN]; + const float b = srcLight.shaderParms[SHADERPARM_BLUE]; + const float intensity = 1.0f; glRaytracingLight_t light = {}; - light.color.x = vLight->lightDef->parms.shaderParms[SHADERPARM_RED]; - light.color.y = vLight->lightDef->parms.shaderParms[SHADERPARM_GREEN]; - light.color.z = vLight->lightDef->parms.shaderParms[SHADERPARM_BLUE]; - light.type = GL_RAYTRACING_LIGHT_TYPE_POINT; - light.pointRadius.x = vLight->lightDef->parms.lightRadius[0] * 2; - light.pointRadius.y = vLight->lightDef->parms.lightRadius[1] * 2; - light.pointRadius.z = vLight->lightDef->parms.lightRadius[2] * 2; - light.intensity = 1; - light.position.x = vLight->globalLightOrigin.x; - light.position.y = vLight->globalLightOrigin.y; - light.position.z = vLight->globalLightOrigin.z; - glRaytracingLightingAddLight(&light); + bool supported = true; + + if (srcLight.pointLight) + { + light = glRaytracingLightingMakePointLight( + vLight->globalLightOrigin.x, + vLight->globalLightOrigin.y, + vLight->globalLightOrigin.z, + srcLight.lightRadius[0] * 1.4f, + srcLight.lightRadius[1] * 1.4f, + srcLight.lightRadius[2] * 1.4f, + r, g, b, + intensity); + + light.samples = srcLight.noShadows ? 0u : 1u; + light.pointRadiusPad = srcLight.noSpecular ? 1.0f : 0.0f; + } + else if (!srcLight.parallel) + { + light = RB_DXRMakeSpotLightFromRenderLight( + srcLight, + vLight->globalLightOrigin, + r, g, b, + intensity); + } + else + { + // Parallel projected lights are not spot lights. + // Handle them in a separate directional/orthographic path. + supported = false; + common->Warning("Parallel lights are not needed with raytracing! Just place a skybox."); + } + + if (supported && glRaytracingLightingAddLight(&light)) + { + hasLight = true; + } } if (!hasLight) + { return; - + } glFinish(); glLightScene(backEnd.viewDef->renderWorld->dxrWorldId); glRaytracingLightingClearLights(false); -} +} \ No newline at end of file