Added animations to objects and ui elements

master
Mark 2024-01-04 21:30:12 -08:00
parent 1f154c1a58
commit 8dc040cf08
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
6 changed files with 68 additions and 94 deletions

View File

@ -0,0 +1,26 @@
// Pick frame of animation from an instance.
//
// This function assumes that the uniform header has been loaded,
// and that `InstanceInput` contains a field `texture_index`
fn animate(instance: InstanceInput, age: f32) -> u32 {
let idx = instance.texture_index;
let len = sprites.data[idx].frame_count;
let rep = sprites.data[idx].repeatmode;
let fps = sprites.data[idx].fps;
var frame: u32 = u32(0);
if rep == u32(1) { // Repeat
let x = age / fps;
let m = f32(len);
// x fmod m
frame = u32(x - floor(x / m) * m);
} else { // Once
frame = u32(min(
(age / fps),
f32(len) - 1.0
));
}
return frame + sprites.data[idx].first_frame;
}

View File

@ -26,39 +26,12 @@ var texture_array: binding_array<texture_2d<f32>>;
var sampler_array: binding_array<sampler>; var sampler_array: binding_array<sampler>;
fn fmod(x: f32, m: f32) -> f32 { // INCLUDE: animate.wgsl
return x - floor(x / m) * m;
}
// Returns texture index
// TODO: random age // TODO: random age
// TODO: preprocessor include function // TODO: configure packed asset location, better error
// TODO: packed location config, better error
// TODO: bounce animations // TODO: bounce animations
// TODO: animation randomness? // TODO: animation randomness?
fn animate(instance: InstanceInput) -> u32 {
// Age doesn't make sense here, so arbitrarily pick zero.
let age = global.current_time.x;
let len = sprites.data[instance.texture_index].frame_count;
let rep = sprites.data[instance.texture_index].repeatmode;
let fps = sprites.data[instance.texture_index].fps;
var frame: u32 = u32(0);
if rep == u32(1) {
// Repeat
frame = u32(fmod(
(age / fps),
f32(len)
));
} else {
// Once
frame = u32(min(
(age / fps),
f32(len) - 1.0
));
}
return frame + sprites.data[instance.texture_index].first_frame;
}
@vertex @vertex
fn vertex_main( fn vertex_main(
@ -76,7 +49,7 @@ fn vertex_main(
var out: VertexOutput; var out: VertexOutput;
out.position = transform * vec4<f32>(vertex.position, 1.0); out.position = transform * vec4<f32>(vertex.position, 1.0);
let t = atlas.data[animate(instance)]; let t = atlas.data[animate(instance, global.current_time.x)];
out.texture_index = u32(0); out.texture_index = u32(0);
out.texture_coords = vec2(t.xpos, t.ypos); out.texture_coords = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {

View File

@ -29,33 +29,7 @@ var texture_array: binding_array<texture_2d<f32>>;
var sampler_array: binding_array<sampler>; var sampler_array: binding_array<sampler>;
// Returns texture index // INCLUDE: animate.wgsl
fn animate(instance: InstanceInput) -> u32 {
let age = global.current_time.x - instance.created;
let len = sprites.data[instance.texture_index].frame_count;
let rep = sprites.data[instance.texture_index].repeatmode;
let fps = sprites.data[instance.texture_index].fps;
var frame: u32 = u32(0);
if rep == u32(1) {
// Repeat
frame = u32(fmod(
(age / fps),
f32(len)
));
} else {
// Once
frame = u32(min(
(age / fps),
f32(len) - 1.0
));
}
return frame + sprites.data[instance.texture_index].first_frame;
}
fn fmod(x: f32, m: f32) -> f32 {
return x - floor(x / m) * m;
}
@vertex @vertex
fn vertex_main( fn vertex_main(
@ -66,51 +40,25 @@ fn vertex_main(
var out: VertexOutput; var out: VertexOutput;
out.texture_coords = vertex.texture_coords; out.texture_coords = vertex.texture_coords;
// Skip expired particles
if instance.expires < global.current_time.x { if instance.expires < global.current_time.x {
out.texture_index = u32(0); out.texture_index = u32(0);
// Draw off screen
out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0); out.position = vec4<f32>(2.0, 2.0, 0.0, 1.0);
return out; return out;
} }
let age = global.current_time.x - instance.created; let age = global.current_time.x - instance.created;
let len = sprites.data[instance.texture_index].frame_count; // Apply transformations
let rep = sprites.data[instance.texture_index].repeatmode;
let fps = sprites.data[instance.texture_index].fps;
var frame: u32 = u32(0);
if rep == u32(1) {
// Repeat
frame = u32(fmod(
(age / fps),
f32(len)
));
} else {
// Once
frame = u32(min(
(age / fps),
f32(len) - 1.0
));
}
let t = atlas.data[animate(instance)];
out.texture_index = u32(0);
out.texture_coords = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 {
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
}
if vertex.texture_coords.y == 1.0 {
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
}
let rotation = mat2x2(instance.rotation_0, instance.rotation_1); let rotation = mat2x2(instance.rotation_0, instance.rotation_1);
var scale: f32 = instance.size / global.camera_zoom.x; var scale: f32 = instance.size / global.camera_zoom.x;
var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y); var pos: vec2<f32> = vec2(vertex.position.x, vertex.position.y);
pos = pos * vec2<f32>( pos = pos * vec2<f32>(
sprites.data[instance.texture_index].aspect * scale / global.window_aspect.x, sprites.data[instance.texture_index].aspect * scale / global.window_aspect.x,
scale scale
); );
pos = rotation * pos; pos = rotation * pos;
var ipos: vec2<f32> = ( var ipos: vec2<f32> = (
@ -125,6 +73,19 @@ fn vertex_main(
); );
out.position = vec4<f32>(pos, 0.0, 1.0); out.position = vec4<f32>(pos, 0.0, 1.0);
// Compute texture coordinates
let t = atlas.data[animate(instance, age)];
out.texture_index = u32(0);
out.texture_coords = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 {
out.texture_coords = vec2(out.texture_coords.x + t.width, out.texture_coords.y);
}
if vertex.texture_coords.y == 1.0 {
out.texture_coords = vec2(out.texture_coords.x, out.texture_coords.y + t.height);
}
return out; return out;
} }

View File

@ -25,7 +25,6 @@ var sampler_array: binding_array<sampler>;
fn fmod(x: vec2<f32>, m: f32) -> vec2<f32> { fn fmod(x: vec2<f32>, m: f32) -> vec2<f32> {
return x - floor(x / m) * m; return x - floor(x / m) * m;
} }
@ -108,6 +107,8 @@ fn vertex_main(
out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z; out.position = vec4<f32>(pos, 0.0, 1.0) * instance.position.z;
// Starfield sprites may not be animated
let i = sprites.data[global.starfield_sprite.x].first_frame; let i = sprites.data[global.starfield_sprite.x].first_frame;
let t = atlas.data[i]; let t = atlas.data[i];
out.texture_index = u32(0); out.texture_index = u32(0);

View File

@ -27,6 +27,7 @@ var texture_array: binding_array<texture_2d<f32>>;
var sampler_array: binding_array<sampler>; var sampler_array: binding_array<sampler>;
// INCLUDE: animate.wgsl
@vertex @vertex
@ -46,8 +47,8 @@ fn vertex_main(
out.position = transform * vec4<f32>(vertex.position, 1.0); out.position = transform * vec4<f32>(vertex.position, 1.0);
out.color_transform = instance.color_transform; out.color_transform = instance.color_transform;
let i = sprites.data[instance.texture_index].first_frame; // Pick texture frame
let t = atlas.data[i]; let t = atlas.data[animate(instance, global.current_time.x)];
out.texture_index = u32(0); out.texture_index = u32(0);
out.texture_coords = vec2(t.xpos, t.ypos); out.texture_coords = vec2(t.xpos, t.ypos);
if vertex.texture_coords.x == 1.0 { if vertex.texture_coords.x == 1.0 {

View File

@ -60,17 +60,29 @@ struct VertexBuffers {
particle: Rc<VertexBuffer>, particle: Rc<VertexBuffer>,
} }
/// Preprocess shader files /// Basic wgsl preprocesser
fn preprocess_shader( fn preprocess_shader(
shader: &str, shader: &str,
global_uniform: &GlobalUniform, global_uniform: &GlobalUniform,
global_uniform_group: u32, global_uniform_group: u32,
) -> String { ) -> String {
// Insert common headers // Insert dynamically-generated global definitions
shader.replace( let shader = shader.replace(
"// INCLUDE: global uniform header", "// INCLUDE: global uniform header",
&global_uniform.shader_header(global_uniform_group), &global_uniform.shader_header(global_uniform_group),
) );
// Insert common functions
let shader = shader.replace(
"// INCLUDE: animate.wgsl",
&include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/include/",
"animate.wgsl"
)),
);
return shader;
} }
impl GPUState { impl GPUState {