2026-03-26 17:47:08 +01:00
|
|
|
// BVLE Voxels - Toping Vertex Shader (Phase 4.2)
|
|
|
|
|
// Instanced vertex pulling: reads mesh vertices from t4, instance positions from t5.
|
|
|
|
|
// Push constants carry per-draw-group offsets.
|
|
|
|
|
|
|
|
|
|
#include "voxelCommon.hlsli"
|
|
|
|
|
|
|
|
|
|
// Toping mesh vertex (must match C++ TopingVertex, 24 bytes)
|
|
|
|
|
struct TopingVtx {
|
|
|
|
|
float3 position; // local to voxel [0,1]^3
|
|
|
|
|
float3 normal;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Toping instance (just the world position, 12 bytes)
|
|
|
|
|
struct TopingInst {
|
|
|
|
|
float3 worldPos;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
StructuredBuffer<TopingVtx> topingVertices : register(t4);
|
|
|
|
|
StructuredBuffer<TopingInst> topingInstances : register(t5);
|
|
|
|
|
|
|
|
|
|
// Push constants — repurposed fields for toping draws:
|
|
|
|
|
// chunkIndex → vertexOffset (into t4)
|
|
|
|
|
// quadOffset → instanceOffset (into t5)
|
|
|
|
|
// flags → materialID
|
|
|
|
|
struct TopingPush {
|
|
|
|
|
uint vertexOffset;
|
|
|
|
|
uint instanceOffset;
|
|
|
|
|
uint materialID;
|
|
|
|
|
uint pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7, pad8;
|
|
|
|
|
};
|
|
|
|
|
[[vk::push_constant]] ConstantBuffer<TopingPush> push : register(b999);
|
|
|
|
|
|
|
|
|
|
struct VSOutput {
|
|
|
|
|
float4 position : SV_POSITION;
|
|
|
|
|
float3 worldPos : WORLDPOS;
|
|
|
|
|
float3 normal : NORMAL;
|
2026-03-29 19:46:25 +02:00
|
|
|
float localHeight : LOCALHEIGHT; // height above voxel face (0=base, 1=tip)
|
2026-03-26 17:47:08 +01:00
|
|
|
nointerpolation uint materialID : MATERIALID;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
[RootSignature(VOXEL_ROOTSIG)]
|
|
|
|
|
VSOutput main(uint vertexID : SV_VertexID, uint instanceID : SV_InstanceID) {
|
|
|
|
|
TopingVtx vtx = topingVertices[push.vertexOffset + vertexID];
|
|
|
|
|
TopingInst inst = topingInstances[push.instanceOffset + instanceID];
|
|
|
|
|
|
|
|
|
|
float3 worldPos = inst.worldPos + vtx.position;
|
|
|
|
|
|
2026-03-26 18:58:19 +01:00
|
|
|
// ── Wind animation (vegetation only) ────────────────────────
|
|
|
|
|
// Height above the voxel face (y=1 in local space) drives amplitude.
|
|
|
|
|
// Quadratic scaling: base stays anchored, tips sway the most.
|
|
|
|
|
if (push.materialID != 3u) { // not stone
|
|
|
|
|
float localHeight = vtx.position.y - 1.0;
|
2026-03-31 08:53:37 +02:00
|
|
|
float amplitude = 2.0;
|
|
|
|
|
float frequency = 1.4;
|
2026-03-26 18:58:19 +01:00
|
|
|
if (localHeight > 0.0) {
|
|
|
|
|
float heightFactor = localHeight * localHeight; // quadratic
|
2026-03-31 08:53:37 +02:00
|
|
|
float phase = worldPos.x * 1.8 + worldPos.z * 1.3 + windTime * 3.5 * frequency;
|
|
|
|
|
float phase2 = worldPos.x * 0.7 - worldPos.z * 2.1 + windTime * 2.7 * frequency;
|
|
|
|
|
float swayX = sin(phase) * 0.11 * heightFactor * amplitude;
|
|
|
|
|
float swayZ = cos(phase2) * 0.08 * heightFactor * amplitude;
|
|
|
|
|
float swayY = -abs(sin(phase * 0.7)) * 0.02 * heightFactor * amplitude; // slight droop
|
2026-03-26 18:58:19 +01:00
|
|
|
worldPos.x += swayX;
|
|
|
|
|
worldPos.y += swayY;
|
|
|
|
|
worldPos.z += swayZ;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 17:47:08 +01:00
|
|
|
VSOutput output;
|
2026-03-29 19:46:25 +02:00
|
|
|
output.position = mul(viewProjection, float4(worldPos, 1.0));
|
|
|
|
|
output.worldPos = worldPos;
|
|
|
|
|
output.normal = vtx.normal;
|
|
|
|
|
output.localHeight = saturate((vtx.position.y - 1.0) * 5.0); // normalized: 0=base, 1=tip (blades ~0.06-0.24 tall)
|
|
|
|
|
output.materialID = push.materialID;
|
2026-03-26 17:47:08 +01:00
|
|
|
return output;
|
|
|
|
|
}
|