bevy-aqua 0.1.2

Camera-centred ocean rendering for Bevy with Gerstner and FFT waves.
use super::*;

#[test]
fn every_patch_has_finite_static_bounds() {
    for patch in geometry::Patch::ALL {
        let bounds = geometry::build_patch(patch)
            .compute_aabb()
            .expect("patch bounds");
        assert!(bounds.center.is_finite());
        assert!(bounds.half_extents.is_finite());
        assert!(bounds.half_extents.x > 0.0 && bounds.half_extents.z > 0.0);
    }
}

#[test]
fn expanded_bounds_convert_world_displacement_to_tile_local_space() {
    let base = Aabb::from_min_max(Vec3::splat(-0.5), Vec3::splat(0.5));
    let displacement = waves::DisplacementBounds {
        horizontal: 3.3,
        vertical: 2.1,
    };
    let lod = 2;
    let scale = lod_scale(lod);
    let padding = identity_parent_padding(lod, displacement);
    let expanded = expanded_bounds(base, padding);
    let snap_world = scale * SNAP_AND_MORPH_CELLS / TILE_RESOLUTION as f32;
    assert!(
        ((expanded.half_extents.x - base.half_extents.x) * scale
            - displacement.horizontal
            - snap_world)
            .abs()
            < 1e-5
    );
    assert_eq!(
        expanded.half_extents.y - base.half_extents.y,
        displacement.vertical
    );
    assert_eq!(expanded.half_extents.x, expanded.half_extents.z);
    assert_eq!(expanded.center, base.center);
}

#[test]
fn inverse_affine_padding_supports_transformed_ancestors() {
    let transform = GlobalTransform::from(
        Transform::from_rotation(Quat::from_euler(EulerRot::YXZ, 0.4, -0.2, 0.1))
            .with_scale(Vec3::new(2.0, 0.75, 1.5)),
    );
    let world_padding = Vec3A::new(7.0, 3.0, 5.0);
    let local_padding = world_to_local_padding(&transform, world_padding).unwrap();
    let linear = transform.affine().matrix3;
    let absolute_linear = Mat3A::from_cols(
        linear.x_axis.abs(),
        linear.y_axis.abs(),
        linear.z_axis.abs(),
    );
    let enclosed_world = absolute_linear * local_padding;
    assert!((enclosed_world.cmpge(world_padding)).all());

    let singular = GlobalTransform::from(Transform::from_scale(Vec3::new(1.0, 0.0, 1.0)));
    assert!(world_to_local_padding(&singular, world_padding).is_none());
}

#[test]
fn root_tracks_ocean_and_only_valid_resolved_bodies() {
    let mut app = App::new();
    app.add_plugins(bevy::transform::TransformPlugin)
        .init_resource::<Assets<Mesh>>()
        .insert_resource(OceanWaves::default())
        .insert_resource(waves::StartupAmplitude(1.0))
        .insert_resource(ResolvedWaterBodies::default())
        .insert_resource(lod::Data::new(
            Handle::default(),
            Handle::default(),
            Handle::default(),
            lod::GpuLayout::new(&lod::layout(Vec2::ZERO), Vec2::ZERO, 0.0),
        ))
        .add_systems(Startup, init)
        .add_systems(Update, sync);

    app.update();
    assert_eq!(
        app.world_mut().query::<&Root>().iter(app.world()).count(),
        0
    );

    app.world_mut().insert_resource(Ocean { level: 4.0 });
    app.update();
    let mut roots = app.world_mut().query::<(&Root, &Transform)>();
    let transforms = roots.iter(app.world()).collect::<Vec<_>>();
    assert_eq!(transforms.len(), 1);
    assert_eq!(transforms[0].1.translation.y, 4.0);
    let root = app
        .world_mut()
        .query_filtered::<Entity, With<Root>>()
        .single(app.world())
        .unwrap();
    assert_eq!(
        app.world()
            .get::<GlobalTransform>(root)
            .unwrap()
            .translation()
            .y,
        4.0
    );
    let tile = app
        .world_mut()
        .query_filtered::<Entity, With<Tile>>()
        .iter(app.world())
        .next()
        .unwrap();
    assert_eq!(
        app.world()
            .get::<GlobalTransform>(tile)
            .unwrap()
            .translation()
            .y,
        4.0
    );

    app.world_mut().resource_mut::<Ocean>().level = 6.0;
    app.update();
    assert_eq!(
        app.world()
            .get::<GlobalTransform>(root)
            .unwrap()
            .translation()
            .y,
        6.0
    );
    assert_eq!(
        app.world()
            .get::<GlobalTransform>(tile)
            .unwrap()
            .translation()
            .y,
        6.0
    );

    app.world_mut().remove_resource::<Ocean>();
    app.update();
    assert_eq!(
        app.world_mut().query::<&Root>().iter(app.world()).count(),
        0
    );

    let body = app
        .world_mut()
        .spawn((
            WaterBody,
            WaterShape::Circle { radius: 2.0 },
            Transform::default(),
        ))
        .id();
    app.update();
    assert_eq!(
        app.world_mut().query::<&Root>().iter(app.world()).count(),
        1
    );

    app.world_mut().entity_mut(body).remove::<WaterShape>();
    app.update();
    assert_eq!(
        app.world_mut().query::<&Root>().iter(app.world()).count(),
        0
    );
}