nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
use super::context::EditorContext;
use crate::prelude::*;

#[derive(Clone, Copy, PartialEq)]
pub enum CameraView {
    Front,
    Back,
    Right,
    Left,
    Top,
    Bottom,
}

pub fn set_camera_view(world: &mut World, view: CameraView) {
    let Some(camera_entity) = world.resources.active_camera else {
        return;
    };

    let Some(pan_orbit) = world.get_pan_orbit_camera_mut(camera_entity) else {
        return;
    };

    let (yaw, pitch) = match view {
        CameraView::Front => (0.0, 0.0),
        CameraView::Back => (std::f32::consts::PI, 0.0),
        CameraView::Right => (std::f32::consts::FRAC_PI_2, 0.0),
        CameraView::Left => (-std::f32::consts::FRAC_PI_2, 0.0),
        CameraView::Top => (0.0, std::f32::consts::FRAC_PI_2 - 0.001),
        CameraView::Bottom => (0.0, -(std::f32::consts::FRAC_PI_2 - 0.001)),
    };

    pan_orbit.target_yaw = yaw;
    pan_orbit.target_pitch = pitch;
}

pub fn focus_on_selection(context: &EditorContext, world: &mut World) {
    if context.selection.is_empty() {
        return;
    }

    let mut center = Vec3::zeros();
    let mut count = 0u32;
    let mut max_radius = 0.0_f32;

    for &entity in context.selection.iter() {
        let Some(global_transform) = world.get_global_transform(entity) else {
            continue;
        };
        let position = nalgebra_glm::vec3(
            global_transform.0.column(3).x,
            global_transform.0.column(3).y,
            global_transform.0.column(3).z,
        );
        center += position;
        count += 1;

        if let Some(bounding) = world.get_bounding_volume(entity) {
            max_radius = max_radius.max(bounding.sphere_radius);
        }
    }

    if count == 0 {
        return;
    }

    center /= count as f32;

    let mut extent = max_radius;
    for &entity in context.selection.iter() {
        if let Some(global_transform) = world.get_global_transform(entity) {
            let position = nalgebra_glm::vec3(
                global_transform.0.column(3).x,
                global_transform.0.column(3).y,
                global_transform.0.column(3).z,
            );
            let distance = nalgebra_glm::length(&(position - center));
            extent = extent.max(distance);
        }
    }

    let Some(camera_entity) = world.resources.active_camera else {
        return;
    };

    let Some(pan_orbit) = world.get_pan_orbit_camera_mut(camera_entity) else {
        return;
    };

    pan_orbit.target_focus = center;
    pan_orbit.target_radius = (extent * 2.5).max(2.0);
}

pub fn toggle_camera_orthographic(world: &mut World) {
    use crate::ecs::camera::components::{OrthographicCamera, PerspectiveCamera};

    let Some(camera_entity) = world.resources.active_camera else {
        return;
    };

    let camera_distance = world
        .get_pan_orbit_camera(camera_entity)
        .map(|orbit| orbit.radius)
        .unwrap_or(10.0);

    let Some(camera) = world.get_camera_mut(camera_entity) else {
        return;
    };

    camera.projection = match &camera.projection {
        Projection::Perspective(persp) => {
            let half_height = camera_distance * (persp.y_fov_rad * 0.5).tan();
            let aspect = persp.aspect_ratio.unwrap_or(16.0 / 9.0);
            let half_width = half_height * aspect;
            Projection::Orthographic(OrthographicCamera {
                x_mag: half_width,
                y_mag: half_height,
                z_near: persp.z_near,
                z_far: persp.z_far.unwrap_or(1000.0),
            })
        }
        Projection::Orthographic(ortho) => Projection::Perspective(PerspectiveCamera {
            y_fov_rad: 45.0_f32.to_radians(),
            aspect_ratio: None,
            z_near: ortho.z_near,
            z_far: Some(ortho.z_far),
        }),
    };
}

pub fn is_mouse_in_selected_viewport(world: &World) -> bool {
    if let Some(viewport_rect) = &world.resources.window.active_viewport_rect {
        let mouse_pos = world.resources.input.mouse.position;
        return viewport_rect.contains(mouse_pos);
    }
    true
}