1use crate::color::Color;
2use gizmo_core::{Entity, EntityName, World};
13use gizmo_math::{Quat, Vec3};
14use gizmo_physics::{
15 components::{Collider, RigidBody, Velocity},
16 Transform,
17};
18use gizmo_renderer::{
19 asset::AssetManager,
20 components::{Camera, DirectionalLight, Material, MeshRenderer, PointLight},
21 Renderer,
22};
23
24pub struct Commands<'a> {
27 pub world: &'a mut World,
28 pub renderer: &'a Renderer,
29 pub asset_manager: Option<AssetManager>,
30}
31
32impl<'a> Drop for Commands<'a> {
33 fn drop(&mut self) {
34 if let Some(am) = self.asset_manager.take() {
35 self.world.insert_resource(am);
36 }
37 }
38}
39
40impl<'a> Commands<'a> {
41 pub fn new(world: &'a mut World, renderer: &'a Renderer) -> Self {
42 let am = world.remove_resource::<AssetManager>().unwrap_or_default();
43 Self {
44 world,
45 renderer,
46 asset_manager: Some(am),
47 }
48 }
49
50 pub fn spawn_cube(&mut self, pos: Vec3, color: Color) -> EntityBuilder<'_, 'a> {
54 let mesh = AssetManager::create_cube(&self.renderer.device);
55 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
56 &self.renderer.device,
57 &self.renderer.queue,
58 &self.renderer.scene.texture_bind_group_layout,
59 );
60 let mat = Material::new(bg).with_unlit(color.to_vec4());
61 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
62 EntityBuilder {
63 commands: self,
64 entity: id,
65 }
66 }
67
68 pub fn spawn_sphere(&mut self, pos: Vec3, radius: f32, color: Color) -> EntityBuilder<'_, 'a> {
70 let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 20, 20);
71 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
72 &self.renderer.device,
73 &self.renderer.queue,
74 &self.renderer.scene.texture_bind_group_layout,
75 );
76 let mat = Material::new(bg).with_unlit(color.to_vec4());
77 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
78 EntityBuilder {
79 commands: self,
80 entity: id,
81 }
82 }
83
84 pub fn spawn_plane(&mut self, pos: Vec3, size: f32, color: Color) -> EntityBuilder<'_, 'a> {
86 let mesh = AssetManager::create_plane(&self.renderer.device, size);
87 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
88 &self.renderer.device,
89 &self.renderer.queue,
90 &self.renderer.scene.texture_bind_group_layout,
91 );
92 let mat = Material::new(bg).with_unlit(color.to_vec4());
93 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
94 EntityBuilder {
95 commands: self,
96 entity: id,
97 }
98 }
99
100 pub fn spawn_model(&mut self, pos: Vec3, path: &str) -> EntityBuilder<'_, 'a> {
102 let mesh = self
103 .asset_manager
104 .as_mut()
105 .unwrap()
106 .load_obj(&self.renderer.device, path);
107 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
108 &self.renderer.device,
109 &self.renderer.queue,
110 &self.renderer.scene.texture_bind_group_layout,
111 );
112 let mat = Material::new(bg);
113 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
114 EntityBuilder {
115 commands: self,
116 entity: id,
117 }
118 }
119
120 pub fn spawn_camera(&mut self, pos: Vec3) -> EntityBuilder<'_, 'a> {
125 if let Some(mut cameras) = self.world.query::<gizmo_core::prelude::Mut<Camera>>() {
126 for (_, mut c) in cameras.iter_mut() {
127 c.primary = false;
128 }
129 }
130 let id = self.world.spawn();
131 let trans = Transform::new(pos);
132
133 self.world.add_component(id, trans);
134 self.world.add_component(
135 id,
136 Camera {
137 fov: 60.0_f32.to_radians(),
138 near: 0.1,
139 far: 1000.0,
140 yaw: -std::f32::consts::FRAC_PI_2,
141 pitch: 0.0,
142 primary: true,
143 },
144 );
145 EntityBuilder {
146 commands: self,
147 entity: id,
148 }
149 }
150
151 pub fn spawn_camera_with(
153 &mut self,
154 pos: Vec3,
155 fov_deg: f32,
156 near: f32,
157 far: f32,
158 ) -> EntityBuilder<'_, 'a> {
159 if let Some(mut cameras) = self.world.query::<gizmo_core::prelude::Mut<Camera>>() {
160 for (_, mut c) in cameras.iter_mut() {
161 c.primary = false;
162 }
163 }
164 let id = self.world.spawn();
165 let trans = Transform::new(pos);
166
167 self.world.add_component(id, trans);
168 self.world.add_component(
169 id,
170 Camera {
171 fov: fov_deg.to_radians(),
172 near,
173 far,
174 yaw: -std::f32::consts::FRAC_PI_2,
175 pitch: 0.0,
176 primary: true,
177 },
178 );
179 EntityBuilder {
180 commands: self,
181 entity: id,
182 }
183 }
184
185 pub fn spawn_point_light(
189 &mut self,
190 pos: Vec3,
191 color: Color,
192 intensity: f32,
193 ) -> EntityBuilder<'_, 'a> {
194 let id = self.world.spawn();
195 let trans = Transform::new(pos);
196
197 self.world.add_component(id, trans);
198 self.world.add_component(
199 id,
200 PointLight::new(
201 gizmo_math::Vec3::new(color.0.x, color.0.y, color.0.z),
202 intensity,
203 10.0,
204 ),
205 );
206 EntityBuilder {
207 commands: self,
208 entity: id,
209 }
210 }
211
212 pub fn spawn_sun(
215 &mut self,
216 _direction: Vec3,
217 color: Color,
218 intensity: f32,
219 ) -> EntityBuilder<'_, 'a> {
220 let id = self.world.spawn();
221 let pos = Vec3::ZERO; let trans = Transform::new(pos);
223
224 self.world.add_component(id, trans);
225 self.world.add_component(
226 id,
227 DirectionalLight {
228 color: Vec3::new(color.0.x, color.0.y, color.0.z),
229 intensity,
230 role: crate::renderer::components::LightRole::Sun,
231 },
232 );
233 EntityBuilder {
234 commands: self,
235 entity: id,
236 }
237 }
238
239 pub fn spawn_skybox(&mut self, color: Color) -> EntityBuilder<'_, 'a> {
243 let mesh = AssetManager::create_inverted_cube(&self.renderer.device);
247 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
248 &self.renderer.device,
249 &self.renderer.queue,
250 &self.renderer.scene.texture_bind_group_layout,
251 );
252 let mat = Material::new(bg).with_skybox().with_unlit(color.to_vec4());
253 let id = self.world.spawn();
254 let mut trans = Transform::new(Vec3::ZERO);
255 trans.scale = Vec3::new(500.0, 500.0, 500.0);
256 trans.update_local_matrix();
257
258 self.world.add_component(id, trans);
259 self.world.add_component(id, mesh);
260 self.world.add_component(id, mat);
261 self.world.add_component(id, MeshRenderer::new());
262 EntityBuilder {
263 commands: self,
264 entity: id,
265 }
266 }
267
268 pub fn spawn_rigid_cube(
273 &mut self,
274 pos: Vec3,
275 half_extents: Vec3,
276 color: Color,
277 mass: f32,
278 ) -> EntityBuilder<'_, 'a> {
279 let mesh = AssetManager::create_cube(&self.renderer.device);
280 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
281 &self.renderer.device,
282 &self.renderer.queue,
283 &self.renderer.scene.texture_bind_group_layout,
284 );
285 let mat = Material::new(bg).with_unlit(color.to_vec4());
286 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
287 {
289 let mut trans_store = self.world.borrow_mut::<Transform>();
290 if let Some(trans) = trans_store.get_mut(id.id()) {
291 trans.scale = half_extents * 2.0;
292 trans.update_local_matrix();
293 }
294 }
295 let mut rb = if mass > 0.0 {
296 RigidBody::new(mass, 0.3, 0.5, true)
297 } else {
298 RigidBody::new_static()
299 };
300 let col = Collider::box_collider(half_extents);
301 rb.update_inertia_from_collider(&col);
302 self.world.add_component(id, rb);
303 if mass > 0.0 {
304 self.world.add_component(id, Velocity::new(Vec3::ZERO));
305 }
306 self.world.add_component(id, col);
307 EntityBuilder {
308 commands: self,
309 entity: id,
310 }
311 }
312
313 pub fn spawn_rigid_sphere(
315 &mut self,
316 pos: Vec3,
317 radius: f32,
318 color: Color,
319 mass: f32,
320 ) -> EntityBuilder<'_, 'a> {
321 let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 16, 16);
322 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
323 &self.renderer.device,
324 &self.renderer.queue,
325 &self.renderer.scene.texture_bind_group_layout,
326 );
327 let mat = Material::new(bg).with_unlit(color.to_vec4());
328 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
329 let mut rb = if mass > 0.0 {
330 RigidBody::new(mass, 0.3, 0.5, true)
331 } else {
332 RigidBody::new_static()
333 };
334 let col = Collider::sphere(radius);
335 rb.update_inertia_from_collider(&col);
336 self.world.add_component(id, rb);
337 if mass > 0.0 {
338 self.world.add_component(id, Velocity::new(Vec3::ZERO));
339 }
340 self.world.add_component(id, col);
341 EntityBuilder {
342 commands: self,
343 entity: id,
344 }
345 }
346
347 pub fn spawn_static_plane(
349 &mut self,
350 pos: Vec3,
351 size: f32,
352 color: Color,
353 ) -> EntityBuilder<'_, 'a> {
354 let mesh = AssetManager::create_plane(&self.renderer.device, size);
355 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
356 &self.renderer.device,
357 &self.renderer.queue,
358 &self.renderer.scene.texture_bind_group_layout,
359 );
360 let mat = Material::new(bg).with_pbr(color.to_vec4(), 0.9, 0.0);
361 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
362 self.world.add_component(id, RigidBody::new_static());
363 self.world.add_component(
364 id,
365 Collider::box_collider(Vec3::new(size / 2.0, 0.05, size / 2.0)),
366 );
367 EntityBuilder {
368 commands: self,
369 entity: id,
370 }
371 }
372
373 pub fn spawn_textured_cube(&mut self, pos: Vec3, texture_path: &str) -> EntityBuilder<'_, 'a> {
377 let mesh = AssetManager::create_cube(&self.renderer.device);
378 let bg = self
379 .asset_manager
380 .as_mut()
381 .unwrap()
382 .load_material_texture(
383 &self.renderer.device,
384 &self.renderer.queue,
385 &self.renderer.scene.texture_bind_group_layout,
386 texture_path,
387 )
388 .unwrap_or_else(|_| {
389 self.asset_manager.as_mut().unwrap().create_white_texture(
390 &self.renderer.device,
391 &self.renderer.queue,
392 &self.renderer.scene.texture_bind_group_layout,
393 )
394 });
395 let mat = Material::new(bg);
396 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
397 EntityBuilder {
398 commands: self,
399 entity: id,
400 }
401 }
402
403 pub fn spawn_textured_plane(
405 &mut self,
406 pos: Vec3,
407 size: f32,
408 texture_path: &str,
409 ) -> EntityBuilder<'_, 'a> {
410 let mesh = AssetManager::create_plane(&self.renderer.device, size);
411 let bg = self
412 .asset_manager
413 .as_mut()
414 .unwrap()
415 .load_material_texture(
416 &self.renderer.device,
417 &self.renderer.queue,
418 &self.renderer.scene.texture_bind_group_layout,
419 texture_path,
420 )
421 .unwrap_or_else(|_| {
422 self.asset_manager.as_mut().unwrap().create_white_texture(
423 &self.renderer.device,
424 &self.renderer.queue,
425 &self.renderer.scene.texture_bind_group_layout,
426 )
427 });
428 let mat = Material::new(bg);
429 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
430 EntityBuilder {
431 commands: self,
432 entity: id,
433 }
434 }
435
436 pub fn spawn_gltf(
441 &mut self,
442 pos: Vec3,
443 path: &str,
444 attach_colliders: bool,
445 ) -> Result<EntityBuilder<'_, 'a>, String> {
446 let default_bg = self.asset_manager.as_mut().unwrap().create_white_texture(
447 &self.renderer.device,
448 &self.renderer.queue,
449 &self.renderer.scene.texture_bind_group_layout,
450 );
451 let default_mat = Material::new(default_bg.clone());
452
453 match self.asset_manager.as_mut().unwrap().load_gltf_scene(
454 &self.renderer.device,
455 &self.renderer.queue,
456 &self.renderer.scene.texture_bind_group_layout,
457 default_bg,
458 path,
459 ) {
460 Ok(asset) => {
461 let root = self.world.spawn();
462 let mut trans = Transform::new(pos);
463 trans.update_local_matrix();
464
465 self.world.add_component(root, trans);
466 self.world
467 .add_component(root, gizmo_physics::GlobalTransform::default());
468 self.world
469 .add_component(root, EntityName(format!("GLTF: {}", path)));
470 self.world
471 .add_component(root, gizmo_core::component::Children(Vec::new()));
472
473 for node in &asset.roots {
474 spawn_gltf_node_flat(
475 self.world,
476 node,
477 root.id(),
478 default_mat.clone(),
479 attach_colliders,
480 );
481 }
482
483 if !asset.animations.is_empty() {
484 self.world.add_component(
485 root,
486 gizmo_renderer::components::AnimationPlayer {
487 active_animation: 0,
488 current_time: 0.0,
489 loop_anim: true,
490 speed: 1.0,
491 animations: std::sync::Arc::from(
492 asset.animations.clone().into_boxed_slice(),
493 ),
494 ..Default::default()
495 },
496 );
497 }
498
499 Ok(EntityBuilder {
500 commands: self,
501 entity: root,
502 })
503 }
504 Err(e) => Err(format!(
505 "[Commands::spawn_gltf] '{}' yuklenemedi: {}",
506 path, e
507 )),
508 }
509 }
510}
511
512pub struct EntityBuilder<'b, 'a> {
516 commands: &'b mut Commands<'a>,
517 entity: Entity,
518}
519
520impl<'b, 'a> EntityBuilder<'b, 'a> {
521 pub fn with_name(self, name: &str) -> Self {
523 self.commands
524 .world
525 .add_component(self.entity, EntityName(name.to_string()));
526 self
527 }
528
529 pub fn with<C: gizmo_core::Component + 'static>(self, component: C) -> Self {
531 self.commands.world.add_component(self.entity, component);
532 self
533 }
534
535 pub fn id(self) -> Entity {
537 self.entity
538 }
539}
540
541impl<'b, 'a> From<EntityBuilder<'b, 'a>> for Entity {
542 fn from(b: EntityBuilder<'b, 'a>) -> Entity {
543 b.entity
544 }
545}
546
547fn spawn_mesh_entity(
550 world: &mut World,
551 pos: Vec3,
552 mesh: gizmo_renderer::components::Mesh,
553 mat: Material,
554) -> Entity {
555 let id = world.spawn();
556 let mut trans = Transform::new(pos);
557 trans.update_local_matrix();
558
559 world.add_component(id, trans);
560 world.add_component(id, mesh);
561 world.add_component(id, mat);
562 world.add_component(id, MeshRenderer::new());
563 id
564}
565
566fn spawn_gltf_node_flat(
568 world: &mut World,
569 node: &gizmo_renderer::asset::GltfNodeData,
570 parent_id: u32,
571 default_mat: Material,
572 attach_colliders: bool,
573) {
574 use gizmo_core::component::{Children, Parent};
575 let entity = world.spawn();
576 let name = node.name.clone().unwrap_or_else(|| "GLTF_Node".to_string());
577 world.add_component(entity, EntityName(name));
578 world.add_component(entity, Parent(parent_id));
579 world.add_component(entity, Children(Vec::new()));
580
581 {
582 let mut ch_store = world.borrow_mut::<Children>();
583 if let Some(parent_ch) = ch_store.get_mut(parent_id) {
585 parent_ch.0.push(entity.id());
586 }
587 }
588
589 let raw_rot = Quat::from_xyzw(
590 node.rotation[0],
591 node.rotation[1],
592 node.rotation[2],
593 node.rotation[3],
594 );
595 let rot = if raw_rot.is_nan() || raw_rot.length() < 0.0001 {
596 Quat::IDENTITY
597 } else {
598 raw_rot.normalize()
599 };
600
601 let t = Transform::new(Vec3::new(
602 node.translation[0],
603 node.translation[1],
604 node.translation[2],
605 ))
606 .with_rotation(rot)
607 .with_scale(Vec3::new(node.scale[0], node.scale[1], node.scale[2]));
608 world.add_component(entity, t);
609 world.add_component(entity, gizmo_physics::GlobalTransform::default());
610
611 let mut newly_added_prims = Vec::new();
612 for (mesh, mat_opt) in node.primitives.iter() {
613 let prim = world.spawn();
614 world.add_component(prim, Transform::new(Vec3::ZERO));
615 world.add_component(prim, gizmo_physics::GlobalTransform::default());
616 world.add_component(prim, Parent(entity.id()));
617 world.add_component(prim, Children(Vec::new()));
618
619 newly_added_prims.push(prim.id());
620
621 world.add_component(prim, mesh.clone());
622 world.add_component(prim, mat_opt.clone().unwrap_or_else(|| default_mat.clone()));
623 world.add_component(prim, MeshRenderer::new());
624
625 if attach_colliders {
626 let extents = (mesh.bounds.max - mesh.bounds.min) / 2.0;
627 let cx = extents.x.max(0.01);
628 let cy = extents.y.max(0.01);
629 let cz = extents.z.max(0.01);
630 world.add_component(prim, gizmo_physics::shape::Collider::new_aabb(cx, cy, cz));
631 }
632 }
633
634 if !newly_added_prims.is_empty() {
636 {
637 let mut ch_store = world.borrow_mut::<Children>();
638 if let Some(parent_ch) = ch_store.get_mut(entity.id()) {
639 parent_ch.0.extend(newly_added_prims);
640 }
641 }
642 }
643
644 for child_node in &node.children {
645 spawn_gltf_node_flat(
646 world,
647 child_node,
648 entity.id(),
649 default_mat.clone(),
650 attach_colliders,
651 );
652 }
653}
654
655pub trait WorldExt {
660 fn entity_named(&self, name: &str) -> Option<u32>;
662
663 fn move_entity_named<F: FnMut(&mut gizmo_physics::Transform)>(&mut self, name: &str, f: F);
665
666 fn position_of(&self, name: &str) -> Option<Vec3>;
668
669 fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(&mut self, name: &str, f: F);
677}
678
679impl WorldExt for World {
680 fn entity_named(&self, name: &str) -> Option<u32> {
681 let mut names = self.query::<&EntityName>()?;
682 for (id, n) in names.iter_mut() {
683 if n.0 == name {
684 return Some(id);
685 }
686 }
687 None
688 }
689
690 fn move_entity_named<F: FnMut(&mut gizmo_physics::Transform)>(&mut self, name: &str, mut f: F) {
691 let target: Option<u32> = {
692 if let Some(mut names) = self.query::<&EntityName>() {
693 let mut found = None;
694 for (id, n) in names.iter_mut() {
695 if n.0 == name {
696 found = Some(id);
697 break;
698 }
699 }
700 found
701 } else {
702 None
703 }
704 };
705 if let Some(target_id) = target {
706 if let Some(mut transforms) =
707 self.query::<gizmo_core::prelude::Mut<gizmo_physics::Transform>>()
708 {
709 for (tid, mut trans) in transforms.iter_mut() {
710 if tid == target_id {
711 f(&mut trans);
712 trans.update_local_matrix();
713 }
714 }
715 }
716 }
717 }
718
719 fn position_of(&self, name: &str) -> Option<Vec3> {
720 let target_id = self.entity_named(name)?;
721 let transforms = self.borrow::<gizmo_physics::components::Transform>();
722 transforms.get(target_id).map(|t| t.position)
723 }
724
725 fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(
726 &mut self,
727 name: &str,
728 mut f: F,
729 ) {
730 let target: Option<u32> = {
731 if let Some(mut names) = self.query::<&EntityName>() {
732 let mut found = None;
733 for (id, n) in names.iter_mut() {
734 if n.0 == name {
735 found = Some(id);
736 break;
737 }
738 }
739 found
740 } else {
741 None
742 }
743 };
744 if let Some(target_id) = target {
745 {
746 let mut storage = self.borrow_mut::<T>();
747 if let Some(comp) = storage.get_mut(target_id) {
748 f(comp);
749 }
750 }
751 }
752 }
753}
754
755pub trait InputExt {
767 fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
769
770 fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
772
773 fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool;
775}
776
777impl InputExt for gizmo_core::input::Input {
778 #[inline]
779 fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
780 self.is_key_pressed(keycode as u32)
781 }
782 #[inline]
783 fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
784 self.is_key_just_pressed(keycode as u32)
785 }
786 #[inline]
787 fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool {
788 self.is_key_just_released(keycode as u32)
789 }
790}