#version 450
layout(location = 0) in vec3 position;
#if defined(RUSTING_VERTEX_INPUTS) || defined(RUSTING_SHADOW_MASK)
// Only a shader pack's vertex hook and alpha-tested casters read the rest
// of the vertex; the engine defines these when it adds one.
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 uv;
layout(location = 5) in vec4 color;
layout(location = 6) in uint data;
#endif
#ifdef RUSTING_SHADOW_MASK
// Alpha-tested casters: shadow.frag cuts the holes of Mask materials with
// the uv and alpha the scene pass uses.
layout(location = 0) out vec2 v_uv;
layout(location = 1) out float v_alpha;
layout(location = 2) flat out float v_cutoff;
#endif
layout(push_constant) uniform Camera {
mat4 view_projection;
vec4 eye;
vec4 ambient;
vec4 ground_ambient;
uvec4 light_info;
} camera;
struct PhysicsState {
mat4 model;
vec4 velocity;
vec4 angular_velocity;
vec4 properties;
vec4 custom_values;
uvec4 metadata;
};
layout(set = 0, binding = 0) readonly buffer PhysicsStates {
PhysicsState data[];
} physics_states;
struct RenderInstance {
mat4 model;
mat3x4 normal;
vec4 color;
vec4 emissive;
vec4 surface;
uvec4 physics;
vec4 transmission;
vec4 uv_transform;
vec4 splat_scale;
vec4 water;
};
layout(set = 0, binding = 1) readonly buffer RenderInstances {
RenderInstance data[];
} render_instances;
// Wind sway: `Foliage` copies and swaying materials carry their lean per
// unit of wind in normal[0].w, a gust frequency in normal[1].w and a bend
// height in normal[2].w, and bend by the force fields at their base, as
// `lean_matrix` on the CPU did.
#define NO_BODY_SHAPES
#include <physics_shapes.glsl>
#include <physics_fields.glsl>
vec2 foliage_lean(float sway, float frequency, vec3 base, float seconds) {
if (sway <= 0.0) return vec2(0.0);
vec3 push = field_push(base, 0.001, uint(force_fields.data.length()), seconds);
vec2 lean = push.xz * sway;
if (frequency > 0.0) {
// Gusts, phased by position so neighbours do not move together.
float phase = dot(base.xz, vec2(0.37, 0.61));
lean *= 1.0 + 0.5 * sin(6.2831853 * frequency * seconds + phase);
}
// Past 45 degrees a copy would stretch more than it leans.
float size = length(lean);
return size > 1.0 ? lean / size : lean;
}
// Sideways reach per unit of lean at `y` above the base, and its slope.
// With a bend height the base stays rooted: y^2 / 2h curves up to slope 1
// at h, then runs straight. Without one the whole mesh shears.
vec2 sway_bend(float y, float height) {
if (height <= 0.0) return vec2(y, 1.0);
if (y <= 0.0) return vec2(0.0);
return y < height ? vec2(y * y / (2.0 * height), y / height) : vec2(y - 0.5 * height, 1.0);
}
// The shader pack vertex hook; see vertex_hook.glsl.
#include "vertex_hook.glsl"
void main() {
RenderInstance instance = render_instances.data[gl_InstanceIndex];
if ((instance.physics.w & 1u) == 0u) {
// Every vertex at one point: a zero-area triangle draws nothing.
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
return;
}
mat4 model = instance.physics.x == 0xffffffffu
? instance.model
: physics_states.data[instance.physics.x].model;
vec4 world_position = model * vec4(position, 1.0);
vec2 lean = foliage_lean(instance.normal[0].w, instance.normal[1].w, model[3].xyz, camera.ambient.w);
world_position.xz += lean * sway_bend(world_position.y - model[3].y, instance.normal[2].w).x;
#ifdef RUSTING_SHADOW_MASK
v_uv = uv * instance.uv_transform.xy + instance.uv_transform.zw;
// Terrain splat weights are not coverage, as in scene.vert.
bool splat = abs(instance.transmission.w - 1.0) < 0.5;
v_alpha = splat ? 1.0 : instance.color.a * color.a;
v_cutoff = uintBitsToFloat(instance.physics.z);
#endif
#ifdef RUSTING_CUSTOM_VERTEX
vec3 world_normal = normalize(mat3(instance.normal) * normal);
vec4 tinted = instance.color * color;
VertexOutput o = VertexOutput(world_position.xyz, world_normal, tinted);
vertex(VertexInput(position, world_position.xyz, world_normal,
uv * instance.uv_transform.xy + instance.uv_transform.zw, tinted,
data & 0xffffu, data & ~0xffffu, camera.ambient.w), o);
world_position.xyz = o.world_position;
#ifdef RUSTING_SHADOW_MASK
v_alpha = splat ? 1.0 : o.color.a;
#endif
#endif
gl_Position = camera.view_projection * world_position;
}