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