Skip to content

Commit e59d44e

Browse files
nicopapvyb
andcommitted
Fix transform propagation for orphaned hierarchies
Co-authored-by: vyb <[email protected]>
1 parent daa45eb commit e59d44e

File tree

1 file changed

+83
-17
lines changed

1 file changed

+83
-17
lines changed

crates/bevy_transform/src/systems.rs

+83-17
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::{ParamSet, RemovedComponents},
56
};
67
use bevy_hierarchy::{Children, Parent};
78

@@ -10,16 +11,29 @@ use bevy_hierarchy::{Children, Parent};
1011
/// Third party plugins should use [`transform_propagate_system_set`](crate::transform_propagate_system_set)
1112
/// to propagate transforms correctly.
1213
pub fn sync_simple_transforms(
13-
mut query: Query<
14-
(&Transform, &mut GlobalTransform),
15-
(Changed<Transform>, Without<Parent>, Without<Children>),
16-
>,
14+
mut query: ParamSet<(
15+
Query<
16+
(&Transform, &mut GlobalTransform),
17+
(Changed<Transform>, Without<Parent>, Without<Children>),
18+
>,
19+
Query<(Ref<Transform>, &mut GlobalTransform), Without<Children>>,
20+
)>,
21+
orphaned: RemovedComponents<Parent>,
1722
) {
1823
query
24+
.p0()
1925
.par_iter_mut()
2026
.for_each_mut(|(transform, mut global_transform)| {
2127
*global_transform = GlobalTransform::from(*transform);
2228
});
29+
// updated orphaned entities
30+
let mut query = query.p1();
31+
let mut iter = query.iter_many_mut(orphaned.iter());
32+
while let Some((transform, mut global_transform)) = iter.fetch_next() {
33+
if !transform.is_changed() {
34+
*global_transform = GlobalTransform::from(*transform);
35+
}
36+
}
2337
}
2438

2539
/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
@@ -32,12 +46,15 @@ pub fn propagate_transforms(
3246
(Entity, &Children, Ref<Transform>, &mut GlobalTransform),
3347
Without<Parent>,
3448
>,
49+
orphaned: RemovedComponents<Parent>,
3550
transform_query: Query<(Ref<Transform>, &mut GlobalTransform, Option<&Children>), With<Parent>>,
3651
parent_query: Query<(Entity, Ref<Parent>)>,
3752
) {
53+
let mut orphaned = orphaned.iter().collect::<Vec<_>>();
54+
orphaned.sort_unstable();
3855
root_query.par_iter_mut().for_each_mut(
3956
|(entity, children, transform, mut global_transform)| {
40-
let changed = transform.is_changed();
57+
let changed = transform.is_changed() || orphaned.binary_search(&entity).is_ok();
4158
if changed {
4259
*global_transform = GlobalTransform::from(*transform);
4360
}
@@ -163,21 +180,74 @@ mod test {
163180
use bevy_tasks::{ComputeTaskPool, TaskPool};
164181

165182
use crate::components::{GlobalTransform, Transform};
166-
use crate::systems::*;
167-
use crate::TransformBundle;
183+
use crate::{transform_propagate_system_set, TransformBundle};
168184
use bevy_hierarchy::{BuildChildren, BuildWorldChildren, Children, Parent};
169185

170186
#[derive(StageLabel)]
171187
struct Update;
172188

189+
#[test]
190+
fn correct_parent_removed() {
191+
ComputeTaskPool::init(TaskPool::default);
192+
let mut world = World::default();
193+
let offset_global_transform =
194+
|offset| GlobalTransform::from(Transform::from_xyz(offset, offset, offset));
195+
let offset_transform =
196+
|offset| TransformBundle::from_transform(Transform::from_xyz(offset, offset, offset));
197+
198+
let mut update_stage = SystemStage::parallel();
199+
update_stage.add_system_set(transform_propagate_system_set());
200+
201+
let mut schedule = Schedule::default();
202+
schedule.add_stage(Update, update_stage);
203+
204+
let mut command_queue = CommandQueue::default();
205+
let mut commands = Commands::new(&mut command_queue, &world);
206+
let root = commands.spawn(offset_transform(3.3)).id();
207+
let parent = commands.spawn(offset_transform(4.4)).id();
208+
let child = commands.spawn(offset_transform(5.5)).id();
209+
commands.entity(parent).set_parent(root);
210+
commands.entity(child).set_parent(parent);
211+
command_queue.apply(&mut world);
212+
schedule.run(&mut world);
213+
214+
assert_ne!(
215+
world.get::<GlobalTransform>(parent).unwrap(),
216+
&GlobalTransform::from(Transform::IDENTITY)
217+
);
218+
219+
// Remove parent of `parent`
220+
let mut command_queue = CommandQueue::default();
221+
let mut commands = Commands::new(&mut command_queue, &world);
222+
commands.entity(parent).remove_parent();
223+
command_queue.apply(&mut world);
224+
schedule.run(&mut world);
225+
226+
assert_eq!(
227+
world.get::<GlobalTransform>(parent).unwrap(),
228+
&offset_global_transform(4.4)
229+
);
230+
231+
// Remove parent of `child`
232+
let mut command_queue = CommandQueue::default();
233+
let mut commands = Commands::new(&mut command_queue, &world);
234+
commands.entity(child).remove_parent();
235+
command_queue.apply(&mut world);
236+
schedule.run(&mut world);
237+
238+
assert_eq!(
239+
world.get::<GlobalTransform>(child).unwrap(),
240+
&offset_global_transform(5.5)
241+
);
242+
}
243+
173244
#[test]
174245
fn did_propagate() {
175246
ComputeTaskPool::init(TaskPool::default);
176247
let mut world = World::default();
177248

178249
let mut update_stage = SystemStage::parallel();
179-
update_stage.add_system(sync_simple_transforms);
180-
update_stage.add_system(propagate_transforms);
250+
update_stage.add_system_set(transform_propagate_system_set());
181251

182252
let mut schedule = Schedule::default();
183253
schedule.add_stage(Update, update_stage);
@@ -218,8 +288,7 @@ mod test {
218288
let mut world = World::default();
219289

220290
let mut update_stage = SystemStage::parallel();
221-
update_stage.add_system(sync_simple_transforms);
222-
update_stage.add_system(propagate_transforms);
291+
update_stage.add_system_set(transform_propagate_system_set());
223292

224293
let mut schedule = Schedule::default();
225294
schedule.add_stage(Update, update_stage);
@@ -262,8 +331,7 @@ mod test {
262331
let mut world = World::default();
263332

264333
let mut update_stage = SystemStage::parallel();
265-
update_stage.add_system(sync_simple_transforms);
266-
update_stage.add_system(propagate_transforms);
334+
update_stage.add_system_set(transform_propagate_system_set());
267335

268336
let mut schedule = Schedule::default();
269337
schedule.add_stage(Update, update_stage);
@@ -342,8 +410,7 @@ mod test {
342410
let mut app = App::new();
343411
ComputeTaskPool::init(TaskPool::default);
344412

345-
app.add_system(sync_simple_transforms);
346-
app.add_system(propagate_transforms);
413+
app.add_system_set(transform_propagate_system_set());
347414

348415
let translation = vec3(1.0, 0.0, 0.0);
349416

@@ -389,8 +456,7 @@ mod test {
389456
let mut temp = World::new();
390457
let mut app = App::new();
391458

392-
app.add_system(propagate_transforms)
393-
.add_system(sync_simple_transforms);
459+
app.add_system_set(transform_propagate_system_set());
394460

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

0 commit comments

Comments
 (0)