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 pub fn spawn_gltf_async_completed(
513 &mut self,
514 completion: gizmo_renderer::async_assets::GltfImportCompletion,
515 pos: Vec3,
516 attach_colliders: bool,
517 ) -> Result<EntityBuilder<'_, 'a>, String> {
518 let default_bg = self.asset_manager.as_mut().unwrap().create_white_texture(
519 &self.renderer.device,
520 &self.renderer.queue,
521 &self.renderer.scene.texture_bind_group_layout,
522 );
523 let default_mat = Material::new(default_bg.clone());
524
525 match self.asset_manager.as_mut().unwrap().load_gltf_from_import(
526 &self.renderer.device,
527 &self.renderer.queue,
528 &self.renderer.scene.texture_bind_group_layout,
529 default_bg,
530 &completion.path,
531 completion.document,
532 completion.buffers,
533 completion.images,
534 ) {
535 Ok(asset) => {
536 let root = self.world.spawn();
537 let mut trans = Transform::new(pos);
538 trans.update_local_matrix();
539
540 self.world.add_component(root, trans);
541 self.world
542 .add_component(root, gizmo_physics::GlobalTransform::default());
543 self.world
544 .add_component(root, EntityName(format!("GLTF: {}", completion.path)));
545 self.world
546 .add_component(root, gizmo_core::component::Children(Vec::new()));
547
548 for node in &asset.roots {
549 spawn_gltf_node_flat(
550 self.world,
551 node,
552 root.id(),
553 default_mat.clone(),
554 attach_colliders,
555 );
556 }
557
558 if !asset.animations.is_empty() {
559 self.world.add_component(
560 root,
561 gizmo_renderer::components::AnimationPlayer {
562 active_animation: 0,
563 current_time: 0.0,
564 loop_anim: true,
565 speed: 1.0,
566 animations: std::sync::Arc::from(
567 asset.animations.clone().into_boxed_slice(),
568 ),
569 ..Default::default()
570 },
571 );
572 }
573
574 Ok(EntityBuilder {
575 commands: self,
576 entity: root,
577 })
578 }
579 Err(e) => Err(format!(
580 "[Commands::spawn_gltf_async_completed] '{}' yüklenemedi: {}",
581 completion.path, e
582 )),
583 }
584 }
585}
586
587pub struct EntityBuilder<'b, 'a> {
591 commands: &'b mut Commands<'a>,
592 entity: Entity,
593}
594
595impl<'b, 'a> EntityBuilder<'b, 'a> {
596 pub fn with_name(self, name: &str) -> Self {
598 self.commands
599 .world
600 .add_component(self.entity, EntityName(name.to_string()));
601 self
602 }
603
604 pub fn with<C: gizmo_core::Component + 'static>(self, component: C) -> Self {
606 self.commands.world.add_component(self.entity, component);
607 self
608 }
609
610 pub fn id(self) -> Entity {
612 self.entity
613 }
614}
615
616impl<'b, 'a> From<EntityBuilder<'b, 'a>> for Entity {
617 fn from(b: EntityBuilder<'b, 'a>) -> Entity {
618 b.entity
619 }
620}
621
622fn spawn_mesh_entity(
625 world: &mut World,
626 pos: Vec3,
627 mesh: gizmo_renderer::components::Mesh,
628 mat: Material,
629) -> Entity {
630 let id = world.spawn();
631 let mut trans = Transform::new(pos);
632 trans.update_local_matrix();
633
634 world.add_component(id, trans);
635 world.add_component(id, mesh);
636 world.add_component(id, mat);
637 world.add_component(id, MeshRenderer::new());
638 id
639}
640
641fn spawn_gltf_node_flat(
643 world: &mut World,
644 node: &gizmo_renderer::asset::GltfNodeData,
645 parent_id: u32,
646 default_mat: Material,
647 attach_colliders: bool,
648) {
649 use gizmo_core::component::{Children, Parent};
650 let entity = world.spawn();
651 let name = node.name.clone().unwrap_or_else(|| "GLTF_Node".to_string());
652 world.add_component(entity, EntityName(name));
653 world.add_component(entity, Parent(parent_id));
654 world.add_component(entity, Children(Vec::new()));
655
656 {
657 let mut ch_store = world.borrow_mut::<Children>();
658 if let Some(parent_ch) = ch_store.get_mut(parent_id) {
660 parent_ch.0.push(entity.id());
661 }
662 }
663
664 let raw_rot = Quat::from_xyzw(
665 node.rotation[0],
666 node.rotation[1],
667 node.rotation[2],
668 node.rotation[3],
669 );
670 let rot = if raw_rot.is_nan() || raw_rot.length() < 0.0001 {
671 Quat::IDENTITY
672 } else {
673 raw_rot.normalize()
674 };
675
676 let t = Transform::new(Vec3::new(
677 node.translation[0],
678 node.translation[1],
679 node.translation[2],
680 ))
681 .with_rotation(rot)
682 .with_scale(Vec3::new(node.scale[0], node.scale[1], node.scale[2]));
683 world.add_component(entity, t);
684 world.add_component(entity, gizmo_physics::GlobalTransform::default());
685
686 let mut newly_added_prims = Vec::new();
687 for (mesh, mat_opt) in node.primitives.iter() {
688 let prim = world.spawn();
689 world.add_component(prim, Transform::new(Vec3::ZERO));
690 world.add_component(prim, gizmo_physics::GlobalTransform::default());
691 world.add_component(prim, Parent(entity.id()));
692 world.add_component(prim, Children(Vec::new()));
693
694 newly_added_prims.push(prim.id());
695
696 world.add_component(prim, mesh.clone());
697 world.add_component(prim, mat_opt.clone().unwrap_or_else(|| default_mat.clone()));
698 world.add_component(prim, MeshRenderer::new());
699
700 if attach_colliders {
701 let extents = (mesh.bounds.max - mesh.bounds.min) / 2.0;
702 let cx = extents.x.max(0.01);
703 let cy = extents.y.max(0.01);
704 let cz = extents.z.max(0.01);
705 world.add_component(prim, gizmo_physics::shape::Collider::new_aabb(cx, cy, cz));
706 }
707 }
708
709 if !newly_added_prims.is_empty() {
711 {
712 let mut ch_store = world.borrow_mut::<Children>();
713 if let Some(parent_ch) = ch_store.get_mut(entity.id()) {
714 parent_ch.0.extend(newly_added_prims);
715 }
716 }
717 }
718
719 for child_node in &node.children {
720 spawn_gltf_node_flat(
721 world,
722 child_node,
723 entity.id(),
724 default_mat.clone(),
725 attach_colliders,
726 );
727 }
728}
729
730pub trait WorldExt {
735 fn entity_named(&self, name: &str) -> Option<u32>;
737
738 fn move_entity_named<F: FnMut(&mut gizmo_physics::Transform)>(&mut self, name: &str, f: F);
740
741 fn position_of(&self, name: &str) -> Option<Vec3>;
743
744 fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(&mut self, name: &str, f: F);
752}
753
754impl WorldExt for World {
755 fn entity_named(&self, name: &str) -> Option<u32> {
756 let mut names = self.query::<&EntityName>()?;
757 for (id, n) in names.iter_mut() {
758 if n.0 == name {
759 return Some(id);
760 }
761 }
762 None
763 }
764
765 fn move_entity_named<F: FnMut(&mut gizmo_physics::Transform)>(&mut self, name: &str, mut f: F) {
766 let target: Option<u32> = {
767 if let Some(mut names) = self.query::<&EntityName>() {
768 let mut found = None;
769 for (id, n) in names.iter_mut() {
770 if n.0 == name {
771 found = Some(id);
772 break;
773 }
774 }
775 found
776 } else {
777 None
778 }
779 };
780 if let Some(target_id) = target {
781 if let Some(mut transforms) =
782 self.query::<gizmo_core::prelude::Mut<gizmo_physics::Transform>>()
783 {
784 for (tid, mut trans) in transforms.iter_mut() {
785 if tid == target_id {
786 f(&mut trans);
787 trans.update_local_matrix();
788 }
789 }
790 }
791 }
792 }
793
794 fn position_of(&self, name: &str) -> Option<Vec3> {
795 let target_id = self.entity_named(name)?;
796 let transforms = self.borrow::<gizmo_physics::components::Transform>();
797 transforms.get(target_id).map(|t| t.position)
798 }
799
800 fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(
801 &mut self,
802 name: &str,
803 mut f: F,
804 ) {
805 let target: Option<u32> = {
806 if let Some(mut names) = self.query::<&EntityName>() {
807 let mut found = None;
808 for (id, n) in names.iter_mut() {
809 if n.0 == name {
810 found = Some(id);
811 break;
812 }
813 }
814 found
815 } else {
816 None
817 }
818 };
819 if let Some(target_id) = target {
820 {
821 let mut storage = self.borrow_mut::<T>();
822 if let Some(comp) = storage.get_mut(target_id) {
823 f(comp);
824 }
825 }
826 }
827 }
828}
829
830pub trait InputExt {
842 fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
844
845 fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
847
848 fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool;
850}
851
852impl InputExt for gizmo_core::input::Input {
853 #[inline]
854 fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
855 self.is_key_pressed(keycode as u32)
856 }
857 #[inline]
858 fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
859 self.is_key_just_pressed(keycode as u32)
860 }
861 #[inline]
862 fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool {
863 self.is_key_just_released(keycode as u32)
864 }
865}