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
11pub 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 query
30 .p0()
31 .par_iter_mut()
32 .for_each(|(transform, mut global_transform)| {
33 *global_transform = GlobalTransform::from(*transform);
34 });
35 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
45pub 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 #[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#[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 (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 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 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 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 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 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 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 {
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 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 assert_eq!(&**app.world().get::<Children>(parent).unwrap(), &[child]);
437 assert_eq!(
438 &**app.world().get::<Children>(child).unwrap(),
439 &[grandchild]
440 );
441 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 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 let mut schedule = Schedule::default();
496 schedule.add_systems((sync_simple_transforms, propagate_transforms));
497
498 let mut spawn_transform_bundle = || {
500 world
501 .spawn(TransformBundle::from_transform(
502 Transform::from_translation(translation),
503 ))
504 .id()
505 };
506
507 let parent = spawn_transform_bundle();
509 let child = spawn_transform_bundle();
510 world.entity_mut(parent).add_child(child);
511
512 schedule.run(&mut world);
514
515 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 world.entity_mut(child).remove_parent();
527 world.entity_mut(parent).add_child(child);
528
529 schedule.run(&mut world);
531
532 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}