From 2704e9d0e9d203f68925e490fffcd587b9b94e07 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Thu, 30 Apr 2026 15:07:29 +0100 Subject: [PATCH] feat(gameengine): add minimal BSP shaders so quake3 package can render MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quake 3 package was missing GPU shaders entirely. With these in place, the engine boots quake3 successfully: pak0 loads, BSP parses, lightmap atlas and geometry build, and shaders compile/bind correctly on the Vulkan path. bsp.vert: matches BspRenderVertex layout (position/uv/lmuv/normal) and re-uses the existing VertexUniformData uniform block from the seed pipeline so no C++ changes are needed. bsp.frag: classic Q3 model — albedo × lightmap × overbright + ambient. Skips PBR / shadow mapping entirely; Q3's baked lightmaps already encode direct + bounce lighting and shadowing. Q3 shader-script effects (animated sky, scrolling water, env-mapped chrome) render flat — adding them is a follow-up. SPIR-V only for now (Windows/Vulkan/D3D12 via SDL3 cross-compile). MSL and DXIL still missing for Mac/native-D3D12 paths. Co-Authored-By: Claude Opus 4.6 --- .../quake3/shaders/spirv/bsp.frag.glsl | 49 ++++++++++++++++++ .../quake3/shaders/spirv/bsp.frag.spv | Bin 0 -> 2844 bytes .../quake3/shaders/spirv/bsp.vert.glsl | 40 ++++++++++++++ .../quake3/shaders/spirv/bsp.vert.spv | Bin 0 -> 2776 bytes 4 files changed, 89 insertions(+) create mode 100644 gameengine/packages/quake3/shaders/spirv/bsp.frag.glsl create mode 100644 gameengine/packages/quake3/shaders/spirv/bsp.frag.spv create mode 100644 gameengine/packages/quake3/shaders/spirv/bsp.vert.glsl create mode 100644 gameengine/packages/quake3/shaders/spirv/bsp.vert.spv diff --git a/gameengine/packages/quake3/shaders/spirv/bsp.frag.glsl b/gameengine/packages/quake3/shaders/spirv/bsp.frag.glsl new file mode 100644 index 000000000..01e716dec --- /dev/null +++ b/gameengine/packages/quake3/shaders/spirv/bsp.frag.glsl @@ -0,0 +1,49 @@ +#version 450 + +// Quake 3 BSP fragment shader (minimal). +// Q3 bakes radiosity into a lightmap atlas at compile time, so runtime lighting +// is just: albedo × lightmap × overbright. The classic Q3 "overbright bits = 1" +// doubles lightmap intensity — modern screens are brighter, so we expose it as +// part of the existing FragmentUniformData.material slot (z) with a 2.0 default. +// +// Fragment sampler bindings come from workflow_draw_map_step.cpp BSP path: +// set=2 binding=0 albedo (per-surface diffuse from extracted pk3 textures) +// set=2 binding=1 shadowMap (shadow map — ignored for BSP, lightmap is canonical) +// set=2 binding=2 lightmap (shared atlas built by bsp.lightmap_atlas) + +layout(set = 2, binding = 0) uniform sampler2D albedoTex; +layout(set = 2, binding = 1) uniform sampler2D shadowMap; +layout(set = 2, binding = 2) uniform sampler2D lightmapTex; + +layout(set = 3, binding = 0) uniform PBRUniforms { + vec4 u_lightDir; + vec4 u_lightColor; // a = exposure + vec4 u_ambient; + vec4 u_material; // z = lightmap overbright multiplier (defaults via C++) + vec4 u_flashPos; + vec4 u_flashDir; + vec4 u_flashColor; +}; + +layout(location = 0) in vec2 v_uv; +layout(location = 1) in vec2 v_lmuv; +layout(location = 2) in vec3 v_worldNormal; +layout(location = 3) in vec3 v_worldPos; +layout(location = 4) in vec3 v_cameraPos; + +layout(location = 0) out vec4 o_color; + +void main() { + vec3 albedo = texture(albedoTex, v_uv).rgb; + vec3 lightmap = texture(lightmapTex, v_lmuv).rgb; + + // Q3 overbright. material.z falls back to 2.0 (engine pushes 0 today, so + // fall through to a sane default rather than rendering a black map). + float overbright = (u_material.z > 0.0) ? u_material.z : 2.0; + + vec3 ambient = u_ambient.rgb * albedo; + vec3 lit = albedo * lightmap * overbright + ambient; + + float exposure = (u_lightColor.a > 0.0) ? u_lightColor.a : 1.0; + o_color = vec4(lit * exposure, 1.0); +} diff --git a/gameengine/packages/quake3/shaders/spirv/bsp.frag.spv b/gameengine/packages/quake3/shaders/spirv/bsp.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..372002ca5eb7388a529f2c6473a96199809e013c GIT binary patch literal 2844 zcmZ{kYjYG;5QZnj4JgX}BBHnA1jmPM6yr-Y``fLSf7kjJu`|@f#dl=a63g$`AnOi??wLNL< zeb{-w(OK+xcz}HfHh=c|&6VbZywmRT5@U&&tCtqH%rrX*==uWpElT`c-pcXl8=SGu zUYfNRn~fF1ui?I8FKuV5jZQOb4RPP1mp*7^-R1ebJH%^4{C{_k4e_N2*=G|VWfm-nGU7p@AO7V?;#qKU=OZlVA#pBkf!ru1NM|r2U zbcIKCUPnfdgQY!z?wjfVh`o-u z^lN{NM2!bZ-8%HUe}=er!1|m$@FNA9OvOFNup`d#Qn!}ypGS|py!QfPUJ=jPOp-s& zSm$*#eV(^Y?S&GHw{_lJ^6#)4+xOJ3?fi*_?fg}3=TG}M-=TTiH<$N51M!>jtaIjn zKVob9PY>HZI>%M}i;6AZ|6KUB{m+H%`I(pOA4>Za_dB1P5NA`{`P+s#d$-8aH`;;7 z5x+{t#@>mDD=urb&eK$AJ^Ca5yXekp#D5RnS&jI6(dCFo{P)qt6<0OB@1uX3338@B zLB^2@#Ch``XX{f$-#^qbn5_Zk%snN~Np{x@_y^e!AzvZ(M?KiZG7ir5qN=<8+lSH-^quFstO zRHXkP$RdBN>e5cB(6BbgVg~<`l~-ZoJnw z@DY3lpUNAn{J!p9Q(d#C{_M5>b=-UHPHkcJRFXWCoJme6KP1IEpDe&6a7+2Vy}Q4= zJ{ljb-@LV<$JwNoJL+?epKB5H(oSFb1@H>E4%Wa2z`#w=1iN4l)c7s1|3jiW8LBzz z)INjlr*6BI4G*&6IQtb9w&0bu4Mv@DXV8Z|mHXRM&UgCb{myTheqpl~wg=tL@x#t& zoc0g1navYvn{>M~Tb=$qi_3n_vlpHG*zY_U411#@x4di8?hTHz?m;IzX$=Rz5K+%# z?5VWn_4K!wNqaOIK1mO=N90Po*oqqVGHFky`-f@Q0|f8=OxlNOFB_&De8$&{TtBBr zgOh`nz1EToaIQK(XOXs#$H6`77}@@O3)9NEw&~y5dq+Fim0ht1=e^3g2WO0& zcNmZVNbflK{5dsX)vRu6yQMpfS^b!!D|!h z)_aNF^XX?jb!R1~{pC{D-*aDP_Z;@4{R+Fi$Q!TjEamJ^-`7BrJiV{~3X-w*V7^s$ zeWKo*$lB%2r|w&jGyW}hbBA4ht>of9-a!xlcahC)y~yX=(XMX%1~8}jKVsKcM82EI zVZVoLY@Ao!w-))-o2BkN9wO@-cJIfxXFlue@7vQJ^;*cz?>1kVy(_!)`4`4-%UFB$ zf1tfKVV%`Q;QV|O_waFUmw>#wy}SyX(PLt~`>Q3#Hsf9|Ir|JhzYk;WsYO-mz5$Ha zU%RvZlf2p*z}ouQ-wOLR;Cr^eO-`iV0QR=Z{x(`B}+T@A593@%n48-sR`$*4D>fox5Dz$$ezGJ-&JS+5&RIWo>I2>wDb-an3K0 z=g;{7PTqdK^Dlv%D{6d&EH7M9BVxAUjFFF+9b|donveM!&KP@*m~W8fg)3rOyk5Vr zZ-Kp-Ti=+?cgTKgzDMum5ztrOduWziytT*Z-h5FXr<**8U&t G9q>P*u(l%r literal 0 HcmV?d00001