use mirage_engine::prelude::*;
meshes! { enum Only { Quad } }
#[derive(Default, ShaderValues)]
struct Ripples {
height: f32,
center: Vec2,
tint: Color,
axis: Vec3,
speed: f32,
turn: Mat4,
steps: u32,
}
impl SurfaceStyle for Ripples {
const PASS: DrawPass = DrawPass::Translucent;
const SURFACE: Option<&'static str> =
Some("fn surface(surface: Surface) -> Surface { return surface; }");
}
#[derive(ShaderValues)]
struct Grain;
impl PostEffect for Grain {
const STAGE: EffectStage = EffectStage::ToneMapped;
const SHADER: &'static str = "fn draw(pixel: Pixel) -> vec4<f32> { return pixel.color; }";
}
surface_styles! { enum Looks { Ripples } }
post_effects! { enum Over { Grain } }
struct Sea;
impl Game for Sea {
type Meshes = Only;
type Sounds = NoSounds;
type InputActions = NoInputActions;
type Skyboxes = NoSkyboxes;
type SurfaceStyles = Looks;
type PostEffects = Over;
fn tick(&mut self, _ctx: &mut TickContext<'_, Sea>) {}
fn frame(&mut self, ctx: &mut FrameContext<'_, Sea>) {
ctx.set_surface_style(Ripples::default());
ctx.set_post_effect(Grain);
ctx.draw(Quad.at(Vec3::ZERO).surface_style::<Ripples>());
}
}
fn written(values: &Ripples) -> Vec<u8> {
let mut into = Vec::new();
values.write(&mut into);
into
}
fn number(written: &[u8], at: usize) -> f32 {
f32::from_le_bytes(written[at..at + 4].try_into().expect("four bytes"))
}
fn count(written: &[u8], at: usize) -> u32 {
u32::from_le_bytes(written[at..at + 4].try_into().expect("four bytes"))
}
#[test]
fn every_field_is_declared_by_its_own_name_in_the_order_it_was_written() {
assert_eq!(Ripples::TYPE, "Ripples");
assert_eq!(
Ripples::DECLARATION,
"struct Ripples {\n \
height: f32,\n \
center: vec2<f32>,\n \
tint: vec4<f32>,\n \
axis: vec3<f32>,\n \
speed: f32,\n \
turn: mat4x4<f32>,\n \
steps: u32,\n\
}"
);
}
#[test]
fn every_value_is_laid_out_where_the_shader_reads_it() {
let values = Ripples {
height: 1.5,
center: Vec2::new(2.0, 3.0),
tint: Color::rgba(0.1, 0.2, 0.3, 0.4),
axis: Vec3::new(4.0, 5.0, 6.0),
speed: 7.0,
turn: Mat4::from_scale(Vec3::splat(8.0)),
steps: 9,
};
let written = written(&values);
assert_eq!(written.len(), 128, "and the whole is a round block of them");
assert_eq!(number(&written, 0), 1.5);
assert_eq!(
(number(&written, 8), number(&written, 12)),
(2.0, 3.0),
"a pair starts at the next pair of lanes"
);
assert_eq!(number(&written, 16), 0.1, "and a color at the next block");
assert_eq!(number(&written, 28), 0.4);
assert_eq!(number(&written, 32), 4.0);
assert_eq!(
number(&written, 44),
7.0,
"a number packs into the lane a triple leaves free"
);
assert_eq!(number(&written, 48), 8.0, "and a matrix reads column first");
assert_eq!(number(&written, 52), 0.0);
assert_eq!(count(&written, 112), 9, "a count reads its own four bytes");
}
#[test]
fn the_values_a_style_reads_before_a_frame_writes_any_are_the_defaults() {
let written = written(&Ripples::default());
assert_eq!(written.len(), 128);
assert!(
written[..48].iter().all(|&byte| byte == 0),
"every number of them reads zero"
);
assert_eq!(
(number(&written, 48), number(&written, 108)),
(1.0, 1.0),
"and the matrix is the one that moves nothing"
);
}
#[test]
fn a_type_with_no_fields_declares_nothing_and_writes_nothing() {
let mut written = Vec::new();
Grain.write(&mut written);
assert_eq!(Grain::DECLARATION, "");
assert!(written.is_empty());
}
#[test]
fn a_game_names_the_styles_and_the_effects_it_draws_with() {
let mut sea = Sea;
let _ = &mut sea;
assert_eq!(<Ripples as SurfaceStyle>::PASS, DrawPass::Translucent);
assert_eq!(<Grain as PostEffect>::STAGE, EffectStage::ToneMapped);
assert_eq!(
(
Looks::from(Ripples::default()).seat(),
Over::from(Grain).seat()
),
(0, 0),
"each at the one seat its own set holds it at"
);
}