From 373b0cb2539aa90ce7b3b0fb6dddcf7ddfee536b Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Fri, 22 May 2026 09:26:03 -0700 Subject: [PATCH] Decal screen space is now projection based. --- neo/engine/opengl/gl_d3d12shim.cpp | 19 ++++++------------- neo/engine/renderer/RenderWorld_load.cpp | 9 +++++++-- neo/engine/tools/radiant/CamWnd.cpp | 18 +++++++++++------- neo/engine/tools/radiant/EditorMap.cpp | 8 +++++++- neo/engine/tools/radiant/NewTexWnd.cpp | 9 +++++++++ 5 files changed, 40 insertions(+), 23 deletions(-) diff --git a/neo/engine/opengl/gl_d3d12shim.cpp b/neo/engine/opengl/gl_d3d12shim.cpp index 26c387e0..226c5117 100644 --- a/neo/engine/opengl/gl_d3d12shim.cpp +++ b/neo/engine/opengl/gl_d3d12shim.cpp @@ -4513,8 +4513,6 @@ float4 PSScreenDecals(VSOut i) : SV_Target0 { uint2 pixel = uint2(i.pos.xy); float3 worldPos = gScreenDecalPosition.Load(int3(pixel, 0)).xyz; - float3 normal = QD3D12_SafeNormalizeOr(gScreenDecalNormal.Load(int3(pixel, 0)).xyz, float3(0.0, 0.0, 1.0)); - float3 geometricNormal = QD3D12_SafeNormalizeOr(cross(ddx(worldPos), ddy(worldPos)), normal); float3 decalColor = 0.0; float decalAlpha = 0.0; uint decalIndex = (uint)gScreenDecalIndex; @@ -4522,28 +4520,23 @@ float4 PSScreenDecals(VSOut i) : SV_Target0 return float4(0.0, 0.0, 0.0, 0.0); ScreenSpaceDecal d = gScreenDecals[decalIndex]; - float3 decalN = QD3D12_SafeNormalizeOr(d.normal, normal); - float facing = saturate(abs(dot(geometricNormal, decalN)) * 4.0); - if (facing <= 0.0) - return float4(0.0, 0.0, 0.0, 0.0); + float3 decalN = QD3D12_SafeNormalizeOr(d.normal, float3(0.0, 0.0, 1.0)); float3 rel = worldPos - d.origin; - float decalDepth = max(d.depth, 1.0e-3); - float depthDistance = abs(dot(rel, decalN)); - float depthFade = 1.0 - smoothstep(decalDepth, decalDepth + max(decalDepth * 0.25, 1.0), depthDistance); - if (depthFade <= 0.0) + float projectorDepth = max(d.depth, 1.0e-3); + float projectorZ = dot(rel, decalN); + if (abs(projectorZ) > projectorDepth) return float4(0.0, 0.0, 0.0, 0.0); float3 axisU = QD3D12_SafeNormalizeOr(d.axisU, float3(1.0, 0.0, 0.0)); float3 axisV = QD3D12_SafeNormalizeOr(d.axisV, cross(decalN, axisU)); float2 uv = float2(dot(rel, axisU), dot(rel, axisV)) / max(d.halfSize, 1.0e-3); - if (abs(uv.x) >= 1.0 || abs(uv.y) >= 1.0) + if (abs(uv.x) > 1.0 || abs(uv.y) > 1.0) return float4(0.0, 0.0, 0.0, 0.0); float2 texUv = float2(uv.x * 0.5 + 0.5, -uv.y * 0.5 + 0.5); float4 texel = gTex0.Sample(gSamp0, texUv); - float edge = smoothstep(1.0, 0.75, max(abs(uv.x), abs(uv.y))); - float mask = saturate(d.opacity) * facing * depthFade * edge * saturate(d.color.a) * saturate(texel.a); + float mask = saturate(d.opacity) * saturate(d.color.a) * saturate(texel.a); if (d.blendMode == 1u) { decalColor = saturate(texel.rgb * mask); diff --git a/neo/engine/renderer/RenderWorld_load.cpp b/neo/engine/renderer/RenderWorld_load.cpp index c2f626b3..bcfbacce 100644 --- a/neo/engine/renderer/RenderWorld_load.cpp +++ b/neo/engine/renderer/RenderWorld_load.cpp @@ -124,8 +124,12 @@ static void R_NormalizeDecalBasis(renderWorldDecal_t& decal) { if (decal.size <= 0.0f) { decal.size = 64.0f; } + const float minProjectorDepth = decal.size * 2.0f; if (decal.depth <= 0.0f) { - decal.depth = 8.0f; + decal.depth = minProjectorDepth; + } + else if (decal.depth < minProjectorDepth) { + decal.depth = minProjectorDepth; } if (decal.opacity <= 0.0f) { decal.opacity = 1.0f; @@ -190,7 +194,8 @@ void idRenderWorldLocal::SubmitWorldDecalsToScreenSpace() const { dst.axisV.y = src.axisV.y; dst.axisV.z = src.axisV.z; dst.halfSize = src.size * 0.5f; - dst.depth = src.depth; + const float minProjectorDepth = src.size * 2.0f; + dst.depth = (src.depth > minProjectorDepth) ? src.depth : minProjectorDepth; dst.opacity = src.opacity; dst.color[0] = src.color.x; dst.color[1] = src.color.y; diff --git a/neo/engine/tools/radiant/CamWnd.cpp b/neo/engine/tools/radiant/CamWnd.cpp index d6c962cf..17b3d741 100644 --- a/neo/engine/tools/radiant/CamWnd.cpp +++ b/neo/engine/tools/radiant/CamWnd.cpp @@ -1980,18 +1980,22 @@ static bool CamWnd_BuildDecalAtPoint(CCamWnd* cam, const CPoint& point, renderWo } CamWnd_DecalColorFromSelectedMaterial(decal.material, decal.color); decal.origin = hitPoint; - decal.normal = hitNormal; - if ((decal.normal * dir) > 0.0f) { - decal.normal = -decal.normal; - } - decal.axisU = decal.normal.Cross(cam->Camera().vup); + decal.normal = -dir; + decal.normal.Normalize(); + + decal.axisU = cam->Camera().vright - decal.normal * (cam->Camera().vright * decal.normal); if (decal.axisU.LengthSqr() < 0.0001f) { - decal.axisU = decal.normal.Cross(cam->Camera().vright); + decal.axisU = decal.normal.Cross(cam->Camera().vup); } + decal.axisU.Normalize(); decal.axisV = decal.normal.Cross(decal.axisU); CamWnd_RotateDecalBasis(decal, s_camWndDecalPreviewRotation); decal.size = CamWnd_DecalFloat("size", "64"); - decal.depth = CamWnd_DecalFloat("depth", "8"); + decal.depth = CamWnd_DecalFloat("depth", "0"); + const float minProjectorDepth = decal.size * 2.0f; + if (decal.depth < minProjectorDepth) { + decal.depth = minProjectorDepth; + } decal.opacity = CamWnd_DecalFloat("opacity", "1"); if (hitSource) { *hitSource = localHitSource; diff --git a/neo/engine/tools/radiant/EditorMap.cpp b/neo/engine/tools/radiant/EditorMap.cpp index c920f851..a5a9a269 100644 --- a/neo/engine/tools/radiant/EditorMap.cpp +++ b/neo/engine/tools/radiant/EditorMap.cpp @@ -109,8 +109,12 @@ static void Map_NormalizeDecal(renderWorldDecal_t& decal) { if (decal.size <= 0.0f) { decal.size = 64.0f; } + const float minProjectorDepth = decal.size * 2.0f; if (decal.depth <= 0.0f) { - decal.depth = 8.0f; + decal.depth = minProjectorDepth; + } + else if (decal.depth < minProjectorDepth) { + decal.depth = minProjectorDepth; } if (decal.opacity <= 0.0f) { decal.opacity = 1.0f; @@ -346,6 +350,7 @@ void Map_LoadDecals(const char* filename) { if (!token.Icmp("material")) { src.ExpectAnyToken(&token); decal.material = token; + Texture_ForName(decal.material.c_str()); } else if (!token.Icmp("origin")) { src.Parse1DMatrix(3, decal.origin.ToFloatPtr()); } else if (!token.Icmp("normal")) { @@ -364,6 +369,7 @@ void Map_LoadDecals(const char* filename) { src.Parse1DMatrix(4, decal.color.ToFloatPtr()); } } + Texture_ForName(decal.material.c_str()); Map_AddDecal(decal); } s_selectedWorldDecal = -1; diff --git a/neo/engine/tools/radiant/NewTexWnd.cpp b/neo/engine/tools/radiant/NewTexWnd.cpp index b56247d2..f818df49 100644 --- a/neo/engine/tools/radiant/NewTexWnd.cpp +++ b/neo/engine/tools/radiant/NewTexWnd.cpp @@ -757,6 +757,15 @@ void Texture_ShowInuse(void) { } } + if (g_qeglobals.rw) { + for (int i = 0; i < g_qeglobals.rw->NumWorldDecals(); ++i) { + const renderWorldDecal_t* decal = g_qeglobals.rw->GetWorldDecal(i); + if (decal) { + Texture_ForName(decal->material.c_str()); + } + } + } + Sys_UpdateWindows(W_TEXTURE); g_Inspectors->SetWindowText("Textures (in use)");