use crate::color::Color;
use gizmo_core::{Entity, EntityName, World};
use gizmo_math::{Quat, Vec3};
use gizmo_physics_core::{Collider, Transform};
use gizmo_physics_rigid::components::{RigidBody, Velocity};
use gizmo_renderer::{
asset::AssetManager,
components::{Camera, DirectionalLight, Material, MeshRenderer, PointLight},
Renderer,
};
pub struct Commands<'a> {
pub world: &'a mut World,
pub renderer: &'a Renderer,
pub asset_manager: Option<AssetManager>,
}
impl<'a> Drop for Commands<'a> {
fn drop(&mut self) {
if let Some(am) = self.asset_manager.take() {
self.world.insert_resource(am);
}
}
}
impl<'a> Commands<'a> {
pub fn new(world: &'a mut World, renderer: &'a Renderer) -> Self {
let am = world.remove_resource::<AssetManager>().unwrap_or_default();
Self {
world,
renderer,
asset_manager: Some(am),
}
}
pub fn spawn_cube(&mut self, pos: Vec3, color: Color) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_cube(&self.renderer.device);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg).with_unlit(color.to_vec4());
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_sphere(&mut self, pos: Vec3, radius: f32, color: Color) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 20, 20);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg).with_unlit(color.to_vec4());
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_plane(&mut self, pos: Vec3, size: f32, color: Color) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_plane(&self.renderer.device, size);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg).with_unlit(color.to_vec4());
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_model(&mut self, pos: Vec3, path: &str) -> EntityBuilder<'_, 'a> {
let mesh = self
.asset_manager
.as_mut()
.unwrap()
.load_obj(&self.renderer.device, path);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg);
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_camera(&mut self, pos: Vec3) -> EntityBuilder<'_, 'a> {
if let Some(mut cameras) = self.world.query::<gizmo_core::prelude::Mut<Camera>>() {
for (_, mut c) in cameras.iter_mut() {
c.primary = false;
}
}
let id = self.world.spawn();
let trans = Transform::new(pos);
self.world.add_component(id, trans);
self.world.add_component(
id,
Camera {
fov: 60.0_f32.to_radians(),
near: 0.1,
far: 1000.0,
yaw: -std::f32::consts::FRAC_PI_2,
pitch: 0.0,
exposure: 1.0,
primary: true,
},
);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_camera_with(
&mut self,
pos: Vec3,
fov_deg: f32,
near: f32,
far: f32,
) -> EntityBuilder<'_, 'a> {
if let Some(mut cameras) = self.world.query::<gizmo_core::prelude::Mut<Camera>>() {
for (_, mut c) in cameras.iter_mut() {
c.primary = false;
}
}
let id = self.world.spawn();
let trans = Transform::new(pos);
self.world.add_component(id, trans);
self.world.add_component(
id,
Camera {
fov: fov_deg.to_radians(),
near,
far,
yaw: -std::f32::consts::FRAC_PI_2,
pitch: 0.0,
exposure: 1.0,
primary: true,
},
);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_point_light(
&mut self,
pos: Vec3,
color: Color,
intensity: f32,
) -> EntityBuilder<'_, 'a> {
let id = self.world.spawn();
let trans = Transform::new(pos);
self.world.add_component(id, trans);
self.world.add_component(
id,
PointLight::new(
gizmo_math::Vec3::new(color.0.x, color.0.y, color.0.z),
intensity,
10.0,
),
);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_sun(
&mut self,
_direction: Vec3,
color: Color,
intensity: f32,
) -> EntityBuilder<'_, 'a> {
let id = self.world.spawn();
let pos = Vec3::ZERO; let trans = Transform::new(pos);
self.world.add_component(id, trans);
self.world.add_component(
id,
DirectionalLight {
color: Vec3::new(color.0.x, color.0.y, color.0.z),
intensity,
role: crate::renderer::components::LightRole::Sun,
},
);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_skybox(&mut self, color: Color) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_inverted_cube(&self.renderer.device);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg).with_unlit(color.to_vec4()).with_skybox();
let id = self.world.spawn();
let mut trans = Transform::new(Vec3::ZERO);
trans.scale = Vec3::new(500.0, 500.0, 500.0);
trans.update_local_matrix();
self.world.add_component(id, trans);
self.world.add_component(id, mesh);
self.world.add_component(id, mat);
self.world.add_component(id, MeshRenderer::new());
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_rigid_cube(
&mut self,
pos: Vec3,
half_extents: Vec3,
color: Color,
mass: f32,
) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_cube(&self.renderer.device);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg).with_unlit(color.to_vec4());
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
{
let trans_store = self.world.borrow_mut::<Transform>();
if let Some(mut trans) = trans_store.get_mut(id.id()) {
trans.scale = half_extents * 2.0;
trans.update_local_matrix();
}
}
let mut rb = if mass > 0.0 {
RigidBody::new(mass, 0.3, 0.5, true)
} else {
RigidBody::new_static()
};
let col = Collider::box_collider(half_extents);
rb.update_inertia_from_collider(&col);
self.world.add_component(id, rb);
if mass > 0.0 {
self.world.add_component(id, Velocity::new(Vec3::ZERO));
}
self.world.add_component(id, col);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_rigid_sphere(
&mut self,
pos: Vec3,
radius: f32,
color: Color,
mass: f32,
) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 16, 16);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg).with_unlit(color.to_vec4());
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
let mut rb = if mass > 0.0 {
RigidBody::new(mass, 0.3, 0.5, true)
} else {
RigidBody::new_static()
};
let col = Collider::sphere(radius);
rb.update_inertia_from_collider(&col);
self.world.add_component(id, rb);
if mass > 0.0 {
self.world.add_component(id, Velocity::new(Vec3::ZERO));
}
self.world.add_component(id, col);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_static_plane(
&mut self,
pos: Vec3,
size: f32,
color: Color,
) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_plane(&self.renderer.device, size);
let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let mat = Material::new(bg).with_pbr(color.to_vec4(), 0.9, 0.0);
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
self.world.add_component(id, RigidBody::new_static());
self.world.add_component(
id,
Collider::box_collider(Vec3::new(size / 2.0, 0.05, size / 2.0)),
);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_textured_cube(&mut self, pos: Vec3, texture_path: &str) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_cube(&self.renderer.device);
let bg = self
.asset_manager
.as_mut()
.unwrap()
.load_material_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
texture_path,
)
.unwrap_or_else(|_| {
self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
)
});
let mat = Material::new(bg);
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_textured_plane(
&mut self,
pos: Vec3,
size: f32,
texture_path: &str,
) -> EntityBuilder<'_, 'a> {
let mesh = AssetManager::create_plane(&self.renderer.device, size);
let bg = self
.asset_manager
.as_mut()
.unwrap()
.load_material_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
texture_path,
)
.unwrap_or_else(|_| {
self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
)
});
let mat = Material::new(bg);
let id = spawn_mesh_entity(self.world, pos, mesh, mat);
EntityBuilder {
commands: self,
entity: id,
}
}
pub fn spawn_gltf(
&mut self,
pos: Vec3,
path: &str,
attach_colliders: bool,
) -> Result<EntityBuilder<'_, 'a>, String> {
let default_bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let default_mat = Material::new(default_bg.clone());
match self.asset_manager.as_mut().unwrap().load_gltf_scene(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
default_bg,
path,
) {
Ok(asset) => {
let root = self.world.spawn();
let mut trans = Transform::new(pos);
trans.update_local_matrix();
self.world.add_component(root, trans);
self.world
.add_component(root, gizmo_physics_core::components::GlobalTransform::default());
self.world
.add_component(root, EntityName(format!("GLTF: {}", path)));
self.world
.add_component(root, gizmo_core::component::Children(Vec::new()));
let mut skeletons = Vec::new();
for skel_data in &asset.skeletons {
skeletons.push(self.renderer.create_skeleton(std::sync::Arc::new(skel_data.clone())));
}
for node in &asset.roots {
spawn_gltf_node_flat(
self.world,
node,
root.id(),
default_mat.clone(),
attach_colliders,
&skeletons,
);
}
if !skeletons.is_empty() {
self.world.add_component(root, skeletons[0].clone());
}
if !asset.animations.is_empty() {
self.world.add_component(
root,
gizmo_renderer::components::AnimationPlayer {
active_animation: 0,
current_time: 0.0,
loop_anim: true,
speed: 1.0,
animations: std::sync::Arc::from(
asset.animations.clone().into_boxed_slice(),
),
..Default::default()
},
);
}
Ok(EntityBuilder {
commands: self,
entity: root,
})
}
Err(e) => Err(format!(
"[Commands::spawn_gltf] '{}' yuklenemedi: {}",
path, e
)),
}
}
pub fn spawn_gltf_async_completed(
&mut self,
completion: gizmo_renderer::async_assets::GltfImportCompletion,
pos: Vec3,
attach_colliders: bool,
) -> Result<EntityBuilder<'_, 'a>, String> {
let default_bg = self.asset_manager.as_mut().unwrap().create_white_texture(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
);
let default_mat = Material::new(default_bg.clone());
match self.asset_manager.as_mut().unwrap().load_gltf_from_import(
&self.renderer.device,
&self.renderer.queue,
&self.renderer.scene.texture_bind_group_layout,
default_bg,
&completion.path,
completion.document,
completion.buffers,
completion.images,
) {
Ok(asset) => {
let root = self.world.spawn();
let mut trans = Transform::new(pos);
trans.update_local_matrix();
self.world.add_component(root, trans);
self.world
.add_component(root, gizmo_physics_core::components::GlobalTransform::default());
self.world
.add_component(root, EntityName(format!("GLTF: {}", completion.path)));
self.world
.add_component(root, gizmo_core::component::Children(Vec::new()));
let mut skeletons = Vec::new();
for skel_data in &asset.skeletons {
skeletons.push(self.renderer.create_skeleton(std::sync::Arc::new(skel_data.clone())));
}
for node in &asset.roots {
spawn_gltf_node_flat(
self.world,
node,
root.id(),
default_mat.clone(),
attach_colliders,
&skeletons,
);
}
if !skeletons.is_empty() {
self.world.add_component(root, skeletons[0].clone());
}
if !asset.animations.is_empty() {
self.world.add_component(
root,
gizmo_renderer::components::AnimationPlayer {
active_animation: 0,
current_time: 0.0,
loop_anim: true,
speed: 1.0,
animations: std::sync::Arc::from(
asset.animations.clone().into_boxed_slice(),
),
..Default::default()
},
);
}
Ok(EntityBuilder {
commands: self,
entity: root,
})
}
Err(e) => Err(format!(
"[Commands::spawn_gltf_async_completed] '{}' yüklenemedi: {}",
completion.path, e
)),
}
}
}
pub struct EntityBuilder<'b, 'a> {
commands: &'b mut Commands<'a>,
entity: Entity,
}
impl<'b, 'a> EntityBuilder<'b, 'a> {
pub fn with_name(self, name: &str) -> Self {
self.commands
.world
.add_component(self.entity, EntityName(name.to_string()));
self
}
pub fn with<C: gizmo_core::Component + 'static>(self, component: C) -> Self {
self.commands.world.add_component(self.entity, component);
self
}
pub fn id(self) -> Entity {
self.entity
}
}
impl<'b, 'a> From<EntityBuilder<'b, 'a>> for Entity {
fn from(b: EntityBuilder<'b, 'a>) -> Entity {
b.entity
}
}
fn spawn_mesh_entity(
world: &mut World,
pos: Vec3,
mesh: gizmo_renderer::components::Mesh,
mat: Material,
) -> Entity {
let id = world.spawn();
let mut trans = Transform::new(pos);
trans.update_local_matrix();
world.add_component(id, trans);
world.add_component(id, gizmo_physics_core::components::GlobalTransform::default());
world.add_component(id, mesh);
world.add_component(id, mat);
world.add_component(id, MeshRenderer::new());
id
}
fn spawn_gltf_node_flat(
world: &mut World,
node: &gizmo_renderer::asset::GltfNodeData,
parent_id: u32,
default_mat: Material,
attach_colliders: bool,
skeletons: &[gizmo_renderer::components::Skeleton],
) {
use gizmo_core::component::{Children, Parent};
let entity = world.spawn();
let name = node.name.clone().unwrap_or_else(|| "GLTF_Node".to_string());
world.add_component(entity, EntityName(name));
world.add_component(entity, Parent(parent_id));
world.add_component(entity, Children(Vec::new()));
{
let ch_store = world.borrow_mut::<Children>();
if let Some(mut parent_ch) = ch_store.get_mut(parent_id) {
parent_ch.0.push(entity.id());
}
}
let raw_rot = Quat::from_xyzw(
node.rotation[0],
node.rotation[1],
node.rotation[2],
node.rotation[3],
);
let rot = if raw_rot.is_nan() || raw_rot.length() < 0.0001 {
Quat::IDENTITY
} else {
raw_rot.normalize()
};
let mut t = Transform::new(Vec3::new(
node.translation[0],
node.translation[1],
node.translation[2],
))
.with_rotation(rot)
.with_scale(Vec3::new(node.scale[0], node.scale[1], node.scale[2]));
t.update_local_matrix();
if node.skin_index.is_some() || node.name.as_deref() == Some("Armature") {
t = Transform::default();
t.update_local_matrix();
}
world.add_component(entity, t);
world.add_component(entity, gizmo_physics_core::components::GlobalTransform::default());
println!("SPAWN GLTF NODE: name={:?}, num_primitives={}", node.name, node.primitives.len());
let mut newly_added_prims = Vec::new();
for (mesh, mat_opt) in node.primitives.iter() {
println!(" SPAWN PRIM: mesh_source='{}', bounds_min={:?}, bounds_max={:?}", mesh.source, mesh.bounds.min, mesh.bounds.max);
let prim = world.spawn();
let mut prim_t = Transform::new(Vec3::ZERO);
prim_t.update_local_matrix();
world.add_component(prim, prim_t);
world.add_component(prim, gizmo_physics_core::components::GlobalTransform::default());
world.add_component(prim, Parent(entity.id()));
world.add_component(prim, Children(Vec::new()));
newly_added_prims.push(prim.id());
world.add_component(prim, mesh.clone());
world.add_component(prim, mat_opt.clone().unwrap_or_else(|| default_mat.clone()));
world.add_component(prim, MeshRenderer::new());
if let Some(skin_idx) = node.skin_index {
if skin_idx < skeletons.len() {
world.add_component(prim, skeletons[skin_idx].clone());
}
}
if attach_colliders {
let extents = (mesh.bounds.max - mesh.bounds.min) / 2.0;
let center_offset = (mesh.bounds.max + mesh.bounds.min) / 2.0;
let cx = extents.x.max(0.01);
let cy = extents.y.max(0.01);
let cz = extents.z.max(0.01);
if center_offset.length_squared() > 0.0001 {
world.add_component(
prim,
gizmo_physics_core::Collider::offset_box(center_offset.into(), gizmo_math::Vec3::new(cx, cy, cz)),
);
} else {
world.add_component(prim, gizmo_physics_core::Collider::new_aabb(cx, cy, cz));
}
}
}
if !newly_added_prims.is_empty() {
{
let ch_store = world.borrow_mut::<Children>();
if let Some(mut parent_ch) = ch_store.get_mut(entity.id()) {
parent_ch.0.extend(newly_added_prims);
}
}
}
for child_node in &node.children {
spawn_gltf_node_flat(
world,
child_node,
entity.id(),
default_mat.clone(),
attach_colliders,
skeletons,
);
}
}
pub trait WorldExt {
fn entity_named(&self, name: &str) -> Option<u32>;
fn move_entity_named<F: FnMut(&mut gizmo_physics_core::Transform)>(&mut self, name: &str, f: F);
fn position_of(&self, name: &str) -> Option<Vec3>;
fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(&mut self, name: &str, f: F);
}
impl WorldExt for World {
fn entity_named(&self, name: &str) -> Option<u32> {
let mut names = self.query::<&EntityName>()?;
for (id, n) in names.iter_mut() {
if n.0 == name {
return Some(id);
}
}
None
}
fn move_entity_named<F: FnMut(&mut gizmo_physics_core::Transform)>(&mut self, name: &str, mut f: F) {
let target: Option<u32> = {
if let Some(mut names) = self.query::<&EntityName>() {
let mut found = None;
for (id, n) in names.iter_mut() {
if n.0 == name {
found = Some(id);
break;
}
}
found
} else {
None
}
};
if let Some(target_id) = target {
if let Some(mut transforms) =
self.query::<gizmo_core::prelude::Mut<gizmo_physics_core::Transform>>()
{
for (tid, mut trans) in transforms.iter_mut() {
if tid == target_id {
f(&mut trans);
trans.update_local_matrix();
}
}
}
}
}
fn position_of(&self, name: &str) -> Option<Vec3> {
let target_id = self.entity_named(name)?;
let transforms = self.borrow::<gizmo_physics_core::Transform>();
transforms.get(target_id).map(|t| t.position)
}
fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(
&mut self,
name: &str,
mut f: F,
) {
let target: Option<u32> = {
if let Some(mut names) = self.query::<&EntityName>() {
let mut found = None;
for (id, n) in names.iter_mut() {
if n.0 == name {
found = Some(id);
break;
}
}
found
} else {
None
}
};
if let Some(target_id) = target {
{
let storage = self.borrow_mut::<T>();
if let Some(mut comp) = storage.get_mut(target_id) {
f(&mut *comp);
}
}
}
}
}
pub trait InputExt {
fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool;
}
impl InputExt for gizmo_core::input::Input {
#[inline]
fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
self.is_key_pressed(keycode as u32)
}
#[inline]
fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
self.is_key_just_pressed(keycode as u32)
}
#[inline]
fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool {
self.is_key_just_released(keycode as u32)
}
}