bevy_transform/
systems.rs

1use crate::components::{GlobalTransform, Transform};
2use bevy_ecs::{
3    change_detection::Ref,
4    prelude::{Changed, DetectChanges, Entity, Query, With, Without},
5    query::{Added, Or},
6    removal_detection::RemovedComponents,
7    system::{Local, ParamSet},
8};
9use bevy_hierarchy::{Children, Parent};
10
11/// Update [`GlobalTransform`] component of entities that aren't in the hierarchy
12///
13/// Third party plugins should ensure that this is used in concert with [`propagate_transforms`].
14pub fn sync_simple_transforms(
15    mut query: ParamSet<(
16        Query<
17            (&Transform, &mut GlobalTransform),
18            (
19                Or<(Changed<Transform>, Added<GlobalTransform>)>,
20                Without<Parent>,
21                Without<Children>,
22            ),
23        >,
24        Query<(Ref<Transform>, &mut GlobalTransform), (Without<Parent>, Without<Children>)>,
25    )>,
26    mut orphaned: RemovedComponents<Parent>,
27) {
28    // Update changed entities.
29    query
30        .p0()
31        .par_iter_mut()
32        .for_each(|(transform, mut global_transform)| {
33            *global_transform = GlobalTransform::from(*transform);
34        });
35    // Update orphaned entities.
36    let mut query = query.p1();
37    let mut iter = query.iter_many_mut(orphaned.read());
38    while let Some((transform, mut global_transform)) = iter.fetch_next() {
39        if !transform.is_changed() && !global_transform.is_added() {
40            *global_transform = GlobalTransform::from(*transform);
41        }
42    }
43}
44
45/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
46/// [`Transform`] component.
47///
48/// Third party plugins should ensure that this is used in concert with [`sync_simple_transforms`].
49pub fn propagate_transforms(
50    mut root_query: Query<
51        (Entity, &Children, Ref<Transform>, &mut GlobalTransform),
52        Without<Parent>,
53    >,
54    mut orphaned: RemovedComponents<Parent>,
55    transform_query: Query<(Ref<Transform>, &mut GlobalTransform, Option<&Children>), With<Parent>>,
56    parent_query: Query<(Entity, Ref<Parent>)>,
57    mut orphaned_entities: Local<Vec<Entity>>,
58) {
59    orphaned_entities.clear();
60    orphaned_entities.extend(orphaned.read());
61    orphaned_entities.sort_unstable();
62    root_query.par_iter_mut().for_each(
63        |(entity, children, transform, mut global_transform)| {
64            let changed = transform.is_changed() || global_transform.is_added() || orphaned_entities.binary_search(&entity).is_ok();
65            if changed {
66                *global_transform = GlobalTransform::from(*transform);
67            }
68
69            for (child, actual_parent) in parent_query.iter_many(children) {
70                assert_eq!(
71                    actual_parent.get(), entity,
72                    "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
73                );
74                // SAFETY:
75                // - `child` must have consistent parentage, or the above assertion would panic.
76                // Since `child` is parented to a root entity, the entire hierarchy leading to it is consistent.
77                // - We may operate as if all descendants are consistent, since `propagate_recursive` will panic before 
78                //   continuing to propagate if it encounters an entity with inconsistent parentage.
79                // - Since each root entity is unique and the hierarchy is consistent and forest-like,
80                //   other root entities' `propagate_recursive` calls will not conflict with this one.
81                // - Since this is the only place where `transform_query` gets used, there will be no conflicting fetches elsewhere.
82                #[allow(unsafe_code)]
83                unsafe {
84                    propagate_recursive(
85                        &global_transform,
86                        &transform_query,
87                        &parent_query,
88                        child,
89                        changed || actual_parent.is_changed(),
90                    );
91                }
92            }
93        },
94    );
95}
96
97/// Recursively propagates the transforms for `entity` and all of its descendants.
98///
99/// # Panics
100///
101/// If `entity`'s descendants have a malformed hierarchy, this function will panic occur before propagating
102/// the transforms of any malformed entities and their descendants.
103///
104/// # Safety
105///
106/// - While this function is running, `transform_query` must not have any fetches for `entity`,
107/// nor any of its descendants.
108/// - The caller must ensure that the hierarchy leading to `entity`
109/// is well-formed and must remain as a tree or a forest. Each entity must have at most one parent.
110#[allow(unsafe_code)]
111unsafe fn propagate_recursive(
112    parent: &GlobalTransform,
113    transform_query: &Query<
114        (Ref<Transform>, &mut GlobalTransform, Option<&Children>),
115        With<Parent>,
116    >,
117    parent_query: &Query<(Entity, Ref<Parent>)>,
118    entity: Entity,
119    mut changed: bool,
120) {
121    let (global_matrix, children) = {
122        let Ok((transform, mut global_transform, children)) =
123            // SAFETY: This call cannot create aliased mutable references.
124            //   - The top level iteration parallelizes on the roots of the hierarchy.
125            //   - The caller ensures that each child has one and only one unique parent throughout the entire
126            //     hierarchy.
127            //
128            // For example, consider the following malformed hierarchy:
129            //
130            //     A
131            //   /   \
132            //  B     C
133            //   \   /
134            //     D
135            //
136            // D has two parents, B and C. If the propagation passes through C, but the Parent component on D points to B,
137            // the above check will panic as the origin parent does match the recorded parent.
138            //
139            // Also consider the following case, where A and B are roots:
140            //
141            //  A       B
142            //   \     /
143            //    C   D
144            //     \ /
145            //      E
146            //
147            // Even if these A and B start two separate tasks running in parallel, one of them will panic before attempting
148            // to mutably access E.
149            (unsafe { transform_query.get_unchecked(entity) }) else {
150                return;
151            };
152
153        changed |= transform.is_changed() || global_transform.is_added();
154        if changed {
155            *global_transform = parent.mul_transform(*transform);
156        }
157        (*global_transform, children)
158    };
159
160    let Some(children) = children else { return };
161    for (child, actual_parent) in parent_query.iter_many(children) {
162        assert_eq!(
163            actual_parent.get(), entity,
164            "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
165        );
166        // SAFETY: The caller guarantees that `transform_query` will not be fetched
167        // for any descendants of `entity`, so it is safe to call `propagate_recursive` for each child.
168        //
169        // The above assertion ensures that each child has one and only one unique parent throughout the
170        // entire hierarchy.
171        unsafe {
172            propagate_recursive(
173                &global_matrix,
174                transform_query,
175                parent_query,
176                child,
177                changed || actual_parent.is_changed(),
178            );
179        }
180    }
181}
182
183#[cfg(test)]
184mod test {
185    use bevy_app::prelude::*;
186    use bevy_ecs::prelude::*;
187    use bevy_ecs::world::CommandQueue;
188    use bevy_math::{vec3, Vec3};
189    use bevy_tasks::{ComputeTaskPool, TaskPool};
190
191    use crate::bundles::TransformBundle;
192    use crate::systems::*;
193    use bevy_hierarchy::{BuildChildren, BuildWorldChildren};
194
195    #[test]
196    fn correct_parent_removed() {
197        ComputeTaskPool::get_or_init(TaskPool::default);
198        let mut world = World::default();
199        let offset_global_transform =
200            |offset| GlobalTransform::from(Transform::from_xyz(offset, offset, offset));
201        let offset_transform =
202            |offset| TransformBundle::from_transform(Transform::from_xyz(offset, offset, offset));
203
204        let mut schedule = Schedule::default();
205        schedule.add_systems((sync_simple_transforms, propagate_transforms));
206
207        let mut command_queue = CommandQueue::default();
208        let mut commands = Commands::new(&mut command_queue, &world);
209        let root = commands.spawn(offset_transform(3.3)).id();
210        let parent = commands.spawn(offset_transform(4.4)).id();
211        let child = commands.spawn(offset_transform(5.5)).id();
212        commands.entity(parent).set_parent(root);
213        commands.entity(child).set_parent(parent);
214        command_queue.apply(&mut world);
215        schedule.run(&mut world);
216
217        assert_eq!(
218            world.get::<GlobalTransform>(parent).unwrap(),
219            &offset_global_transform(4.4 + 3.3),
220            "The transform systems didn't run, ie: `GlobalTransform` wasn't updated",
221        );
222
223        // Remove parent of `parent`
224        let mut command_queue = CommandQueue::default();
225        let mut commands = Commands::new(&mut command_queue, &world);
226        commands.entity(parent).remove_parent();
227        command_queue.apply(&mut world);
228        schedule.run(&mut world);
229
230        assert_eq!(
231            world.get::<GlobalTransform>(parent).unwrap(),
232            &offset_global_transform(4.4),
233            "The global transform of an orphaned entity wasn't updated properly",
234        );
235
236        // Remove parent of `child`
237        let mut command_queue = CommandQueue::default();
238        let mut commands = Commands::new(&mut command_queue, &world);
239        commands.entity(child).remove_parent();
240        command_queue.apply(&mut world);
241        schedule.run(&mut world);
242
243        assert_eq!(
244            world.get::<GlobalTransform>(child).unwrap(),
245            &offset_global_transform(5.5),
246            "The global transform of an orphaned entity wasn't updated properly",
247        );
248    }
249
250    #[test]
251    fn did_propagate() {
252        ComputeTaskPool::get_or_init(TaskPool::default);
253        let mut world = World::default();
254
255        let mut schedule = Schedule::default();
256        schedule.add_systems((sync_simple_transforms, propagate_transforms));
257
258        // Root entity
259        world.spawn(TransformBundle::from(Transform::from_xyz(1.0, 0.0, 0.0)));
260
261        let mut children = Vec::new();
262        world
263            .spawn(TransformBundle::from(Transform::from_xyz(1.0, 0.0, 0.0)))
264            .with_children(|parent| {
265                children.push(
266                    parent
267                        .spawn(TransformBundle::from(Transform::from_xyz(0.0, 2.0, 0.)))
268                        .id(),
269                );
270                children.push(
271                    parent
272                        .spawn(TransformBundle::from(Transform::from_xyz(0.0, 0.0, 3.)))
273                        .id(),
274                );
275            });
276        schedule.run(&mut world);
277
278        assert_eq!(
279            *world.get::<GlobalTransform>(children[0]).unwrap(),
280            GlobalTransform::from_xyz(1.0, 0.0, 0.0) * Transform::from_xyz(0.0, 2.0, 0.0)
281        );
282
283        assert_eq!(
284            *world.get::<GlobalTransform>(children[1]).unwrap(),
285            GlobalTransform::from_xyz(1.0, 0.0, 0.0) * Transform::from_xyz(0.0, 0.0, 3.0)
286        );
287    }
288
289    #[test]
290    fn did_propagate_command_buffer() {
291        let mut world = World::default();
292
293        let mut schedule = Schedule::default();
294        schedule.add_systems((sync_simple_transforms, propagate_transforms));
295
296        // Root entity
297        let mut queue = CommandQueue::default();
298        let mut commands = Commands::new(&mut queue, &world);
299        let mut children = Vec::new();
300        commands
301            .spawn(TransformBundle::from(Transform::from_xyz(1.0, 0.0, 0.0)))
302            .with_children(|parent| {
303                children.push(
304                    parent
305                        .spawn(TransformBundle::from(Transform::from_xyz(0.0, 2.0, 0.0)))
306                        .id(),
307                );
308                children.push(
309                    parent
310                        .spawn(TransformBundle::from(Transform::from_xyz(0.0, 0.0, 3.0)))
311                        .id(),
312                );
313            });
314        queue.apply(&mut world);
315        schedule.run(&mut world);
316
317        assert_eq!(
318            *world.get::<GlobalTransform>(children[0]).unwrap(),
319            GlobalTransform::from_xyz(1.0, 0.0, 0.0) * Transform::from_xyz(0.0, 2.0, 0.0)
320        );
321
322        assert_eq!(
323            *world.get::<GlobalTransform>(children[1]).unwrap(),
324            GlobalTransform::from_xyz(1.0, 0.0, 0.0) * Transform::from_xyz(0.0, 0.0, 3.0)
325        );
326    }
327
328    #[test]
329    fn correct_children() {
330        ComputeTaskPool::get_or_init(TaskPool::default);
331        let mut world = World::default();
332
333        let mut schedule = Schedule::default();
334        schedule.add_systems((sync_simple_transforms, propagate_transforms));
335
336        // Add parent entities
337        let mut children = Vec::new();
338        let parent = {
339            let mut command_queue = CommandQueue::default();
340            let mut commands = Commands::new(&mut command_queue, &world);
341            let parent = commands.spawn(Transform::from_xyz(1.0, 0.0, 0.0)).id();
342            commands.entity(parent).with_children(|parent| {
343                children.push(parent.spawn(Transform::from_xyz(0.0, 2.0, 0.0)).id());
344                children.push(parent.spawn(Transform::from_xyz(0.0, 3.0, 0.0)).id());
345            });
346            command_queue.apply(&mut world);
347            schedule.run(&mut world);
348            parent
349        };
350
351        assert_eq!(
352            world
353                .get::<Children>(parent)
354                .unwrap()
355                .iter()
356                .cloned()
357                .collect::<Vec<_>>(),
358            children,
359        );
360
361        // Parent `e1` to `e2`.
362        {
363            let mut command_queue = CommandQueue::default();
364            let mut commands = Commands::new(&mut command_queue, &world);
365            commands.entity(children[1]).add_child(children[0]);
366            command_queue.apply(&mut world);
367            schedule.run(&mut world);
368        }
369
370        assert_eq!(
371            world
372                .get::<Children>(parent)
373                .unwrap()
374                .iter()
375                .cloned()
376                .collect::<Vec<_>>(),
377            vec![children[1]]
378        );
379
380        assert_eq!(
381            world
382                .get::<Children>(children[1])
383                .unwrap()
384                .iter()
385                .cloned()
386                .collect::<Vec<_>>(),
387            vec![children[0]]
388        );
389
390        assert!(world.despawn(children[0]));
391
392        schedule.run(&mut world);
393
394        assert_eq!(
395            world
396                .get::<Children>(parent)
397                .unwrap()
398                .iter()
399                .cloned()
400                .collect::<Vec<_>>(),
401            vec![children[1]]
402        );
403    }
404
405    #[test]
406    fn correct_transforms_when_no_children() {
407        let mut app = App::new();
408        ComputeTaskPool::get_or_init(TaskPool::default);
409
410        app.add_systems(Update, (sync_simple_transforms, propagate_transforms));
411
412        let translation = vec3(1.0, 0.0, 0.0);
413
414        // These will be overwritten.
415        let mut child = Entity::from_raw(0);
416        let mut grandchild = Entity::from_raw(1);
417        let parent = app
418            .world_mut()
419            .spawn((
420                Transform::from_translation(translation),
421                GlobalTransform::IDENTITY,
422            ))
423            .with_children(|builder| {
424                child = builder
425                    .spawn(TransformBundle::IDENTITY)
426                    .with_children(|builder| {
427                        grandchild = builder.spawn(TransformBundle::IDENTITY).id();
428                    })
429                    .id();
430            })
431            .id();
432
433        app.update();
434
435        // check the `Children` structure is spawned
436        assert_eq!(&**app.world().get::<Children>(parent).unwrap(), &[child]);
437        assert_eq!(
438            &**app.world().get::<Children>(child).unwrap(),
439            &[grandchild]
440        );
441        // Note that at this point, the `GlobalTransform`s will not have updated yet, due to `Commands` delay
442        app.update();
443
444        let mut state = app.world_mut().query::<&GlobalTransform>();
445        for global in state.iter(app.world()) {
446            assert_eq!(global, &GlobalTransform::from_translation(translation));
447        }
448    }
449
450    #[test]
451    #[should_panic]
452    fn panic_when_hierarchy_cycle() {
453        ComputeTaskPool::get_or_init(TaskPool::default);
454        // We cannot directly edit Parent and Children, so we use a temp world to break
455        // the hierarchy's invariants.
456        let mut temp = World::new();
457        let mut app = App::new();
458
459        app.add_systems(Update, (propagate_transforms, sync_simple_transforms));
460
461        fn setup_world(world: &mut World) -> (Entity, Entity) {
462            let mut grandchild = Entity::from_raw(0);
463            let child = world
464                .spawn(TransformBundle::IDENTITY)
465                .with_children(|builder| {
466                    grandchild = builder.spawn(TransformBundle::IDENTITY).id();
467                })
468                .id();
469            (child, grandchild)
470        }
471
472        let (temp_child, temp_grandchild) = setup_world(&mut temp);
473        let (child, grandchild) = setup_world(app.world_mut());
474
475        assert_eq!(temp_child, child);
476        assert_eq!(temp_grandchild, grandchild);
477
478        app.world_mut()
479            .spawn(TransformBundle::IDENTITY)
480            .push_children(&[child]);
481        std::mem::swap(
482            &mut *app.world_mut().get_mut::<Parent>(child).unwrap(),
483            &mut *temp.get_mut::<Parent>(grandchild).unwrap(),
484        );
485
486        app.update();
487    }
488
489    #[test]
490    fn global_transform_should_not_be_overwritten_after_reparenting() {
491        let translation = Vec3::ONE;
492        let mut world = World::new();
493
494        // Create transform propagation schedule
495        let mut schedule = Schedule::default();
496        schedule.add_systems((sync_simple_transforms, propagate_transforms));
497
498        // Spawn a `TransformBundle` entity with a local translation of `Vec3::ONE`
499        let mut spawn_transform_bundle = || {
500            world
501                .spawn(TransformBundle::from_transform(
502                    Transform::from_translation(translation),
503                ))
504                .id()
505        };
506
507        // Spawn parent and child with identical transform bundles
508        let parent = spawn_transform_bundle();
509        let child = spawn_transform_bundle();
510        world.entity_mut(parent).add_child(child);
511
512        // Run schedule to propagate transforms
513        schedule.run(&mut world);
514
515        // Child should be positioned relative to its parent
516        let parent_global_transform = *world.entity(parent).get::<GlobalTransform>().unwrap();
517        let child_global_transform = *world.entity(child).get::<GlobalTransform>().unwrap();
518        assert!(parent_global_transform
519            .translation()
520            .abs_diff_eq(translation, 0.1));
521        assert!(child_global_transform
522            .translation()
523            .abs_diff_eq(2. * translation, 0.1));
524
525        // Reparent child
526        world.entity_mut(child).remove_parent();
527        world.entity_mut(parent).add_child(child);
528
529        // Run schedule to propagate transforms
530        schedule.run(&mut world);
531
532        // Translations should be unchanged after update
533        assert_eq!(
534            parent_global_transform,
535            *world.entity(parent).get::<GlobalTransform>().unwrap()
536        );
537        assert_eq!(
538            child_global_transform,
539            *world.entity(child).get::<GlobalTransform>().unwrap()
540        );
541    }
542}