bevy_vrm1 0.6.3

Allows you to use VRM and VRMA in Bevy
Documentation
use bevy::{
    camera::{RenderTarget, visibility::RenderLayers},
    ecs::system::SystemParam,
    math::{Vec2, Vec3},
    prelude::{Camera, Camera3d, Component, Entity, GlobalTransform, InfinitePlane3d, Query, With},
    window::{PrimaryWindow, WindowRef},
};

pub type CameraQuery<'w> = (
    Entity,
    &'w Camera,
    Option<&'w RenderTarget>,
    &'w GlobalTransform,
    Option<&'w RenderLayers>,
);

#[derive(SystemParam)]
pub struct Cameras<'w, 's, Camera: Component = Camera3d> {
    pub cameras: Query<'w, 's, CameraQuery<'static>, With<Camera>>,
    primary_window: Query<'w, 's, Entity, With<PrimaryWindow>>,
}

impl<Camera: Component> Cameras<'_, '_, Camera> {
    pub fn all_layers(&self) -> RenderLayers {
        self.cameras
            .iter()
            .fold(RenderLayers::none(), |l1, (_, _, _, _, l2)| match l2 {
                Some(l2) => l1 | l2.clone(),
                None => l1,
            })
    }

    #[inline]
    pub fn find_camera_from_window(
        &self,
        window_entity: Entity,
    ) -> Option<CameraQuery<'_>> {
        self.cameras.iter().find(|(_, _, target, _, _)| {
            let target = target.cloned().unwrap_or_default();
            match target {
                RenderTarget::Window(WindowRef::Entity(entity)) => entity == window_entity,
                RenderTarget::Window(WindowRef::Primary) => self
                    .primary_window
                    .single()
                    .is_ok_and(|e| e == window_entity),
                _ => false,
            }
        })
    }

    #[inline]
    pub fn find_by_world(
        &self,
        world_pos: Vec3,
    ) -> Option<CameraQuery<'_>> {
        self.cameras.iter().find(|(_, camera, _, gtf, _)| {
            camera.logical_viewport_rect().is_some_and(|viewport| {
                let Ok(pos) = camera.world_to_viewport(gtf, world_pos) else {
                    return false;
                };
                viewport.contains(pos)
            })
        })
    }

    #[inline]
    pub fn find_camera_from_layers(
        &self,
        layers: &RenderLayers,
    ) -> Option<CameraQuery<'_>> {
        self.cameras
            .iter()
            .find(|(_, _, _, _, layer)| layer.is_some_and(|l| layers.intersects(l)))
    }

    #[inline]
    pub fn to_viewport_pos(
        &self,
        layers: &RenderLayers,
        world_pos: Vec3,
    ) -> Option<Vec2> {
        let (_, camera, _, camera_tf, _) = self.find_camera_from_layers(layers)?;
        camera.world_to_viewport(camera_tf, world_pos).ok()
    }

    #[inline]
    pub fn to_world_by_viewport(
        &self,
        window_entity: Entity,
        viewport_pos: Vec2,
        mascot_pos: Vec3,
    ) -> Option<Vec3> {
        let (_, camera, _, camera_gtf, _) = self.find_camera_from_window(window_entity)?;
        let ray = camera.viewport_to_world(camera_gtf, viewport_pos).ok()?;
        let plane = InfinitePlane3d::new(camera_gtf.back());
        let distance = ray.intersect_plane(mascot_pos, plane)?;
        Some(ray.get_point(distance))
    }

    #[inline]
    pub fn to_world_2d_pos_from_viewport(
        &self,
        window_entity: Entity,
        viewport_pos: Vec2,
    ) -> Option<Vec2> {
        let (_, camera, _, camera_gtf, _) = self.find_camera_from_window(window_entity)?;
        camera.viewport_to_world_2d(camera_gtf, viewport_pos).ok()
    }
}

#[cfg(test)]
mod tests {
    use crate::system_param::cameras::Cameras;
    use crate::tests::{TestResult, test_app};
    use bevy::camera::visibility::RenderLayers;
    use bevy::ecs::system::RunSystemOnce;
    use bevy::prelude::{Camera, Camera3d, Commands, GlobalTransform};

    #[test]
    fn test_all_layers() -> TestResult {
        let mut app = test_app();
        app.world_mut()
            .run_system_once(|mut commands: Commands| {
                commands.spawn((
                    Camera::default(),
                    GlobalTransform::default(),
                    RenderLayers::layer(1),
                    Camera3d::default(),
                ));
                commands.spawn((
                    Camera::default(),
                    GlobalTransform::default(),
                    RenderLayers::layer(2),
                    Camera3d::default(),
                ));
            })
            .expect("Failed to run system");
        app.update();

        let layers = app
            .world_mut()
            .run_system_once(|cameras: Cameras| cameras.all_layers())
            .expect("Failed to run system");
        assert_eq!(layers, RenderLayers::from_layers(&[1, 2]));
        Ok(())
    }
}