Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_transform/src/helper.rs
6595 views
1
//! System parameter for computing up-to-date [`GlobalTransform`]s.
2
3
use bevy_ecs::{
4
hierarchy::ChildOf,
5
prelude::Entity,
6
query::QueryEntityError,
7
system::{Query, SystemParam},
8
};
9
use thiserror::Error;
10
11
use crate::components::{GlobalTransform, Transform};
12
13
/// System parameter for computing up-to-date [`GlobalTransform`]s.
14
///
15
/// Computing an entity's [`GlobalTransform`] can be expensive so it is recommended
16
/// you use the [`GlobalTransform`] component stored on the entity, unless you need
17
/// a [`GlobalTransform`] that reflects the changes made to any [`Transform`]s since
18
/// the last time the transform propagation systems ran.
19
#[derive(SystemParam)]
20
pub struct TransformHelper<'w, 's> {
21
parent_query: Query<'w, 's, &'static ChildOf>,
22
transform_query: Query<'w, 's, &'static Transform>,
23
}
24
25
impl<'w, 's> TransformHelper<'w, 's> {
26
/// Computes the [`GlobalTransform`] of the given entity from the [`Transform`] component on it and its ancestors.
27
pub fn compute_global_transform(
28
&self,
29
entity: Entity,
30
) -> Result<GlobalTransform, ComputeGlobalTransformError> {
31
let transform = self
32
.transform_query
33
.get(entity)
34
.map_err(|err| map_error(err, false))?;
35
36
let mut global_transform = GlobalTransform::from(*transform);
37
38
for entity in self.parent_query.iter_ancestors(entity) {
39
let transform = self
40
.transform_query
41
.get(entity)
42
.map_err(|err| map_error(err, true))?;
43
44
global_transform = *transform * global_transform;
45
}
46
47
Ok(global_transform)
48
}
49
}
50
51
fn map_error(err: QueryEntityError, ancestor: bool) -> ComputeGlobalTransformError {
52
use ComputeGlobalTransformError::*;
53
match err {
54
QueryEntityError::QueryDoesNotMatch(entity, _) => MissingTransform(entity),
55
QueryEntityError::EntityDoesNotExist(error) => {
56
if ancestor {
57
MalformedHierarchy(error.entity)
58
} else {
59
NoSuchEntity(error.entity)
60
}
61
}
62
QueryEntityError::AliasedMutability(_) => unreachable!(),
63
}
64
}
65
66
/// Error returned by [`TransformHelper::compute_global_transform`].
67
#[derive(Debug, Error)]
68
pub enum ComputeGlobalTransformError {
69
/// The entity or one of its ancestors is missing the [`Transform`] component.
70
#[error("The entity {0:?} or one of its ancestors is missing the `Transform` component")]
71
MissingTransform(Entity),
72
/// The entity does not exist.
73
#[error("The entity {0:?} does not exist")]
74
NoSuchEntity(Entity),
75
/// An ancestor is missing.
76
/// This probably means that your hierarchy has been improperly maintained.
77
#[error("The ancestor {0:?} is missing")]
78
MalformedHierarchy(Entity),
79
}
80
81
#[cfg(test)]
82
mod tests {
83
use alloc::{vec, vec::Vec};
84
use core::f32::consts::TAU;
85
86
use bevy_app::App;
87
use bevy_ecs::{hierarchy::ChildOf, system::SystemState};
88
use bevy_math::{Quat, Vec3};
89
90
use crate::{
91
components::{GlobalTransform, Transform},
92
helper::TransformHelper,
93
plugins::TransformPlugin,
94
};
95
96
#[test]
97
fn match_transform_propagation_systems() {
98
// Single transform
99
match_transform_propagation_systems_inner(vec![Transform::from_translation(Vec3::X)
100
.with_rotation(Quat::from_rotation_y(TAU / 4.))
101
.with_scale(Vec3::splat(2.))]);
102
103
// Transform hierarchy
104
match_transform_propagation_systems_inner(vec![
105
Transform::from_translation(Vec3::X)
106
.with_rotation(Quat::from_rotation_y(TAU / 4.))
107
.with_scale(Vec3::splat(2.)),
108
Transform::from_translation(Vec3::Y)
109
.with_rotation(Quat::from_rotation_z(TAU / 3.))
110
.with_scale(Vec3::splat(1.5)),
111
Transform::from_translation(Vec3::Z)
112
.with_rotation(Quat::from_rotation_x(TAU / 2.))
113
.with_scale(Vec3::splat(0.3)),
114
]);
115
}
116
117
fn match_transform_propagation_systems_inner(transforms: Vec<Transform>) {
118
let mut app = App::new();
119
app.add_plugins(TransformPlugin);
120
121
let mut entity = None;
122
123
for transform in transforms {
124
let mut e = app.world_mut().spawn(transform);
125
126
if let Some(parent) = entity {
127
e.insert(ChildOf(parent));
128
}
129
130
entity = Some(e.id());
131
}
132
133
let leaf_entity = entity.unwrap();
134
135
app.update();
136
137
let transform = *app.world().get::<GlobalTransform>(leaf_entity).unwrap();
138
139
let mut state = SystemState::<TransformHelper>::new(app.world_mut());
140
let helper = state.get(app.world());
141
142
let computed_transform = helper.compute_global_transform(leaf_entity).unwrap();
143
144
approx::assert_abs_diff_eq!(transform.affine(), computed_transform.affine());
145
}
146
}
147
148