From 686422ec838d7799089465760007bcdb7ea982d6 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Fri, 22 May 2026 08:12:09 -0700 Subject: [PATCH] Fixed stamped decals not rendering correctly --- neo/doom3/game/gamesys/GameTypeInfo.h | 3 ++- neo/engine/opengl/gl_d3d12shim.cpp | 28 +++++++++++++++++++----- neo/engine/opengl/opengl.h | 3 ++- neo/engine/renderer/RenderWorld_load.cpp | 9 ++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/neo/doom3/game/gamesys/GameTypeInfo.h b/neo/doom3/game/gamesys/GameTypeInfo.h index b72050ac..42a10305 100644 --- a/neo/doom3/game/gamesys/GameTypeInfo.h +++ b/neo/doom3/game/gamesys/GameTypeInfo.h @@ -4088,7 +4088,8 @@ static classVariableInfo_t glScreenSpaceDecal_t_typeInfo[] = { { "float", "pad0", (intptr_t)(&((glScreenSpaceDecal_t *)0)->pad0), sizeof( ((glScreenSpaceDecal_t *)0)->pad0 ) }, { "float[4]", "color", (intptr_t)(&((glScreenSpaceDecal_t *)0)->color), sizeof( ((glScreenSpaceDecal_t *)0)->color ) }, { "uint32_t", "textureId", (intptr_t)(&((glScreenSpaceDecal_t *)0)->textureId), sizeof( ((glScreenSpaceDecal_t *)0)->textureId ) }, - { "float[3]", "pad1", (intptr_t)(&((glScreenSpaceDecal_t *)0)->pad1), sizeof( ((glScreenSpaceDecal_t *)0)->pad1 ) }, + { "uint32_t", "blendMode", (intptr_t)(&((glScreenSpaceDecal_t *)0)->blendMode), sizeof( ((glScreenSpaceDecal_t *)0)->blendMode ) }, + { "float[2]", "pad1", (intptr_t)(&((glScreenSpaceDecal_t *)0)->pad1), sizeof( ((glScreenSpaceDecal_t *)0)->pad1 ) }, { NULL, 0 } }; diff --git a/neo/engine/opengl/gl_d3d12shim.cpp b/neo/engine/opengl/gl_d3d12shim.cpp index 24acdfd2..26c387e0 100644 --- a/neo/engine/opengl/gl_d3d12shim.cpp +++ b/neo/engine/opengl/gl_d3d12shim.cpp @@ -1705,6 +1705,7 @@ struct GLState ComPtr postDepthResolveMsaaPSO; ComPtr postGBufferPointResolveMsaaPSO; ComPtr postScreenDecalPSO; + ComPtr postScreenDecalStainPSO; ComPtr vsMainBlob; ComPtr hsMainBlob; @@ -4407,7 +4408,8 @@ struct ScreenSpaceDecal float pad0; float4 color; uint textureId; - float3 pad1; + uint blendMode; + float2 pad1; }; StructuredBuffer gScreenDecals : register(t13); @@ -4542,8 +4544,16 @@ float4 PSScreenDecals(VSOut i) : SV_Target0 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); - decalColor = texel.rgb * saturate(d.color.rgb); - decalAlpha = mask; + if (d.blendMode == 1u) + { + decalColor = saturate(texel.rgb * mask); + decalAlpha = 1.0; + } + else + { + decalColor = texel.rgb * saturate(d.color.rgb); + decalAlpha = mask; + } return float4(decalColor, decalAlpha); } @@ -4842,7 +4852,8 @@ float PSDepthResolveMS(VSOut i) : SV_Depth return depth; } - +)HLSL" +R"HLSL( struct PSGBufferPointResolveOut { float4 normal : SV_Target0; @@ -8049,7 +8060,6 @@ void APIENTRY glDrawScreenSpaceDecalsQD3D12(void) cl->SetDescriptorHeaps(_countof(heaps), heaps); cl->SetGraphicsRootSignature(g_gl.postRootSig.Get()); QD3D12_SetPostConstants(cl, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, false, (float)g_gl.screenSpaceDecalCount); - cl->SetPipelineState(g_gl.postScreenDecalPSO.Get()); D3D12_CPU_DESCRIPTOR_HANDLE sceneColorRtv = CurrentResolvedSceneColorRTV(); cl->OMSetRenderTargets(1, &sceneColorRtv, FALSE, nullptr); cl->RSSetViewports(1, &viewport); @@ -8059,6 +8069,8 @@ void APIENTRY glDrawScreenSpaceDecalsQD3D12(void) { D3D12_GPU_DESCRIPTOR_HANDLE decalTextureSrv = g_gl.whiteTexture.srvGpu; const glScreenSpaceDecal_t& decal = g_gl.screenSpaceDecals[decalIndex]; + ID3D12PipelineState* decalPSO = (decal.blendMode == 1 && g_gl.postScreenDecalStainPSO.Get() != nullptr) ? g_gl.postScreenDecalStainPSO.Get() : g_gl.postScreenDecalPSO.Get(); + cl->SetPipelineState(decalPSO); TextureResource* decalTexture = decal.textureId ? QD3D12_FindTextureResource(decal.textureId) : nullptr; if (decalTexture) { @@ -9795,6 +9807,12 @@ static void QD3D12_CreatePSOs() d.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; QD3D12_CHECK(g_gl.device->CreateGraphicsPipelineState(&d, IID_PPV_ARGS(&g_gl.postScreenDecalPSO))); + d.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_ZERO; + d.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_COLOR; + d.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ZERO; + d.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ONE; + QD3D12_CHECK(g_gl.device->CreateGraphicsPipelineState(&d, IID_PPV_ARGS(&g_gl.postScreenDecalStainPSO))); + D3D12_GRAPHICS_PIPELINE_STATE_DESC depthDesc{}; depthDesc.pRootSignature = g_gl.postRootSig.Get(); depthDesc.VS = { g_gl.postVsBlob->GetBufferPointer(), g_gl.postVsBlob->GetBufferSize() }; diff --git a/neo/engine/opengl/opengl.h b/neo/engine/opengl/opengl.h index 383cf6d1..dc307952 100644 --- a/neo/engine/opengl/opengl.h +++ b/neo/engine/opengl/opengl.h @@ -1740,7 +1740,8 @@ typedef struct glScreenSpaceDecal_s float pad0; float color[4]; uint32_t textureId; - float pad1[3]; + uint32_t blendMode; // 0 = alpha blend, 1 = multiplicative stain + float pad1[2]; } glScreenSpaceDecal_t; int glRaytracingInit(void); diff --git a/neo/engine/renderer/RenderWorld_load.cpp b/neo/engine/renderer/RenderWorld_load.cpp index af52d8d8..a13c7f83 100644 --- a/neo/engine/renderer/RenderWorld_load.cpp +++ b/neo/engine/renderer/RenderWorld_load.cpp @@ -187,9 +187,18 @@ void idRenderWorldLocal::SubmitWorldDecalsToScreenSpace() const { dst.color[2] = src.color.z; dst.color[3] = src.color.w; dst.textureId = 0; + dst.blendMode = 0; const idMaterial* mat = declManager->FindMaterial(src.material.c_str()); idImage* image = mat ? mat->GetEditorImage() : NULL; + if (mat && mat->GetNumStages() > 0) { + const shaderStage_t* stage = mat->GetStage(0); + const int srcBlend = stage->drawStateBits & GLS_SRCBLEND_BITS; + const int dstBlend = stage->drawStateBits & GLS_DSTBLEND_BITS; + if (srcBlend == GLS_SRCBLEND_ZERO && dstBlend == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR) { + dst.blendMode = 1; + } + } #ifndef QUAKE4 if (mat && src.color.x == 1.0f && src.color.y == 1.0f && src.color.z == 1.0f && src.color.w == 1.0f) { decalInfo_t decalInfo = mat->GetDecalInfo();