use crate::scene::{Body, Object, Shape};
use nightshade::prelude::{Entity, KeyCode, MouseButton, TextAlignment, Vec3, World, vec3};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Copy, Debug, enum2schema::Schema)]
pub enum Ref {
Entity(#[schema(with = entity_schema)] Entity),
Result(u32),
Existing(u32),
}
#[derive(Serialize, Deserialize, Clone, Debug, enum2schema::Schema)]
pub enum CommandReply {
None,
Entity(#[schema(with = entity_schema)] Entity),
Bool(bool),
Float(f32),
Int(i64),
Text(String),
Vector([f32; 3]),
Entities(#[schema(with = entities_schema)] Vec<Entity>),
Strings(Vec<String>),
Bytes(Vec<u8>),
Json(#[schema(with = any_schema)] enum2schema::serde_json::Value),
Error(String),
}
#[derive(Serialize, Clone, Debug)]
pub struct FieldSpec {
pub name: &'static str,
pub type_name: &'static str,
pub role: &'static str,
}
#[derive(Serialize, Clone, Debug)]
pub struct CommandSpec {
pub name: &'static str,
pub fields: Vec<FieldSpec>,
pub reply: &'static str,
}
pub fn command_manifest_json() -> String {
enum2schema::serde_json::to_string(&command_manifest()).unwrap_or_default()
}
fn entity_schema() -> enum2schema::serde_json::Value {
enum2schema::serde_json::json!({
"type": "object",
"properties": {
"id": { "type": "integer" },
"generation": { "type": "integer" }
},
"required": ["id", "generation"]
})
}
fn entities_schema() -> enum2schema::serde_json::Value {
enum2schema::serde_json::json!({ "type": "array", "items": entity_schema() })
}
fn any_schema() -> enum2schema::serde_json::Value {
enum2schema::serde_json::json!({})
}
pub fn command_schema() -> enum2schema::serde_json::Value {
<Command as enum2schema::Schema>::schema()
}
pub fn command_reply_schema() -> enum2schema::serde_json::Value {
<CommandReply as enum2schema::Schema>::schema()
}
pub fn submit_command(world: &mut World, command: &Command) -> CommandReply {
dispatch(world, command, &[])
}
pub fn submit_commands(world: &mut World, commands: &[Command]) -> Vec<CommandReply> {
let mut produced: Vec<Option<Entity>> = Vec::with_capacity(commands.len());
let mut replies = Vec::with_capacity(commands.len());
for command in commands {
let reply = dispatch(world, command, &produced);
produced.push(match &reply {
CommandReply::Entity(entity) => Some(*entity),
_ => None,
});
replies.push(reply);
}
replies
}
fn resolve(world: &World, reference: Ref, produced: &[Option<Entity>]) -> Option<Entity> {
match reference {
Ref::Entity(entity) => Some(entity),
Ref::Result(index) => produced.get(index as usize).copied().flatten(),
Ref::Existing(id) => world
.core
.entity_locations
.get(id)
.filter(|location| location.allocated)
.map(|location| Entity {
id,
generation: location.generation,
}),
}
}
fn array_to_vec3(values: [f32; 3]) -> Vec3 {
vec3(values[0], values[1], values[2])
}
fn array_to_vec2(values: [f32; 2]) -> nightshade::prelude::Vec2 {
nightshade::prelude::vec2(values[0], values[1])
}
fn spawn_object_command(
world: &mut World,
shape: Shape,
position: Vec3,
scale: Vec3,
color: [f32; 4],
body: Body,
) -> Entity {
crate::scene::spawn_object(
world,
Object {
shape,
position,
scale,
color,
body,
},
)
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, enum2schema::Schema)]
pub struct InstanceWire {
pub position: [f32; 3],
pub rotation: [f32; 4],
pub scale: [f32; 3],
}
fn instance_from_wire(wire: &InstanceWire) -> nightshade::prelude::InstanceTransform {
use nightshade::prelude::nalgebra_glm::Quat;
nightshade::prelude::InstanceTransform::new(
array_to_vec3(wire.position),
Quat::new(
wire.rotation[3],
wire.rotation[0],
wire.rotation[1],
wire.rotation[2],
),
array_to_vec3(wire.scale),
)
}
fn register_material_command(
world: &mut World,
name: &str,
base_color: [f32; 4],
metallic: f32,
roughness: f32,
emissive: [f32; 3],
) -> String {
crate::materials::register_material(
world,
name,
nightshade::ecs::material::components::Material {
base_color,
metallic,
roughness,
emissive_factor: emissive,
..Default::default()
},
)
}
fn spawn_objects_command(
world: &mut World,
shape: Shape,
scale: Vec3,
color: [f32; 4],
body: Body,
positions: &[Vec3],
) -> Vec<Entity> {
crate::scene::spawn_objects(
world,
Object {
shape,
position: vec3(0.0, 0.0, 0.0),
scale,
color,
body,
},
positions,
)
}
#[cfg(feature = "navmesh")]
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, enum2schema::Schema)]
pub struct RecastConfigWire {
pub agent_radius: f32,
pub agent_height: f32,
pub cell_size_fraction: f32,
pub cell_height_fraction: f32,
pub walkable_climb: f32,
pub walkable_slope_angle: f32,
pub min_region_size: u32,
pub merge_region_size: u32,
pub max_simplification_error: f32,
pub edge_max_len_factor: u32,
pub max_vertices_per_polygon: u32,
pub detail_sample_dist: f32,
pub detail_sample_max_error: f32,
}
#[cfg(feature = "navmesh")]
fn bake_navmesh_with_command(world: &mut World, config: RecastConfigWire) {
crate::navigation::bake_navmesh_with(
world,
&nightshade::prelude::RecastNavMeshConfig {
agent_radius: config.agent_radius,
agent_height: config.agent_height,
cell_size_fraction: config.cell_size_fraction,
cell_height_fraction: config.cell_height_fraction,
walkable_climb: config.walkable_climb,
walkable_slope_angle: config.walkable_slope_angle,
min_region_size: config.min_region_size as u16,
merge_region_size: config.merge_region_size as u16,
max_simplification_error: config.max_simplification_error,
edge_max_len_factor: config.edge_max_len_factor as u16,
max_vertices_per_polygon: config.max_vertices_per_polygon as u16,
detail_sample_dist: config.detail_sample_dist,
detail_sample_max_error: config.detail_sample_max_error,
},
);
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, enum2schema::Schema)]
pub struct EmitterWire {
pub position: [f32; 3],
pub color: [f32; 3],
pub spawn_rate: f32,
pub burst_count: u32,
pub lifetime: [f32; 2],
pub size: [f32; 2],
pub gravity: [f32; 3],
pub drag: f32,
pub one_shot: bool,
}
fn emitter_from_wire(wire: &EmitterWire) -> nightshade::prelude::ParticleEmitter {
let mut emitter = nightshade::prelude::ParticleEmitter::firework_explosion(
array_to_vec3(wire.position),
array_to_vec3(wire.color),
wire.burst_count,
);
emitter.spawn_rate = wire.spawn_rate;
emitter.particle_lifetime_min = wire.lifetime[0];
emitter.particle_lifetime_max = wire.lifetime[1];
emitter.size_start = wire.size[0];
emitter.size_end = wire.size[1];
emitter.gravity = array_to_vec3(wire.gravity);
emitter.drag = wire.drag;
emitter.one_shot = wire.one_shot;
emitter.enabled = true;
emitter
}
fn spawn_particle_emitter_command(world: &mut World, emitter: EmitterWire) -> Entity {
crate::effects::spawn_particle_emitter(world, emitter_from_wire(&emitter))
}
fn set_emitter_command(world: &mut World, emitter_entity: Entity, emitter: EmitterWire) {
crate::effects::set_emitter(world, emitter_entity, emitter_from_wire(&emitter));
}
fn update_material_command(
world: &mut World,
name: &str,
base_color: [f32; 4],
metallic: f32,
roughness: f32,
emissive: [f32; 3],
) {
crate::materials::update_material(
world,
name,
nightshade::ecs::material::components::Material {
base_color,
metallic,
roughness,
emissive_factor: emissive,
..Default::default()
},
);
}
fn set_material_variant_command(world: &mut World, variant: &str) -> usize {
let variant = if variant.is_empty() {
None
} else {
Some(variant)
};
crate::materials::set_material_variant(world, variant)
}
fn set_fog_command(world: &mut World, enabled: bool, color: [f32; 3], start: f32, end: f32) {
let fog = if enabled {
Some(nightshade::ecs::graphics::resources::Fog { color, start, end })
} else {
None
};
crate::environment::set_fog(world, fog);
}
fn set_depth_of_field_command(
world: &mut World,
enabled: bool,
focus_distance: f32,
focus_range: f32,
max_blur_radius: f32,
bokeh_threshold: f32,
) {
crate::environment::set_depth_of_field(
world,
nightshade::ecs::graphics::resources::DepthOfField {
enabled,
focus_distance,
focus_range,
max_blur_radius,
bokeh_threshold,
..Default::default()
},
);
}
fn panel_data_grid_command(
world: &mut World,
panel: Entity,
headers: &[&str],
widths: &[f32],
pool_size: usize,
) -> Entity {
let columns: Vec<(&str, f32)> = headers
.iter()
.zip(widths.iter())
.map(|(header, width)| (*header, *width))
.collect();
crate::ui::panel_data_grid(world, panel, &columns, pool_size)
}
fn panel_selectable_command(
world: &mut World,
panel: Entity,
text: &str,
group: u32,
grouped: bool,
) -> Entity {
crate::ui::panel_selectable(world, panel, text, grouped.then_some(group))
}
fn panel_splitter_command(
world: &mut World,
panel: Entity,
horizontal: bool,
ratio: f32,
) -> Entity {
let direction = if horizontal {
nightshade::prelude::SplitDirection::Horizontal
} else {
nightshade::prelude::SplitDirection::Vertical
};
crate::ui::panel_splitter(world, panel, direction, ratio)
}
fn screenshot_command(world: &mut World, path: &str) {
crate::environment::screenshot(world, std::path::PathBuf::from(path));
}
fn easing_from_name(name: &str) -> nightshade::prelude::EasingFunction {
use nightshade::prelude::EasingFunction::*;
match name.to_ascii_lowercase().as_str() {
"quadin" => QuadIn,
"quadout" => QuadOut,
"quadinout" => QuadInOut,
"cubicin" => CubicIn,
"cubicout" => CubicOut,
"cubicinout" => CubicInOut,
"quartin" => QuartIn,
"quartout" => QuartOut,
"quartinout" => QuartInOut,
"quintin" => QuintIn,
"quintout" => QuintOut,
"quintinout" => QuintInOut,
"sinein" => SineIn,
"sineout" => SineOut,
"sineinout" => SineInOut,
"expoin" => ExpoIn,
"expoout" => ExpoOut,
"expoinout" => ExpoInOut,
_ => Linear,
}
}
fn animate_position_command(
world: &mut World,
entity: Entity,
to: Vec3,
seconds: f32,
easing: &str,
) {
crate::animate::animate_position(world, entity, to, seconds, easing_from_name(easing));
}
fn animate_scale_command(world: &mut World, entity: Entity, to: Vec3, seconds: f32, easing: &str) {
crate::animate::animate_scale(world, entity, to, seconds, easing_from_name(easing));
}
fn animate_color_command(
world: &mut World,
entity: Entity,
to: [f32; 4],
seconds: f32,
easing: &str,
) {
crate::animate::animate_color(world, entity, to, seconds, easing_from_name(easing));
}
fn set_shading_mode_command(world: &mut World, mode: &str) {
use nightshade::prelude::ShadingMode;
let mode = match mode.to_ascii_lowercase().as_str() {
"wireframe" => ShadingMode::Wireframe,
"flat" => ShadingMode::Flat,
"rendered" => ShadingMode::Rendered,
_ => ShadingMode::Solid,
};
crate::camera::set_shading_mode(world, mode);
}
#[cfg(feature = "physics")]
#[derive(Serialize)]
struct RaycastResultWire {
entity_id: u32,
distance: f32,
point: [f32; 3],
normal: [f32; 3],
}
#[cfg(feature = "physics")]
fn raycast_command(
world: &mut World,
origin: Vec3,
direction: Vec3,
max_distance: f32,
) -> Option<RaycastResultWire> {
crate::physics::raycast(world, origin, direction, max_distance).map(|hit| RaycastResultWire {
entity_id: hit.entity.id,
distance: hit.distance,
point: [hit.point.x, hit.point.y, hit.point.z],
normal: [hit.normal.x, hit.normal.y, hit.normal.z],
})
}
#[cfg(feature = "physics")]
fn attach_fixed_command(world: &mut World, parent: Entity, child: Entity) -> bool {
crate::physics::attach_fixed(world, parent, child).is_some()
}
#[cfg(feature = "physics")]
fn attach_hinge_command(world: &mut World, parent: Entity, child: Entity, axis: &str) -> bool {
use nightshade::ecs::physics::joints::JointAxisDirection;
let axis = match axis.to_ascii_lowercase().as_str() {
"y" => JointAxisDirection::Y,
"z" => JointAxisDirection::Z,
_ => JointAxisDirection::X,
};
crate::physics::attach_hinge(world, parent, child, axis).is_some()
}
#[cfg(feature = "physics")]
fn attach_spring_command(
world: &mut World,
parent: Entity,
child: Entity,
rest_length: f32,
stiffness: f32,
damping: f32,
) -> bool {
crate::physics::attach_spring(world, parent, child, rest_length, stiffness, damping).is_some()
}
#[cfg(feature = "physics")]
fn attach_rope_command(
world: &mut World,
parent: Entity,
child: Entity,
max_distance: f32,
) -> bool {
crate::physics::attach_rope(world, parent, child, max_distance).is_some()
}
#[cfg(feature = "picking")]
#[derive(Serialize)]
struct SurfacePickWire {
world_position: [f32; 3],
world_normal: [f32; 3],
depth: f32,
entity_id: Option<u32>,
}
#[cfg(feature = "picking")]
fn take_surface_pick_command(world: &mut World) -> Option<SurfacePickWire> {
crate::picking::take_surface_pick(world).map(|result| SurfacePickWire {
world_position: [
result.world_position.x,
result.world_position.y,
result.world_position.z,
],
world_normal: [
result.world_normal.x,
result.world_normal.y,
result.world_normal.z,
],
depth: result.depth,
entity_id: result.entity_id,
})
}
fn save_scene_command(world: &mut World, name: &str) -> Vec<u8> {
crate::serialize::save_scene(world, name).unwrap_or_default()
}
fn load_scene_command(world: &mut World, bytes: &[u8]) -> Vec<Entity> {
crate::serialize::load_scene(world, bytes).unwrap_or_default()
}
fn key_from_name(name: &str) -> Option<KeyCode> {
let lower = name.to_ascii_lowercase();
Some(match lower.as_str() {
"a" => KeyCode::KeyA,
"b" => KeyCode::KeyB,
"c" => KeyCode::KeyC,
"d" => KeyCode::KeyD,
"e" => KeyCode::KeyE,
"f" => KeyCode::KeyF,
"g" => KeyCode::KeyG,
"h" => KeyCode::KeyH,
"i" => KeyCode::KeyI,
"j" => KeyCode::KeyJ,
"k" => KeyCode::KeyK,
"l" => KeyCode::KeyL,
"m" => KeyCode::KeyM,
"n" => KeyCode::KeyN,
"o" => KeyCode::KeyO,
"p" => KeyCode::KeyP,
"q" => KeyCode::KeyQ,
"r" => KeyCode::KeyR,
"s" => KeyCode::KeyS,
"t" => KeyCode::KeyT,
"u" => KeyCode::KeyU,
"v" => KeyCode::KeyV,
"w" => KeyCode::KeyW,
"x" => KeyCode::KeyX,
"y" => KeyCode::KeyY,
"z" => KeyCode::KeyZ,
"0" => KeyCode::Digit0,
"1" => KeyCode::Digit1,
"2" => KeyCode::Digit2,
"3" => KeyCode::Digit3,
"4" => KeyCode::Digit4,
"5" => KeyCode::Digit5,
"6" => KeyCode::Digit6,
"7" => KeyCode::Digit7,
"8" => KeyCode::Digit8,
"9" => KeyCode::Digit9,
"space" => KeyCode::Space,
"enter" | "return" => KeyCode::Enter,
"escape" | "esc" => KeyCode::Escape,
"tab" => KeyCode::Tab,
"backspace" => KeyCode::Backspace,
"delete" => KeyCode::Delete,
"left" => KeyCode::ArrowLeft,
"right" => KeyCode::ArrowRight,
"up" => KeyCode::ArrowUp,
"down" => KeyCode::ArrowDown,
"shift" | "lshift" => KeyCode::ShiftLeft,
"rshift" => KeyCode::ShiftRight,
"ctrl" | "control" | "lctrl" => KeyCode::ControlLeft,
"rctrl" => KeyCode::ControlRight,
"alt" | "lalt" => KeyCode::AltLeft,
"ralt" => KeyCode::AltRight,
_ => return None,
})
}
fn mouse_button_from_index(index: u8) -> MouseButton {
match index {
1 => MouseButton::Middle,
2 => MouseButton::Right,
_ => MouseButton::Left,
}
}
fn key_down_command(world: &World, key: &str) -> bool {
key_from_name(key)
.map(|key| crate::input::key_down(world, key))
.unwrap_or(false)
}
fn key_pressed_command(world: &World, key: &str) -> bool {
key_from_name(key)
.map(|key| crate::input::key_pressed(world, key))
.unwrap_or(false)
}
fn mouse_down_command(world: &World, button: u8) -> bool {
crate::input::mouse_down(world, mouse_button_from_index(button))
}
fn mouse_clicked_command(world: &World, button: u8) -> bool {
crate::input::mouse_clicked(world, mouse_button_from_index(button))
}
macro_rules! bind_argument {
($field:ident, entity, $produced:ident, $world:ident) => {
let $field = match resolve($world, *$field, $produced) {
Some(entity) => entity,
None => {
return CommandReply::Error(
concat!(stringify!($field), ": unresolved entity reference").to_string(),
);
}
};
};
($field:ident, opt_entity, $produced:ident, $world:ident) => {
let $field = match $field {
Some(reference) => match resolve($world, *reference, $produced) {
Some(entity) => Some(entity),
None => {
return CommandReply::Error(
concat!(stringify!($field), ": unresolved entity reference").to_string(),
);
}
},
None => None,
};
};
($field:ident, vec3, $produced:ident, $world:ident) => {
let $field = array_to_vec3(*$field);
};
($field:ident, vec2, $produced:ident, $world:ident) => {
let $field = array_to_vec2(*$field);
};
($field:ident, copy, $produced:ident, $world:ident) => {
let $field = *$field;
};
($field:ident, owned, $produced:ident, $world:ident) => {
let $field = $field.clone();
};
($field:ident, text, $produced:ident, $world:ident) => {
let $field = $field.as_str();
};
($field:ident, bytes, $produced:ident, $world:ident) => {
let $field = $field.as_slice();
};
($field:ident, strs, $produced:ident, $world:ident) => {
let $field: Vec<&str> = $field.iter().map(|value| value.as_str()).collect();
let $field = $field.as_slice();
};
($field:ident, vec3_list, $produced:ident, $world:ident) => {
let $field: Vec<Vec3> = $field.iter().map(|value| array_to_vec3(*value)).collect();
let $field = $field.as_slice();
};
($field:ident, floats, $produced:ident, $world:ident) => {
let $field = $field.as_slice();
};
($field:ident, indices, $produced:ident, $world:ident) => {
let $field: Vec<usize> = $field.iter().map(|value| *value as usize).collect();
let $field = $field.as_slice();
};
($field:ident, opt_vec3, $produced:ident, $world:ident) => {
let $field = (*$field).map(array_to_vec3);
};
($field:ident, transforms, $produced:ident, $world:ident) => {
let $field: Vec<nightshade::prelude::InstanceTransform> =
$field.iter().map(instance_from_wire).collect();
};
($field:ident, refs, $produced:ident, $world:ident) => {
let mut resolved = Vec::with_capacity($field.len());
for reference in $field.iter() {
match resolve($world, *reference, $produced) {
Some(entity) => resolved.push(entity),
None => {
return CommandReply::Error(
concat!(stringify!($field), ": unresolved entity reference").to_string(),
);
}
}
}
let $field = resolved.as_slice();
};
}
macro_rules! wrap_reply {
(none, $call:expr) => {{
$call;
CommandReply::None
}};
(entity, $call:expr) => {
CommandReply::Entity($call)
};
(opt_entity, $call:expr) => {
match $call {
Some(entity) => CommandReply::Entity(entity),
None => CommandReply::None,
}
};
(bool, $call:expr) => {
CommandReply::Bool($call)
};
(float, $call:expr) => {
CommandReply::Float($call)
};
(vector, $call:expr) => {{
let value = $call;
CommandReply::Vector([value.x, value.y, value.z])
}};
(opt_vector, $call:expr) => {
match $call {
Some(value) => CommandReply::Vector([value.x, value.y, value.z]),
None => CommandReply::None,
}
};
(entities, $call:expr) => {
CommandReply::Entities($call)
};
(strings, $call:expr) => {
CommandReply::Strings($call)
};
(int, $call:expr) => {
CommandReply::Int($call as i64)
};
(text, $call:expr) => {
CommandReply::Text($call)
};
(bytes, $call:expr) => {
CommandReply::Bytes($call)
};
(json, $call:expr) => {
CommandReply::Json(
enum2schema::serde_json::to_value($call)
.unwrap_or(enum2schema::serde_json::Value::Null),
)
};
}
macro_rules! commands {
(
$(
$(#[$meta:meta])*
$variant:ident { $( $field:ident : $field_type:ty [$role:ident] ),* $(,)? }
=> $func:path , $reply:ident ;
)*
) => {
#[derive(Serialize, Deserialize, Clone, Debug, enum2schema::Schema)]
pub enum Command {
$(
$(#[$meta])*
$variant { $( $field : $field_type ),* },
)*
}
impl Command {
pub fn name(&self) -> &'static str {
match self {
$(
$(#[$meta])*
Command::$variant { .. } => stringify!($variant),
)*
}
}
}
fn dispatch(
world: &mut World,
command: &Command,
produced: &[Option<Entity>],
) -> CommandReply {
match command {
$(
$(#[$meta])*
Command::$variant { $( $field ),* } => {
$( bind_argument!($field, $role, produced, world); )*
wrap_reply!($reply, $func(world $(, $field)*))
}
)*
}
}
pub fn command_manifest() -> Vec<CommandSpec> {
let mut specs = Vec::new();
$(
$(#[$meta])*
specs.extend([CommandSpec {
name: stringify!($variant),
fields: vec![
$( FieldSpec {
name: stringify!($field),
type_name: stringify!($field_type),
role: stringify!($role),
} ),*
],
reply: stringify!($reply),
}]);
)*
specs
}
};
}
commands! {
SpawnCube { position: [f32; 3] [vec3] } => crate::scene::spawn_cube, entity;
SpawnSphere { position: [f32; 3] [vec3] } => crate::scene::spawn_sphere, entity;
SpawnCylinder { position: [f32; 3] [vec3] } => crate::scene::spawn_cylinder, entity;
SpawnCone { position: [f32; 3] [vec3] } => crate::scene::spawn_cone, entity;
SpawnPlane { position: [f32; 3] [vec3] } => crate::scene::spawn_plane, entity;
SpawnTorus { position: [f32; 3] [vec3] } => crate::scene::spawn_torus, entity;
SpawnFloor { half_extent: f32 [copy] } => crate::scene::spawn_floor, entity;
SpawnGroup { position: [f32; 3] [vec3] } => crate::scene::spawn_group, entity;
SpawnModel { glb: Vec<u8> [bytes], position: [f32; 3] [vec3] } => crate::scene::spawn_model, entity;
SpawnObject { shape: Shape [copy], position: [f32; 3] [vec3], scale: [f32; 3] [vec3], color: [f32; 4] [copy], body: Body [copy] } => spawn_object_command, entity;
SetColor { entity: Ref [entity], color: [f32; 4] [copy] } => crate::appearance::set_color, none;
SetMetallicRoughness { entity: Ref [entity], metallic: f32 [copy], roughness: f32 [copy] } => crate::appearance::set_metallic_roughness, none;
SetEmissive { entity: Ref [entity], color: [f32; 3] [copy], strength: f32 [copy] } => crate::appearance::set_emissive, none;
SetUnlit { entity: Ref [entity], unlit: bool [copy] } => crate::appearance::set_unlit, none;
SetTexture { entity: Ref [entity], texture: String [text] } => crate::appearance::set_texture, none;
SetTextureTiling { entity: Ref [entity], repeats: f32 [copy] } => crate::appearance::set_texture_tiling, none;
SetNormalTexture { entity: Ref [entity], texture: String [text] } => crate::appearance::set_normal_texture, none;
SetMetallicRoughnessTexture { entity: Ref [entity], texture: String [text] } => crate::appearance::set_metallic_roughness_texture, none;
SetEmissiveTexture { entity: Ref [entity], texture: String [text] } => crate::appearance::set_emissive_texture, none;
SetOcclusionTexture { entity: Ref [entity], texture: String [text] } => crate::appearance::set_occlusion_texture, none;
SetPosition { entity: Ref [entity], position: [f32; 3] [vec3] } => crate::placement::set_position, none;
SetScale { entity: Ref [entity], scale: [f32; 3] [vec3] } => crate::placement::set_scale, none;
SetRotation { entity: Ref [entity], axis: [f32; 3] [vec3], radians: f32 [copy] } => crate::placement::set_rotation, none;
Rotate { entity: Ref [entity], axis: [f32; 3] [vec3], radians: f32 [copy] } => crate::placement::rotate, none;
Position { entity: Ref [entity] } => crate::placement::position, vector;
SetParent { child: Ref [entity], parent: Option<Ref> [opt_entity] } => crate::scene::set_parent, none;
SetVisible { entity: Ref [entity], visible: bool [copy] } => crate::scene::set_visible, none;
Despawn { entity: Ref [entity] } => crate::scene::despawn, none;
Tag { entity: Ref [entity], label: String [text] } => crate::groups::tag, none;
Untag { entity: Ref [entity], label: String [text] } => crate::groups::untag, none;
HasTag { entity: Ref [entity], label: String [text] } => crate::groups::has_tag, bool;
QueryTagged { label: String [text] } => crate::groups::tagged, entities;
PointLight { position: [f32; 3] [vec3], color: [f32; 3] [copy], intensity: f32 [copy] } => crate::lighting::point_light, entity;
SpotLight { position: [f32; 3] [vec3], target: [f32; 3] [vec3], color: [f32; 3] [copy], intensity: f32 [copy] } => crate::lighting::spot_light, entity;
SetSun { color: [f32; 3] [copy], intensity: f32 [copy] } => crate::lighting::set_sun, none;
SetBackground { background: crate::environment::Background [owned] } => crate::environment::set_background, none;
ShowGrid { enabled: bool [copy] } => crate::environment::show_grid, none;
SetAmbient { color: [f32; 4] [copy] } => crate::environment::set_ambient, none;
SetBloom { enabled: bool [copy] } => crate::environment::set_bloom, none;
SetBloomIntensity { intensity: f32 [copy] } => crate::environment::set_bloom_intensity, none;
SetSsao { enabled: bool [copy] } => crate::environment::set_ssao, none;
SetSsr { enabled: bool [copy] } => crate::environment::set_ssr, none;
SetSsgi { enabled: bool [copy] } => crate::environment::set_ssgi, none;
SetFxaa { enabled: bool [copy] } => crate::environment::set_fxaa, none;
SetExposure { exposure: f32 [copy] } => crate::environment::set_exposure, none;
SetColorGrading { saturation: f32 [copy], contrast: f32 [copy], brightness: f32 [copy] } => crate::environment::set_color_grading, none;
SetTimeOfDay { hour: f32 [copy] } => crate::environment::set_time_of_day, none;
SetTitle { title: String [text] } => crate::environment::set_title, none;
EmitFire { position: [f32; 3] [vec3] } => crate::effects::emit_fire, entity;
EmitSmoke { position: [f32; 3] [vec3] } => crate::effects::emit_smoke, entity;
EmitBurst { position: [f32; 3] [vec3], color: [f32; 4] [copy], count: u32 [copy] } => crate::effects::emit_burst, entity;
DrawCube { position: [f32; 3] [vec3], scale: [f32; 3] [vec3], color: [f32; 4] [copy] } => crate::draw::draw_cube, none;
DrawSphere { position: [f32; 3] [vec3], radius: f32 [copy], color: [f32; 4] [copy] } => crate::draw::draw_sphere, none;
DrawCylinder { position: [f32; 3] [vec3], scale: [f32; 3] [vec3], color: [f32; 4] [copy] } => crate::draw::draw_cylinder, none;
DrawCone { position: [f32; 3] [vec3], scale: [f32; 3] [vec3], color: [f32; 4] [copy] } => crate::draw::draw_cone, none;
DrawTorus { position: [f32; 3] [vec3], scale: [f32; 3] [vec3], color: [f32; 4] [copy] } => crate::draw::draw_torus, none;
DrawLine { start: [f32; 3] [vec3], end: [f32; 3] [vec3], color: [f32; 4] [copy] } => crate::draw::draw_line, none;
DrawText3d { text: String [text], position: [f32; 3] [vec3] } => crate::draw::draw_text_3d, none;
SpawnLabel { text: String [text], position: [f32; 3] [vec3] } => crate::text::spawn_label, entity;
SpawnText { text: String [text], anchor: crate::text::ScreenAnchor [copy] } => crate::text::spawn_text, entity;
SetText { entity: Ref [entity], text: String [text] } => crate::text::set_text, none;
SetTextColor { entity: Ref [entity], color: [f32; 4] [copy] } => crate::text::set_text_color, none;
SetTextSize { entity: Ref [entity], size: f32 [copy] } => crate::text::set_text_size, none;
SpawnPanel { anchor: crate::text::ScreenAnchor [copy], width: f32 [copy], height: f32 [copy] } => crate::ui::spawn_panel, entity;
PanelLabel { panel: Ref [entity], text: String [text] } => crate::ui::panel_label, entity;
PanelButton { panel: Ref [entity], text: String [text] } => crate::ui::panel_button, entity;
ButtonClicked { button: Ref [entity] } => crate::ui::button_clicked, bool;
ButtonHovered { button: Ref [entity] } => crate::ui::button_hovered, bool;
DespawnPanel { panel: Ref [entity] } => crate::ui::despawn_panel, none;
PanelRow { panel: Ref [entity], height: f32 [copy] } => crate::ui::panel_row, entity;
PanelGrid { panel: Ref [entity], columns: usize [copy], row_height: f32 [copy], height: f32 [copy] } => crate::ui::panel_grid, entity;
PanelScroll { panel: Ref [entity], height: f32 [copy] } => crate::ui::panel_scroll, entity;
SetScrollOffset { scroll_area: Ref [entity], offset: f32 [copy] } => crate::ui::set_scroll_offset, none;
SetFocusOrder { entity: Ref [entity], order: i32 [copy] } => crate::ui::set_focus_order, none;
FocusWidget { entity: Ref [entity] } => crate::ui::focus_widget, none;
SpawnPanelAt { anchor: crate::text::ScreenAnchor [copy], offset: [f32; 2] [vec2], size: [f32; 2] [vec2], color: [f32; 4] [copy] } => crate::ui::spawn_panel_at, entity;
PanelText { parent: Ref [entity], text: String [text], rect: [f32; 4] [copy], font_size: f32 [copy], color: [f32; 4] [copy], align: TextAlignment [copy] } => crate::ui::panel_text, entity;
PanelBox { parent: Ref [entity], offset: [f32; 2] [vec2], size: [f32; 2] [vec2], color: [f32; 4] [copy] } => crate::ui::panel_box, entity;
PanelButtonAt { parent: Ref [entity], label: String [text], offset: [f32; 2] [vec2], size: [f32; 2] [vec2] } => crate::ui::panel_button_at, entity;
SetPanelRect { node: Ref [entity], offset: [f32; 2] [vec2], size: [f32; 2] [vec2] } => crate::ui::set_panel_rect, none;
SetPanelColor { node: Ref [entity], color: [f32; 4] [copy] } => crate::ui::set_panel_color, none;
SetPanelText { label: Ref [entity], text: String [text] } => crate::ui::set_panel_text, none;
SetPanelTextColor { label: Ref [entity], color: [f32; 4] [copy] } => crate::ui::set_panel_text_color, none;
SetPanelSelected { button: Ref [entity], selected: bool [copy], accent: [f32; 4] [copy] } => crate::ui::set_panel_selected, none;
SetPanelVisible { node: Ref [entity], visible: bool [copy] } => crate::ui::set_panel_visible, none;
PlayAnimation { entity: Ref [entity], clip: usize [copy] } => crate::scene::play_animation, none;
PlayAnimationNamed { entity: Ref [entity], name: String [text] } => crate::scene::play_animation_named, bool;
SetAnimationLooping { entity: Ref [entity], looping: bool [copy] } => crate::scene::set_animation_looping, none;
SetAnimationSpeed { entity: Ref [entity], speed: f32 [copy] } => crate::scene::set_animation_speed, none;
BlendToAnimation { entity: Ref [entity], clip: usize [copy], seconds: f32 [copy] } => crate::scene::blend_to_animation, none;
PauseAnimation { entity: Ref [entity] } => crate::scene::pause_animation, none;
ResumeAnimation { entity: Ref [entity] } => crate::scene::resume_animation, none;
StopAnimation { entity: Ref [entity] } => crate::scene::stop_animation, none;
AnimationClips { entity: Ref [entity] } => crate::scene::animation_clips, strings;
AddAnimationEvent { entity: Ref [entity], clip_index: usize [copy], time: f32 [copy], name: String [text] } => crate::scene::add_animation_event, bool;
AddAnimationEventNamed { entity: Ref [entity], clip_name: String [text], time: f32 [copy], name: String [text] } => crate::scene::add_animation_event_named, bool;
SetAnimationLayerWeight { entity: Ref [entity], layer_index: usize [copy], weight: f32 [copy] } => crate::scene::set_animation_layer_weight, none;
ClearAnimationLayers { entity: Ref [entity] } => crate::scene::clear_animation_layers, none;
AimAt { bone: Ref [entity], target: [f32; 3] [vec3], forward: [f32; 3] [vec3] } => crate::animate::aim_at, none;
OrbitCamera { focus: [f32; 3] [vec3], radius: f32 [copy] } => crate::camera::orbit_camera, entity;
FlyCamera { position: [f32; 3] [vec3] } => crate::camera::fly_camera, entity;
FixedCamera { eye: [f32; 3] [vec3], target: [f32; 3] [vec3] } => crate::camera::fixed_camera, entity;
LookAt { eye: [f32; 3] [vec3], target: [f32; 3] [vec3] } => crate::camera::look_at, none;
SetOrbitFocus { focus: [f32; 3] [vec3] } => crate::camera::set_orbit_focus, none;
SetOrbitView { focus: [f32; 3] [vec3], radius: f32 [copy], yaw: f32 [copy], pitch: f32 [copy] } => crate::camera::set_orbit_view, none;
SetOrbitZoom { enabled: bool [copy] } => crate::camera::set_orbit_zoom, none;
SetFieldOfView { degrees: f32 [copy] } => crate::camera::set_field_of_view, none;
SetOrthographic { half_height: f32 [copy] } => crate::camera::set_orthographic, none;
SetPerspective { degrees: f32 [copy] } => crate::camera::set_perspective, none;
CameraPosition {} => crate::camera::camera_position, vector;
CameraForward {} => crate::camera::camera_forward, vector;
#[cfg(feature = "physics")]
FirstPerson { position: [f32; 3] [vec3] } => crate::camera::first_person, entity;
DeltaTime {} => crate::input::delta_time, float;
ElapsedSeconds {} => crate::input::elapsed_seconds, float;
KeyDown { key: String [text] } => key_down_command, bool;
KeyPressed { key: String [text] } => key_pressed_command, bool;
MouseDown { button: u8 [copy] } => mouse_down_command, bool;
MouseClicked { button: u8 [copy] } => mouse_clicked_command, bool;
Wasd {} => crate::input::wasd, vector;
PointerOverUi {} => crate::input::pointer_over_ui, bool;
MouseScroll {} => crate::input::mouse_scroll, float;
#[cfg(feature = "physics")]
Push { entity: Ref [entity], impulse: [f32; 3] [vec3] } => crate::physics::push, none;
#[cfg(feature = "physics")]
SetVelocity { entity: Ref [entity], velocity: [f32; 3] [vec3] } => crate::physics::set_velocity, none;
#[cfg(feature = "physics")]
ApplyForce { entity: Ref [entity], force: [f32; 3] [vec3] } => crate::physics::apply_force, none;
#[cfg(feature = "physics")]
ApplyTorque { entity: Ref [entity], torque: [f32; 3] [vec3] } => crate::physics::apply_torque, none;
#[cfg(feature = "physics")]
SetAngularVelocity { entity: Ref [entity], velocity: [f32; 3] [vec3] } => crate::physics::set_angular_velocity, none;
#[cfg(feature = "physics")]
Velocity { entity: Ref [entity] } => crate::physics::velocity, opt_vector;
#[cfg(feature = "physics")]
AngularVelocity { entity: Ref [entity] } => crate::physics::angular_velocity, opt_vector;
#[cfg(feature = "physics")]
MakeSensor { entity: Ref [entity] } => crate::physics::make_sensor, none;
#[cfg(feature = "physics")]
OverlapSphere { center: [f32; 3] [vec3], radius: f32 [copy] } => crate::physics::overlap_sphere, entities;
#[cfg(feature = "physics")]
SetCollisionGroups { entity: Ref [entity], membership: u32 [copy], filter: u32 [copy] } => crate::physics::set_collision_groups, none;
#[cfg(feature = "physics")]
SetFriction { entity: Ref [entity], friction: f32 [copy] } => crate::physics::set_friction, none;
#[cfg(feature = "physics")]
SetRestitution { entity: Ref [entity], restitution: f32 [copy] } => crate::physics::set_restitution, none;
#[cfg(feature = "physics")]
SetLinearDamping { entity: Ref [entity], damping: f32 [copy] } => crate::physics::set_linear_damping, none;
#[cfg(feature = "physics")]
SetAngularDamping { entity: Ref [entity], damping: f32 [copy] } => crate::physics::set_angular_damping, none;
#[cfg(feature = "physics")]
SetMass { entity: Ref [entity], mass: f32 [copy] } => crate::physics::set_mass, none;
#[cfg(feature = "physics")]
SetGravityScale { entity: Ref [entity], scale: f32 [copy] } => crate::physics::set_gravity_scale, none;
#[cfg(feature = "navmesh")]
BakeNavmesh {} => crate::navigation::bake_navmesh, none;
#[cfg(feature = "navmesh")]
SpawnWalker { position: [f32; 3] [vec3] } => crate::navigation::spawn_walker, entity;
#[cfg(feature = "navmesh")]
WalkTo { agent: Ref [entity], destination: [f32; 3] [vec3] } => crate::navigation::walk_to, none;
#[cfg(feature = "navmesh")]
SetWalkSpeed { agent: Ref [entity], speed: f32 [copy] } => crate::navigation::set_walk_speed, none;
#[cfg(feature = "navmesh")]
StopWalking { agent: Ref [entity] } => crate::navigation::stop_walking, none;
#[cfg(feature = "picking")]
ClickedEntity {} => crate::picking::clicked_entity, opt_entity;
#[cfg(feature = "picking")]
EntityUnderCursor {} => crate::picking::entity_under_cursor, opt_entity;
#[cfg(feature = "picking")]
CursorOnGround {} => crate::picking::cursor_on_ground, opt_vector;
#[cfg(feature = "picking")]
SpawnWorldPanel { position: [f32; 3] [vec3], width: f32 [copy], height: f32 [copy], color: [f32; 4] [copy] } => crate::world_ui::spawn_world_panel, entity;
#[cfg(feature = "picking")]
WorldPanelButton { panel: Ref [entity], x: f32 [copy], y: f32 [copy], width: f32 [copy], height: f32 [copy], color: [f32; 4] [copy] } => crate::world_ui::world_panel_button, entity;
#[cfg(feature = "picking")]
WorldPanelLabel { panel: Ref [entity], text: String [text], x: f32 [copy], y: f32 [copy] } => crate::world_ui::world_panel_label, entity;
#[cfg(feature = "picking")]
WorldButtonClicked { button: Ref [entity] } => crate::world_ui::world_button_clicked, bool;
#[cfg(feature = "audio")]
PauseSound { entity: Ref [entity] } => crate::audio::pause_sound, none;
#[cfg(feature = "audio")]
ResumeSound { entity: Ref [entity] } => crate::audio::resume_sound, none;
#[cfg(feature = "audio")]
FadeVolume { entity: Ref [entity], volume: f32 [copy], seconds: f32 [copy] } => crate::audio::fade_volume, none;
#[cfg(feature = "audio")]
Crossfade { fade_out: Ref [entity], fade_in: Ref [entity], volume: f32 [copy], seconds: f32 [copy] } => crate::audio::crossfade, none;
#[cfg(feature = "audio")]
SetBusVolume { bus: nightshade::prelude::AudioBus [copy], decibels: f32 [copy], fade_seconds: f32 [copy] } => crate::audio::set_bus_volume, none;
#[cfg(feature = "audio")]
DuckVoice { amount: f32 [copy], fade_seconds: f32 [copy] } => crate::audio::duck_voice, none;
DirectionalLight { direction: [f32; 3] [vec3], color: [f32; 3] [copy], intensity: f32 [copy] } => crate::lighting::directional_light, entity;
AreaLight { position: [f32; 3] [vec3], target: [f32; 3] [vec3], width: f32 [copy], height: f32 [copy], color: [f32; 3] [copy], intensity: f32 [copy] } => crate::lighting::area_light, entity;
SetLightShadows { light: Ref [entity], enabled: bool [copy] } => crate::lighting::set_light_shadows, none;
EmitSparks { position: [f32; 3] [vec3] } => crate::effects::emit_sparks, entity;
EmitFirework { position: [f32; 3] [vec3], velocity: [f32; 3] [vec3] } => crate::effects::emit_firework, entity;
EmitParticles { position: [f32; 3] [vec3], rate: f32 [copy], lifetime: f32 [copy], size: f32 [copy], gravity: [f32; 3] [vec3] } => crate::effects::emit_particles, entity;
SetAlphaBlend { entity: Ref [entity], enabled: bool [copy] } => crate::appearance::set_alpha_blend, none;
SetAlphaCutoff { entity: Ref [entity], cutoff: f32 [copy] } => crate::appearance::set_alpha_cutoff, none;
SetDoubleSided { entity: Ref [entity], double_sided: bool [copy] } => crate::appearance::set_double_sided, none;
SetIor { entity: Ref [entity], ior: f32 [copy] } => crate::appearance::set_ior, none;
SetTransmission { entity: Ref [entity], factor: f32 [copy] } => crate::appearance::set_transmission, none;
SetClearcoat { entity: Ref [entity], factor: f32 [copy], roughness: f32 [copy] } => crate::appearance::set_clearcoat, none;
SetAnisotropy { entity: Ref [entity], strength: f32 [copy], rotation: f32 [copy] } => crate::appearance::set_anisotropy, none;
SetUvTransform { entity: Ref [entity], offset: [f32; 2] [copy], scale: [f32; 2] [copy], rotation: f32 [copy] } => crate::appearance::set_uv_transform, none;
SetSheen { entity: Ref [entity], color: [f32; 3] [copy], roughness: f32 [copy] } => crate::appearance::set_sheen, none;
SetIridescence { entity: Ref [entity], factor: f32 [copy], ior: f32 [copy] } => crate::appearance::set_iridescence, none;
SetSpecular { entity: Ref [entity], factor: f32 [copy], color: [f32; 3] [copy] } => crate::appearance::set_specular, none;
SetNormalScale { entity: Ref [entity], scale: f32 [copy] } => crate::appearance::set_normal_scale, none;
SetOcclusionStrength { entity: Ref [entity], strength: f32 [copy] } => crate::appearance::set_occlusion_strength, none;
SetEmissiveStrength { entity: Ref [entity], strength: f32 [copy] } => crate::appearance::set_emissive_strength, none;
SetThickness { entity: Ref [entity], thickness: f32 [copy] } => crate::appearance::set_thickness, none;
SetTextOutline { entity: Ref [entity], width: f32 [copy], color: [f32; 4] [copy] } => crate::text::set_text_outline, none;
SetMorphWeight { entity: Ref [entity], index: u32 [copy], weight: f32 [copy] } => crate::morph::set_morph_weight, none;
SetWindowTitle { title: String [text] } => crate::window::set_window_title, none;
LockCursor { locked: bool [copy] } => crate::window::lock_cursor, none;
RequestExit {} => crate::window::request_exit, none;
SetRenderLayer { entity: Ref [entity], layer: u32 [copy] } => crate::render::set_render_layer, none;
SetCameraLayers { camera: Ref [entity], mask: u32 [copy] } => crate::render::set_camera_layers, none;
ThirdPersonCamera { target: Ref [entity], distance: f32 [copy] } => crate::camera::third_person_camera, entity;
SpawnCloth { position: [f32; 3] [vec3], width: f32 [copy], height: f32 [copy], columns: u32 [copy], rows: u32 [copy] } => crate::cloth::spawn_cloth, entity;
ResetCloth { entity: Ref [entity] } => crate::cloth::reset_cloth, none;
SetWind { direction: [f32; 3] [vec3], strength: f32 [copy] } => crate::cloth::set_wind, none;
PauseCutscene {} => crate::cutscene::pause_cutscene, none;
ResumeCutscene {} => crate::cutscene::resume_cutscene, none;
StopCutscene {} => crate::cutscene::stop_cutscene, none;
SeekCutscene { seconds: f32 [copy] } => crate::cutscene::seek_cutscene, none;
SetCutsceneCamera { camera: Ref [entity] } => crate::cutscene::set_cutscene_camera, none;
BindCutsceneActor { name: String [text], entity: Ref [entity] } => crate::cutscene::bind_cutscene_actor, none;
#[cfg(feature = "physics")]
SpawnCylinderBody { position: [f32; 3] [vec3], half_height: f32 [copy], radius: f32 [copy], mass: f32 [copy], color: [f32; 4] [copy] } => crate::physics::spawn_cylinder_body, entity;
#[cfg(feature = "physics")]
SpawnCapsuleBody { position: [f32; 3] [vec3], half_height: f32 [copy], radius: f32 [copy], mass: f32 [copy], color: [f32; 4] [copy] } => crate::scene::spawn_capsule_body, entity;
#[cfg(feature = "physics")]
SetControllerSpeed { entity: Ref [entity], speed: f32 [copy] } => crate::character::set_controller_speed, none;
#[cfg(feature = "physics")]
SetControllerJump { entity: Ref [entity], impulse: f32 [copy] } => crate::character::set_controller_jump, none;
#[cfg(feature = "physics")]
IsGrounded { entity: Ref [entity] } => crate::character::is_grounded, bool;
#[cfg(feature = "terrain")]
EnableTerrain { seed: u32 [copy] } => crate::terrain::enable_terrain, none;
#[cfg(feature = "terrain")]
DisableTerrain {} => crate::terrain::disable_terrain, none;
#[cfg(feature = "terrain")]
SetTerrainHeightRange { min: f32 [copy], max: f32 [copy] } => crate::terrain::set_terrain_height_range, none;
#[cfg(feature = "terrain")]
SetTerrainSnowHeight { height: f32 [copy] } => crate::terrain::set_terrain_snow_height, none;
#[cfg(feature = "grass")]
EnableGrass {} => crate::terrain::enable_grass, none;
#[cfg(feature = "grass")]
DisableGrass {} => crate::terrain::disable_grass, none;
LoadTexture { name: String [text], image_bytes: Vec<u8> [bytes] } => crate::appearance::load_texture, none;
LoadTextureLinear { name: String [text], image_bytes: Vec<u8> [bytes] } => crate::appearance::load_texture_linear, none;
RegisterTexture { name: String [text], width: u32 [copy], height: u32 [copy], rgba: Vec<u8> [bytes] } => crate::appearance::register_texture, none;
ListMaterials {} => crate::materials::list_materials, json;
GetMaterial { name: String [text] } => crate::materials::get_material, json;
RegisterMaterial { name: String [text], base_color: [f32; 4] [copy], metallic: f32 [copy], roughness: f32 [copy], emissive: [f32; 3] [copy] } => register_material_command, text;
UpdateMaterial { name: String [text], base_color: [f32; 4] [copy], metallic: f32 [copy], roughness: f32 [copy], emissive: [f32; 3] [copy] } => update_material_command, none;
SetMaterialVariant { variant: String [text] } => set_material_variant_command, int;
SpawnObjects { shape: Shape [copy], scale: [f32; 3] [vec3], color: [f32; 4] [copy], body: Body [copy], positions: Vec<[f32; 3]> [vec3_list] } => spawn_objects_command, entities;
SpawnInstanced { shape: Shape [copy], transforms: Vec<InstanceWire> [transforms], color: [f32; 4] [copy] } => crate::scene::spawn_instanced, entity;
SpawnInstancedWithMaterial { shape: Shape [copy], transforms: Vec<InstanceWire> [transforms], material: String [text] } => crate::scene::spawn_instanced_with_material, entity;
SetInstances { batch: Ref [entity], transforms: Vec<InstanceWire> [transforms] } => crate::scene::set_instances, none;
SpawnClothSheet { position: [f32; 3] [vec3], width: f32 [copy], height: f32 [copy] } => crate::scene::spawn_cloth_sheet, entity;
BlendToAnimationNamed { entity: Ref [entity], name: String [text], seconds: f32 [copy] } => crate::scene::blend_to_animation_named, bool;
AddAnimationLayer { entity: Ref [entity], clip_index: usize [copy], weight: f32 [copy] } => crate::scene::add_animation_layer, none;
NameEntity { name: String [text], entity: Ref [entity] } => crate::scene::name_entity, none;
Name { entity: Ref [entity] } => crate::hierarchy::name, text;
SetEntityName { entity: Ref [entity], name: String [text] } => crate::hierarchy::set_name, none;
Children { entity: Ref [entity] } => crate::hierarchy::children, entities;
Descendants { entity: Ref [entity] } => crate::hierarchy::descendants, entities;
Roots {} => crate::hierarchy::roots, entities;
SceneTree {} => crate::hierarchy::scene_tree, json;
MaterialOf { entity: Ref [entity] } => crate::inspect::material_of, json;
GetColor { entity: Ref [entity] } => crate::inspect::get_color, json;
GetMetallicRoughness { entity: Ref [entity] } => crate::inspect::get_metallic_roughness, json;
GetEmissive { entity: Ref [entity] } => crate::inspect::get_emissive, json;
GetUnlit { entity: Ref [entity] } => crate::inspect::get_unlit, json;
GetTexture { entity: Ref [entity] } => crate::inspect::get_texture, json;
DescribeEntity { entity: Ref [entity] } => crate::inspect::describe_entity, json;
SetTextAlignment { entity: Ref [entity], alignment: TextAlignment [copy] } => crate::text::set_text_alignment, none;
SetMorphWeights { entity: Ref [entity], weights: Vec<f32> [floats] } => crate::morph::set_morph_weights, none;
MorphWeight { entity: Ref [entity], index: u32 [copy] } => crate::morph::morph_weight, float;
MorphTargetCount { entity: Ref [entity] } => crate::morph::morph_target_count, int;
SetFog { enabled: bool [copy], color: [f32; 3] [copy], start: f32 [copy], end: f32 [copy] } => set_fog_command, none;
SetDepthOfField { enabled: bool [copy], focus_distance: f32 [copy], focus_range: f32 [copy], max_blur_radius: f32 [copy], bokeh_threshold: f32 [copy] } => set_depth_of_field_command, none;
Screenshot { path: String [text] } => screenshot_command, none;
SetShadingMode { mode: String [text] } => set_shading_mode_command, none;
AnimatePosition { entity: Ref [entity], to: [f32; 3] [vec3], seconds: f32 [copy], easing: String [text] } => animate_position_command, none;
AnimateScale { entity: Ref [entity], to: [f32; 3] [vec3], seconds: f32 [copy], easing: String [text] } => animate_scale_command, none;
AnimateColor { entity: Ref [entity], to: [f32; 4] [copy], seconds: f32 [copy], easing: String [text] } => animate_color_command, none;
ShakeCamera { strength: f32 [copy], seconds: f32 [copy] } => crate::animate::shake_camera, none;
ReachTo { root: Ref [entity], mid: Ref [entity], tip: Ref [entity], target: [f32; 3] [vec3], pole: Option<[f32; 3]> [opt_vec3] } => crate::animate::reach_to, none;
Bounds { entity: Ref [entity] } => crate::bounds::bounds, json;
BoundsOf { entities: Vec<Ref> [refs] } => crate::bounds::bounds_of, json;
FrameEntities { entities: Vec<Ref> [refs] } => crate::bounds::frame_entities, none;
SpawnParticleEmitter { emitter: EmitterWire [owned] } => spawn_particle_emitter_command, entity;
SetEmitter { emitter_entity: Ref [entity], emitter: EmitterWire [owned] } => set_emitter_command, none;
SpawnDecal { texture: String [text], position: [f32; 3] [vec3], normal: [f32; 3] [vec3], size: f32 [copy] } => crate::decals::spawn_decal, entity;
SaveScene { name: String [text] } => save_scene_command, bytes;
LoadScene { bytes: Vec<u8> [bytes] } => load_scene_command, entities;
WindowSize {} => crate::window::window_size, json;
CursorLocked {} => crate::window::cursor_locked, bool;
FramesPerSecond {} => crate::window::frames_per_second, float;
FrameCount {} => crate::window::frame_count, int;
UptimeMilliseconds {} => crate::window::uptime_milliseconds, int;
#[cfg(feature = "navmesh")]
BakeNavmeshWith { config: RecastConfigWire [owned] } => bake_navmesh_with_command, none;
#[cfg(feature = "physics")]
Raycast { origin: [f32; 3] [vec3], direction: [f32; 3] [vec3], max_distance: f32 [copy] } => raycast_command, json;
#[cfg(feature = "physics")]
AttachFixed { parent: Ref [entity], child: Ref [entity] } => attach_fixed_command, bool;
#[cfg(feature = "physics")]
AttachHinge { parent: Ref [entity], child: Ref [entity], axis: String [text] } => attach_hinge_command, bool;
#[cfg(feature = "physics")]
AttachSpring { parent: Ref [entity], child: Ref [entity], rest_length: f32 [copy], stiffness: f32 [copy], damping: f32 [copy] } => attach_spring_command, bool;
#[cfg(feature = "physics")]
AttachRope { parent: Ref [entity], child: Ref [entity], max_distance: f32 [copy] } => attach_rope_command, bool;
#[cfg(feature = "physics")]
ControllerVelocity { entity: Ref [entity] } => crate::character::controller_velocity, vector;
#[cfg(feature = "physics")]
MoveCharacter { entity: Ref [entity], movement: [f32; 2] [vec2], jump: bool [copy] } => crate::character::move_character, none;
#[cfg(feature = "picking")]
RequestSurfacePick { screen_pos: [f32; 2] [vec2] } => crate::picking::request_surface_pick, none;
#[cfg(feature = "picking")]
TakeSurfacePick {} => take_surface_pick_command, json;
#[cfg(feature = "picking")]
WorldButtonHovered { button: Ref [entity] } => crate::world_ui::world_button_hovered, bool;
#[cfg(feature = "audio")]
LoadSound { name: String [text], bytes: Vec<u8> [bytes] } => crate::audio::load_sound, none;
#[cfg(feature = "audio")]
PlaySound { name: String [text] } => crate::audio::play_sound, entity;
#[cfg(feature = "audio")]
PlaySoundLooping { name: String [text] } => crate::audio::play_sound_looping, entity;
#[cfg(feature = "audio")]
PlaySoundAt { name: String [text], position: [f32; 3] [vec3] } => crate::audio::play_sound_at, entity;
#[cfg(feature = "audio")]
SetVolume { entity: Ref [entity], volume: f32 [copy] } => crate::audio::set_volume, none;
#[cfg(feature = "audio")]
StopSound { entity: Ref [entity] } => crate::audio::stop_sound, none;
#[cfg(feature = "audio")]
SetPitch { entity: Ref [entity], rate: f32 [copy] } => crate::audio::set_pitch, none;
#[cfg(feature = "audio")]
SetSpatialDistance { entity: Ref [entity], min: f32 [copy], max: f32 [copy] } => crate::audio::set_spatial_distance, none;
PanelCheckbox { panel: Ref [entity], label: String [text], initial: bool [copy] } => crate::ui::panel_checkbox, entity;
CheckboxValue { checkbox: Ref [entity] } => crate::ui::checkbox_value, bool;
PanelSlider { panel: Ref [entity], min: f32 [copy], max: f32 [copy], initial: f32 [copy] } => crate::ui::panel_slider, entity;
SliderValue { slider: Ref [entity] } => crate::ui::slider_value, float;
SetSliderValue { slider: Ref [entity], value: f32 [copy] } => crate::ui::set_slider_value, none;
PanelTextInput { panel: Ref [entity], placeholder: String [text] } => crate::ui::panel_text_input, entity;
TextInputChanged { input: Ref [entity] } => crate::ui::text_input_changed, json;
PanelDropdown { panel: Ref [entity], options: Vec<String> [strs], initial: usize [copy] } => crate::ui::panel_dropdown, entity;
DropdownSelected { dropdown: Ref [entity] } => crate::ui::dropdown_selected, json;
PanelProgressBar { panel: Ref [entity], initial: f32 [copy] } => crate::ui::panel_progress_bar, entity;
SetProgress { bar: Ref [entity], value: f32 [copy] } => crate::ui::set_progress, none;
PanelToggle { panel: Ref [entity], initial: bool [copy] } => crate::ui::panel_toggle, entity;
ToggleValue { toggle: Ref [entity] } => crate::ui::toggle_value, bool;
PanelRadio { panel: Ref [entity], label: String [text], group_id: u32 [copy], option_index: usize [copy] } => crate::ui::panel_radio, entity;
RadioSelected { group_id: u32 [copy] } => crate::ui::radio_selected, json;
PanelRangeSlider { panel: Ref [entity], min: f32 [copy], max: f32 [copy], low: f32 [copy], high: f32 [copy] } => crate::ui::panel_range_slider, entity;
SetRange { slider: Ref [entity], low: f32 [copy], high: f32 [copy] } => crate::ui::set_range, none;
PanelTabs { panel: Ref [entity], labels: Vec<String> [strs], initial: usize [copy] } => crate::ui::panel_tabs, entity;
SetTab { tabs: Ref [entity], index: usize [copy] } => crate::ui::set_tab, none;
PanelCollapsing { panel: Ref [entity], label: String [text], open: bool [copy] } => crate::ui::panel_collapsing, entity;
PanelColorPicker { panel: Ref [entity], initial: [f32; 4] [copy] } => crate::ui::panel_color_picker, entity;
ColorValue { picker: Ref [entity] } => crate::ui::color_value, json;
PanelTextArea { panel: Ref [entity], placeholder: String [text], rows: usize [copy] } => crate::ui::panel_text_area, entity;
PanelTextAreaWithValue { panel: Ref [entity], placeholder: String [text], rows: usize [copy], initial: String [text] } => crate::ui::panel_text_area_with_value, entity;
SetTextArea { area: Ref [entity], text: String [text] } => crate::ui::set_text_area, none;
PanelMultiSelect { panel: Ref [entity], options: Vec<String> [strs] } => crate::ui::panel_multi_select, entity;
SetMultiSelect { widget: Ref [entity], indices: Vec<u32> [indices] } => crate::ui::set_multi_select, none;
PanelDatePicker { panel: Ref [entity], year: i32 [copy], month: u32 [copy], day: u32 [copy] } => crate::ui::panel_date_picker, entity;
SetDate { picker: Ref [entity], year: i32 [copy], month: u32 [copy], day: u32 [copy] } => crate::ui::set_date, none;
PanelMenu { panel: Ref [entity], label: String [text], items: Vec<String> [strs] } => crate::ui::panel_menu, entity;
PanelColorPickerHsv { panel: Ref [entity], initial: [f32; 4] [copy] } => crate::ui::panel_color_picker_hsv, entity;
PanelSplitter { panel: Ref [entity], horizontal: bool [copy], ratio: f32 [copy] } => panel_splitter_command, entity;
PanelBreadcrumb { panel: Ref [entity], segments: Vec<String> [strs] } => crate::ui::panel_breadcrumb, entity;
PanelVirtualList { panel: Ref [entity], item_height: f32 [copy], pool_size: usize [copy] } => crate::ui::panel_virtual_list, entity;
PanelTable { panel: Ref [entity], headers: Vec<String> [strs], widths: Vec<f32> [floats] } => crate::ui::panel_table, entity;
PanelDataGrid { panel: Ref [entity], headers: Vec<String> [strs], widths: Vec<f32> [floats], pool_size: usize [copy] } => panel_data_grid_command, entity;
SetDataGridRows { grid: Ref [entity], count: usize [copy] } => crate::ui::set_data_grid_rows, none;
SetDataGridCell { grid: Ref [entity], row: usize [copy], column: usize [copy], text: String [text] } => crate::ui::set_data_grid_cell, none;
DataGridSelectionChanged { grid: Ref [entity] } => crate::ui::data_grid_selection_changed, bool;
PanelCommandPalette { panel: Ref [entity], pool_size: usize [copy] } => crate::ui::panel_command_palette, entity;
PanelPropertyGrid { panel: Ref [entity], label_width: f32 [copy] } => crate::ui::panel_property_grid, entity;
PanelPropertyRow { grid: Ref [entity], label: String [text] } => crate::ui::panel_property_row, entity;
PanelTreeView { panel: Ref [entity], multi_select: bool [copy] } => crate::ui::panel_tree_view, entity;
TreeContent { tree_view: Ref [entity] } => crate::ui::tree_content, entity;
TreeNode { tree_view: Ref [entity], parent_container: Ref [entity], label: String [text], depth: usize [copy], user_data: u64 [copy] } => crate::ui::tree_node, entity;
TreeNodeChildren { node: Ref [entity] } => crate::ui::tree_node_children, entity;
SetTreeNodeExpanded { node: Ref [entity], expanded: bool [copy] } => crate::ui::set_tree_node_expanded, none;
TreeViewSelected { tree_view: Ref [entity] } => crate::ui::tree_view_selected, entities;
PanelDragValue { panel: Ref [entity], min: f32 [copy], max: f32 [copy], initial: f32 [copy] } => crate::ui::panel_drag_value, entity;
DragValue { widget: Ref [entity] } => crate::ui::drag_value, float;
PanelSelectable { panel: Ref [entity], text: String [text], group: u32 [copy], grouped: bool [copy] } => panel_selectable_command, entity;
PanelModal { panel: Ref [entity], title: String [text], width: f32 [copy], height: f32 [copy] } => crate::ui::panel_modal, entity;
PanelSpinner { panel: Ref [entity] } => crate::ui::panel_spinner, entity;
PanelSeparator { panel: Ref [entity] } => crate::ui::panel_separator, entity;
PanelHeading { panel: Ref [entity], text: String [text] } => crate::ui::panel_heading, entity;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn command_schema_covers_the_surface() {
let schema = command_schema();
assert!(schema.get("oneOf").is_some());
let text = enum2schema::serde_json::to_string(&schema).unwrap();
for variant in [
"SpawnCube",
"SpawnObject",
"SetColor",
"Rotate",
"QueryTagged",
] {
assert!(text.contains(variant), "schema missing {variant}");
}
}
#[test]
fn reply_schema_is_generated() {
assert!(command_reply_schema().get("oneOf").is_some());
}
#[test]
fn manifest_covers_the_surface() {
let manifest = command_manifest();
assert!(!manifest.is_empty());
let names: Vec<&str> = manifest.iter().map(|spec| spec.name).collect();
for variant in [
"SpawnCube",
"SpawnObject",
"SetColor",
"Rotate",
"QueryTagged",
] {
assert!(names.contains(&variant), "manifest missing {variant}");
}
let replies = [
"none",
"entity",
"opt_entity",
"bool",
"float",
"int",
"text",
"vector",
"opt_vector",
"entities",
"strings",
"bytes",
"json",
];
for spec in &manifest {
assert!(
replies.contains(&spec.reply),
"unknown reply {}",
spec.reply
);
}
}
#[test]
fn entity_reference_serializes_as_its_schema_claims() {
let entity = Entity {
id: 3,
generation: 1,
};
let value = enum2schema::serde_json::to_value(Ref::Entity(entity)).unwrap();
let inner = value
.get("Entity")
.and_then(|tagged| tagged.as_object())
.expect("Ref::Entity serializes as an externally tagged object");
assert!(inner.contains_key("id"));
assert!(inner.contains_key("generation"));
assert_eq!(inner.len(), 2);
}
}