#[cfg(feature = "physics")]
use crate::runner::{CAMERA_FIRST_PERSON, PLAYER_NAME, lookup_named, register_named};
use crate::runner::{CAMERA_FIXED, CAMERA_FLY, CAMERA_NAME_PREFIX, CAMERA_ORBIT};
use nightshade::ecs::camera::commands::spawn_third_person_camera;
#[cfg(feature = "physics")]
use nightshade::ecs::physics::commands::spawn_first_person_player;
use nightshade::ecs::world::VIEWPORT_SHADING;
use nightshade::prelude::*;
pub fn orbit_camera(world: &mut World, focus: Vec3, radius: f32) -> Entity {
despawn_api_camera(world);
let camera = spawn_pan_orbit_camera(world, focus, radius, 0.6, 0.4, CAMERA_ORBIT.to_string());
activate_camera(world, camera);
camera
}
pub fn fly_camera(world: &mut World, position: Vec3) -> Entity {
despawn_api_camera(world);
let camera = spawn_camera(world, position, CAMERA_FLY.to_string());
activate_camera(world, camera);
camera
}
#[cfg(feature = "physics")]
pub fn first_person(world: &mut World, position: Vec3) -> Entity {
despawn_api_camera(world);
let (player, camera) = spawn_first_person_player(world, position);
world.core.set_name(player, Name(PLAYER_NAME.to_string()));
register_named(world, PLAYER_NAME, player);
world
.core
.set_name(camera, Name(CAMERA_FIRST_PERSON.to_string()));
activate_camera(world, camera);
set_cursor_locked(world, true);
set_cursor_visible(world, false);
player
}
pub fn third_person_camera(world: &mut World, target: Entity, distance: f32) -> Entity {
despawn_api_camera(world);
let camera = spawn_third_person_camera(
world,
target,
distance,
0.0,
0.4,
format!("{CAMERA_NAME_PREFIX}third_person"),
);
activate_camera(world, camera);
camera
}
pub fn set_shading_mode(world: &mut World, mode: ShadingMode) {
let Some(camera) = world.resources.active_camera else {
return;
};
world.core.add_components(camera, VIEWPORT_SHADING);
world.core.set_viewport_shading(
camera,
ViewportShading {
mode,
show_overlays: true,
},
);
}
pub fn fixed_camera(world: &mut World, eye: Vec3, target: Vec3) -> Entity {
despawn_api_camera(world);
let camera = spawn_camera(world, eye, CAMERA_FIXED.to_string());
if let Some(transform) = mutate_local_transform(world, camera) {
transform.rotation = camera_look_rotation(eye, target, 0.0);
}
activate_camera(world, camera);
camera
}
#[cfg(feature = "physics")]
#[inline]
pub fn set_player_speed(world: &mut World, player: Entity, speed: f32) {
if let Some(controller) = world.core.get_character_controller_mut(player) {
controller.max_speed = speed;
}
}
#[cfg(feature = "physics")]
#[inline]
pub fn set_player_jump(world: &mut World, player: Entity, impulse: f32) {
if let Some(controller) = world.core.get_character_controller_mut(player) {
controller.jump_impulse = impulse;
}
}
pub fn look_at(world: &mut World, eye: Vec3, target: Vec3) {
let Some(camera) = world.resources.active_camera else {
return;
};
if let Some(transform) = mutate_local_transform(world, camera) {
transform.translation = eye;
transform.rotation = camera_look_rotation(eye, target, 0.0);
}
}
pub fn set_orbit_focus(world: &mut World, focus: Vec3) {
let Some(camera) = world.resources.active_camera else {
return;
};
if let Some(orbit) = world.core.get_pan_orbit_camera_mut(camera) {
orbit.target_focus = focus;
}
}
pub fn set_orbit_view(world: &mut World, focus: Vec3, radius: f32, yaw: f32, pitch: f32) {
let Some(camera) = world.resources.active_camera else {
return;
};
if let Some(orbit) = world.core.get_pan_orbit_camera_mut(camera) {
orbit.focus = focus;
orbit.target_focus = focus;
orbit.radius = radius;
orbit.target_radius = radius;
orbit.yaw = yaw;
orbit.target_yaw = yaw;
orbit.pitch = pitch;
orbit.target_pitch = pitch;
}
}
pub fn set_orbit_zoom(world: &mut World, enabled: bool) {
let Some(camera) = world.resources.active_camera else {
return;
};
if let Some(orbit) = world.core.get_pan_orbit_camera_mut(camera) {
orbit.sensitivity.zoom = if enabled { 1.0 } else { 0.0 };
}
}
pub fn set_orbit_modifier(world: &mut World, modifier: &str) {
use nightshade::ecs::camera::components::PanOrbitModifier;
let Some(camera) = world.resources.active_camera else {
return;
};
let modifier = match modifier.to_ascii_lowercase().as_str() {
"alt" => Some(PanOrbitModifier::Alt),
"shift" => Some(PanOrbitModifier::Shift),
"control" | "ctrl" => Some(PanOrbitModifier::Control),
_ => None,
};
if let Some(orbit) = world.core.get_pan_orbit_camera_mut(camera) {
orbit.bindings.orbit_modifier = modifier;
}
}
pub fn camera_position(world: &World) -> Vec3 {
world
.resources
.active_camera
.map(|camera| crate::placement::position(world, camera))
.unwrap_or_else(Vec3::zeros)
}
pub fn camera_forward(world: &World) -> Vec3 {
let Some(camera) = world.resources.active_camera else {
return Vec3::new(0.0, 0.0, -1.0);
};
let matrix = crate::placement::world_matrix(world, camera);
nalgebra_glm::vec3(-matrix[(0, 2)], -matrix[(1, 2)], -matrix[(2, 2)]).normalize()
}
pub fn set_field_of_view(world: &mut World, degrees: f32) {
let Some(camera) = world.resources.active_camera else {
return;
};
if let Some(component) = world.core.get_camera_mut(camera)
&& let Projection::Perspective(perspective) = &mut component.projection
{
perspective.y_fov_rad = degrees.to_radians();
}
}
pub fn set_orthographic(world: &mut World, half_height: f32) {
let Some(camera) = world.resources.active_camera else {
return;
};
let aspect = world
.resources
.window
.cached_viewport_size
.map(|(width, height)| width as f32 / height.max(1) as f32)
.unwrap_or(16.0 / 9.0);
if let Some(component) = world.core.get_camera_mut(camera) {
component.projection = Projection::Orthographic(OrthographicCamera {
x_mag: half_height * aspect,
y_mag: half_height,
z_near: 0.01,
z_far: 1000.0,
});
}
}
pub fn set_perspective(world: &mut World, degrees: f32) {
let Some(camera) = world.resources.active_camera else {
return;
};
if let Some(component) = world.core.get_camera_mut(camera) {
component.projection = Projection::Perspective(PerspectiveCamera {
aspect_ratio: None,
y_fov_rad: degrees.to_radians(),
z_near: 0.1,
z_far: Some(1000.0),
});
}
}
fn despawn_api_camera(world: &mut World) {
#[cfg(feature = "physics")]
if let Some(player) = lookup_named(world, PLAYER_NAME) {
despawn_recursive_immediate(world, player);
world.resources.entities.names.remove(PLAYER_NAME);
set_cursor_locked(world, false);
set_cursor_visible(world, true);
}
let api_camera = world.resources.active_camera.filter(|&camera| {
world
.core
.get_name(camera)
.is_some_and(|name| name.0.starts_with(CAMERA_NAME_PREFIX))
});
if let Some(camera) = api_camera {
despawn_recursive_immediate(world, camera);
}
world.resources.active_camera = None;
}
fn activate_camera(world: &mut World, camera: Entity) {
world.resources.active_camera = Some(camera);
#[cfg(feature = "audio")]
{
world.core.add_components(camera, AUDIO_LISTENER);
world.core.set_audio_listener(camera, AudioListener);
}
}