phase(N.5) Task 15: delete legacy mesh_instanced shader files
mesh_instanced.vert + .frag deleted. WbDrawDispatcher always uses mesh_modern when WB foundation is on. Legacy escape hatch (ACDREAM_USE_WB_FOUNDATION=0 or bindless missing) runs through InstancedMeshRenderer which has its own shader path — untouched. GameWindow's else-branch removed; if bindless is missing, _meshShader stays unloaded, _wbDrawDispatcher stays null, and _staticMesh is not constructed (its guard requires _meshShader non-null). All downstream _staticMesh usages were already null-safe (null-conditional operators or explicit null guards). Two null-forgiving suppressors added at the WbDrawDispatcher + SkyRenderer construction sites where the compiler couldn't prove non-null but the logic guarantees it (both require _bindlessSupport non-null, which implies _meshShader was assigned; _textureCache is assigned unconditionally). InstancedMeshRenderer.cs: the one reference to mesh_instanced was a code comment (location 3 NOT used by mesh_instanced.vert) — not a file load. Escape hatch code path is preserved; the shader comment is now stale but low priority. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
39ccd29030
commit
e6378b90ed
3 changed files with 16 additions and 145 deletions
|
|
@ -1447,9 +1447,11 @@ public sealed class GameWindow : IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.5 Task 10: load mesh_modern when both extensions are present;
|
// N.5 Task 10/15: load mesh_modern when both extensions are present.
|
||||||
// fall back to mesh_instanced otherwise. Must be after capability
|
// If bindless is missing _meshShader stays null, _wbDrawDispatcher won't
|
||||||
// detection so _bindlessSupport is known.
|
// be constructed (its guard requires _bindlessSupport non-null), and
|
||||||
|
// rendering falls back to InstancedMeshRenderer — but only when
|
||||||
|
// _meshShader is non-null (see _staticMesh construction below).
|
||||||
if (_bindlessSupport is not null)
|
if (_bindlessSupport is not null)
|
||||||
{
|
{
|
||||||
_meshShader = new Shader(_gl,
|
_meshShader = new Shader(_gl,
|
||||||
|
|
@ -1457,12 +1459,7 @@ public sealed class GameWindow : IDisposable
|
||||||
Path.Combine(shadersDir, "mesh_modern.frag"));
|
Path.Combine(shadersDir, "mesh_modern.frag"));
|
||||||
Console.WriteLine("[N.5] mesh_modern shader loaded");
|
Console.WriteLine("[N.5] mesh_modern shader loaded");
|
||||||
}
|
}
|
||||||
else
|
// else: bindless missing — _meshShader stays null.
|
||||||
{
|
|
||||||
_meshShader = new Shader(_gl,
|
|
||||||
Path.Combine(shadersDir, "mesh_instanced.vert"),
|
|
||||||
Path.Combine(shadersDir, "mesh_instanced.frag"));
|
|
||||||
}
|
|
||||||
|
|
||||||
_textureCache = new TextureCache(_gl, _dats, _bindlessSupport);
|
_textureCache = new TextureCache(_gl, _dats, _bindlessSupport);
|
||||||
// Two persistent GL sampler objects (Repeat + ClampToEdge) so
|
// Two persistent GL sampler objects (Repeat + ClampToEdge) so
|
||||||
|
|
@ -1538,14 +1535,21 @@ public sealed class GameWindow : IDisposable
|
||||||
_worldState = new AcDream.App.Streaming.GpuWorldState(wbSpawnAdapter, wbEntitySpawnAdapter);
|
_worldState = new AcDream.App.Streaming.GpuWorldState(wbSpawnAdapter, wbEntitySpawnAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
_staticMesh = new InstancedMeshRenderer(_gl, _meshShader, _textureCache, _wbMeshAdapter);
|
// Task 15: _meshShader is null when bindless is missing; skip constructing
|
||||||
|
// _staticMesh in that case. All downstream _staticMesh usages are already
|
||||||
|
// null-safe (null-conditional operators or explicit null guards).
|
||||||
|
if (_meshShader is not null && _textureCache is not null)
|
||||||
|
_staticMesh = new InstancedMeshRenderer(_gl, _meshShader, _textureCache, _wbMeshAdapter);
|
||||||
|
|
||||||
if (AcDream.App.Rendering.Wb.WbFoundationFlag.IsEnabled
|
if (AcDream.App.Rendering.Wb.WbFoundationFlag.IsEnabled
|
||||||
&& _wbMeshAdapter is not null && _wbEntitySpawnAdapter is not null
|
&& _wbMeshAdapter is not null && _wbEntitySpawnAdapter is not null
|
||||||
&& _bindlessSupport is not null)
|
&& _bindlessSupport is not null)
|
||||||
{
|
{
|
||||||
|
// _meshShader is non-null here: the _bindlessSupport guard implies
|
||||||
|
// the if(_bindlessSupport is not null) block above ran and assigned it.
|
||||||
|
// _textureCache is always non-null (assigned unconditionally above).
|
||||||
_wbDrawDispatcher = new AcDream.App.Rendering.Wb.WbDrawDispatcher(
|
_wbDrawDispatcher = new AcDream.App.Rendering.Wb.WbDrawDispatcher(
|
||||||
_gl, _meshShader, _textureCache, _wbMeshAdapter, _wbEntitySpawnAdapter, _bindlessSupport);
|
_gl, _meshShader!, _textureCache!, _wbMeshAdapter, _wbEntitySpawnAdapter, _bindlessSupport);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase G.1 sky renderer — its own shader (sky.vert / sky.frag)
|
// Phase G.1 sky renderer — its own shader (sky.vert / sky.frag)
|
||||||
|
|
@ -1555,7 +1559,7 @@ public sealed class GameWindow : IDisposable
|
||||||
Path.Combine(shadersDir, "sky.vert"),
|
Path.Combine(shadersDir, "sky.vert"),
|
||||||
Path.Combine(shadersDir, "sky.frag"));
|
Path.Combine(shadersDir, "sky.frag"));
|
||||||
_skyRenderer = new AcDream.App.Rendering.Sky.SkyRenderer(
|
_skyRenderer = new AcDream.App.Rendering.Sky.SkyRenderer(
|
||||||
_gl, _dats, skyShader, _textureCache, _samplerCache);
|
_gl, _dats, skyShader, _textureCache!, _samplerCache);
|
||||||
|
|
||||||
// Phase G.1 particle renderer — renders rain / snow / spell auras
|
// Phase G.1 particle renderer — renders rain / snow / spell auras
|
||||||
// spawned into the shared ParticleSystem as billboard quads.
|
// spawned into the shared ParticleSystem as billboard quads.
|
||||||
|
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
#version 430 core
|
|
||||||
|
|
||||||
in vec2 vTex;
|
|
||||||
in vec3 vWorldNormal;
|
|
||||||
in vec3 vWorldPos;
|
|
||||||
|
|
||||||
out vec4 fragColor;
|
|
||||||
|
|
||||||
// One 2D texture per draw call — same binding point as mesh.frag so the
|
|
||||||
// C# side can use the same TextureCache without a texture-array pipeline.
|
|
||||||
uniform sampler2D uDiffuse;
|
|
||||||
|
|
||||||
// Translucency kind — matches TranslucencyKind C# enum (same as mesh.frag):
|
|
||||||
// 0 = Opaque — depth write+test, no blend; shader never discards
|
|
||||||
// 1 = ClipMap — alpha-key discard at 0.5 (doors, windows, vegetation)
|
|
||||||
// 2 = AlphaBlend — GL blending handles compositing; do NOT discard
|
|
||||||
// 3 = Additive — GL additive blending; do NOT discard
|
|
||||||
// 4 = InvAlpha — GL inverted-alpha blending; do NOT discard
|
|
||||||
uniform int uTranslucencyKind;
|
|
||||||
|
|
||||||
// Phase G.1+G.2: shared scene-lighting UBO (see mesh.frag for layout docs).
|
|
||||||
struct Light {
|
|
||||||
vec4 posAndKind;
|
|
||||||
vec4 dirAndRange;
|
|
||||||
vec4 colorAndIntensity;
|
|
||||||
vec4 coneAngleEtc;
|
|
||||||
};
|
|
||||||
layout(std140, binding = 1) uniform SceneLighting {
|
|
||||||
Light uLights[8];
|
|
||||||
vec4 uCellAmbient;
|
|
||||||
vec4 uFogParams;
|
|
||||||
vec4 uFogColor;
|
|
||||||
vec4 uCameraAndTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
vec3 accumulateLights(vec3 N, vec3 worldPos) {
|
|
||||||
vec3 lit = uCellAmbient.xyz;
|
|
||||||
int activeLights = int(uCellAmbient.w);
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
if (i >= activeLights) break;
|
|
||||||
|
|
||||||
int kind = int(uLights[i].posAndKind.w);
|
|
||||||
vec3 Lcol = uLights[i].colorAndIntensity.xyz * uLights[i].colorAndIntensity.w;
|
|
||||||
|
|
||||||
if (kind == 0) {
|
|
||||||
vec3 Ldir = -uLights[i].dirAndRange.xyz;
|
|
||||||
float ndl = max(0.0, dot(N, Ldir));
|
|
||||||
lit += Lcol * ndl;
|
|
||||||
} else {
|
|
||||||
vec3 toL = uLights[i].posAndKind.xyz - worldPos;
|
|
||||||
float d = length(toL);
|
|
||||||
float range = uLights[i].dirAndRange.w;
|
|
||||||
if (d < range && range > 1e-3) {
|
|
||||||
vec3 Ldir = toL / max(d, 1e-4);
|
|
||||||
float ndl = max(0.0, dot(N, Ldir));
|
|
||||||
float atten = 1.0;
|
|
||||||
if (kind == 2) {
|
|
||||||
float cos_edge = cos(uLights[i].coneAngleEtc.x * 0.5);
|
|
||||||
float cos_l = dot(-Ldir, uLights[i].dirAndRange.xyz);
|
|
||||||
atten *= (cos_l > cos_edge) ? 1.0 : 0.0;
|
|
||||||
}
|
|
||||||
lit += Lcol * ndl * atten;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lit;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 applyFog(vec3 lit, vec3 worldPos) {
|
|
||||||
int mode = int(uFogParams.w);
|
|
||||||
if (mode == 0) return lit;
|
|
||||||
float d = length(worldPos - uCameraAndTime.xyz);
|
|
||||||
float fogStart = uFogParams.x;
|
|
||||||
float fogEnd = uFogParams.y;
|
|
||||||
float span = max(1e-3, fogEnd - fogStart);
|
|
||||||
float fog = clamp((d - fogStart) / span, 0.0, 1.0);
|
|
||||||
return mix(lit, uFogColor.xyz, fog);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 color = texture(uDiffuse, vTex);
|
|
||||||
|
|
||||||
// Alpha cutout only for clip-map surfaces (doors, windows, vegetation).
|
|
||||||
if (uTranslucencyKind == 1 && color.a < 0.5) discard;
|
|
||||||
|
|
||||||
vec3 N = normalize(vWorldNormal);
|
|
||||||
vec3 lit = accumulateLights(N, vWorldPos);
|
|
||||||
|
|
||||||
// Lightning flash — additive scene bump.
|
|
||||||
lit += uFogParams.z * vec3(0.6, 0.6, 0.75);
|
|
||||||
|
|
||||||
// Retail clamp per-channel to 1.0 (r13 §13.1).
|
|
||||||
lit = min(lit, vec3(1.0));
|
|
||||||
|
|
||||||
vec3 rgb = color.rgb * lit;
|
|
||||||
rgb = applyFog(rgb, vWorldPos);
|
|
||||||
fragColor = vec4(rgb, color.a);
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
#version 430 core
|
|
||||||
|
|
||||||
// Per-vertex attributes
|
|
||||||
layout(location = 0) in vec3 aPosition;
|
|
||||||
layout(location = 1) in vec3 aNormal;
|
|
||||||
layout(location = 2) in vec2 aTexCoord;
|
|
||||||
|
|
||||||
// Per-instance model matrix, split across four vec4 attribute slots.
|
|
||||||
// A mat4 consumes 4 consecutive attribute locations, so locations 3-6 are
|
|
||||||
// all occupied by this single logical matrix. The C# side must call
|
|
||||||
// VertexAttribPointer four times (one per row) and VertexAttribDivisor(loc, 1)
|
|
||||||
// on each of the four slots.
|
|
||||||
layout(location = 3) in vec4 aInstanceRow0;
|
|
||||||
layout(location = 4) in vec4 aInstanceRow1;
|
|
||||||
layout(location = 5) in vec4 aInstanceRow2;
|
|
||||||
layout(location = 6) in vec4 aInstanceRow3;
|
|
||||||
|
|
||||||
uniform mat4 uViewProjection;
|
|
||||||
|
|
||||||
out vec2 vTex;
|
|
||||||
out vec3 vWorldNormal;
|
|
||||||
out vec3 vWorldPos;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Reconstruct the per-instance model matrix from its four row vectors.
|
|
||||||
mat4 model = mat4(aInstanceRow0, aInstanceRow1, aInstanceRow2, aInstanceRow3);
|
|
||||||
|
|
||||||
vec4 worldPos = model * vec4(aPosition, 1.0);
|
|
||||||
gl_Position = uViewProjection * worldPos;
|
|
||||||
|
|
||||||
vWorldPos = worldPos.xyz;
|
|
||||||
// Transform normal into world space.
|
|
||||||
vWorldNormal = normalize(mat3(model) * aNormal);
|
|
||||||
vTex = aTexCoord;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue