mirror of
https://github.com/eliasstepanik/big_space_with_trim.git
synced 2026-01-11 18:48:27 +00:00
Update to use new transform propagation systems
This commit is contained in:
parent
f86da2d35d
commit
023170b718
@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use big_space::{FloatingOrigin, GridCell};
|
use big_space::{FloatingOrigin, GridCell};
|
||||||
|
|
||||||
@ -32,8 +34,7 @@ fn movement(
|
|||||||
|t: f32| -> Vec3 { Vec3::new(t.cos() * 2.0, t.sin() * 2.0, (t * 1.3).sin() * 2.0) };
|
|t: f32| -> Vec3 { Vec3::new(t.cos() * 2.0, t.sin() * 2.0, (t * 1.3).sin() * 2.0) };
|
||||||
let p0 = pos(t_0);
|
let p0 = pos(t_0);
|
||||||
let p1 = pos(t_1);
|
let p1 = pos(t_1);
|
||||||
let dp = p1 - p0;
|
p1 - p0
|
||||||
dp
|
|
||||||
};
|
};
|
||||||
|
|
||||||
q.p0().single_mut().translation += delta_translation(20.0);
|
q.p0().single_mut().translation += delta_translation(20.0);
|
||||||
|
|||||||
@ -58,7 +58,7 @@ fn setup(
|
|||||||
|
|
||||||
let mut translation = Vec3::ZERO;
|
let mut translation = Vec3::ZERO;
|
||||||
for i in 1..=37_i128 {
|
for i in 1..=37_i128 {
|
||||||
let j = 10_f32.powf(i as f32 - 10.0) as f32;
|
let j = 10_f32.powf(i as f32 - 10.0);
|
||||||
translation.x += j;
|
translation.x += j;
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
PbrBundle {
|
PbrBundle {
|
||||||
|
|||||||
@ -54,15 +54,13 @@ fn toggle_plugin(
|
|||||||
} else {
|
} else {
|
||||||
"Floating Origin Disabled"
|
"Floating Origin Disabled"
|
||||||
}
|
}
|
||||||
} else {
|
} else if cell_max >= cell.x + i {
|
||||||
if cell_max >= cell.x + i {
|
|
||||||
cell.x = i64::min(cell_max, cell.x + i);
|
cell.x = i64::min(cell_max, cell.x + i);
|
||||||
cell.y = i64::min(cell_max, cell.y + i);
|
cell.y = i64::min(cell_max, cell.y + i);
|
||||||
cell.z = i64::min(cell_max, cell.z + i);
|
cell.z = i64::min(cell_max, cell.z + i);
|
||||||
"Enabling..."
|
"Enabling..."
|
||||||
} else {
|
} else {
|
||||||
"Floating Origin Enabled"
|
"Floating Origin Enabled"
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let dist = (cell_max - cell.x) * 10_000;
|
let dist = (cell_max - cell.x) * 10_000;
|
||||||
@ -92,7 +90,7 @@ fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
sections: vec![TextSection {
|
sections: vec![TextSection {
|
||||||
value: "hello: ".to_string(),
|
value: "hello: ".to_string(),
|
||||||
style: TextStyle {
|
style: TextStyle {
|
||||||
font: font.clone(),
|
font,
|
||||||
font_size: 30.0,
|
font_size: 30.0,
|
||||||
color: Color::WHITE,
|
color: Color::WHITE,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -131,8 +131,7 @@ impl CameraInput {
|
|||||||
self.roll * dt,
|
self.roll * dt,
|
||||||
);
|
);
|
||||||
|
|
||||||
let translation =
|
let translation = DVec3::new(self.right, self.up, self.forward) * speed * dt;
|
||||||
DVec3::new(self.right as f64, self.up as f64, self.forward as f64) * speed * dt as f64;
|
|
||||||
|
|
||||||
(translation, rotation)
|
(translation, rotation)
|
||||||
}
|
}
|
||||||
|
|||||||
210
src/lib.rs
210
src/lib.rs
@ -53,13 +53,13 @@
|
|||||||
//!
|
//!
|
||||||
//! Instead of:
|
//! Instead of:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```ignore
|
||||||
//! transform.translation = a_huge_imprecise_position;
|
//! transform.translation = a_huge_imprecise_position;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! do:
|
//! do:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```ignore
|
||||||
//! let delta = new_pos - old_pos;
|
//! let delta = new_pos - old_pos;
|
||||||
//! transform.translation += delta;
|
//! transform.translation += delta;
|
||||||
//! ```
|
//! ```
|
||||||
@ -88,40 +88,51 @@
|
|||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use bevy::{math::DVec3, prelude::*, transform::TransformSystem};
|
use bevy::{math::DVec3, prelude::*, transform::TransformSystem};
|
||||||
|
use propagation::propagate_transforms;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod precision;
|
pub mod precision;
|
||||||
|
pub mod propagation;
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
|
|
||||||
use precision::*;
|
use precision::*;
|
||||||
|
|
||||||
/// Add this plugin to your [`App`] to for floating origin functionality.
|
/// Add this plugin to your [`App`] for floating origin functionality.
|
||||||
#[derive(Default)]
|
|
||||||
pub struct FloatingOriginPlugin<P: GridPrecision> {
|
pub struct FloatingOriginPlugin<P: GridPrecision> {
|
||||||
/// Initial floating origin settings.
|
/// The edge length of a single cell.
|
||||||
pub settings: FloatingOriginSettings,
|
pub grid_edge_length: f32,
|
||||||
|
/// How far past the extents of a cell an entity must travel before a grid recentering occurs.
|
||||||
|
/// This prevents entities from rapidly switching between cells when moving along a boundary.
|
||||||
|
pub switching_threshold: f32,
|
||||||
phantom: PhantomData<P>,
|
phantom: PhantomData<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<P: GridPrecision> Default for FloatingOriginPlugin<P> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(10_000f32, 100f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<P: GridPrecision> FloatingOriginPlugin<P> {
|
impl<P: GridPrecision> FloatingOriginPlugin<P> {
|
||||||
/// # `switching_threshold`:
|
/// Construct a new plugin with the following settings.
|
||||||
///
|
|
||||||
/// How far past the extents of a cell an entity must travel before a grid recentering occurs.
|
|
||||||
/// This prevents entities from rapidly switching between cells when moving along a boundary.
|
|
||||||
pub fn new(grid_edge_length: f32, switching_threshold: f32) -> Self {
|
pub fn new(grid_edge_length: f32, switching_threshold: f32) -> Self {
|
||||||
FloatingOriginPlugin {
|
FloatingOriginPlugin {
|
||||||
settings: FloatingOriginSettings::new(grid_edge_length, switching_threshold),
|
grid_edge_length,
|
||||||
..Default::default()
|
switching_threshold,
|
||||||
|
phantom: PhantomData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: GridPrecision> Plugin for FloatingOriginPlugin<P> {
|
impl<P: GridPrecision> Plugin for FloatingOriginPlugin<P> {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.insert_resource(self.settings.clone())
|
app.insert_resource(FloatingOriginSettings::new(
|
||||||
|
self.grid_edge_length,
|
||||||
|
self.switching_threshold,
|
||||||
|
))
|
||||||
.register_type::<Transform>()
|
.register_type::<Transform>()
|
||||||
.register_type::<GlobalTransform>()
|
.register_type::<GlobalTransform>()
|
||||||
.register_type::<GridCell<P>>()
|
.register_type::<GridCell<P>>()
|
||||||
@ -136,19 +147,27 @@ impl<P: GridPrecision> Plugin for FloatingOriginPlugin<P> {
|
|||||||
.add_startup_systems(
|
.add_startup_systems(
|
||||||
(
|
(
|
||||||
recenter_transform_on_grid::<P>,
|
recenter_transform_on_grid::<P>,
|
||||||
update_global_from_grid::<P>,
|
sync_simple_transforms::<P>
|
||||||
transform_propagate_system::<P>,
|
.after(recenter_transform_on_grid::<P>)
|
||||||
|
.before(propagate_transforms::<P>),
|
||||||
|
update_global_from_grid::<P>
|
||||||
|
.after(recenter_transform_on_grid::<P>)
|
||||||
|
.before(propagate_transforms::<P>),
|
||||||
|
propagate_transforms::<P>,
|
||||||
)
|
)
|
||||||
.chain()
|
|
||||||
.in_set(TransformSystem::TransformPropagate),
|
.in_set(TransformSystem::TransformPropagate),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
(
|
(
|
||||||
recenter_transform_on_grid::<P>,
|
recenter_transform_on_grid::<P>,
|
||||||
update_global_from_grid::<P>,
|
sync_simple_transforms::<P>
|
||||||
transform_propagate_system::<P>,
|
.after(recenter_transform_on_grid::<P>)
|
||||||
|
.before(propagate_transforms::<P>),
|
||||||
|
update_global_from_grid::<P>
|
||||||
|
.after(recenter_transform_on_grid::<P>)
|
||||||
|
.before(propagate_transforms::<P>),
|
||||||
|
propagate_transforms::<P>,
|
||||||
)
|
)
|
||||||
.chain()
|
|
||||||
.in_set(TransformSystem::TransformPropagate),
|
.in_set(TransformSystem::TransformPropagate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -236,12 +255,6 @@ impl FloatingOriginSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FloatingOriginSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new(10_000f32, 100f32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Minimal bundle needed to position an entity in floating origin space.
|
/// Minimal bundle needed to position an entity in floating origin space.
|
||||||
///
|
///
|
||||||
/// This is the floating origin equivalent of the [`SpatialBundle`].
|
/// This is the floating origin equivalent of the [`SpatialBundle`].
|
||||||
@ -282,7 +295,7 @@ pub struct FloatingSpatialBundle<P: GridPrecision> {
|
|||||||
/// define a type alias!
|
/// define a type alias!
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use crate::GridCell;
|
/// # use big_space::GridCell;
|
||||||
/// type GalacticGrid = GridCell<i64>;
|
/// type GalacticGrid = GridCell<i64>;
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@ -380,16 +393,16 @@ pub fn recenter_transform_on_grid<P: GridPrecision>(
|
|||||||
{
|
{
|
||||||
let (grid_cell_delta, translation) =
|
let (grid_cell_delta, translation) =
|
||||||
settings.imprecise_translation_to_grid(transform.as_ref().translation);
|
settings.imprecise_translation_to_grid(transform.as_ref().translation);
|
||||||
*grid_pos = *grid_pos + grid_cell_delta;
|
*grid_pos += grid_cell_delta;
|
||||||
transform.translation = translation;
|
transform.translation = translation;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the `GlobalTransform` relative to the floating origin.
|
/// Compute the `GlobalTransform` relative to the floating origin's cell.
|
||||||
pub fn update_global_from_grid<P: GridPrecision>(
|
pub fn update_global_from_grid<P: GridPrecision>(
|
||||||
settings: Res<FloatingOriginSettings>,
|
settings: Res<FloatingOriginSettings>,
|
||||||
origin: Query<(&GridCell<P>, Changed<GridCell<P>>), With<FloatingOrigin>>,
|
origin: Query<Ref<GridCell<P>>, With<FloatingOrigin>>,
|
||||||
mut entities: ParamSet<(
|
mut entities: ParamSet<(
|
||||||
Query<
|
Query<
|
||||||
(&Transform, &mut GlobalTransform, &GridCell<P>),
|
(&Transform, &mut GlobalTransform, &GridCell<P>),
|
||||||
@ -398,21 +411,21 @@ pub fn update_global_from_grid<P: GridPrecision>(
|
|||||||
Query<(&Transform, &mut GlobalTransform, &GridCell<P>)>,
|
Query<(&Transform, &mut GlobalTransform, &GridCell<P>)>,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
let (origin_cell, origin_grid_pos_changed) = origin.single();
|
let origin_cell = origin.single();
|
||||||
|
|
||||||
if origin_grid_pos_changed {
|
if origin_cell.is_changed() {
|
||||||
let mut all_entities = entities.p1();
|
let mut all_entities = entities.p1();
|
||||||
all_entities
|
all_entities
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.for_each_mut(|(local, global, entity_cell)| {
|
.for_each_mut(|(local, global, entity_cell)| {
|
||||||
update_global_from_cell_local(&settings, entity_cell, origin_cell, local, global);
|
update_global_from_cell_local(&settings, entity_cell, &origin_cell, local, global);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let mut moved_cell_entities = entities.p0();
|
let mut moved_cell_entities = entities.p0();
|
||||||
moved_cell_entities
|
moved_cell_entities
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.for_each_mut(|(local, global, entity_cell)| {
|
.for_each_mut(|(local, global, entity_cell)| {
|
||||||
update_global_from_cell_local(&settings, entity_cell, origin_cell, local, global);
|
update_global_from_cell_local(&settings, entity_cell, &origin_cell, local, global);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,128 +443,23 @@ fn update_global_from_cell_local<P: GridPrecision>(
|
|||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
|
/// Update [`GlobalTransform`] component of entities that aren't in the hierarchy
|
||||||
/// [`Transform`] component.
|
///
|
||||||
pub fn transform_propagate_system<P: GridPrecision>(
|
/// Third party plugins should ensure that this is used in concert with [`propagate_transforms`].
|
||||||
origin_moved: Query<(), (Changed<GridCell<P>>, With<FloatingOrigin>)>,
|
pub fn sync_simple_transforms<P: GridPrecision>(
|
||||||
mut root_query_no_grid: Query<
|
mut query: Query<
|
||||||
|
(&Transform, &mut GlobalTransform),
|
||||||
(
|
(
|
||||||
Option<(&Children, Changed<Children>)>,
|
|
||||||
&Transform,
|
|
||||||
Changed<Transform>,
|
Changed<Transform>,
|
||||||
&mut GlobalTransform,
|
Without<Parent>,
|
||||||
Entity,
|
Without<Children>,
|
||||||
|
Without<GridCell<P>>,
|
||||||
),
|
),
|
||||||
(Without<GridCell<P>>, Without<Parent>),
|
|
||||||
>,
|
>,
|
||||||
mut root_query_grid: Query<
|
|
||||||
(
|
|
||||||
Option<(&Children, Changed<Children>)>,
|
|
||||||
Changed<Transform>,
|
|
||||||
Changed<GridCell<P>>,
|
|
||||||
&GlobalTransform,
|
|
||||||
Entity,
|
|
||||||
),
|
|
||||||
(With<GridCell<P>>, Without<Parent>),
|
|
||||||
>,
|
|
||||||
mut transform_query: Query<(
|
|
||||||
&Transform,
|
|
||||||
Changed<Transform>,
|
|
||||||
&mut GlobalTransform,
|
|
||||||
&Parent,
|
|
||||||
)>,
|
|
||||||
children_query: Query<(&Children, Changed<Children>), (With<Parent>, With<GlobalTransform>)>,
|
|
||||||
) {
|
) {
|
||||||
let origin_cell_changed = !origin_moved.is_empty();
|
query
|
||||||
|
.par_iter_mut()
|
||||||
for (children, transform, transform_changed, mut global_transform, entity) in
|
.for_each_mut(|(transform, mut global_transform)| {
|
||||||
root_query_no_grid.iter_mut()
|
|
||||||
{
|
|
||||||
let mut changed = transform_changed || origin_cell_changed;
|
|
||||||
|
|
||||||
if transform_changed {
|
|
||||||
*global_transform = GlobalTransform::from(*transform);
|
*global_transform = GlobalTransform::from(*transform);
|
||||||
}
|
});
|
||||||
|
|
||||||
if let Some((children, changed_children)) = children {
|
|
||||||
// If our `Children` has changed, we need to recalculate everything below us
|
|
||||||
changed |= changed_children;
|
|
||||||
for child in children {
|
|
||||||
let _ = propagate_recursive(
|
|
||||||
&global_transform,
|
|
||||||
&mut transform_query,
|
|
||||||
&children_query,
|
|
||||||
*child,
|
|
||||||
entity,
|
|
||||||
changed,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (children, cell_changed, transform_changed, global_transform, entity) in
|
|
||||||
root_query_grid.iter_mut()
|
|
||||||
{
|
|
||||||
let mut changed = transform_changed || cell_changed || origin_cell_changed;
|
|
||||||
|
|
||||||
if let Some((children, changed_children)) = children {
|
|
||||||
// If our `Children` has changed, we need to recalculate everything below us
|
|
||||||
changed |= changed_children;
|
|
||||||
for child in children {
|
|
||||||
let _ = propagate_recursive(
|
|
||||||
global_transform,
|
|
||||||
&mut transform_query,
|
|
||||||
&children_query,
|
|
||||||
*child,
|
|
||||||
entity,
|
|
||||||
changed,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn propagate_recursive(
|
|
||||||
parent: &GlobalTransform,
|
|
||||||
transform_query: &mut Query<(
|
|
||||||
&Transform,
|
|
||||||
Changed<Transform>,
|
|
||||||
&mut GlobalTransform,
|
|
||||||
&Parent,
|
|
||||||
)>,
|
|
||||||
children_query: &Query<(&Children, Changed<Children>), (With<Parent>, With<GlobalTransform>)>,
|
|
||||||
entity: Entity,
|
|
||||||
expected_parent: Entity,
|
|
||||||
mut changed: bool,
|
|
||||||
// We use a result here to use the `?` operator. Ideally we'd use a try block instead
|
|
||||||
) -> Result<(), ()> {
|
|
||||||
let global_matrix = {
|
|
||||||
let (transform, transform_changed, mut global_transform, child_parent) =
|
|
||||||
transform_query.get_mut(entity).map_err(drop)?;
|
|
||||||
// Note that for parallelising, this check cannot occur here, since there is an `&mut GlobalTransform` (in global_transform)
|
|
||||||
assert_eq!(
|
|
||||||
child_parent.get(), expected_parent,
|
|
||||||
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
|
||||||
);
|
|
||||||
changed |= transform_changed;
|
|
||||||
if changed {
|
|
||||||
*global_transform = parent.mul_transform(*transform);
|
|
||||||
}
|
|
||||||
*global_transform
|
|
||||||
};
|
|
||||||
|
|
||||||
let (children, changed_children) = children_query.get(entity).map_err(drop)?;
|
|
||||||
// If our `Children` has changed, we need to recalculate everything below us
|
|
||||||
changed |= changed_children;
|
|
||||||
for child in children {
|
|
||||||
let _ = propagate_recursive(
|
|
||||||
&global_matrix,
|
|
||||||
transform_query,
|
|
||||||
children_query,
|
|
||||||
*child,
|
|
||||||
entity,
|
|
||||||
changed,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
148
src/propagation.rs
Normal file
148
src/propagation.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
//! Propagates transforms through the entity hierarchy.
|
||||||
|
//!
|
||||||
|
//! This is a slightly modified version of Bevy's own transform propagation system.
|
||||||
|
|
||||||
|
use crate::{precision::GridPrecision, FloatingOrigin, GridCell};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
|
||||||
|
/// [`Transform`] component.
|
||||||
|
pub fn propagate_transforms<P: GridPrecision>(
|
||||||
|
origin_moved: Query<(), (Changed<GridCell<P>>, With<FloatingOrigin>)>,
|
||||||
|
mut root_query: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&Children,
|
||||||
|
Ref<Transform>,
|
||||||
|
&mut GlobalTransform,
|
||||||
|
Option<Ref<GridCell<P>>>,
|
||||||
|
),
|
||||||
|
Without<Parent>,
|
||||||
|
>,
|
||||||
|
transform_query: Query<(Ref<Transform>, &mut GlobalTransform, Option<&Children>), With<Parent>>,
|
||||||
|
parent_query: Query<(Entity, Ref<Parent>)>,
|
||||||
|
) {
|
||||||
|
let origin_cell_changed = !origin_moved.is_empty();
|
||||||
|
|
||||||
|
for (entity, children, transform, mut global_transform, cell) in root_query.iter_mut() {
|
||||||
|
let cell_changed = cell.as_ref().filter(|cell| cell.is_changed()).is_some();
|
||||||
|
let transform_changed = transform.is_changed();
|
||||||
|
|
||||||
|
if transform_changed && cell.is_none() {
|
||||||
|
*global_transform = GlobalTransform::from(*transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
let changed = transform_changed || cell_changed || origin_cell_changed;
|
||||||
|
|
||||||
|
for (child, actual_parent) in parent_query.iter_many(children) {
|
||||||
|
assert_eq!(
|
||||||
|
actual_parent.get(), entity,
|
||||||
|
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||||
|
);
|
||||||
|
// SAFETY:
|
||||||
|
// - `child` must have consistent parentage, or the above assertion would panic.
|
||||||
|
// Since `child` is parented to a root entity, the entire hierarchy leading to it is consistent.
|
||||||
|
// - We may operate as if all descendants are consistent, since `propagate_recursive` will panic before
|
||||||
|
// continuing to propagate if it encounters an entity with inconsistent parentage.
|
||||||
|
// - Since each root entity is unique and the hierarchy is consistent and forest-like,
|
||||||
|
// other root entities' `propagate_recursive` calls will not conflict with this one.
|
||||||
|
// - Since this is the only place where `transform_query` gets used, there will be no conflicting fetches elsewhere.
|
||||||
|
unsafe {
|
||||||
|
propagate_recursive(
|
||||||
|
&global_transform,
|
||||||
|
&transform_query,
|
||||||
|
&parent_query,
|
||||||
|
child,
|
||||||
|
changed || actual_parent.is_changed(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// COPIED EXACTLY FROM BEVY
|
||||||
|
///
|
||||||
|
/// Recursively propagates the transforms for `entity` and all of its descendants.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `entity`'s descendants have a malformed hierarchy, this function will panic occur before propagating
|
||||||
|
/// the transforms of any malformed entities and their descendants.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - While this function is running, `transform_query` must not have any fetches for `entity`,
|
||||||
|
/// nor any of its descendants.
|
||||||
|
/// - The caller must ensure that the hierarchy leading to `entity`
|
||||||
|
/// is well-formed and must remain as a tree or a forest. Each entity must have at most one parent.
|
||||||
|
unsafe fn propagate_recursive(
|
||||||
|
parent: &GlobalTransform,
|
||||||
|
transform_query: &Query<
|
||||||
|
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
|
||||||
|
With<Parent>,
|
||||||
|
>,
|
||||||
|
parent_query: &Query<(Entity, Ref<Parent>)>,
|
||||||
|
entity: Entity,
|
||||||
|
mut changed: bool,
|
||||||
|
) {
|
||||||
|
let (global_matrix, children) = {
|
||||||
|
let Ok((transform, mut global_transform, children)) =
|
||||||
|
// SAFETY: This call cannot create aliased mutable references.
|
||||||
|
// - The top level iteration parallelizes on the roots of the hierarchy.
|
||||||
|
// - The caller ensures that each child has one and only one unique parent throughout the entire
|
||||||
|
// hierarchy.
|
||||||
|
//
|
||||||
|
// For example, consider the following malformed hierarchy:
|
||||||
|
//
|
||||||
|
// A
|
||||||
|
// / \
|
||||||
|
// B C
|
||||||
|
// \ /
|
||||||
|
// D
|
||||||
|
//
|
||||||
|
// D has two parents, B and C. If the propagation passes through C, but the Parent component on D points to B,
|
||||||
|
// the above check will panic as the origin parent does match the recorded parent.
|
||||||
|
//
|
||||||
|
// Also consider the following case, where A and B are roots:
|
||||||
|
//
|
||||||
|
// A B
|
||||||
|
// \ /
|
||||||
|
// C D
|
||||||
|
// \ /
|
||||||
|
// E
|
||||||
|
//
|
||||||
|
// Even if these A and B start two separate tasks running in parallel, one of them will panic before attempting
|
||||||
|
// to mutably access E.
|
||||||
|
(unsafe { transform_query.get_unchecked(entity) }) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
changed |= transform.is_changed();
|
||||||
|
if changed {
|
||||||
|
*global_transform = parent.mul_transform(*transform);
|
||||||
|
}
|
||||||
|
(*global_transform, children)
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(children) = children else { return };
|
||||||
|
for (child, actual_parent) in parent_query.iter_many(children) {
|
||||||
|
assert_eq!(
|
||||||
|
actual_parent.get(), entity,
|
||||||
|
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||||
|
);
|
||||||
|
// SAFETY: The caller guarantees that `transform_query` will not be fetched
|
||||||
|
// for any descendants of `entity`, so it is safe to call `propagate_recursive` for each child.
|
||||||
|
//
|
||||||
|
// The above assertion ensures that each child has one and only one unique parent throughout the
|
||||||
|
// entire hierarchy.
|
||||||
|
unsafe {
|
||||||
|
propagate_recursive(
|
||||||
|
&global_matrix,
|
||||||
|
transform_query,
|
||||||
|
parent_query,
|
||||||
|
child,
|
||||||
|
changed || actual_parent.is_changed(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user