#version 430 core #extension GL_ARB_shader_draw_parameters : require layout(location = 0) in vec3 aPosition; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aTexCoord; struct InstanceData { mat4 transform; // Reserved for Phase B.4 follow-up (selection-blink retail-faithful // highlight): vec4 highlightColor; — extend stride here, increase the // _instanceSsbo upload size in WbDrawDispatcher, add a flat varying out, // and consume in mesh_modern.frag. }; struct BatchData { uvec2 textureHandle; // bindless handle for sampler2DArray uint textureLayer; // layer index (always 0 for per-instance composites) uint flags; // reserved — N.5 dispatcher owns all blend state // (glBlendFunc per pass). If a future phase wants // shader-side per-batch additive flag (Decision 2 // fallback), encode it here as bit 0. }; layout(std430, binding = 0) readonly buffer InstanceBuffer { InstanceData Instances[]; }; // binding=1 here is the SSBO namespace — distinct from the UBO namespace. // SceneLighting UBO also uses binding=1 in the fragment shader; GL keeps // GL_SHADER_STORAGE_BUFFER and GL_UNIFORM_BUFFER binding tables separate. // Task 10 dispatcher binds: // glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, instanceSsbo) // glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, batchSsbo) // Existing SceneLightingUboBinding handles the UBO side. layout(std430, binding = 1) readonly buffer BatchBuffer { BatchData Batches[]; }; uniform mat4 uViewProjection; out vec3 vNormal; out vec2 vTexCoord; out vec3 vWorldPos; out flat uvec2 vTextureHandle; out flat uint vTextureLayer; void main() { int instanceIndex = gl_BaseInstanceARB + gl_InstanceID; mat4 model = Instances[instanceIndex].transform; vec4 worldPos = model * vec4(aPosition, 1.0); gl_Position = uViewProjection * worldPos; vWorldPos = worldPos.xyz; vNormal = normalize(mat3(model) * aNormal); vTexCoord = aTexCoord; BatchData b = Batches[gl_DrawIDARB]; vTextureHandle = b.textureHandle; vTextureLayer = b.textureLayer; }