#version 450
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 uv;
layout(location = 3) in vec4 tangent;
layout(location = 5) in vec4 color;
// Per-vertex data: bits 0-15 a block id, bits 16-22 RUSTING_FLAG_* flags.
layout(location = 6) in uint data;
layout(location = 0) out vec3 v_normal;
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;
layout(location = 1) out vec4 v_color;
layout(location = 2) out vec3 v_world_position;
layout(location = 3) flat out uvec3 v_alpha;
layout(location = 4) out vec2 v_uv;
layout(location = 5) out vec4 v_tangent;
layout(location = 6) flat out vec4 v_emissive;
layout(location = 7) flat out vec4 v_surface;
layout(location = 8) flat out vec4 v_transmission;
layout(location = 9) flat out vec4 v_splat_scale;
layout(location = 10) flat out vec3 v_tint;
layout(location = 11) flat out vec4 v_water;
layout(location = 12) out float v_crest;
layout(location = 13) flat out uint v_data;
// Index into render_instances from the per-frame visible list.
layout(location = 4) in uint instance_index;
// 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);
}
// Gerstner waves of water surfaces (`physics.w` bit 3), two vec4s each:
// direction xz, wavenumber, height; then steepness and travelled phase.
// The instance's normal[0].w is its crest foam, normal[1].w its first wave
// and normal[2].w the count.
layout(set = 0, binding = 16) readonly buffer WaterWaves { vec4 data[]; } water_waves;
// Moves a flat grid point to its spot on the waves; returns the normal.
// `pinch` is how far the sideways sway squeezes the surface: one minus the
// determinant of its Jacobian, 1 where a crest folds to a point.
vec3 water_wave(inout vec3 point, uint first, uint count, out float pinch) {
vec2 start = point.xz;
vec2 tilt = vec2(0.0);
float up = 1.0;
// xx, zz and xz of the Jacobian's shrink, steepness * sin * D D^T.
vec3 squeeze = vec3(0.0);
for (uint index = first; index < first + count; index++) {
vec4 shape = water_waves.data[2u * index];
vec4 motion = water_waves.data[2u * index + 1u];
float phase = shape.z * dot(shape.xy, start) - motion.y;
float s = sin(phase);
float c = cos(phase);
point.y += shape.w * s;
point.xz += motion.x / shape.z * shape.xy * c;
tilt += shape.z * shape.w * shape.xy * c;
up -= motion.x * s;
squeeze += motion.x * s * shape.xyx * shape.xyy;
}
pinch = 1.0 - ((1.0 - squeeze.x) * (1.0 - squeeze.y) - squeeze.z * squeeze.z);
return normalize(vec3(-tilt.x, max(up, 1e-3), -tilt.y));
}
// The shader pack vertex hook; see vertex_hook.glsl.
#include "vertex_hook.glsl"
void main() {
RenderInstance instance = render_instances.data[instance_index];
mat4 model = instance.physics.x == 0xffffffffu
? instance.model
: physics_states.data[instance.physics.x].model;
vec4 world_position = model * vec4(position, 1.0);
bool waves = (instance.physics.w & 8u) != 0u;
vec3 wave_normal = vec3(0.0, 1.0, 0.0);
float pinch = 0.0;
if (waves) {
wave_normal = water_wave(world_position.xyz, uint(instance.normal[1].w), uint(instance.normal[2].w), pinch);
}
v_crest = waves ? clamp(instance.normal[0].w * pinch, 0.0, 1.0) : 0.0;
vec2 lean = waves ? vec2(0.0) : foliage_lean(instance.normal[0].w, instance.normal[1].w, model[3].xyz, camera.ambient.w);
vec2 bend = sway_bend(world_position.y - model[3].y, instance.normal[2].w);
world_position.xz += lean * bend.x;
// Normals and tangents follow the local shear, the lean times the slope.
lean *= bend.y;
mat3 normal_matrix;
if (instance.physics.x == 0xffffffffu) {
normal_matrix = mat3(instance.normal);
} else {
// GPU bodies are rotation times scale with no shear, so dividing
// each column by its squared length gives the inverse-transpose.
mat3 linear = mat3(model);
normal_matrix = mat3(
linear[0] / dot(linear[0], linear[0]),
linear[1] / dot(linear[1], linear[1]),
linear[2] / dot(linear[2], linear[2]));
}
v_normal = waves ? wave_normal : normal_matrix * normal;
// The lean is a shear; normals take its inverse transpose.
v_normal.y -= lean.x * v_normal.x + lean.y * v_normal.z;
// Terrain vertex colors are splat weights, normalized per pixel: the
// material color tints the blended layers instead of the weights.
bool splat = abs(instance.transmission.w - 1.0) < 0.5;
v_color = splat ? color : instance.color * color;
v_tint = splat ? instance.color.rgb : vec3(1.0);
v_world_position = world_position.xyz;
v_alpha = instance.physics.yzw;
v_uv = uv * instance.uv_transform.xy + instance.uv_transform.zw;
// Tangents follow the surface, so they take the model's linear part.
v_tangent = vec4(mat3(model) * tangent.xyz, tangent.w);
v_tangent.xz += lean * v_tangent.y;
v_emissive = instance.emissive;
v_surface = instance.surface;
v_transmission = instance.transmission;
v_splat_scale = instance.splat_scale;
v_water = instance.water;
v_data = data;
#ifdef RUSTING_CUSTOM_VERTEX
VertexOutput o = VertexOutput(world_position.xyz, v_normal, v_color);
vertex(VertexInput(position, world_position.xyz, v_normal, v_uv, v_color,
data & 0xffffu, data & ~0xffffu, camera.ambient.w), o);
world_position.xyz = o.world_position;
v_world_position = o.world_position;
v_normal = o.normal;
v_color = o.color;
#endif
gl_Position = camera.view_projection * world_position;
// Viewmodel meshes take their own field of view and squash toward the
// near plane, so they draw over the world without a depth clear.
if ((instance.physics.w & 4u) != 0u) {
gl_Position.xy *= camera.ground_ambient.w;
gl_Position.z *= 0.05;
}
}