Skip to content

Commit f19db01

Browse files
committed
Fix transform propagation for orphaned hierarchies
1 parent 88b353c commit f19db01

File tree

1 file changed

+56
-13
lines changed

1 file changed

+56
-13
lines changed

crates/bevy_transform/src/systems.rs

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::components::{GlobalTransform, Transform};
22
use bevy_ecs::{
33
change_detection::Ref,
44
prelude::{Changed, DetectChanges, Entity, Query, With, Without},
5+
system::RemovedComponents,
56
};
67
use bevy_hierarchy::{Children, Parent};
78

@@ -23,23 +24,28 @@ pub fn sync_simple_transforms(
2324
/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
2425
/// [`Transform`] component.
2526
///
27+
/// Note that this depends on the [`World::clear_trackers`] system to be present.
28+
///
2629
/// Third party plugins should use [`transform_propagate_system_set`](crate::transform_propagate_system_set)
2730
/// to propagate transforms correctly.
2831
pub fn propagate_transforms(
2932
mut root_query: Query<
3033
(Entity, &Children, Ref<Transform>, &mut GlobalTransform),
3134
Without<Parent>,
3235
>,
36+
orphaned: RemovedComponents<Parent>,
3337
transform_query: Query<(Ref<Transform>, &mut GlobalTransform, Option<&Children>), With<Parent>>,
3438
parent_query: Query<(Entity, Ref<Parent>)>,
3539
) {
40+
let mut orphaned = orphaned.iter().collect::<Vec<_>>();
41+
orphaned.sort_unstable();
3642
root_query.par_for_each_mut(
3743
// The differing depths and sizes of hierarchy trees causes the work for each root to be
3844
// different. A batch size of 1 ensures that each tree gets it's own task and multiple
3945
// large trees are not clumped together.
4046
1,
4147
|(entity, children, transform, mut global_transform)| {
42-
let changed = transform.is_changed();
48+
let changed = transform.is_changed() || orphaned.binary_search(&entity).is_ok();
4349
if changed {
4450
*global_transform = GlobalTransform::from(*transform);
4551
}
@@ -165,21 +171,62 @@ mod test {
165171
use bevy_tasks::{ComputeTaskPool, TaskPool};
166172

167173
use crate::components::{GlobalTransform, Transform};
168-
use crate::systems::*;
169-
use crate::TransformBundle;
174+
use crate::{transform_propagate_system_set, TransformBundle};
170175
use bevy_hierarchy::{BuildChildren, BuildWorldChildren, Children, Parent};
171176

172177
#[derive(StageLabel)]
173178
struct Update;
174179

180+
#[test]
181+
fn correct_parent_removed() {
182+
ComputeTaskPool::init(TaskPool::default);
183+
let mut world = World::default();
184+
185+
let mut update_stage = SystemStage::parallel();
186+
update_stage.add_system_set(transform_propagate_system_set());
187+
188+
let mut schedule = Schedule::default();
189+
schedule.add_stage(Update, update_stage);
190+
191+
let mut command_queue = CommandQueue::default();
192+
let mut commands = Commands::new(&mut command_queue, &world);
193+
let root = commands
194+
.spawn(TransformBundle::from_transform(Transform::from_xyz(
195+
9.9, 9.9, 9.9,
196+
)))
197+
.id();
198+
let parent = commands.spawn(TransformBundle::IDENTITY).id();
199+
let child = commands.spawn(TransformBundle::IDENTITY).id();
200+
commands.entity(parent).set_parent(root);
201+
commands.entity(child).set_parent(parent);
202+
command_queue.apply(&mut world);
203+
schedule.run(&mut world);
204+
205+
assert_ne!(
206+
world.get::<GlobalTransform>(parent).unwrap(),
207+
&GlobalTransform::from(Transform::IDENTITY)
208+
);
209+
210+
// Remove parent of `parent`
211+
let mut command_queue = CommandQueue::default();
212+
let mut commands = Commands::new(&mut command_queue, &world);
213+
commands.entity(parent).remove_parent();
214+
command_queue.apply(&mut world);
215+
schedule.run(&mut world);
216+
217+
assert_eq!(
218+
world.get::<GlobalTransform>(parent).unwrap(),
219+
&GlobalTransform::from(Transform::IDENTITY)
220+
);
221+
}
222+
175223
#[test]
176224
fn did_propagate() {
177225
ComputeTaskPool::init(TaskPool::default);
178226
let mut world = World::default();
179227

180228
let mut update_stage = SystemStage::parallel();
181-
update_stage.add_system(sync_simple_transforms);
182-
update_stage.add_system(propagate_transforms);
229+
update_stage.add_system_set(transform_propagate_system_set());
183230

184231
let mut schedule = Schedule::default();
185232
schedule.add_stage(Update, update_stage);
@@ -220,8 +267,7 @@ mod test {
220267
let mut world = World::default();
221268

222269
let mut update_stage = SystemStage::parallel();
223-
update_stage.add_system(sync_simple_transforms);
224-
update_stage.add_system(propagate_transforms);
270+
update_stage.add_system_set(transform_propagate_system_set());
225271

226272
let mut schedule = Schedule::default();
227273
schedule.add_stage(Update, update_stage);
@@ -264,8 +310,7 @@ mod test {
264310
let mut world = World::default();
265311

266312
let mut update_stage = SystemStage::parallel();
267-
update_stage.add_system(sync_simple_transforms);
268-
update_stage.add_system(propagate_transforms);
313+
update_stage.add_system_set(transform_propagate_system_set());
269314

270315
let mut schedule = Schedule::default();
271316
schedule.add_stage(Update, update_stage);
@@ -344,8 +389,7 @@ mod test {
344389
let mut app = App::new();
345390
ComputeTaskPool::init(TaskPool::default);
346391

347-
app.add_system(sync_simple_transforms);
348-
app.add_system(propagate_transforms);
392+
app.add_system_set(transform_propagate_system_set());
349393

350394
let translation = vec3(1.0, 0.0, 0.0);
351395

@@ -391,8 +435,7 @@ mod test {
391435
let mut temp = World::new();
392436
let mut app = App::new();
393437

394-
app.add_system(propagate_transforms)
395-
.add_system(sync_simple_transforms);
438+
app.add_system_set(transform_propagate_system_set());
396439

397440
fn setup_world(world: &mut World) -> (Entity, Entity) {
398441
let mut grandchild = Entity::from_raw(0);

0 commit comments

Comments
 (0)