use crate::ecs::camera::components::Projection;
use crate::ecs::graphics::resources::Atmosphere;
use crate::ecs::transform::commands::mark_local_transform_dirty;
use crate::ecs::world::World;
use crate::ecs::world::commands::{
spawn_cone_at, spawn_cube_at, spawn_cylinder_at, spawn_plane_at, spawn_sphere_at,
spawn_torus_at,
};
use nalgebra_glm::Vec3;
use super::{Command, ShellState};
impl<C> ShellState<C> {
pub fn register_builtin_commands(&mut self) {
self.registry.register_builtin(Box::new(SpawnCubeCmd));
self.registry.register_builtin(Box::new(SpawnSphereCmd));
self.registry.register_builtin(Box::new(SpawnCylinderCmd));
self.registry.register_builtin(Box::new(SpawnConeCmd));
self.registry.register_builtin(Box::new(SpawnTorusCmd));
self.registry.register_builtin(Box::new(SpawnPlaneCmd));
self.registry.register_builtin(Box::new(GridCmd));
self.registry.register_builtin(Box::new(AtmosphereCmd));
self.registry.register_builtin(Box::new(FovCmd));
self.registry.register_builtin(Box::new(TimescaleCmd));
self.registry.register_builtin(Box::new(FpsCmd));
self.registry.register_builtin(Box::new(QuitCmd));
self.registry.register_builtin(Box::new(PosCmd));
self.registry.register_builtin(Box::new(TeleportCmd));
self.registry.register_builtin(Box::new(UnlitCmd));
self.registry.register_builtin(Box::new(BloomCmd));
self.registry.register_builtin(Box::new(RotateCmd));
self.registry.register_builtin(Box::new(ScaleCmd));
self.registry.register_builtin(Box::new(MoveCmd));
self.registry.register_builtin(Box::new(DeleteCmd));
self.registry.register_builtin(Box::new(InspectCmd));
}
}
struct SpawnCubeCmd;
impl<C> Command<C> for SpawnCubeCmd {
fn name(&self) -> &str {
"spawn_cube"
}
fn description(&self) -> &str {
"Spawn a cube at the specified position"
}
fn usage(&self) -> &str {
"spawn_cube <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 3 {
return "Usage: spawn_cube <x> <y> <z>".to_string();
}
let x: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid x coordinate".to_string(),
};
let y: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid y coordinate".to_string(),
};
let z: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid z coordinate".to_string(),
};
spawn_cube_at(world, Vec3::new(x, y, z));
format!("Spawned cube at ({}, {}, {})", x, y, z)
}
}
struct SpawnSphereCmd;
impl<C> Command<C> for SpawnSphereCmd {
fn name(&self) -> &str {
"spawn_sphere"
}
fn description(&self) -> &str {
"Spawn a sphere at the specified position"
}
fn usage(&self) -> &str {
"spawn_sphere <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 3 {
return "Usage: spawn_sphere <x> <y> <z>".to_string();
}
let x: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid x coordinate".to_string(),
};
let y: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid y coordinate".to_string(),
};
let z: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid z coordinate".to_string(),
};
spawn_sphere_at(world, Vec3::new(x, y, z));
format!("Spawned sphere at ({}, {}, {})", x, y, z)
}
}
struct SpawnCylinderCmd;
impl<C> Command<C> for SpawnCylinderCmd {
fn name(&self) -> &str {
"spawn_cylinder"
}
fn description(&self) -> &str {
"Spawn a cylinder at the specified position"
}
fn usage(&self) -> &str {
"spawn_cylinder <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 3 {
return "Usage: spawn_cylinder <x> <y> <z>".to_string();
}
let x: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid x coordinate".to_string(),
};
let y: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid y coordinate".to_string(),
};
let z: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid z coordinate".to_string(),
};
spawn_cylinder_at(world, Vec3::new(x, y, z));
format!("Spawned cylinder at ({}, {}, {})", x, y, z)
}
}
struct SpawnConeCmd;
impl<C> Command<C> for SpawnConeCmd {
fn name(&self) -> &str {
"spawn_cone"
}
fn description(&self) -> &str {
"Spawn a cone at the specified position"
}
fn usage(&self) -> &str {
"spawn_cone <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 3 {
return "Usage: spawn_cone <x> <y> <z>".to_string();
}
let x: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid x coordinate".to_string(),
};
let y: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid y coordinate".to_string(),
};
let z: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid z coordinate".to_string(),
};
spawn_cone_at(world, Vec3::new(x, y, z));
format!("Spawned cone at ({}, {}, {})", x, y, z)
}
}
struct SpawnTorusCmd;
impl<C> Command<C> for SpawnTorusCmd {
fn name(&self) -> &str {
"spawn_torus"
}
fn description(&self) -> &str {
"Spawn a torus at the specified position"
}
fn usage(&self) -> &str {
"spawn_torus <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 3 {
return "Usage: spawn_torus <x> <y> <z>".to_string();
}
let x: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid x coordinate".to_string(),
};
let y: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid y coordinate".to_string(),
};
let z: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid z coordinate".to_string(),
};
spawn_torus_at(world, Vec3::new(x, y, z));
format!("Spawned torus at ({}, {}, {})", x, y, z)
}
}
struct SpawnPlaneCmd;
impl<C> Command<C> for SpawnPlaneCmd {
fn name(&self) -> &str {
"spawn_plane"
}
fn description(&self) -> &str {
"Spawn a plane at the specified position"
}
fn usage(&self) -> &str {
"spawn_plane <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 3 {
return "Usage: spawn_plane <x> <y> <z>".to_string();
}
let x: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid x coordinate".to_string(),
};
let y: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid y coordinate".to_string(),
};
let z: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid z coordinate".to_string(),
};
spawn_plane_at(world, Vec3::new(x, y, z));
format!("Spawned plane at ({}, {}, {})", x, y, z)
}
}
struct GridCmd;
impl<C> Command<C> for GridCmd {
fn name(&self) -> &str {
"grid"
}
fn description(&self) -> &str {
"Toggle grid visibility (on/off)"
}
fn usage(&self) -> &str {
"grid <on|off>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.is_empty() {
let state = if world.resources.graphics.show_grid {
"on"
} else {
"off"
};
return format!("Grid is currently {}", state);
}
match args[0].to_lowercase().as_str() {
"on" | "true" | "1" => {
world.resources.graphics.show_grid = true;
"Grid enabled".to_string()
}
"off" | "false" | "0" => {
world.resources.graphics.show_grid = false;
"Grid disabled".to_string()
}
_ => "Usage: grid <on|off>".to_string(),
}
}
}
struct AtmosphereCmd;
impl<C> Command<C> for AtmosphereCmd {
fn name(&self) -> &str {
"atmosphere"
}
fn description(&self) -> &str {
"Set the atmosphere type"
}
fn usage(&self) -> &str {
"atmosphere <none|sky|cloudy|space|nebula|sunset>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.is_empty() {
let current = match world.resources.graphics.atmosphere {
Atmosphere::None => "none",
Atmosphere::Sky => "sky",
Atmosphere::CloudySky => "cloudy",
Atmosphere::Space => "space",
Atmosphere::Nebula => "nebula",
Atmosphere::Sunset => "sunset",
_ => "other",
};
return format!("Current atmosphere: {}", current);
}
let atmosphere = match args[0].to_lowercase().as_str() {
"none" => Atmosphere::None,
"sky" => Atmosphere::Sky,
"cloudy" | "cloudysky" => Atmosphere::CloudySky,
"space" => Atmosphere::Space,
"nebula" => Atmosphere::Nebula,
"sunset" => Atmosphere::Sunset,
_ => {
return "Unknown atmosphere type. Usage: atmosphere <none|sky|cloudy|space|nebula|sunset>".to_string();
}
};
world.resources.graphics.atmosphere = atmosphere;
format!("Atmosphere set to {}", args[0].to_lowercase())
}
}
struct FovCmd;
impl<C> Command<C> for FovCmd {
fn name(&self) -> &str {
"fov"
}
fn description(&self) -> &str {
"Get or set camera field of view in degrees"
}
fn usage(&self) -> &str {
"fov [degrees]"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
let Some(camera_entity) = world.resources.active_camera else {
return "No active camera".to_string();
};
let Some(camera) = world.core.get_camera_mut(camera_entity) else {
return "Camera component not found".to_string();
};
let Projection::Perspective(perspective) = &mut camera.projection else {
return "Camera is not using perspective projection".to_string();
};
if args.is_empty() {
let fov_degrees = perspective.y_fov_rad.to_degrees();
return format!("Current FOV: {:.1} degrees", fov_degrees);
}
let fov: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid FOV value".to_string(),
};
if !(1.0..=179.0).contains(&fov) {
return "FOV must be between 1 and 179 degrees".to_string();
}
perspective.y_fov_rad = fov.to_radians();
format!("FOV set to {:.1} degrees", fov)
}
}
struct TimescaleCmd;
impl<C> Command<C> for TimescaleCmd {
fn name(&self) -> &str {
"timescale"
}
fn description(&self) -> &str {
"Get or set time scale (1.0 = normal, 0.5 = slow-mo, 2.0 = fast)"
}
fn usage(&self) -> &str {
"timescale [value]"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.is_empty() {
return format!(
"Current timescale: {:.2}",
world.resources.window.timing.time_speed
);
}
let scale: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid timescale value".to_string(),
};
if !(0.0..=10.0).contains(&scale) {
return "Timescale must be between 0.0 and 10.0".to_string();
}
world.resources.window.timing.time_speed = scale;
format!("Timescale set to {:.2}", scale)
}
}
struct FpsCmd;
impl<C> Command<C> for FpsCmd {
fn name(&self) -> &str {
"fps"
}
fn description(&self) -> &str {
"Show current frames per second"
}
fn usage(&self) -> &str {
"fps"
}
fn execute(&self, _args: &[&str], world: &mut World, _context: &mut C) -> String {
let timing = &world.resources.window.timing;
format!(
"FPS: {:.1} | Frame time: {:.2}ms | Uptime: {:.1}s",
timing.frames_per_second,
timing.delta_time * 1000.0,
timing.uptime_milliseconds as f32 / 1000.0
)
}
}
struct QuitCmd;
impl<C> Command<C> for QuitCmd {
fn name(&self) -> &str {
"quit"
}
fn description(&self) -> &str {
"Exit the application"
}
fn usage(&self) -> &str {
"quit"
}
fn execute(&self, _args: &[&str], world: &mut World, _context: &mut C) -> String {
world.resources.window.should_exit = true;
"Exiting...".to_string()
}
}
struct PosCmd;
impl<C> Command<C> for PosCmd {
fn name(&self) -> &str {
"pos"
}
fn description(&self) -> &str {
"Show current camera position"
}
fn usage(&self) -> &str {
"pos"
}
fn execute(&self, _args: &[&str], world: &mut World, _context: &mut C) -> String {
let Some(camera_entity) = world.resources.active_camera else {
return "No active camera".to_string();
};
let Some(transform) = world.core.get_local_transform(camera_entity) else {
return "Camera transform not found".to_string();
};
format!(
"Position: ({:.2}, {:.2}, {:.2})",
transform.translation.x, transform.translation.y, transform.translation.z
)
}
}
struct TeleportCmd;
impl<C> Command<C> for TeleportCmd {
fn name(&self) -> &str {
"tp"
}
fn description(&self) -> &str {
"Teleport camera to specified position"
}
fn usage(&self) -> &str {
"tp <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 3 {
return "Usage: tp <x> <y> <z>".to_string();
}
let Some(camera_entity) = world.resources.active_camera else {
return "No active camera".to_string();
};
let x: f32 = match args[0].parse() {
Ok(value) => value,
Err(_) => return "Invalid x coordinate".to_string(),
};
let y: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid y coordinate".to_string(),
};
let z: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid z coordinate".to_string(),
};
let Some(transform) = world.core.get_local_transform_mut(camera_entity) else {
return "Camera transform not found".to_string();
};
transform.translation = Vec3::new(x, y, z);
mark_local_transform_dirty(world, camera_entity);
format!("Teleported to ({:.2}, {:.2}, {:.2})", x, y, z)
}
}
struct UnlitCmd;
impl<C> Command<C> for UnlitCmd {
fn name(&self) -> &str {
"unlit"
}
fn description(&self) -> &str {
"Toggle unlit rendering mode"
}
fn usage(&self) -> &str {
"unlit [on|off]"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.is_empty() {
let state = if world.resources.graphics.unlit_mode {
"on"
} else {
"off"
};
return format!("Unlit mode is currently {}", state);
}
match args[0].to_lowercase().as_str() {
"on" | "true" | "1" => {
world.resources.graphics.unlit_mode = true;
"Unlit mode enabled".to_string()
}
"off" | "false" | "0" => {
world.resources.graphics.unlit_mode = false;
"Unlit mode disabled".to_string()
}
_ => "Usage: unlit [on|off]".to_string(),
}
}
}
struct BloomCmd;
impl<C> Command<C> for BloomCmd {
fn name(&self) -> &str {
"bloom"
}
fn description(&self) -> &str {
"Toggle bloom effect, set intensity, or set threshold"
}
fn usage(&self) -> &str {
"bloom [on|off|<intensity>|threshold <value>]"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.is_empty() {
let state = if world.resources.graphics.bloom_enabled {
format!(
"on (intensity: {:.2}, threshold: {:.2})",
world.resources.graphics.bloom_intensity,
world.resources.graphics.bloom_threshold
)
} else {
"off".to_string()
};
return format!("Bloom is currently {}", state);
}
match args[0].to_lowercase().as_str() {
"on" | "true" | "1" => {
world.resources.graphics.bloom_enabled = true;
"Bloom enabled".to_string()
}
"off" | "false" | "0" => {
world.resources.graphics.bloom_enabled = false;
"Bloom disabled".to_string()
}
"threshold" => {
if args.len() < 2 {
return format!(
"Bloom threshold is {:.2}",
world.resources.graphics.bloom_threshold
);
}
if let Ok(threshold) = args[1].parse::<f32>() {
if (0.0..=10.0).contains(&threshold) {
world.resources.graphics.bloom_threshold = threshold;
return format!("Bloom threshold set to {:.2}", threshold);
}
return "Bloom threshold must be between 0.0 and 10.0".to_string();
}
"Usage: bloom threshold <value>".to_string()
}
_ => {
if let Ok(intensity) = args[0].parse::<f32>() {
if (0.0..=5.0).contains(&intensity) {
world.resources.graphics.bloom_enabled = true;
world.resources.graphics.bloom_intensity = intensity;
return format!("Bloom intensity set to {:.2}", intensity);
}
return "Bloom intensity must be between 0.0 and 5.0".to_string();
}
"Usage: bloom [on|off|<intensity>|threshold <value>]".to_string()
}
}
}
}
struct RotateCmd;
impl<C> Command<C> for RotateCmd {
fn name(&self) -> &str {
"rotate"
}
fn description(&self) -> &str {
"Rotate an entity by degrees on each axis"
}
fn usage(&self) -> &str {
"rotate <entity> <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 4 {
return "Usage: rotate <entity> <x> <y> <z>".to_string();
}
let Some(entity) = super::parse_entity(args[0]) else {
return format!("Invalid entity format: {}", args[0]);
};
let x: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid x rotation".to_string(),
};
let y: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid y rotation".to_string(),
};
let z: f32 = match args[3].parse() {
Ok(value) => value,
Err(_) => return "Invalid z rotation".to_string(),
};
let Some(transform) = world.core.get_local_transform_mut(entity) else {
return "Entity not found or has no transform".to_string();
};
let rot_x = nalgebra_glm::quat_angle_axis(x.to_radians(), &Vec3::new(1.0, 0.0, 0.0));
let rot_y = nalgebra_glm::quat_angle_axis(y.to_radians(), &Vec3::new(0.0, 1.0, 0.0));
let rot_z = nalgebra_glm::quat_angle_axis(z.to_radians(), &Vec3::new(0.0, 0.0, 1.0));
let rotation = rot_z * rot_y * rot_x;
transform.rotation = rotation * transform.rotation;
mark_local_transform_dirty(world, entity);
format!("Rotated {} by ({}, {}, {}) degrees", args[0], x, y, z)
}
}
struct ScaleCmd;
impl<C> Command<C> for ScaleCmd {
fn name(&self) -> &str {
"scale"
}
fn description(&self) -> &str {
"Scale an entity uniformly or on each axis"
}
fn usage(&self) -> &str {
"scale <entity> <factor> OR scale <entity> <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() < 2 {
return "Usage: scale <entity> <factor> OR scale <entity> <x> <y> <z>".to_string();
}
let Some(entity) = super::parse_entity(args[0]) else {
return format!("Invalid entity format: {}", args[0]);
};
let scale = if args.len() == 2 {
let factor: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid scale factor".to_string(),
};
Vec3::new(factor, factor, factor)
} else if args.len() == 4 {
let x: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid x scale".to_string(),
};
let y: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid y scale".to_string(),
};
let z: f32 = match args[3].parse() {
Ok(value) => value,
Err(_) => return "Invalid z scale".to_string(),
};
Vec3::new(x, y, z)
} else {
return "Usage: scale <entity> <factor> OR scale <entity> <x> <y> <z>".to_string();
};
let new_scale = {
let Some(transform) = world.core.get_local_transform_mut(entity) else {
return "Entity not found or has no transform".to_string();
};
transform.scale = transform.scale.component_mul(&scale);
transform.scale
};
mark_local_transform_dirty(world, entity);
format!(
"Scaled {} to ({:.2}, {:.2}, {:.2})",
args[0], new_scale.x, new_scale.y, new_scale.z
)
}
}
struct MoveCmd;
impl<C> Command<C> for MoveCmd {
fn name(&self) -> &str {
"move"
}
fn description(&self) -> &str {
"Move an entity by offset or to absolute position"
}
fn usage(&self) -> &str {
"move <entity> <x> <y> <z>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.len() != 4 {
return "Usage: move <entity> <x> <y> <z>".to_string();
}
let Some(entity) = super::parse_entity(args[0]) else {
return format!("Invalid entity format: {}", args[0]);
};
let x: f32 = match args[1].parse() {
Ok(value) => value,
Err(_) => return "Invalid x position".to_string(),
};
let y: f32 = match args[2].parse() {
Ok(value) => value,
Err(_) => return "Invalid y position".to_string(),
};
let z: f32 = match args[3].parse() {
Ok(value) => value,
Err(_) => return "Invalid z position".to_string(),
};
let Some(transform) = world.core.get_local_transform_mut(entity) else {
return "Entity not found or has no transform".to_string();
};
transform.translation = Vec3::new(x, y, z);
mark_local_transform_dirty(world, entity);
format!("Moved {} to ({:.2}, {:.2}, {:.2})", args[0], x, y, z)
}
}
struct DeleteCmd;
impl<C> Command<C> for DeleteCmd {
fn name(&self) -> &str {
"delete"
}
fn description(&self) -> &str {
"Delete an entity from the world"
}
fn usage(&self) -> &str {
"delete <entity>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.is_empty() {
return "Usage: delete <entity>".to_string();
}
let Some(entity) = super::parse_entity(args[0]) else {
return format!("Invalid entity format: {}", args[0]);
};
if world.core.get_local_transform(entity).is_none() {
return "Entity not found".to_string();
}
world.despawn_entities(&[entity]);
format!("Deleted {}", args[0])
}
}
struct InspectCmd;
impl<C> Command<C> for InspectCmd {
fn name(&self) -> &str {
"inspect"
}
fn description(&self) -> &str {
"Show information about an entity"
}
fn usage(&self) -> &str {
"inspect <entity>"
}
fn execute(&self, args: &[&str], world: &mut World, _context: &mut C) -> String {
if args.is_empty() {
return "Usage: inspect <entity>".to_string();
}
let Some(entity) = super::parse_entity(args[0]) else {
return format!("Invalid entity format: {}", args[0]);
};
let mut info = format!("Entity: {}\n", args[0]);
if let Some(name) = world.core.get_name(entity) {
info.push_str(&format!("Name: {}\n", name.0));
}
if let Some(transform) = world.core.get_local_transform(entity) {
info.push_str(&format!(
"Position: ({:.2}, {:.2}, {:.2})\n",
transform.translation.x, transform.translation.y, transform.translation.z
));
info.push_str(&format!(
"Scale: ({:.2}, {:.2}, {:.2})\n",
transform.scale.x, transform.scale.y, transform.scale.z
));
} else {
return "Entity not found".to_string();
}
if let Some(visibility) = world.core.get_visibility(entity) {
info.push_str(&format!("Visible: {}\n", visibility.visible));
}
info
}
}