#version 450 core #include "common.glsl" layout(location = 0) in vec3 aPos; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aUV; layout(location = 3) in vec4 aColor; // rgb = tint/AO, a = wind weight layout(location = 4) in vec4 iPosYaw; // pivot xyz, yaw layout(location = 5) in vec4 iScaleTint; // scale xyz, tint uniform float uWindScale; out VS { vec3 worldPos; vec3 normal; vec2 uv; vec3 color; float height; // above the model's base (ground contact AO) } o; void main() { float c = cos(iPosYaw.w), s = sin(iPosYaw.w); vec3 lp = aPos * iScaleTint.xyz; vec3 p = vec3(c * lp.x + s * lp.z, lp.y, -s * lp.x + c * lp.z) + iPosYaw.xyz; vec3 ln = aNormal / iScaleTint.xyz; vec3 n = normalize(vec3(c * ln.x + s * ln.z, ln.y, -s * ln.x + c * ln.z)); float w = aColor.a * uWindScale; if (w > 0.0) { vec3 pivot = iPosYaw.xyz; float ph = dot(pivot.xz, vec2(0.37, 0.61)); vec2 wdir = normalize(uWind.xy + 1e-5); float gust = vnoise(pivot.xz * 0.12 - wdir * uTime * uWind.w * 0.6); // Whole-crown sway grows with height; leaves add a fast, small flutter. float sway = (sin(uTime * 1.1 + ph) * 0.5 + sin(uTime * 1.9 + ph * 1.3) * 0.25 + gust * 1.2) * uWind.z; float h = max(lp.y, 0.0); p.xz += wdir * sway * w * h * 0.35; // Leaf flutter scales with wind strength, so storms visibly thrash the canopy. p += n * sin(uTime * (3.0 + uWind.w * 2.0) + dot(aPos, vec3(7.1, 11.3, 5.7)) + ph) * 0.4 * uWind.z * w; // Low foliage (bushes) parts around the player like the grass does. vec3 toP = p - uInteractor.xyz; float d = length(toP.xz); float push = clamp(1.0 - d / (uInteractor.w * 1.3), 0.0, 1.0) * step(abs(toP.y), 1.2); p.xz += (d > 1e-3 ? toP.xz / d : vec2(0.0)) * push * 0.3 * w; } o.worldPos = p; o.normal = n; o.uv = aUV; o.color = aColor.rgb * iScaleTint.w; o.height = lp.y; gl_Position = uViewProj * vec4(p, 1.0); gl_ClipDistance[0] = dot(vec4(p, 1.0), uClipPlane); }