benthic_ui 0.3.2

Bevy UI plugin for open metaverse clients
use benthic_protocol::messages::ui::water_update::WaterUpdate;
use bevy::image::{
    ImageAddressMode, ImageFilterMode, ImageLoaderSettings, ImageSampler, ImageSamplerDescriptor,
};
use bevy::pbr::{ExtendedMaterial, MaterialExtension};
use bevy::prelude::*;
use bevy::render::render_resource::{AsBindGroup, ShaderType};
use bevy::shader::ShaderRef;

#[derive(Message)]
pub struct WaterUpdateEvent {
    pub value: WaterUpdate,
}

#[derive(Component)]
pub struct WaterPlane;

#[derive(ShaderType, Debug, Clone)]
struct WaterSettings {
    octave_vectors: [Vec4; 2],
    octave_scales: Vec4,
    octave_strengths: Vec4,
}

#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
pub struct Water {
    #[texture(100)]
    #[sampler(101)]
    normals: Handle<Image>,

    #[uniform(102)]
    settings: WaterSettings,
}

impl MaterialExtension for Water {
    fn deferred_fragment_shader() -> ShaderRef {
        "shaders/water_material.wgsl".into()
    }
}

pub struct WaterPlugin;

impl Plugin for WaterPlugin {
    fn build(&self, app: &mut App) {
        app.add_message::<WaterUpdateEvent>()
            .add_systems(Startup, setup_water)
            .add_systems(Update, handle_water_update)
            .add_plugins(MaterialPlugin::<ExtendedMaterial<StandardMaterial, Water>>::default());
    }
}

fn setup_water(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut water_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, Water>>>,
    asset_server: Res<AssetServer>,
) {
    let mut plane_mesh = Plane3d::new(Vec3::Y, Vec2::new(2000.0, 2000.0))
        .mesh()
        .build();

    plane_mesh.generate_tangents().unwrap();

    let mesh_handle = meshes.add(plane_mesh);

    commands.spawn((
        Mesh3d(mesh_handle),
        MeshMaterial3d(
            water_materials.add(ExtendedMaterial {
                base: StandardMaterial {
                    base_color: Color::BLACK,
                    perceptual_roughness: 0.0,
                    ..default()
                },
                extension: Water {
                    normals: asset_server
                        .load_builder()
                        .with_settings(|settings: &mut ImageLoaderSettings| {
                            settings.is_srgb = false;
                            settings.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor {
                                address_mode_u: ImageAddressMode::Repeat,
                                address_mode_v: ImageAddressMode::Repeat,
                                mag_filter: ImageFilterMode::Linear,
                                min_filter: ImageFilterMode::Linear,
                                ..default()
                            });
                        })
                        .load("textures/water_normals.png"),
                    settings: WaterSettings {
                        octave_vectors: [
                            vec4(0.080, 0.059, 0.073, -0.062),
                            vec4(0.153, 0.138, -0.149, -0.195),
                        ],
                        octave_scales: vec4(1.0, 2.1, 7.9, 14.9) * 500.0,
                        octave_strengths: vec4(0.16, 0.18, 0.093, 0.044) * 0.2,
                    },
                },
            }),
        ),
        Transform::from_scale(Vec3::splat(100.0)),
        WaterPlane,
    ));
}

fn handle_water_update(
    mut ev_water_update: MessageReader<WaterUpdateEvent>,
    mut water_query: Query<&mut Transform, With<WaterPlane>>,
) {
    for update in ev_water_update.read() {
        if let Some(mut transform) = water_query.iter_mut().next() {
            transform.translation.y = update.value.height;
        }
    }
}