benthic_ui 0.3.2

Bevy UI plugin for open metaverse clients
use crate::plugin::{CameraUpdateEvent, SessionData};
use benthic_protocol::messages::ui::land_update::LandUpdate;
use benthic_protocol::messages::ui::mesh_update::{MeshType, MeshUpdate};
use bevy::platform::collections::HashMap;
use bevy::prelude::*;
use bevy_gltf::{Gltf, GltfAssetLabel};
use bevy_panorbit_camera::PanOrbitCamera;
use bevy_world_serialization::WorldAssetRoot;

use std::path::PathBuf;
use uuid::Uuid;

#[derive(Component)]
pub struct WaterPlane;

#[derive(Resource)]
pub struct SceneIDMap {
    pub entities: HashMap<u32, Entity>,
}

#[derive(Resource)]
pub struct AgentIDMap {
    pub entities: HashMap<Uuid, AgentEntity>,
}

pub struct AgentEntity {
    pub entity: Entity,
    pub animation: Option<PathBuf>,
    pub skeleton: Entity,
}

#[derive(Message)]
pub struct MeshUpdateEvent {
    pub value: MeshUpdate,
}

#[derive(Message)]
pub struct LandUpdateEvent {
    pub value: LandUpdate,
}

pub enum RenderableHandle {
    Gltf(Handle<Gltf>),
    Mesh(Handle<Mesh>),
}

#[derive(Resource)]
pub struct Renderable {
    pub handle: RenderableHandle,
    pub transform: Transform,
    pub parent: Option<u32>,
    pub mesh_type: MeshType,
    pub id: Option<Uuid>,
}

#[derive(Resource)]
pub struct MeshQueue {
    pub pending: Vec<Renderable>,
}

#[derive(Component, Debug)]
pub struct AgentID {
    pub id: Uuid,
}

#[derive(Component)]
pub struct MainCamera;

pub fn handle_camera_update(
    mut ev_camera_update: MessageReader<CameraUpdateEvent>,
    mut query: Query<&mut PanOrbitCamera, With<MainCamera>>,
) {
    for ev in ev_camera_update.read() {
        info!("moving the camera to {:?}", ev.value.position);
        for mut camera in &mut query {
            camera.target_focus = ev.value.position;
        }
    }
}

pub fn follow_gltf_with_offset(
    gltf_models: Query<(&Transform, &AgentID), Without<PanOrbitCamera>>,
    session_data: ResMut<SessionData>,
    mut cameras: Query<&mut PanOrbitCamera, With<MainCamera>>,
) {
    if let Some(login_response) = &session_data.login_response
        && let Ok(mut camera) = cameras.single_mut()
    {
        for (model_transform, agent_id) in gltf_models.iter() {
            if agent_id.id == login_response.agent_id {
                camera.target_focus = model_transform.translation;
                break;
            }
        }
    }
}

pub fn handle_mesh_update(
    mut ev_mesh_update: MessageReader<MeshUpdateEvent>,
    asset_server: Res<AssetServer>,
    mut commands: Commands,
    mut agent_id_map: ResMut<AgentIDMap>,
) {
    for renderable in ev_mesh_update.read() {
        let transform = Transform {
            translation: renderable.value.position,
            rotation: renderable.value.rotation,
            scale: renderable.value.scale,
        };

        let scene =
            asset_server.load(GltfAssetLabel::Scene(0).from_asset(renderable.value.path.clone()));

        let mut entity_commands = commands.spawn((
            WorldAssetRoot(scene),
            transform,
            Visibility::Visible,
            Name::new("SceneRoot"),
        ));

        if renderable.value.mesh_type == MeshType::Avatar {
            let agent_id = renderable.value.id.unwrap();

            entity_commands.insert(AgentID { id: agent_id });

            let entity = entity_commands.id();

            agent_id_map.entities.insert(
                agent_id,
                AgentEntity {
                    entity,
                    skeleton: entity,
                    animation: None,
                },
            );

            info!("Spawned avatar {:?} as {:?}", agent_id, entity);
        }
    }
}

pub fn render_land(
    mut commands: Commands,
    mut queue: ResMut<MeshQueue>,
    mut standard_materials: ResMut<Assets<StandardMaterial>>,
) {
    let mut ready = vec![];

    for (i, item) in queue.pending.iter().enumerate() {
        let RenderableHandle::Mesh(mesh_handle) = &item.handle else {
            continue;
        };

        if item.mesh_type != MeshType::Land {
            continue;
        }

        let standard_mat = standard_materials.add(StandardMaterial {
            base_color: Color::WHITE,
            ..default()
        });

        commands.spawn((
            Mesh3d(mesh_handle.clone()),
            item.transform,
            MeshMaterial3d(standard_mat),
        ));

        ready.push(i);
    }

    for i in ready.into_iter().rev() {
        queue.pending.remove(i);
    }
}

pub fn render_meshes(
    mut commands: Commands,
    mut queue: ResMut<MeshQueue>,
    mut standard_materials: ResMut<Assets<StandardMaterial>>,
) {
    let mut ready = vec![];

    for (i, item) in queue.pending.iter().enumerate() {
        let RenderableHandle::Mesh(mesh_handle) = &item.handle else {
            continue;
        };

        if item.mesh_type == MeshType::Land {
            continue;
        }

        let mat_handle = standard_materials.add(StandardMaterial {
            base_color: Color::WHITE,
            ..default()
        });

        commands.spawn((
            Mesh3d(mesh_handle.clone()),
            item.transform,
            MeshMaterial3d(mat_handle),
        ));

        ready.push(i);
    }

    for i in ready.into_iter().rev() {
        queue.pending.remove(i);
    }
}