Add wind to grass toping

This commit is contained in:
Samuel Bouchet 2026-03-26 18:58:19 +01:00
parent ef89bd8c49
commit 9086a794a8
5 changed files with 30 additions and 6 deletions

View file

@ -50,7 +50,7 @@ cbuffer VoxelCB : register(b0) {
uint chunkCount;
uint bleedMask; // bit N set = material N can bleed onto neighbors
uint resistBleedMask; // bit N set = material N resists bleed from neighbors
uint _cullPad2;
float windTime; // elapsed time for wind animation (seconds)
};
// ── Indirect draw args (must match C++ IndirectDrawArgs, 20 bytes) ──

View file

@ -44,6 +44,24 @@ VSOutput main(uint vertexID : SV_VertexID, uint instanceID : SV_InstanceID) {
float3 worldPos = inst.worldPos + vtx.position;
// ── 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;
if (localHeight > 0.0) {
float heightFactor = localHeight * localHeight; // quadratic
float phase = worldPos.x * 1.8 + worldPos.z * 1.3 + windTime * 3.5;
float phase2 = worldPos.x * 0.7 - worldPos.z * 2.1 + windTime * 2.7;
float swayX = sin(phase) * 0.11 * heightFactor;
float swayZ = cos(phase2) * 0.08 * heightFactor;
float swayY = -abs(sin(phase * 0.7)) * 0.02 * heightFactor; // slight droop
worldPos.x += swayX;
worldPos.y += swayY;
worldPos.z += swayZ;
}
}
VSOutput output;
output.position = mul(viewProjection, float4(worldPos, 1.0));
output.worldPos = worldPos;

View file

@ -399,7 +399,7 @@ void TopingSystem::generateGrassVariant(TopingDef& def, uint8_t bitmask) {
angle += (h1 - 0.5f) * 20.0f;
// Height: per-blade × per-tuft
float bladeHeight = (0.06f + h2 * 0.28f) * tuftHeightScale;
float bladeHeight = (0.09f + h2 * 0.42f) * tuftHeightScale; // +50% height
float baseWidth = 0.030f + h3 * 0.030f;
float lean = (0.03f + h4 * 0.12f) * tuftLeanScale;
@ -457,7 +457,7 @@ void TopingSystem::generateGrassVariant(TopingDef& def, uint8_t bitmask) {
float angle = (fanT - 0.5f) * 2.0f * 70.0f;
angle += (h1 - 0.5f) * 15.0f;
float height = (0.10f + h2 * 0.22f) * cornerHeightScale;
float height = (0.15f + h2 * 0.33f) * cornerHeightScale; // +50% height
float baseWidth = 0.030f + h3 * 0.025f;
float lean = (0.04f + h4 * 0.10f) * cornerLeanScale;
float midLean = 0.10f + h5 * 0.30f;

View file

@ -714,7 +714,7 @@ void VoxelRenderer::render(
// Material IDs: 1=Grass, 2=Dirt, 3=Stone, 4=Sand, 5=Snow
cb.bleedMask = (1u << 1) | (1u << 2) | (1u << 4) | (1u << 5); // Grass, Dirt, Sand, Snow can bleed (NOT Stone)
cb.resistBleedMask = (1u << 1); // Grass resists bleed (she bleeds onto others, not the reverse)
cb._cullPad2 = 0;
cb.windTime = windTime_;
dev->UpdateBuffer(&constantBuffer_, &cb, cmd, sizeof(cb));
// Render pass
@ -801,7 +801,7 @@ void VoxelRenderer::render(
cb.debugBlend = 0.0f;
cb.bleedMask = 0;
cb.resistBleedMask = 0;
cb._cullPad2 = 0;
cb.windTime = windTime_;
cb.chunkCount = chunkCount_;
extractFrustumPlanes(vpMatrix, cb.frustumPlanes);
dev->UpdateBuffer(&constantBuffer_, &cb, cmd, sizeof(cb));
@ -1466,6 +1466,8 @@ void VoxelRenderPath::Update(float dt) {
float instantFps = (dt > 0.0f) ? (1.0f / dt) : 0.0f;
smoothFps_ = smoothFps_ * 0.95f + instantFps * 0.05f;
if (camera) handleInput(dt);
windTime_ += dt;
renderer.windTime_ = windTime_;
// Animated terrain: regenerate at 60 Hz with time-shifted noise
// Fused: regenerate + pack voxel data in the same parallel pass

View file

@ -60,6 +60,7 @@ public:
bool debugFaceColors_ = false;
bool debugBlend_ = false;
float windTime_ = 0.0f; // set by VoxelRenderPath::Update each frame
private:
void createPipeline();
@ -137,7 +138,7 @@ private:
uint32_t chunkCount;
uint32_t bleedMask; // bit N set = material N can bleed onto neighbors
uint32_t resistBleedMask; // bit N set = material N resists bleed from neighbors
uint32_t _cullPad2;
float windTime;
};
wi::graphics::GPUBuffer constantBuffer_;
@ -236,6 +237,9 @@ private:
mutable float lastDt_ = 0.016f;
mutable float smoothFps_ = 60.0f;
// Wind animation (continuous, always running)
float windTime_ = 0.0f;
// Animated terrain (wave effect at 60 Hz, toggled with F3)
bool animatedTerrain_ = false;
float animTime_ = 0.0f;