1use crate::color::Color;
2use gizmo_core::{Entity, EntityName, World};
13use gizmo_math::{Quat, Vec3};
14use gizmo_physics_core::{Collider, Transform};
15use gizmo_physics_rigid::components::{RigidBody, Velocity};
16use gizmo_renderer::{
17 asset::AssetManager,
18 components::{Camera, DirectionalLight, Material, MeshRenderer, PointLight},
19 Renderer,
20};
21
22#[derive(Debug)]
32#[non_exhaustive]
33pub enum GltfLoadError {
34 Load {
43 path: String,
45 source: String,
47 },
48}
49
50impl std::fmt::Display for GltfLoadError {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 GltfLoadError::Load { path, source } => {
54 write!(f, "GLTF '{}' yuklenemedi: {}", path, source)
55 }
56 }
57 }
58}
59
60impl std::error::Error for GltfLoadError {}
61
62pub struct Commands<'a> {
65 pub world: &'a mut World,
66 pub renderer: &'a Renderer,
67 pub asset_manager: Option<AssetManager>,
68}
69
70impl<'a> Drop for Commands<'a> {
71 fn drop(&mut self) {
72 if let Some(am) = self.asset_manager.take() {
73 self.world.insert_resource(am);
74 }
75 }
76}
77
78impl<'a> Commands<'a> {
79 pub fn new(world: &'a mut World, renderer: &'a Renderer) -> Self {
80 let am = world.remove_resource::<AssetManager>().unwrap_or_default();
81 Self {
82 world,
83 renderer,
84 asset_manager: Some(am),
85 }
86 }
87
88 pub fn spawn_cube(&mut self, pos: Vec3, color: Color) -> EntityBuilder<'_, 'a> {
92 let mesh = AssetManager::create_cube(&self.renderer.device);
93 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
94 &self.renderer.device,
95 &self.renderer.queue,
96 &self.renderer.scene.texture_bind_group_layout,
97 );
98 let mat = Material::new(bg).with_unlit(color.to_vec4());
99 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
100 EntityBuilder {
101 commands: self,
102 entity: id,
103 }
104 }
105
106 pub fn spawn_sphere(&mut self, pos: Vec3, radius: f32, color: Color) -> EntityBuilder<'_, 'a> {
108 let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 20, 20);
109 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
110 &self.renderer.device,
111 &self.renderer.queue,
112 &self.renderer.scene.texture_bind_group_layout,
113 );
114 let mat = Material::new(bg).with_unlit(color.to_vec4());
115 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
116 EntityBuilder {
117 commands: self,
118 entity: id,
119 }
120 }
121
122 pub fn spawn_plane(&mut self, pos: Vec3, size: f32, color: Color) -> EntityBuilder<'_, 'a> {
124 let mesh = AssetManager::create_plane(&self.renderer.device, size);
125 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
126 &self.renderer.device,
127 &self.renderer.queue,
128 &self.renderer.scene.texture_bind_group_layout,
129 );
130 let mat = Material::new(bg).with_unlit(color.to_vec4());
131 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
132 EntityBuilder {
133 commands: self,
134 entity: id,
135 }
136 }
137
138 pub fn spawn_model(&mut self, pos: Vec3, path: &str) -> EntityBuilder<'_, 'a> {
140 let mesh = self
141 .asset_manager
142 .as_mut()
143 .unwrap()
144 .load_obj(&self.renderer.device, path);
145 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
146 &self.renderer.device,
147 &self.renderer.queue,
148 &self.renderer.scene.texture_bind_group_layout,
149 );
150 let mat = Material::new(bg);
151 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
152 EntityBuilder {
153 commands: self,
154 entity: id,
155 }
156 }
157
158 pub fn spawn_camera(&mut self, pos: Vec3) -> EntityBuilder<'_, 'a> {
163 if let Some(mut cameras) = self.world.query_mut::<gizmo_core::prelude::Mut<Camera>>() {
164 for (_, mut c) in cameras.iter_mut() {
165 c.primary = false;
166 }
167 }
168 let id = self.world.spawn();
169 let trans = Transform::new(pos);
170
171 self.world.add_component(id, trans);
172 self.world.add_component(
173 id,
174 Camera {
175 fov: 60.0_f32.to_radians(),
176 near: 0.1,
177 far: 1000.0,
178 yaw: -std::f32::consts::FRAC_PI_2,
179 pitch: 0.0,
180 exposure: 1.0,
181 primary: true,
182 projection: Default::default(),
183 },
184 );
185 EntityBuilder {
186 commands: self,
187 entity: id,
188 }
189 }
190
191 pub fn spawn_camera_with(
193 &mut self,
194 pos: Vec3,
195 fov_deg: f32,
196 near: f32,
197 far: f32,
198 ) -> EntityBuilder<'_, 'a> {
199 if let Some(mut cameras) = self.world.query_mut::<gizmo_core::prelude::Mut<Camera>>() {
200 for (_, mut c) in cameras.iter_mut() {
201 c.primary = false;
202 }
203 }
204 let id = self.world.spawn();
205 let trans = Transform::new(pos);
206
207 self.world.add_component(id, trans);
208 self.world.add_component(
209 id,
210 Camera {
211 fov: fov_deg.to_radians(),
212 near,
213 far,
214 yaw: -std::f32::consts::FRAC_PI_2,
215 pitch: 0.0,
216 exposure: 1.0,
217 primary: true,
218 projection: Default::default(),
219 },
220 );
221 EntityBuilder {
222 commands: self,
223 entity: id,
224 }
225 }
226
227 pub fn spawn_point_light(
231 &mut self,
232 pos: Vec3,
233 color: Color,
234 intensity: f32,
235 ) -> EntityBuilder<'_, 'a> {
236 let id = self.world.spawn();
237 let trans = Transform::new(pos);
238
239 self.world.add_component(id, trans);
240 self.world.add_component(
241 id,
242 PointLight::new(
243 gizmo_math::Vec3::new(color.0.x, color.0.y, color.0.z),
244 intensity,
245 10.0,
246 ),
247 );
248 EntityBuilder {
249 commands: self,
250 entity: id,
251 }
252 }
253
254 pub fn spawn_sun(
257 &mut self,
258 _direction: Vec3,
259 color: Color,
260 intensity: f32,
261 ) -> EntityBuilder<'_, 'a> {
262 let id = self.world.spawn();
263 let pos = Vec3::ZERO; let trans = Transform::new(pos);
265
266 self.world.add_component(id, trans);
267 self.world.add_component(
268 id,
269 DirectionalLight {
270 color: Vec3::new(color.0.x, color.0.y, color.0.z),
271 intensity,
272 role: crate::renderer::components::LightRole::Sun,
273 },
274 );
275 EntityBuilder {
276 commands: self,
277 entity: id,
278 }
279 }
280
281 pub fn spawn_skybox(&mut self, color: Color) -> EntityBuilder<'_, 'a> {
285 let mesh = AssetManager::create_inverted_cube(&self.renderer.device);
289 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
290 &self.renderer.device,
291 &self.renderer.queue,
292 &self.renderer.scene.texture_bind_group_layout,
293 );
294 let mat = Material::new(bg).with_unlit(color.to_vec4()).with_skybox();
295 let id = self.world.spawn();
296 let mut trans = Transform::new(Vec3::ZERO);
297 trans.scale = Vec3::new(500.0, 500.0, 500.0);
298 trans.update_local_matrix();
299
300 self.world.add_component(id, trans);
301 self.world.add_component(id, mesh);
302 self.world.add_component(id, mat);
303 self.world.add_component(id, MeshRenderer::new());
304 EntityBuilder {
305 commands: self,
306 entity: id,
307 }
308 }
309
310 pub fn spawn_rigid_cube(
315 &mut self,
316 pos: Vec3,
317 half_extents: Vec3,
318 color: Color,
319 mass: f32,
320 ) -> EntityBuilder<'_, 'a> {
321 let mesh = AssetManager::create_cube(&self.renderer.device);
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 {
331 let mut trans_store = self.world.borrow_mut::<Transform>();
332 if let Some(mut trans) = trans_store.get_mut(id.id()) {
333 trans.scale = half_extents * 2.0;
334 trans.update_local_matrix();
335 }
336 }
337 let mut rb = if mass > 0.0 {
338 RigidBody::new(mass, true)
339 } else {
340 RigidBody::new_static()
341 };
342 let col = Collider::box_collider(half_extents);
343 rb.update_inertia_from_collider(&col);
344 self.world.add_component(id, rb);
345 if mass > 0.0 {
346 self.world.add_component(id, Velocity::new(Vec3::ZERO));
347 }
348 self.world.add_component(id, col);
349 EntityBuilder {
350 commands: self,
351 entity: id,
352 }
353 }
354
355 pub fn spawn_rigid_sphere(
357 &mut self,
358 pos: Vec3,
359 radius: f32,
360 color: Color,
361 mass: f32,
362 ) -> EntityBuilder<'_, 'a> {
363 let mesh = AssetManager::create_sphere(&self.renderer.device, radius, 16, 16);
364 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
365 &self.renderer.device,
366 &self.renderer.queue,
367 &self.renderer.scene.texture_bind_group_layout,
368 );
369 let mat = Material::new(bg).with_unlit(color.to_vec4());
370 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
371 let mut rb = if mass > 0.0 {
372 RigidBody::new(mass, true)
373 } else {
374 RigidBody::new_static()
375 };
376 let col = Collider::sphere(radius);
377 rb.update_inertia_from_collider(&col);
378 self.world.add_component(id, rb);
379 if mass > 0.0 {
380 self.world.add_component(id, Velocity::new(Vec3::ZERO));
381 }
382 self.world.add_component(id, col);
383 EntityBuilder {
384 commands: self,
385 entity: id,
386 }
387 }
388
389 pub fn spawn_static_plane(
391 &mut self,
392 pos: Vec3,
393 size: f32,
394 color: Color,
395 ) -> EntityBuilder<'_, 'a> {
396 let mesh = AssetManager::create_plane(&self.renderer.device, size);
397 let bg = self.asset_manager.as_mut().unwrap().create_white_texture(
398 &self.renderer.device,
399 &self.renderer.queue,
400 &self.renderer.scene.texture_bind_group_layout,
401 );
402 let mat = Material::new(bg).with_pbr(color.to_vec4(), 0.9, 0.0);
403 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
404 self.world.add_component(id, RigidBody::new_static());
405 self.world.add_component(
406 id,
407 Collider::box_collider(Vec3::new(size / 2.0, 0.05, size / 2.0)),
408 );
409 EntityBuilder {
410 commands: self,
411 entity: id,
412 }
413 }
414
415 pub fn spawn_textured_cube(&mut self, pos: Vec3, texture_path: &str) -> EntityBuilder<'_, 'a> {
419 let mesh = AssetManager::create_cube(&self.renderer.device);
420 let bg = self
421 .asset_manager
422 .as_mut()
423 .unwrap()
424 .load_material_texture(
425 &self.renderer.device,
426 &self.renderer.queue,
427 &self.renderer.scene.texture_bind_group_layout,
428 texture_path,
429 )
430 .unwrap_or_else(|_| {
431 self.asset_manager.as_mut().unwrap().create_white_texture(
432 &self.renderer.device,
433 &self.renderer.queue,
434 &self.renderer.scene.texture_bind_group_layout,
435 )
436 });
437 let mat = Material::new(bg);
438 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
439 EntityBuilder {
440 commands: self,
441 entity: id,
442 }
443 }
444
445 pub fn spawn_textured_plane(
447 &mut self,
448 pos: Vec3,
449 size: f32,
450 texture_path: &str,
451 ) -> EntityBuilder<'_, 'a> {
452 let mesh = AssetManager::create_plane(&self.renderer.device, size);
453 let bg = self
454 .asset_manager
455 .as_mut()
456 .unwrap()
457 .load_material_texture(
458 &self.renderer.device,
459 &self.renderer.queue,
460 &self.renderer.scene.texture_bind_group_layout,
461 texture_path,
462 )
463 .unwrap_or_else(|_| {
464 self.asset_manager.as_mut().unwrap().create_white_texture(
465 &self.renderer.device,
466 &self.renderer.queue,
467 &self.renderer.scene.texture_bind_group_layout,
468 )
469 });
470 let mat = Material::new(bg);
471 let id = spawn_mesh_entity(self.world, pos, mesh, mat);
472 EntityBuilder {
473 commands: self,
474 entity: id,
475 }
476 }
477
478 pub fn spawn_gltf(
483 &mut self,
484 pos: Vec3,
485 path: &str,
486 attach_colliders: bool,
487 ) -> Result<EntityBuilder<'_, 'a>, GltfLoadError> {
488 let default_bg = self.asset_manager.as_mut().unwrap().create_white_texture(
489 &self.renderer.device,
490 &self.renderer.queue,
491 &self.renderer.scene.texture_bind_group_layout,
492 );
493 let default_mat = Material::new(default_bg.clone());
494
495 match self.asset_manager.as_mut().unwrap().load_gltf_scene(
496 &self.renderer.device,
497 &self.renderer.queue,
498 &self.renderer.scene.texture_bind_group_layout,
499 default_bg,
500 path,
501 ) {
502 Ok(asset) => {
503 let root = self.world.spawn();
504 let mut trans = Transform::new(pos);
505 trans.update_local_matrix();
506
507 self.world.add_component(root, trans);
508 self.world
509 .add_component(root, gizmo_physics_core::components::GlobalTransform::default());
510 self.world
511 .add_component(root, EntityName(format!("GLTF: {}", path)));
512 self.world
513 .add_component(root, gizmo_core::component::Children(Vec::new()));
514
515 let mut skeletons = Vec::new();
516 for skel_data in &asset.skeletons {
517 skeletons.push(self.renderer.create_skeleton(std::sync::Arc::new(skel_data.clone())));
518 }
519
520 for node in &asset.roots {
521 spawn_gltf_node_flat(
522 self.world,
523 node,
524 root.id(),
525 default_mat.clone(),
526 attach_colliders,
527 &skeletons,
528 );
529 }
530
531 if !skeletons.is_empty() {
532 self.world.add_component(root, skeletons[0].clone());
533 }
534
535 if !asset.animations.is_empty() {
536 self.world.add_component(
537 root,
538 gizmo_renderer::components::AnimationPlayer {
539 active_animation: 0,
540 current_time: 0.0,
541 loop_anim: true,
542 speed: 1.0,
543 animations: std::sync::Arc::from(
544 asset.animations.clone().into_boxed_slice(),
545 ),
546 ..Default::default()
547 },
548 );
549 }
550
551 Ok(EntityBuilder {
552 commands: self,
553 entity: root,
554 })
555 }
556 Err(e) => Err(GltfLoadError::Load {
557 path: path.to_string(),
558 source: e.to_string(),
559 }),
560 }
561 }
562
563 pub fn spawn_gltf_async_completed(
565 &mut self,
566 completion: gizmo_renderer::async_assets::GltfImportCompletion,
567 pos: Vec3,
568 attach_colliders: bool,
569 ) -> Result<EntityBuilder<'_, 'a>, GltfLoadError> {
570 let default_bg = self.asset_manager.as_mut().unwrap().create_white_texture(
571 &self.renderer.device,
572 &self.renderer.queue,
573 &self.renderer.scene.texture_bind_group_layout,
574 );
575 let default_mat = Material::new(default_bg.clone());
576
577 match self.asset_manager.as_mut().unwrap().load_gltf_from_import(
578 &self.renderer.device,
579 &self.renderer.queue,
580 &self.renderer.scene.texture_bind_group_layout,
581 default_bg,
582 &completion.path,
583 completion.document,
584 completion.buffers,
585 completion.images,
586 ) {
587 Ok(asset) => {
588 let root = self.world.spawn();
589 let mut trans = Transform::new(pos);
590 trans.update_local_matrix();
591
592 self.world.add_component(root, trans);
593 self.world
594 .add_component(root, gizmo_physics_core::components::GlobalTransform::default());
595 self.world
596 .add_component(root, EntityName(format!("GLTF: {}", completion.path)));
597 self.world
598 .add_component(root, gizmo_core::component::Children(Vec::new()));
599
600 let mut skeletons = Vec::new();
601 for skel_data in &asset.skeletons {
602 skeletons.push(self.renderer.create_skeleton(std::sync::Arc::new(skel_data.clone())));
603 }
604
605 for node in &asset.roots {
606 spawn_gltf_node_flat(
607 self.world,
608 node,
609 root.id(),
610 default_mat.clone(),
611 attach_colliders,
612 &skeletons,
613 );
614 }
615
616 if !skeletons.is_empty() {
617 self.world.add_component(root, skeletons[0].clone());
618 }
619
620 if !asset.animations.is_empty() {
621 self.world.add_component(
622 root,
623 gizmo_renderer::components::AnimationPlayer {
624 active_animation: 0,
625 current_time: 0.0,
626 loop_anim: true,
627 speed: 1.0,
628 animations: std::sync::Arc::from(
629 asset.animations.clone().into_boxed_slice(),
630 ),
631 ..Default::default()
632 },
633 );
634 }
635
636 Ok(EntityBuilder {
637 commands: self,
638 entity: root,
639 })
640 }
641 Err(e) => Err(GltfLoadError::Load {
642 path: completion.path.clone(),
643 source: e.to_string(),
644 }),
645 }
646 }
647}
648
649pub struct EntityBuilder<'b, 'a> {
653 commands: &'b mut Commands<'a>,
654 entity: Entity,
655}
656
657impl<'b, 'a> EntityBuilder<'b, 'a> {
658 pub fn with_name(self, name: &str) -> Self {
660 self.commands
661 .world
662 .add_component(self.entity, EntityName(name.to_string()));
663 self
664 }
665
666 pub fn with<C: gizmo_core::Component + 'static>(self, component: C) -> Self {
668 self.commands.world.add_component(self.entity, component);
669 self
670 }
671
672 pub fn id(self) -> Entity {
674 self.entity
675 }
676}
677
678impl<'b, 'a> From<EntityBuilder<'b, 'a>> for Entity {
679 fn from(b: EntityBuilder<'b, 'a>) -> Entity {
680 b.entity
681 }
682}
683
684fn spawn_mesh_entity(
687 world: &mut World,
688 pos: Vec3,
689 mesh: gizmo_renderer::components::Mesh,
690 mat: Material,
691) -> Entity {
692 let id = world.spawn();
693 let mut trans = Transform::new(pos);
694 trans.update_local_matrix();
695
696 world.add_component(id, trans);
697 world.add_component(id, gizmo_physics_core::components::GlobalTransform::default());
698 world.add_component(id, mesh);
699 world.add_component(id, mat);
700 world.add_component(id, MeshRenderer::new());
701 id
702}
703
704fn spawn_gltf_node_flat(
706 world: &mut World,
707 node: &gizmo_renderer::asset::GltfNodeData,
708 parent_id: u32,
709 default_mat: Material,
710 attach_colliders: bool,
711 skeletons: &[gizmo_renderer::components::Skeleton],
712) {
713 use gizmo_core::component::{Children, Parent};
714 let entity = world.spawn();
715 let name = node.name.clone().unwrap_or_else(|| "GLTF_Node".to_string());
716 world.add_component(entity, EntityName(name));
717 world.add_component(entity, Parent(parent_id));
718 world.add_component(entity, Children(Vec::new()));
719
720 {
721 let mut ch_store = world.borrow_mut::<Children>();
722 if let Some(mut parent_ch) = ch_store.get_mut(parent_id) {
724 parent_ch.0.push(entity.id());
725 }
726 }
727
728 let raw_rot = Quat::from_xyzw(
729 node.rotation[0],
730 node.rotation[1],
731 node.rotation[2],
732 node.rotation[3],
733 );
734 let rot = if raw_rot.is_nan() || raw_rot.length() < 0.0001 {
735 Quat::IDENTITY
736 } else {
737 raw_rot.normalize()
738 };
739
740 let mut t = Transform::new(Vec3::new(
741 node.translation[0],
742 node.translation[1],
743 node.translation[2],
744 ))
745 .with_rotation(rot)
746 .with_scale(Vec3::new(node.scale[0], node.scale[1], node.scale[2]));
747 t.update_local_matrix();
748
749 if node.skin_index.is_some() || node.name.as_deref() == Some("Armature") {
750 t = Transform::default();
751 t.update_local_matrix();
752 }
753
754 world.add_component(entity, t);
755 world.add_component(entity, gizmo_physics_core::components::GlobalTransform::default());
756
757 tracing::debug!("SPAWN GLTF NODE: name={:?}, num_primitives={}", node.name, node.primitives.len());
758 let mut newly_added_prims = Vec::new();
759 for (mesh, mat_opt) in node.primitives.iter() {
760 tracing::debug!(" SPAWN PRIM: mesh_source='{}', bounds_min={:?}, bounds_max={:?}", mesh.source, mesh.bounds.min, mesh.bounds.max);
761 let prim = world.spawn();
762 let mut prim_t = Transform::new(Vec3::ZERO);
763 prim_t.update_local_matrix();
764 world.add_component(prim, prim_t);
765 world.add_component(prim, gizmo_physics_core::components::GlobalTransform::default());
766 world.add_component(prim, Parent(entity.id()));
767 world.add_component(prim, Children(Vec::new()));
768
769 newly_added_prims.push(prim.id());
770
771 world.add_component(prim, mesh.clone());
772 world.add_component(prim, mat_opt.clone().unwrap_or_else(|| default_mat.clone()));
773 world.add_component(prim, MeshRenderer::new());
774
775 if let Some(skin_idx) = node.skin_index {
776 if skin_idx < skeletons.len() {
777 world.add_component(prim, skeletons[skin_idx].clone());
778 }
779 }
780
781 if attach_colliders {
782 let extents = (mesh.bounds.max - mesh.bounds.min) / 2.0;
783 let center_offset = (mesh.bounds.max + mesh.bounds.min) / 2.0;
784 let cx = extents.x.max(0.01);
785 let cy = extents.y.max(0.01);
786 let cz = extents.z.max(0.01);
787
788 if center_offset.length_squared() > 0.0001 {
790 world.add_component(
791 prim,
792 gizmo_physics_core::Collider::offset_box(center_offset.into(), gizmo_math::Vec3::new(cx, cy, cz)),
793 );
794 } else {
795 world.add_component(prim, gizmo_physics_core::Collider::new_aabb(cx, cy, cz));
796 }
797 }
798 }
799
800 if !newly_added_prims.is_empty() {
802 {
803 let mut ch_store = world.borrow_mut::<Children>();
804 if let Some(mut parent_ch) = ch_store.get_mut(entity.id()) {
805 parent_ch.0.extend(newly_added_prims);
806 }
807 }
808 }
809
810 for child_node in &node.children {
811 spawn_gltf_node_flat(
812 world,
813 child_node,
814 entity.id(),
815 default_mat.clone(),
816 attach_colliders,
817 skeletons,
818 );
819 }
820}
821
822pub trait WorldExt {
827 fn entity_named(&self, name: &str) -> Option<u32>;
829
830 fn move_entity_named<F: FnMut(&mut gizmo_physics_core::Transform)>(&mut self, name: &str, f: F);
832
833 fn position_of(&self, name: &str) -> Option<Vec3>;
835
836 fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(&mut self, name: &str, f: F);
844}
845
846impl WorldExt for World {
847 fn entity_named(&self, name: &str) -> Option<u32> {
848 let mut names = self.query::<&EntityName>()?;
849 for (id, n) in names.iter_mut() {
850 if n.0 == name {
851 return Some(id);
852 }
853 }
854 None
855 }
856
857 fn move_entity_named<F: FnMut(&mut gizmo_physics_core::Transform)>(&mut self, name: &str, mut f: F) {
858 let target: Option<u32> = {
859 if let Some(mut names) = self.query::<&EntityName>() {
860 let mut found = None;
861 for (id, n) in names.iter_mut() {
862 if n.0 == name {
863 found = Some(id);
864 break;
865 }
866 }
867 found
868 } else {
869 None
870 }
871 };
872 if let Some(target_id) = target {
873 if let Some(mut transforms) =
874 self.query_mut::<gizmo_core::prelude::Mut<gizmo_physics_core::Transform>>()
875 {
876 for (tid, mut trans) in transforms.iter_mut() {
877 if tid == target_id {
878 f(&mut trans);
879 trans.update_local_matrix();
880 }
881 }
882 }
883 }
884 }
885
886 fn position_of(&self, name: &str) -> Option<Vec3> {
887 let target_id = self.entity_named(name)?;
888 let transforms = self.borrow::<gizmo_physics_core::Transform>();
889 transforms.get(target_id).map(|t| t.position)
890 }
891
892 fn modify<T: gizmo_core::Component + 'static, F: FnMut(&mut T)>(
893 &mut self,
894 name: &str,
895 mut f: F,
896 ) {
897 let target: Option<u32> = {
898 if let Some(mut names) = self.query::<&EntityName>() {
899 let mut found = None;
900 for (id, n) in names.iter_mut() {
901 if n.0 == name {
902 found = Some(id);
903 break;
904 }
905 }
906 found
907 } else {
908 None
909 }
910 };
911 if let Some(target_id) = target {
912 {
913 let mut storage = self.borrow_mut::<T>();
914 if let Some(mut comp) = storage.get_mut(target_id) {
915 f(&mut *comp);
916 }
917 }
918 }
919 }
920}
921
922pub trait InputExt {
934 fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
936
937 fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool;
939
940 fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool;
942}
943
944impl InputExt for gizmo_core::input::Input {
945 #[inline]
946 fn pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
947 self.is_key_pressed(keycode as u32)
948 }
949 #[inline]
950 fn just_pressed(&self, keycode: winit::keyboard::KeyCode) -> bool {
951 self.is_key_just_pressed(keycode as u32)
952 }
953 #[inline]
954 fn just_released(&self, keycode: winit::keyboard::KeyCode) -> bool {
955 self.is_key_just_released(keycode as u32)
956 }
957}