Transform Rewrite (#374)

Remove individual Translation / Rotation / Scale components in favor of a combined Transform component
This commit is contained in:
Marek Legris 2020-09-14 23:00:32 +02:00 committed by GitHub
parent 3bc5e4cb1e
commit 474bb5403e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 478 additions and 1386 deletions

View file

@ -7,7 +7,7 @@ use bevy_render::{
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
render_graph::base::MainPass,
};
use bevy_transform::prelude::{Rotation, Scale, Transform, Translation};
use bevy_transform::prelude::{GlobalTransform, Transform};
/// A component bundle for "pbr mesh" entities
#[derive(Bundle)]
@ -18,9 +18,7 @@ pub struct PbrComponents {
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub scale: Scale,
pub global_transform: GlobalTransform,
}
impl Default for PbrComponents {
@ -49,9 +47,7 @@ impl Default for PbrComponents {
main_pass: Default::default(),
draw: Default::default(),
transform: Default::default(),
translation: Default::default(),
rotation: Default::default(),
scale: Default::default(),
global_transform: Default::default(),
}
}
}
@ -61,6 +57,5 @@ impl Default for PbrComponents {
pub struct LightComponents {
pub light: Light,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub global_transform: GlobalTransform,
}

View file

@ -1,11 +1,10 @@
use bevy_core::Byteable;
use bevy_math::Mat4;
use bevy_property::Properties;
use bevy_render::{
camera::{CameraProjection, PerspectiveProjection},
color::Color,
};
use bevy_transform::components::Translation;
use bevy_transform::components::GlobalTransform;
use std::ops::Range;
/// A point light
@ -37,7 +36,7 @@ pub(crate) struct LightRaw {
unsafe impl Byteable for LightRaw {}
impl LightRaw {
pub fn from(light: &Light, transform: &Mat4, translation: &Translation) -> LightRaw {
pub fn from(light: &Light, global_transform: &GlobalTransform) -> LightRaw {
let perspective = PerspectiveProjection {
fov: light.fov,
aspect_ratio: 1.0,
@ -45,8 +44,8 @@ impl LightRaw {
far: light.depth.end,
};
let proj = perspective.get_projection_matrix() * *transform;
let (x, y, z) = translation.0.into();
let proj = perspective.get_projection_matrix() * *global_transform.value();
let (x, y, z) = global_transform.translation().into();
LightRaw {
proj: proj.to_cols_array_2d(),
pos: [x, y, z, 1.0],

View file

@ -80,7 +80,7 @@ pub fn lights_node_system(
render_resource_context: Res<Box<dyn RenderResourceContext>>,
// TODO: this write on RenderResourceBindings will prevent this system from running in parallel with other systems that do the same
mut render_resource_bindings: ResMut<RenderResourceBindings>,
mut query: Query<(&Light, &Transform, &Translation)>,
mut query: Query<(&Light, &GlobalTransform)>,
) {
let state = &mut state;
let render_resource_context = &**render_resource_context;
@ -132,14 +132,12 @@ pub fn lights_node_system(
data[0..light_count_size].copy_from_slice([light_count as u32, 0, 0, 0].as_bytes());
// light array
for ((light, transform, translation), slot) in query
for ((light, global_transform), slot) in query
.iter()
.iter()
.zip(data[light_count_size..current_light_uniform_size].chunks_exact_mut(size))
{
slot.copy_from_slice(
LightRaw::from(&light, &transform.value, &translation).as_bytes(),
);
slot.copy_from_slice(LightRaw::from(&light, &global_transform).as_bytes());
}
},
);

View file

@ -24,10 +24,13 @@ use bevy_render::{
render_graph::{base, AssetRenderResourcesNode, RenderGraph, RenderResourcesNode},
shader::Shader,
};
use bevy_transform::prelude::Transform;
use bevy_transform::prelude::GlobalTransform;
pub(crate) fn add_pbr_graph(graph: &mut RenderGraph, resources: &Resources) {
graph.add_system_node(node::TRANSFORM, RenderResourcesNode::<Transform>::new(true));
graph.add_system_node(
node::TRANSFORM,
RenderResourcesNode::<GlobalTransform>::new(true),
);
graph.add_system_node(
node::STANDARD_MATERIAL,
AssetRenderResourcesNode::<StandardMaterial>::new(true),

View file

@ -3,7 +3,7 @@ use crate::Draw;
use bevy_core::FloatOrd;
use bevy_ecs::{Entity, Query};
use bevy_property::Properties;
use bevy_transform::prelude::Transform;
use bevy_transform::prelude::GlobalTransform;
#[derive(Debug)]
pub struct VisibleEntity {
@ -24,13 +24,13 @@ impl VisibleEntities {
}
pub fn visible_entities_system(
mut camera_query: Query<(&Camera, &Transform, &mut VisibleEntities)>,
mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities)>,
mut draw_query: Query<(Entity, &Draw)>,
draw_transform_query: Query<(&Draw, &Transform)>,
draw_transform_query: Query<(&Draw, &GlobalTransform)>,
) {
for (camera, camera_transform, mut visible_entities) in &mut camera_query.iter() {
for (camera, camera_global_transform, mut visible_entities) in &mut camera_query.iter() {
visible_entities.value.clear();
let camera_position = camera_transform.value.w_axis().truncate();
let camera_position = camera_global_transform.translation();
let mut no_transform_order = 0.0;
let mut transparent_entities = Vec::new();
@ -39,18 +39,19 @@ pub fn visible_entities_system(
continue;
}
let order = if let Ok(transform) = draw_transform_query.get::<Transform>(entity) {
let position = transform.value.w_axis().truncate();
// smaller distances are sorted to lower indices by using the distance from the camera
FloatOrd(match camera.depth_calculation {
DepthCalculation::ZDifference => camera_position.z() - position.z(),
DepthCalculation::Distance => (camera_position - position).length(),
})
} else {
let order = FloatOrd(no_transform_order);
no_transform_order += 0.1;
order
};
let order =
if let Ok(global_transform) = draw_transform_query.get::<GlobalTransform>(entity) {
let position = global_transform.translation();
// smaller distances are sorted to lower indices by using the distance from the camera
FloatOrd(match camera.depth_calculation {
DepthCalculation::ZDifference => camera_position.z() - position.z(),
DepthCalculation::Distance => (camera_position - position).length(),
})
} else {
let order = FloatOrd(no_transform_order);
no_transform_order += 0.1;
order
};
if draw.is_transparent {
transparent_entities.push(VisibleEntity { entity, order })

View file

@ -7,7 +7,8 @@ use crate::{
use base::MainPass;
use bevy_asset::Handle;
use bevy_ecs::Bundle;
use bevy_transform::components::{Rotation, Scale, Transform, Translation};
use bevy_math::Vec3;
use bevy_transform::components::{GlobalTransform, Transform};
/// A component bundle for "mesh" entities
#[derive(Bundle, Default)]
@ -17,9 +18,7 @@ pub struct MeshComponents {
pub render_pipelines: RenderPipelines,
pub main_pass: MainPass,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub scale: Scale,
pub global_transform: GlobalTransform,
}
/// A component bundle for "3d camera" entities
@ -29,9 +28,7 @@ pub struct Camera3dComponents {
pub perspective_projection: PerspectiveProjection,
pub visible_entities: VisibleEntities,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub scale: Scale,
pub global_transform: GlobalTransform,
}
impl Default for Camera3dComponents {
@ -44,9 +41,7 @@ impl Default for Camera3dComponents {
perspective_projection: Default::default(),
visible_entities: Default::default(),
transform: Default::default(),
translation: Default::default(),
rotation: Default::default(),
scale: Default::default(),
global_transform: Default::default(),
}
}
}
@ -58,9 +53,7 @@ pub struct Camera2dComponents {
pub orthographic_projection: OrthographicProjection,
pub visible_entities: VisibleEntities,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub scale: Scale,
pub global_transform: GlobalTransform,
}
impl Default for Camera2dComponents {
@ -78,10 +71,8 @@ impl Default for Camera2dComponents {
..Default::default()
},
visible_entities: Default::default(),
transform: Default::default(),
translation: Translation::new(0.0, 0.0, far - 0.1),
rotation: Default::default(),
scale: Default::default(),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, far - 0.1)),
global_transform: Default::default(),
}
}
}

View file

@ -73,18 +73,19 @@ pub fn camera_node_system(
// PERF: this write on RenderResourceAssignments will prevent this system from running in parallel
// with other systems that do the same
mut render_resource_bindings: ResMut<RenderResourceBindings>,
query: Query<(&Camera, &Transform)>,
query: Query<(&Camera, &GlobalTransform)>,
) {
let render_resource_context = &**render_resource_context;
let (camera, transform) = if let Some(camera_entity) = active_cameras.get(&state.camera_name) {
(
query.get::<Camera>(camera_entity).unwrap(),
query.get::<Transform>(camera_entity).unwrap(),
)
} else {
return;
};
let (camera, global_transform) =
if let Some(camera_entity) = active_cameras.get(&state.camera_name) {
(
query.get::<Camera>(camera_entity).unwrap(),
query.get::<GlobalTransform>(camera_entity).unwrap(),
)
} else {
return;
};
let staging_buffer = if let Some(staging_buffer) = state.staging_buffer {
render_resource_context.map_buffer(staging_buffer);
@ -118,7 +119,7 @@ pub fn camera_node_system(
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
let camera_matrix: [f32; 16] =
(camera.projection_matrix * transform.value.inverse()).to_cols_array();
(camera.projection_matrix * global_transform.value().inverse()).to_cols_array();
render_resource_context.write_mapped_buffer(
staging_buffer,

View file

@ -179,14 +179,14 @@ where
}
}
impl RenderResources for bevy_transform::prelude::Transform {
impl RenderResources for bevy_transform::prelude::GlobalTransform {
fn render_resources_len(&self) -> usize {
1
}
fn get_render_resource(&self, index: usize) -> Option<&dyn RenderResource> {
if index == 0 {
Some(&self.value)
Some(self.value())
} else {
None
}

View file

@ -10,7 +10,7 @@ use bevy_render::{
prelude::Draw,
render_graph::base::MainPass,
};
use bevy_transform::prelude::{Rotation, Scale, Transform, Translation};
use bevy_transform::prelude::{GlobalTransform, Transform};
#[derive(Bundle)]
pub struct SpriteComponents {
@ -21,9 +21,7 @@ pub struct SpriteComponents {
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub scale: Scale,
pub global_transform: GlobalTransform,
}
impl Default for SpriteComponents {
@ -56,9 +54,7 @@ impl Default for SpriteComponents {
main_pass: MainPass,
material: Default::default(),
transform: Default::default(),
translation: Default::default(),
rotation: Default::default(),
scale: Default::default(),
global_transform: Default::default(),
}
}
}
@ -77,9 +73,7 @@ pub struct SpriteSheetComponents {
pub main_pass: MainPass,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub scale: Scale,
pub global_transform: GlobalTransform,
}
impl Default for SpriteSheetComponents {
@ -112,9 +106,7 @@ impl Default for SpriteSheetComponents {
sprite: Default::default(),
texture_atlas: Default::default(),
transform: Default::default(),
translation: Default::default(),
rotation: Default::default(),
scale: Default::default(),
global_transform: Default::default(),
}
}
}

View file

@ -0,0 +1,119 @@
use bevy_math::{Mat3, Mat4, Quat, Vec3};
use bevy_property::Properties;
use std::fmt;
#[derive(Debug, PartialEq, Clone, Copy, Properties)]
pub struct GlobalTransform {
value: Mat4,
}
impl GlobalTransform {
#[inline(always)]
pub fn new(value: Mat4) -> Self {
GlobalTransform { value }
}
#[inline(always)]
pub fn identity() -> Self {
GlobalTransform {
value: Mat4::identity(),
}
}
pub fn from_translation(translation: Vec3) -> Self {
GlobalTransform::new(Mat4::from_translation(translation))
}
pub fn from_rotation(rotation: Quat) -> Self {
GlobalTransform::new(Mat4::from_quat(rotation))
}
// TODO: make sure scale is positive
pub fn from_scale(scale: Vec3) -> Self {
GlobalTransform::new(Mat4::from_scale(scale))
}
pub fn with_translation(mut self, translation: Vec3) -> Self {
self.translate(translation);
self
}
pub fn with_rotation(mut self, rotation: Quat) -> Self {
self.rotate(rotation);
self
}
pub fn with_scale(mut self, scale: Vec3) -> Self {
self.apply_scale(scale);
self
}
pub fn value(&self) -> &Mat4 {
&self.value
}
pub fn value_mut(&mut self) -> &mut Mat4 {
&mut self.value
}
pub fn translation(&self) -> Vec3 {
Vec3::from(self.value.w_axis().truncate())
}
pub fn rotation(&self) -> Quat {
let scale = self.scale();
Quat::from_rotation_mat3(&Mat3::from_cols(
Vec3::from(self.value.x_axis().truncate()) / scale.x(),
Vec3::from(self.value.y_axis().truncate()) / scale.y(),
Vec3::from(self.value.z_axis().truncate()) / scale.z(),
))
}
pub fn scale(&self) -> Vec3 {
Vec3::new(
self.value.x_axis().truncate().length(),
self.value.y_axis().truncate().length(),
self.value.z_axis().truncate().length(),
)
}
pub fn set_translation(&mut self, translation: Vec3) {
*self.value.w_axis_mut() = translation.extend(1.0);
}
pub fn set_rotation(&mut self, rotation: Quat) {
let rotation = rotation * self.rotation().conjugate();
rotation.normalize();
self.value = Mat4::from_quat(rotation) * self.value;
}
pub fn set_scale(&mut self, scale: Vec3) {
let scale = scale / self.scale();
self.value = Mat4::from_scale(scale) * self.value;
}
pub fn translate(&mut self, translation: Vec3) {
*self.value.w_axis_mut() += translation.extend(0.0);
}
pub fn rotate(&mut self, rotation: Quat) {
self.value = Mat4::from_quat(rotation) * self.value;
}
pub fn apply_scale(&mut self, scale: Vec3) {
self.value = Mat4::from_scale(scale) * self.value;
}
}
impl Default for GlobalTransform {
fn default() -> Self {
Self::identity()
}
}
impl fmt::Display for GlobalTransform {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}

View file

@ -1,41 +0,0 @@
use bevy_math::Mat4;
use bevy_property::Properties;
use std::{
fmt,
ops::{Deref, DerefMut},
};
#[derive(Debug, PartialEq, Clone, Copy, Properties)]
pub struct LocalTransform(pub Mat4);
impl LocalTransform {
pub fn identity() -> Self {
Self(Mat4::identity())
}
}
impl Default for LocalTransform {
fn default() -> Self {
Self::identity()
}
}
impl fmt::Display for LocalTransform {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for LocalTransform {
type Target = Mat4;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for LocalTransform {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View file

@ -1,17 +1,9 @@
mod children;
mod local_transform;
mod non_uniform_scale;
mod global_transform;
mod parent;
mod rotation;
mod scale;
mod transform;
mod translation;
pub use children::Children;
pub use local_transform::*;
pub use non_uniform_scale::*;
pub use global_transform::*;
pub use parent::{Parent, PreviousParent};
pub use rotation::*;
pub use scale::*;
pub use transform::*;
pub use translation::*;

View file

@ -1,60 +0,0 @@
use bevy_math::Vec3;
use bevy_property::Properties;
use std::{
fmt,
ops::{Deref, DerefMut},
};
#[derive(Debug, PartialEq, Clone, Copy, Properties)]
pub struct NonUniformScale(pub Vec3);
impl NonUniformScale {
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self(Vec3::new(x, y, z))
}
}
impl Default for NonUniformScale {
fn default() -> Self {
NonUniformScale(Vec3::new(1.0, 1.0, 1.0))
}
}
impl From<Vec3> for NonUniformScale {
fn from(scale: Vec3) -> Self {
Self(scale)
}
}
impl From<&Vec3> for NonUniformScale {
fn from(scale: &Vec3) -> Self {
Self(*scale)
}
}
impl From<&mut Vec3> for NonUniformScale {
fn from(scale: &mut Vec3) -> Self {
Self(*scale)
}
}
impl fmt::Display for NonUniformScale {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (x, y, z) = self.0.into();
write!(f, "NonUniformScale({}, {}, {})", x, y, z)
}
}
impl Deref for NonUniformScale {
type Target = Vec3;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for NonUniformScale {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View file

@ -1,63 +0,0 @@
use bevy_math::Quat;
use bevy_property::Properties;
use std::ops::{Deref, DerefMut};
#[derive(Debug, PartialEq, Clone, Copy, Properties)]
pub struct Rotation(pub Quat);
impl Rotation {
#[inline(always)]
pub fn identity() -> Self {
Self(Quat::identity())
}
#[inline(always)]
pub fn from_rotation_yxz(yaw: f32, pitch: f32, roll: f32) -> Self {
Self(Quat::from_rotation_ypr(yaw, pitch, roll))
}
#[inline(always)]
pub fn from_rotation_xyz(x: f32, y: f32, z: f32) -> Self {
Self(Quat::from_rotation_ypr(y, x, z))
}
#[inline(always)]
pub fn from_rotation_x(x: f32) -> Self {
Self(Quat::from_rotation_x(x))
}
#[inline(always)]
pub fn from_rotation_y(y: f32) -> Self {
Self(Quat::from_rotation_y(y))
}
#[inline(always)]
pub fn from_rotation_z(z: f32) -> Self {
Self(Quat::from_rotation_z(z))
}
}
impl Default for Rotation {
fn default() -> Self {
Self::identity()
}
}
impl From<Quat> for Rotation {
fn from(rotation: Quat) -> Self {
Self(rotation)
}
}
impl Deref for Rotation {
type Target = Quat;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Rotation {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View file

@ -1,48 +0,0 @@
use bevy_property::Properties;
use std::{
fmt,
ops::{Deref, DerefMut},
};
#[derive(Debug, PartialEq, Clone, Copy, Properties)]
pub struct Scale(pub f32);
impl From<f32> for Scale {
#[inline(always)]
fn from(scale: f32) -> Self {
Self(scale)
}
}
impl Scale {
#[inline(always)]
pub fn identity() -> Self {
Scale(1.0)
}
}
impl Default for Scale {
fn default() -> Self {
Self::identity()
}
}
impl fmt::Display for Scale {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Scale({})", self.0)
}
}
impl Deref for Scale {
type Target = f32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Scale {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View file

@ -1,35 +1,108 @@
use bevy_math::Mat4;
use bevy_math::{Mat3, Mat4, Quat, Vec3};
use bevy_property::Properties;
use std::fmt;
#[derive(Debug, PartialEq, Clone, Copy, Properties)]
pub struct Transform {
pub value: Mat4,
pub sync: bool, // NOTE: this is hopefully a temporary measure to allow setting the transform directly.
// ideally setting the transform automatically propagates back to position / translation / rotation,
// but right now they are always considered the source of truth
value: Mat4,
}
impl Transform {
#[inline(always)]
pub fn new(value: Mat4) -> Self {
Transform { value }
}
#[inline(always)]
pub fn identity() -> Self {
Transform {
value: Mat4::identity(),
sync: true,
}
}
#[inline(always)]
pub fn new(value: Mat4) -> Self {
Transform { value, sync: true }
pub fn from_translation(translation: Vec3) -> Self {
Transform::new(Mat4::from_translation(translation))
}
/// This creates a new `LocalToWorld` transform with the `sync` field set to `false`.
/// While `sync` is false, position, rotation, and scale components will not be synced to the transform.
/// This is helpful if you want to manually set the transform to a value (ex: Mat4::face_toward)
#[inline(always)]
pub fn new_sync_disabled(value: Mat4) -> Self {
Transform { value, sync: false }
pub fn from_rotation(rotation: Quat) -> Self {
Transform::new(Mat4::from_quat(rotation))
}
// TODO: make sure scale is positive
pub fn from_scale(scale: Vec3) -> Self {
Transform::new(Mat4::from_scale(scale))
}
pub fn with_translation(mut self, translation: Vec3) -> Self {
self.translate(translation);
self
}
pub fn with_rotation(mut self, rotation: Quat) -> Self {
self.rotate(rotation);
self
}
pub fn with_scale(mut self, scale: Vec3) -> Self {
self.apply_scale(scale);
self
}
pub fn value(&self) -> &Mat4 {
&self.value
}
pub fn value_mut(&mut self) -> &mut Mat4 {
&mut self.value
}
pub fn translation(&self) -> Vec3 {
Vec3::from(self.value.w_axis().truncate())
}
pub fn rotation(&self) -> Quat {
let scale = self.scale();
Quat::from_rotation_mat3(&Mat3::from_cols(
Vec3::from(self.value.x_axis().truncate()) / scale.x(),
Vec3::from(self.value.y_axis().truncate()) / scale.y(),
Vec3::from(self.value.z_axis().truncate()) / scale.z(),
))
}
pub fn scale(&self) -> Vec3 {
Vec3::new(
self.value.x_axis().truncate().length(),
self.value.y_axis().truncate().length(),
self.value.z_axis().truncate().length(),
)
}
pub fn set_translation(&mut self, translation: Vec3) {
*self.value.w_axis_mut() = translation.extend(1.0);
}
pub fn set_rotation(&mut self, rotation: Quat) {
let rotation = rotation * self.rotation().conjugate();
rotation.normalize();
self.value = Mat4::from_quat(rotation) * self.value;
}
pub fn set_scale(&mut self, scale: Vec3) {
let scale = scale / self.scale();
self.value = Mat4::from_scale(scale) * self.value;
}
pub fn translate(&mut self, translation: Vec3) {
*self.value.w_axis_mut() += translation.extend(0.0);
}
pub fn rotate(&mut self, rotation: Quat) {
self.value = Mat4::from_quat(rotation) * self.value;
}
pub fn apply_scale(&mut self, scale: Vec3) {
self.value = Mat4::from_scale(scale) * self.value;
}
}

View file

@ -1,44 +0,0 @@
use bevy_math::Vec3;
use bevy_property::Properties;
use std::ops::{Deref, DerefMut};
#[derive(Debug, PartialEq, Copy, Clone, Properties)]
pub struct Translation(pub Vec3);
impl Translation {
#[inline(always)]
pub fn identity() -> Self {
Self(Vec3::default())
}
#[inline(always)]
pub fn new(x: f32, y: f32, z: f32) -> Self {
Self(Vec3::new(x, y, z))
}
}
impl Default for Translation {
fn default() -> Self {
Self::identity()
}
}
impl From<Vec3> for Translation {
fn from(translation: Vec3) -> Self {
Self(translation)
}
}
impl Deref for Translation {
type Target = Vec3;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Translation {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View file

@ -1,4 +1,4 @@
use crate::prelude::{Children, LocalTransform, Parent, PreviousParent};
use crate::prelude::{Children, Parent, PreviousParent};
use bevy_ecs::{Commands, CommandsInternal, Component, DynamicBundle, Entity, WorldWriter};
use smallvec::SmallVec;
@ -14,11 +14,7 @@ impl WorldWriter for InsertChildren {
world
.insert(
*child,
(
Parent(self.parent),
PreviousParent(Some(self.parent)),
LocalTransform::default(),
),
(Parent(self.parent), PreviousParent(Some(self.parent))),
)
.unwrap();
}
@ -55,11 +51,7 @@ impl WorldWriter for PushChildren {
world
.insert(
*child,
(
Parent(self.parent),
PreviousParent(Some(self.parent)),
LocalTransform::default(),
),
(Parent(self.parent), PreviousParent(Some(self.parent))),
)
.unwrap();
}
@ -215,7 +207,7 @@ impl<'a> BuildChildren for ChildBuilder<'a> {
#[cfg(test)]
mod tests {
use super::BuildChildren;
use crate::prelude::{Children, LocalTransform, Parent, PreviousParent};
use crate::prelude::{Children, Parent, PreviousParent};
use bevy_ecs::{Commands, Entity, Resources, World};
use smallvec::{smallvec, SmallVec};
@ -261,9 +253,6 @@ mod tests {
*world.get::<PreviousParent>(child2).unwrap(),
PreviousParent(Some(parent))
);
assert!(world.get::<LocalTransform>(child1).is_ok());
assert!(world.get::<LocalTransform>(child2).is_ok());
}
#[test]
@ -301,9 +290,6 @@ mod tests {
PreviousParent(Some(parent))
);
assert!(world.get::<LocalTransform>(child1).is_ok());
assert!(world.get::<LocalTransform>(child2).is_ok());
commands.insert_children(parent, 1, &entities[3..]);
commands.apply(&mut world, &mut resources);
@ -322,8 +308,5 @@ mod tests {
*world.get::<PreviousParent>(child4).unwrap(),
PreviousParent(Some(parent))
);
assert!(world.get::<LocalTransform>(child3).is_ok());
assert!(world.get::<LocalTransform>(child4).is_ok());
}
}

View file

@ -110,6 +110,7 @@ mod test {
use super::*;
use crate::{hierarchy::BuildChildren, transform_systems};
use bevy_ecs::{Resources, Schedule, World};
use bevy_math::Vec3;
#[test]
fn correct_children() {
@ -127,13 +128,13 @@ mod test {
let mut parent = None;
let mut children = Vec::new();
commands
.spawn((Translation::new(1.0, 0.0, 0.0), Transform::identity()))
.spawn((Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),))
.for_current_entity(|entity| parent = Some(entity))
.with_children(|parent| {
parent
.spawn((Translation::new(0.0, 2.0, 0.0), Transform::identity()))
.spawn((Transform::from_translation(Vec3::new(0.0, 2.0, 0.0)),))
.for_current_entity(|entity| children.push(entity))
.spawn((Translation::new(0.0, 0.0, 3.0), Transform::identity()))
.spawn((Transform::from_translation(Vec3::new(0.0, 0.0, 3.0)),))
.for_current_entity(|entity| children.push(entity));
});
let parent = parent.unwrap();

View file

@ -1,4 +1,4 @@
use crate::prelude::{Children, LocalTransform, Parent, PreviousParent};
use crate::prelude::{Children, Parent, PreviousParent};
use bevy_ecs::{Component, DynamicBundle, Entity, WorldBuilder};
pub struct WorldChildBuilder<'a, 'b> {
@ -23,11 +23,7 @@ impl<'a, 'b> WorldChildBuilder<'a, 'b> {
.expect("There should always be a parent at this point.");
self.world_builder
.spawn_as_entity(entity, components)
.with_bundle((
Parent(parent_entity),
PreviousParent(Some(parent_entity)),
LocalTransform::default(),
));
.with_bundle((Parent(parent_entity), PreviousParent(Some(parent_entity))));
{
let world = &mut self.world_builder.world;
let mut added = false;

View file

@ -1,8 +1,6 @@
pub mod components;
pub mod hierarchy;
pub mod local_transform_systems;
pub mod transform_propagate_system;
pub mod transform_systems;
pub mod prelude {
pub use crate::{components::*, hierarchy::*, TransformPlugin};
@ -11,16 +9,12 @@ pub mod prelude {
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_type_registry::RegisterType;
use prelude::{
Children, LocalTransform, NonUniformScale, Parent, Rotation, Scale, Transform, Translation,
};
use prelude::{Children, Parent, Transform};
pub(crate) fn transform_systems() -> Vec<Box<dyn System>> {
let mut systems = Vec::with_capacity(5);
systems.append(&mut hierarchy::hierarchy_maintenance_systems());
systems.append(&mut local_transform_systems::local_transform_systems());
systems.append(&mut transform_systems::transform_systems());
systems.push(transform_propagate_system::transform_propagate_system.system());
systems
@ -33,12 +27,7 @@ impl Plugin for TransformPlugin {
fn build(&self, app: &mut AppBuilder) {
app.register_component::<Children>()
.register_component::<Parent>()
.register_component::<LocalTransform>()
.register_component::<Transform>()
.register_component::<Translation>()
.register_component::<Rotation>()
.register_component::<Scale>()
.register_component::<NonUniformScale>()
// add transform systems to startup so the first update is "correct"
.add_startup_systems(transform_systems())
.add_systems_to_stage(stage::POST_UPDATE, transform_systems());

View file

@ -1,365 +0,0 @@
use crate::components::*;
use bevy_ecs::prelude::*;
use bevy_math::{Mat4, Quat, Vec3};
// TODO: "on changed" for all of these systems
pub fn local_transform_translation_system(
mut query: Query<
Without<
Rotation,
Without<Scale, Without<NonUniformScale, (&mut LocalTransform, Changed<Translation>)>>,
>,
>,
) {
for (mut local, translation) in &mut query.iter() {
*local = LocalTransform(Mat4::from_translation(translation.0));
}
}
pub fn local_transform_rotation_system(
mut query: Query<
Without<
Translation,
Without<Scale, Without<NonUniformScale, (&mut LocalTransform, Changed<Rotation>)>>,
>,
>,
) {
for (mut local, rotation) in &mut query.iter() {
*local = LocalTransform(Mat4::from_quat(rotation.0));
}
}
pub fn local_transform_scale_system(
mut query: Query<
Without<
Translation,
Without<Rotation, Without<NonUniformScale, (&mut LocalTransform, Changed<Scale>)>>,
>,
>,
) {
for (mut local, scale) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale(Vec3::new(scale.0, scale.0, scale.0)));
}
}
pub fn local_transform_non_uniform_scale_system(
mut query: Query<
Without<
Translation,
Without<Rotation, Without<Scale, (&mut LocalTransform, Changed<NonUniformScale>)>>,
>,
>,
) {
for (mut local, non_uniform_scale) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale(non_uniform_scale.0));
}
}
pub fn local_transform_translation_rotation_system(
mut query: Query<
Without<
Scale,
Without<
NonUniformScale,
(
&mut LocalTransform,
Or<(Changed<Translation>, Changed<Rotation>)>,
),
>,
>,
>,
) {
for (mut local, (translation, rotation)) in &mut query.iter() {
*local = LocalTransform(Mat4::from_rotation_translation(rotation.0, translation.0));
}
}
pub fn local_transform_translation_scale_system(
mut query: Query<
Without<
Rotation,
Without<
NonUniformScale,
(
&mut LocalTransform,
Or<(Changed<Translation>, Changed<Scale>)>,
),
>,
>,
>,
) {
for (mut local, (translation, scale)) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale_rotation_translation(
Vec3::new(scale.0, scale.0, scale.0),
Quat::default(),
translation.0,
));
}
}
pub fn local_transform_translation_non_uniform_scale_system(
mut query: Query<
Without<
Rotation,
Without<
Scale,
(
&mut LocalTransform,
Or<(Changed<Translation>, Changed<NonUniformScale>)>,
),
>,
>,
>,
) {
for (mut local, (translation, non_uniform_scale)) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale_rotation_translation(
non_uniform_scale.0,
Quat::default(),
translation.0,
));
}
}
pub fn local_transform_rotation_scale_system(
mut query: Query<
Without<
Translation,
Without<
NonUniformScale,
(&mut LocalTransform, Or<(Changed<Rotation>, Changed<Scale>)>),
>,
>,
>,
) {
for (mut local, (rotation, scale)) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale_rotation_translation(
Vec3::new(scale.0, scale.0, scale.0),
rotation.0,
Vec3::default(),
));
}
}
pub fn local_transform_rotation_non_uniform_scale_system(
mut query: Query<
Without<
Translation,
Without<
Scale,
(
&mut LocalTransform,
Or<(Changed<Rotation>, Changed<NonUniformScale>)>,
),
>,
>,
>,
) {
for (mut local, (rotation, non_uniform_scale)) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale_rotation_translation(
non_uniform_scale.0,
rotation.0,
Vec3::default(),
));
}
}
pub fn local_transform_translation_rotation_scale_system(
mut query: Query<
Without<
NonUniformScale,
(
&mut LocalTransform,
Or<(Changed<Translation>, Changed<Rotation>, Changed<Scale>)>,
),
>,
>,
) {
for (mut local, (translation, rotation, scale)) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale_rotation_translation(
Vec3::new(scale.0, scale.0, scale.0),
rotation.0,
translation.0,
));
}
}
pub fn local_transform_translation_rotation_non_uniform_scale_system(
mut query: Query<
Without<
Scale,
(
&mut LocalTransform,
Or<(
Changed<Translation>,
Changed<Rotation>,
Changed<NonUniformScale>,
)>,
),
>,
>,
) {
for (mut local, (translation, rotation, non_uniform_scale)) in &mut query.iter() {
*local = LocalTransform(Mat4::from_scale_rotation_translation(
non_uniform_scale.0,
rotation.0,
translation.0,
));
}
}
pub fn local_transform_systems() -> Vec<Box<dyn System>> {
vec![
local_transform_translation_system.system(),
local_transform_rotation_system.system(),
local_transform_scale_system.system(),
local_transform_non_uniform_scale_system.system(),
local_transform_translation_rotation_system.system(),
local_transform_translation_scale_system.system(),
local_transform_translation_non_uniform_scale_system.system(),
local_transform_rotation_scale_system.system(),
local_transform_rotation_non_uniform_scale_system.system(),
local_transform_translation_rotation_scale_system.system(),
local_transform_translation_rotation_non_uniform_scale_system.system(),
]
}
#[cfg(test)]
mod test {
use super::*;
use bevy_ecs::{Resources, Schedule, World};
use bevy_math::{Mat4, Quat, Vec3};
#[test]
fn correct_local_transformation() {
let mut world = World::default();
let mut resources = Resources::default();
let mut schedule = Schedule::default();
schedule.add_stage("update");
for system in local_transform_systems() {
schedule.add_system_to_stage("update", system);
}
let local_transform = LocalTransform::identity();
let t = Translation::new(1.0, 2.0, 3.0);
let r = Rotation(Quat::from_rotation_ypr(1.0, 2.0, 3.0));
let s = Scale(2.0);
let nus = NonUniformScale::new(1.0, 2.0, 3.0);
// Add every combination of transform types.
let translation = world.spawn((local_transform, t));
let rotation = world.spawn((local_transform, r));
let scale = world.spawn((local_transform, s));
let non_uniform_scale = world.spawn((local_transform, nus));
let translation_and_rotation = world.spawn((local_transform, t, r));
let translation_and_scale = world.spawn((local_transform, t, s));
let translation_and_nus = world.spawn((local_transform, t, nus));
let rotation_scale = world.spawn((local_transform, r, s));
let rotation_nus = world.spawn((local_transform, r, nus));
let translation_rotation_scale = world.spawn((local_transform, t, r, s));
let translation_rotation_nus = world.spawn((local_transform, t, r, nus));
// Run the system
schedule.run(&mut world, &mut resources);
// Verify that each was transformed correctly.
assert_eq!(
world.get::<LocalTransform>(translation).unwrap().0,
Mat4::from_translation(t.0)
);
assert_eq!(
world.get::<LocalTransform>(rotation).unwrap().0,
Mat4::from_quat(r.0)
);
assert_eq!(
world.get::<LocalTransform>(scale).unwrap().0,
Mat4::from_scale(Vec3::new(s.0, s.0, s.0))
);
assert_eq!(
world.get::<LocalTransform>(non_uniform_scale).unwrap().0,
Mat4::from_scale(nus.0)
);
assert_eq!(
world
.get::<LocalTransform>(translation_and_rotation)
.unwrap()
.0,
Mat4::from_rotation_translation(r.0, t.0)
);
assert_eq!(
world
.get::<LocalTransform>(translation_and_scale)
.unwrap()
.0,
Mat4::from_scale_rotation_translation(Vec3::new(s.0, s.0, s.0), Quat::default(), t.0)
);
assert_eq!(
world.get::<LocalTransform>(translation_and_nus).unwrap().0,
Mat4::from_scale_rotation_translation(nus.0, Quat::default(), t.0)
);
assert_eq!(
world.get::<LocalTransform>(rotation_scale).unwrap().0,
Mat4::from_scale_rotation_translation(Vec3::new(s.0, s.0, s.0), r.0, Vec3::default())
);
assert_eq!(
world.get::<LocalTransform>(rotation_nus).unwrap().0,
Mat4::from_scale_rotation_translation(nus.0, r.0, Vec3::default())
);
assert_eq!(
world
.get::<LocalTransform>(translation_rotation_scale)
.unwrap()
.0,
Mat4::from_scale_rotation_translation(Vec3::new(s.0, s.0, s.0), r.0, t.0)
);
assert_eq!(
world
.get::<LocalTransform>(translation_rotation_nus)
.unwrap()
.0,
Mat4::from_scale_rotation_translation(nus.0, r.0, t.0)
);
}
#[test]
fn only_propagates_local_transform_on_change() {
let mut world = World::default();
let mut resources = Resources::default();
let mut schedule = Schedule::default();
schedule.add_stage("update");
for system in local_transform_systems() {
schedule.add_system_to_stage("update", system);
}
let local_transform = LocalTransform::identity();
let t = Translation::new(1.0, 2.0, 3.0);
let r = Rotation(Quat::from_rotation_ypr(1.0, 2.0, 3.0));
let s = Scale(2.0);
let nus = NonUniformScale::new(1.0, 2.0, 3.0);
// Add every combination of transform types.
world.spawn((local_transform, t));
world.spawn((local_transform, r));
world.spawn((local_transform, s));
world.spawn((local_transform, nus));
world.spawn((local_transform, t, r));
world.spawn((local_transform, t, s));
world.spawn((local_transform, t, nus));
world.spawn((local_transform, r, s));
world.spawn((local_transform, r, nus));
world.spawn((local_transform, t, r, s));
world.spawn((local_transform, t, r, nus));
// Run the system, local transforms should mutate since they are new
schedule.run(&mut world, &mut resources);
// Verify that the local transform is not mutated on the second frame
fn assert_no_local_transforms_changed_system(_: Changed<LocalTransform>) {
assert!(false)
}
schedule.add_system_to_stage("update", assert_no_local_transforms_changed_system.system());
schedule.run(&mut world, &mut resources);
}
}

View file

@ -1,61 +1,49 @@
use crate::components::*;
use bevy_ecs::prelude::*;
use bevy_math::Mat4;
pub fn transform_propagate_system(
mut root_query: Query<
Without<Parent, (Option<&Children>, &mut Transform, Option<&LocalTransform>)>,
>,
mut local_transform_query: Query<(&mut Transform, &LocalTransform, Option<&Children>)>,
mut root_query: Query<Without<Parent, (Option<&Children>, &Transform, &mut GlobalTransform)>>,
mut transform_query: Query<(&Transform, &mut GlobalTransform, Option<&Children>)>,
) {
for (children, mut transform, local_transform) in &mut root_query.iter() {
if let Some(local_transform) = local_transform {
transform.value = local_transform.0;
}
for (children, transform, mut global_transform) in &mut root_query.iter() {
*global_transform.value_mut() = *transform.value();
if let Some(children) = children {
for child in children.0.iter() {
propagate_recursive(*transform, &mut local_transform_query, *child);
propagate_recursive(*global_transform.value(), &mut transform_query, *child);
}
}
}
}
fn propagate_recursive(
parent_local_to_world: Transform,
local_transform_query: &mut Query<(&mut Transform, &LocalTransform, Option<&Children>)>,
parent: Mat4,
transform_query: &mut Query<(&Transform, &mut GlobalTransform, Option<&Children>)>,
entity: Entity,
) {
log::trace!("Updating Transform for {:?}", entity);
let local_transform = {
if let Ok(local_transform) = local_transform_query.get::<LocalTransform>(entity) {
*local_transform
let global_matrix = {
if let (Ok(transform), Ok(mut global_transform)) = (
transform_query.get::<Transform>(entity),
transform_query.get_mut::<GlobalTransform>(entity),
) {
*global_transform.value_mut() = parent * *transform.value();
*global_transform.value()
} else {
log::warn!(
"Entity {:?} is a child in the hierarchy but does not have a LocalTransform",
entity
);
return;
}
};
let new_transform = Transform {
value: parent_local_to_world.value * local_transform.0,
sync: true,
};
{
let mut transform = local_transform_query.get_mut::<Transform>(entity).unwrap();
transform.value = new_transform.value;
}
// Collect children
let children = local_transform_query
let children = transform_query
.get::<Children>(entity)
.map(|e| e.0.iter().cloned().collect::<Vec<_>>())
.unwrap_or_default();
for child in children {
propagate_recursive(new_transform, local_transform_query, child);
propagate_recursive(global_matrix, transform_query, child);
}
}
@ -78,20 +66,21 @@ mod test {
}
// Root entity
let parent = world.spawn((Translation::new(1.0, 0.0, 0.0), Transform::identity()));
let parent = world.spawn((
Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),
GlobalTransform::identity(),
));
let children = world
.spawn_batch(vec![
(
Translation::new(0.0, 2.0, 0.0),
LocalTransform::identity(),
Transform::identity(),
Transform::from_translation(Vec3::new(0.0, 2.0, 0.)),
Parent(parent),
GlobalTransform::identity(),
),
(
Translation::new(0.0, 0.0, 3.0),
LocalTransform::identity(),
Transform::identity(),
Transform::from_translation(Vec3::new(0.0, 0.0, 3.)),
Parent(parent),
GlobalTransform::identity(),
),
])
.collect::<Vec<Entity>>();
@ -103,13 +92,13 @@ mod test {
schedule.run(&mut world, &mut resources);
assert_eq!(
world.get::<Transform>(children[0]).unwrap().value,
*world.get::<GlobalTransform>(children[0]).unwrap().value(),
Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0))
* Mat4::from_translation(Vec3::new(0.0, 2.0, 0.0))
);
assert_eq!(
world.get::<Transform>(children[1]).unwrap().value,
*world.get::<GlobalTransform>(children[1]).unwrap().value(),
Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0))
* Mat4::from_translation(Vec3::new(0.0, 0.0, 3.0))
);
@ -130,25 +119,34 @@ mod test {
let mut commands = Commands::default();
let mut children = Vec::new();
commands
.spawn((Translation::new(1.0, 0.0, 0.0), Transform::identity()))
.spawn((
Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),
GlobalTransform::identity(),
))
.with_children(|parent| {
parent
.spawn((Translation::new(0.0, 2.0, 0.0), Transform::identity()))
.spawn((
Transform::from_translation(Vec3::new(0.0, 2.0, 0.0)),
GlobalTransform::identity(),
))
.for_current_entity(|entity| children.push(entity))
.spawn((Translation::new(0.0, 0.0, 3.0), Transform::identity()))
.spawn((
Transform::from_translation(Vec3::new(0.0, 0.0, 3.0)),
GlobalTransform::identity(),
))
.for_current_entity(|entity| children.push(entity));
});
commands.apply(&mut world, &mut resources);
schedule.run(&mut world, &mut resources);
assert_eq!(
world.get::<Transform>(children[0]).unwrap().value,
*world.get::<GlobalTransform>(children[0]).unwrap().value(),
Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0))
* Mat4::from_translation(Vec3::new(0.0, 2.0, 0.0))
);
assert_eq!(
world.get::<Transform>(children[1]).unwrap().value,
*world.get::<GlobalTransform>(children[1]).unwrap().value(),
Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0))
* Mat4::from_translation(Vec3::new(0.0, 0.0, 3.0))
);

View file

@ -1,433 +0,0 @@
use crate::components::*;
use bevy_ecs::prelude::*;
use bevy_math::{Mat4, Quat, Vec3};
// TODO: on changed for all of these systems
pub fn transform_translation_system(
mut query: Query<
Without<
LocalTransform,
Without<
Rotation,
Without<Scale, Without<NonUniformScale, (&mut Transform, Changed<Translation>)>>,
>,
>,
>,
) {
for (mut transform, translation) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_translation(translation.0));
}
}
pub fn transform_rotation_system(
mut query: Query<
Without<
LocalTransform,
Without<
Translation,
Without<Scale, Without<NonUniformScale, (&mut Transform, Changed<Rotation>)>>,
>,
>,
>,
) {
for (mut transform, rotation) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_quat(rotation.0));
}
}
pub fn transform_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
Translation,
Without<Rotation, Without<NonUniformScale, (&mut Transform, Changed<Scale>)>>,
>,
>,
>,
) {
for (mut transform, scale) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale(Vec3::new(scale.0, scale.0, scale.0)));
}
}
pub fn transform_non_uniform_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
Translation,
Without<Rotation, Without<Scale, (&mut Transform, Changed<NonUniformScale>)>>,
>,
>,
>,
) {
for (mut transform, non_uniform_scale) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale(non_uniform_scale.0));
}
}
pub fn transform_translation_rotation_system(
mut query: Query<
Without<
LocalTransform,
Without<
Scale,
Without<
NonUniformScale,
(
&mut Transform,
Or<(Changed<Translation>, Changed<Rotation>)>,
),
>,
>,
>,
>,
) {
for (mut transform, (translation, rotation)) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_rotation_translation(rotation.0, translation.0));
}
}
pub fn transform_translation_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
Rotation,
Without<
NonUniformScale,
(&mut Transform, Or<(Changed<Translation>, Changed<Scale>)>),
>,
>,
>,
>,
) {
for (mut transform, (translation, scale)) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale_rotation_translation(
Vec3::new(scale.0, scale.0, scale.0),
Quat::default(),
translation.0,
));
}
}
pub fn transform_translation_non_uniform_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
Rotation,
Without<
Scale,
(
&mut Transform,
Or<(Changed<Translation>, Changed<NonUniformScale>)>,
),
>,
>,
>,
>,
) {
for (mut transform, (translation, non_uniform_scale)) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale_rotation_translation(
non_uniform_scale.0,
Quat::default(),
translation.0,
));
}
}
pub fn transform_rotation_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
Translation,
Without<NonUniformScale, (&mut Transform, Or<(Changed<Rotation>, Changed<Scale>)>)>,
>,
>,
>,
) {
for (mut transform, (rotation, scale)) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale_rotation_translation(
Vec3::new(scale.0, scale.0, scale.0),
rotation.0,
Vec3::default(),
));
}
}
pub fn transform_rotation_non_uniform_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
Translation,
Without<
Scale,
(
&mut Transform,
Or<(Changed<Rotation>, Changed<NonUniformScale>)>,
),
>,
>,
>,
>,
) {
for (mut transform, (rotation, non_uniform_scale)) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale_rotation_translation(
non_uniform_scale.0,
rotation.0,
Vec3::default(),
));
}
}
pub fn transform_translation_rotation_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
NonUniformScale,
(
&mut Transform,
Or<(Changed<Translation>, Changed<Rotation>, Changed<Scale>)>,
),
>,
>,
>,
) {
for (mut transform, (translation, rotation, scale)) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale_rotation_translation(
Vec3::new(scale.0, scale.0, scale.0),
rotation.0,
translation.0,
));
}
}
pub fn transform_translation_rotation_non_uniform_scale_system(
mut query: Query<
Without<
LocalTransform,
Without<
Scale,
(
&mut Transform,
Or<(
Changed<Translation>,
Changed<Rotation>,
Changed<NonUniformScale>,
)>,
),
>,
>,
>,
) {
for (mut transform, (translation, rotation, non_uniform_scale)) in &mut query.iter() {
if !transform.sync {
continue;
}
*transform = Transform::new(Mat4::from_scale_rotation_translation(
non_uniform_scale.0,
rotation.0,
translation.0,
));
}
}
pub fn transform_systems() -> Vec<Box<dyn System>> {
vec![
transform_translation_system.system(),
transform_rotation_system.system(),
transform_scale_system.system(),
transform_non_uniform_scale_system.system(),
transform_translation_rotation_system.system(),
transform_translation_scale_system.system(),
transform_translation_non_uniform_scale_system.system(),
transform_rotation_scale_system.system(),
transform_rotation_non_uniform_scale_system.system(),
transform_translation_rotation_scale_system.system(),
transform_translation_rotation_non_uniform_scale_system.system(),
]
}
#[cfg(test)]
mod test {
use super::*;
use bevy_ecs::{Resources, Schedule, World};
use bevy_math::{Mat4, Quat, Vec3};
#[test]
fn correct_world_transformation() {
let mut world = World::default();
let mut resources = Resources::default();
let mut schedule = Schedule::default();
schedule.add_stage("update");
for system in transform_systems() {
schedule.add_system_to_stage("update", system);
}
let transform = Transform::identity();
let t = Translation::new(1.0, 2.0, 3.0);
let r = Rotation(Quat::from_rotation_ypr(1.0, 2.0, 3.0));
let s = Scale(2.0);
let nus = NonUniformScale::new(1.0, 2.0, 3.0);
// Add every combination of transform types.
let translation = world.spawn((transform, t));
let rotation = world.spawn((transform, r));
let scale = world.spawn((transform, s));
let non_uniform_scale = world.spawn((transform, nus));
let translation_and_rotation = world.spawn((transform, t, r));
let translation_and_scale = world.spawn((transform, t, s));
let translation_and_nus = world.spawn((transform, t, nus));
let rotation_scale = world.spawn((transform, r, s));
let rotation_nus = world.spawn((transform, r, nus));
let translation_rotation_scale = world.spawn((transform, t, r, s));
let translation_rotation_nus = world.spawn((transform, t, r, nus));
// Run the system
schedule.run(&mut world, &mut resources);
// Verify that each was transformed correctly.
assert_eq!(
world.get::<Transform>(translation).unwrap().value,
Mat4::from_translation(t.0)
);
assert_eq!(
world.get::<Transform>(rotation).unwrap().value,
Mat4::from_quat(r.0)
);
assert_eq!(
world.get::<Transform>(scale).unwrap().value,
Mat4::from_scale(Vec3::new(s.0, s.0, s.0))
);
assert_eq!(
world.get::<Transform>(non_uniform_scale).unwrap().value,
Mat4::from_scale(nus.0)
);
assert_eq!(
world
.get::<Transform>(translation_and_rotation)
.unwrap()
.value,
Mat4::from_rotation_translation(r.0, t.0)
);
assert_eq!(
world.get::<Transform>(translation_and_scale).unwrap().value,
Mat4::from_scale_rotation_translation(Vec3::new(s.0, s.0, s.0), Quat::default(), t.0)
);
assert_eq!(
world.get::<Transform>(translation_and_nus).unwrap().value,
Mat4::from_scale_rotation_translation(nus.0, Quat::default(), t.0)
);
assert_eq!(
world.get::<Transform>(rotation_scale).unwrap().value,
Mat4::from_scale_rotation_translation(Vec3::new(s.0, s.0, s.0), r.0, Vec3::default())
);
assert_eq!(
world.get::<Transform>(rotation_nus).unwrap().value,
Mat4::from_scale_rotation_translation(nus.0, r.0, Vec3::default())
);
assert_eq!(
world
.get::<Transform>(translation_rotation_scale)
.unwrap()
.value,
Mat4::from_scale_rotation_translation(Vec3::new(s.0, s.0, s.0), r.0, t.0)
);
assert_eq!(
world
.get::<Transform>(translation_rotation_nus)
.unwrap()
.value,
Mat4::from_scale_rotation_translation(nus.0, r.0, t.0)
);
}
#[test]
fn only_propagates_transform_on_change() {
let mut world = World::default();
let mut resources = Resources::default();
let mut schedule = Schedule::default();
schedule.add_stage("update");
for system in transform_systems() {
schedule.add_system_to_stage("update", system);
}
let transform = Transform::identity();
let t = Translation::new(1.0, 2.0, 3.0);
let r = Rotation(Quat::from_rotation_ypr(1.0, 2.0, 3.0));
let s = Scale(2.0);
let nus = NonUniformScale::new(1.0, 2.0, 3.0);
// Add every combination of transform types.
world.spawn((transform, t));
world.spawn((transform, r));
world.spawn((transform, s));
world.spawn((transform, nus));
world.spawn((transform, t, r));
world.spawn((transform, t, s));
world.spawn((transform, t, nus));
world.spawn((transform, r, s));
world.spawn((transform, r, nus));
world.spawn((transform, t, r, s));
world.spawn((transform, t, r, nus));
// Run the system, transforms should mutate since they are new
schedule.run(&mut world, &mut resources);
// Verify that the transform is not mutated on the second frame
fn assert_no_transforms_changed_system(_: Changed<Transform>) {
assert!(false)
}
schedule.add_system_to_stage("update", assert_no_transforms_changed_system.system());
schedule.run(&mut world, &mut resources);
}
}

View file

@ -6,6 +6,7 @@ use crate::{
};
use bevy_asset::Handle;
use bevy_ecs::Bundle;
use bevy_math::Vec3;
use bevy_render::{
camera::{Camera, OrthographicProjection, VisibleEntities, WindowOrigin},
draw::Draw,
@ -13,10 +14,7 @@ use bevy_render::{
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
};
use bevy_sprite::{ColorMaterial, QUAD_HANDLE};
use bevy_transform::{
components::LocalTransform,
prelude::{Rotation, Scale, Transform, Translation},
};
use bevy_transform::prelude::{GlobalTransform, Transform};
#[derive(Bundle, Clone)]
pub struct NodeComponents {
@ -27,7 +25,7 @@ pub struct NodeComponents {
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub local_transform: LocalTransform,
pub global_transform: GlobalTransform,
}
impl Default for NodeComponents {
@ -57,7 +55,7 @@ impl Default for NodeComponents {
material: Default::default(),
draw: Default::default(),
transform: Default::default(),
local_transform: Default::default(),
global_transform: Default::default(),
}
}
}
@ -73,7 +71,7 @@ pub struct ImageComponents {
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub local_transform: LocalTransform,
pub global_transform: GlobalTransform,
}
impl Default for ImageComponents {
@ -105,7 +103,7 @@ impl Default for ImageComponents {
material: Default::default(),
draw: Default::default(),
transform: Default::default(),
local_transform: Default::default(),
global_transform: Default::default(),
}
}
}
@ -119,7 +117,7 @@ pub struct TextComponents {
pub calculated_size: CalculatedSize,
pub focus_policy: FocusPolicy,
pub transform: Transform,
pub local_transform: LocalTransform,
pub global_transform: GlobalTransform,
}
impl Default for TextComponents {
@ -135,7 +133,7 @@ impl Default for TextComponents {
calculated_size: Default::default(),
style: Default::default(),
transform: Default::default(),
local_transform: Default::default(),
global_transform: Default::default(),
}
}
}
@ -152,7 +150,7 @@ pub struct ButtonComponents {
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub transform: Transform,
pub local_transform: LocalTransform,
pub global_transform: GlobalTransform,
}
impl Default for ButtonComponents {
@ -185,7 +183,7 @@ impl Default for ButtonComponents {
material: Default::default(),
draw: Default::default(),
transform: Default::default(),
local_transform: Default::default(),
global_transform: Default::default(),
}
}
}
@ -196,9 +194,7 @@ pub struct UiCameraComponents {
pub orthographic_projection: OrthographicProjection,
pub visible_entities: VisibleEntities,
pub transform: Transform,
pub translation: Translation,
pub rotation: Rotation,
pub scale: Scale,
pub global_transform: GlobalTransform,
}
impl Default for UiCameraComponents {
@ -216,11 +212,9 @@ impl Default for UiCameraComponents {
window_origin: WindowOrigin::BottomLeft,
..Default::default()
},
translation: Translation::new(0.0, 0.0, far - 0.1),
visible_entities: Default::default(),
transform: Default::default(),
rotation: Default::default(),
scale: Default::default(),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, far - 0.1)),
global_transform: Default::default(),
}
}
}

View file

@ -3,7 +3,7 @@ mod convert;
use crate::{CalculatedSize, Node, Style};
use bevy_ecs::{Changed, Entity, Query, Res, ResMut, With, Without};
use bevy_math::Vec2;
use bevy_transform::prelude::{Children, LocalTransform, Parent};
use bevy_transform::prelude::{Children, Parent, Transform};
use bevy_utils::HashMap;
use bevy_window::{Window, WindowId, Windows};
use stretch::{number::Number, Stretch};
@ -154,7 +154,7 @@ pub fn flex_node_system(
mut node_query: Query<With<Node, (Entity, Changed<Style>, Option<&CalculatedSize>)>>,
mut changed_size_query: Query<With<Node, (Entity, &Style, Changed<CalculatedSize>)>>,
mut children_query: Query<With<Node, (Entity, Changed<Children>)>>,
mut node_transform_query: Query<(Entity, &mut Node, &mut LocalTransform, Option<&Parent>)>,
mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>,
) {
// update window root nodes
for window in windows.iter() {
@ -190,10 +190,10 @@ pub fn flex_node_system(
// compute layouts
flex_surface.compute_window_layouts();
for (entity, mut node, mut local, parent) in &mut node_transform_query.iter() {
for (entity, mut node, mut transform, parent) in &mut node_transform_query.iter() {
let layout = flex_surface.get_layout(entity).unwrap();
node.size = Vec2::new(layout.size.width, layout.size.height);
let mut position = local.w_axis();
let mut position = transform.translation();
position.set_x(layout.location.x + layout.size.width / 2.0);
position.set_y(layout.location.y + layout.size.height / 2.0);
if let Some(parent) = parent {
@ -203,6 +203,6 @@ pub fn flex_node_system(
}
}
local.set_w_axis(position);
transform.set_translation(position);
}
}

View file

@ -4,7 +4,7 @@ use bevy_core::FloatOrd;
use bevy_ecs::prelude::*;
use bevy_input::{mouse::MouseButton, Input};
use bevy_math::Vec2;
use bevy_transform::components::Transform;
use bevy_transform::components::GlobalTransform;
use bevy_window::CursorMoved;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
@ -46,7 +46,7 @@ pub fn ui_focus_system(
mut node_query: Query<(
Entity,
&Node,
&Transform,
&GlobalTransform,
Option<&mut Interaction>,
Option<&FocusPolicy>,
)>,
@ -56,7 +56,9 @@ pub fn ui_focus_system(
}
if mouse_button_input.just_released(MouseButton::Left) {
for (_entity, _node, _transform, interaction, _focus_policy) in &mut node_query.iter() {
for (_entity, _node, _global_transform, interaction, _focus_policy) in
&mut node_query.iter()
{
if let Some(mut interaction) = interaction {
if *interaction == Interaction::Clicked {
*interaction = Interaction::None;
@ -72,26 +74,28 @@ pub fn ui_focus_system(
let mut query_iter = node_query.iter();
let mut moused_over_z_sorted_nodes = query_iter
.iter()
.filter_map(|(entity, node, transform, interaction, focus_policy)| {
let position = transform.value.w_axis();
let ui_position = position.truncate().truncate();
let extents = node.size / 2.0;
let min = ui_position - extents;
let max = ui_position + extents;
// if the current cursor position is within the bounds of the node, consider it for clicking
if (min.x()..max.x()).contains(&state.cursor_position.x())
&& (min.y()..max.y()).contains(&state.cursor_position.y())
{
Some((entity, focus_policy, interaction, FloatOrd(position.z())))
} else {
if let Some(mut interaction) = interaction {
if *interaction == Interaction::Hovered {
*interaction = Interaction::None;
.filter_map(
|(entity, node, global_transform, interaction, focus_policy)| {
let position = global_transform.translation();
let ui_position = position.truncate();
let extents = node.size / 2.0;
let min = ui_position - extents;
let max = ui_position + extents;
// if the current cursor position is within the bounds of the node, consider it for clicking
if (min.x()..max.x()).contains(&state.cursor_position.x())
&& (min.y()..max.y()).contains(&state.cursor_position.y())
{
Some((entity, focus_policy, interaction, FloatOrd(position.z())))
} else {
if let Some(mut interaction) = interaction {
if *interaction == Interaction::Hovered {
*interaction = Interaction::None;
}
}
None
}
None
}
})
},
)
.collect::<Vec<_>>();
moused_over_z_sorted_nodes.sort_by_key(|(_, _, _, z)| -*z);

View file

@ -2,14 +2,14 @@ use super::Node;
use bevy_ecs::{Entity, Query, With, Without};
use bevy_transform::{
hierarchy,
prelude::{Children, LocalTransform, Parent},
prelude::{Children, Parent, Transform},
};
pub const UI_Z_STEP: f32 = 0.001;
pub fn ui_z_system(
mut root_node_query: Query<With<Node, Without<Parent, Entity>>>,
mut node_query: Query<(Entity, &Node, &mut LocalTransform)>,
mut node_query: Query<(Entity, &Node, &mut Transform)>,
children_query: Query<&Children>,
) {
let mut current_global_z = 0.0;
@ -34,12 +34,12 @@ pub fn ui_z_system(
}
fn update_node_entity(
node_query: &mut Query<(Entity, &Node, &mut LocalTransform)>,
node_query: &mut Query<(Entity, &Node, &mut Transform)>,
entity: Entity,
parent_result: Option<f32>,
previous_result: Option<f32>,
) -> Option<f32> {
let mut transform = node_query.get_mut::<LocalTransform>(entity).ok()?;
let mut transform = node_query.get_mut::<Transform>(entity).ok()?;
let mut z = UI_Z_STEP;
let parent_global_z = parent_result.unwrap();
if let Some(previous_global_z) = previous_result {
@ -47,9 +47,9 @@ fn update_node_entity(
};
let global_z = z + parent_global_z;
let mut position = transform.w_axis();
let mut position = transform.translation();
position.set_z(z);
transform.set_w_axis(position);
transform.set_translation(position);
Some(global_z)
}

View file

@ -10,7 +10,7 @@ use bevy_render::{
};
use bevy_sprite::TextureAtlas;
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
use bevy_transform::prelude::Transform;
use bevy_transform::prelude::GlobalTransform;
#[derive(Default, Clone)]
pub struct Text {
@ -58,11 +58,11 @@ pub fn draw_text_system(
texture_atlases: Res<Assets<TextureAtlas>>,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
mut query: Query<(&mut Draw, &Text, &Node, &Transform)>,
mut query: Query<(&mut Draw, &Text, &Node, &GlobalTransform)>,
) {
for (mut draw, text, node, transform) in &mut query.iter() {
let position =
Vec3::from(transform.value.w_axis().truncate()) - (node.size / 2.0).extend(0.0);
for (mut draw, text, node, global_transform) in &mut query.iter() {
let position = Vec3::from(global_transform.value().w_axis().truncate())
- (node.size / 2.0).extend(0.0);
let mut drawable_text = DrawableText {
font: fonts.get(&text.font).unwrap(),

View file

@ -39,7 +39,7 @@ fn setup(
.spawn(Camera2dComponents::default())
.spawn(SpriteSheetComponents {
texture_atlas: texture_atlas_handle,
scale: Scale(6.0),
transform: Transform::from_scale(Vec3::one() * 6.0),
..Default::default()
})
.with(Timer::from_seconds(0.1, true));

View file

@ -61,8 +61,8 @@ fn load_atlas(
.spawn(Camera2dComponents::default())
// draw a sprite from the atlas
.spawn(SpriteSheetComponents {
scale: Scale(4.0),
translation: Translation(Vec3::new(150.0, 0.0, 0.0)),
transform: Transform::from_translation(Vec3::new(150.0, 0.0, 0.0))
.with_scale(Vec3::one() * 4.0),
sprite: TextureAtlasSprite::new(vendor_index as u32),
texture_atlas: atlas_handle,
..Default::default()
@ -70,7 +70,7 @@ fn load_atlas(
// draw the atlas itself
.spawn(SpriteComponents {
material: materials.add(texture_atlas_texture.into()),
translation: Vec3::new(-300.0, 0., 0.0).into(),
transform: Transform::from_translation(Vec3::new(-300.0, 0.0, 0.0)),
..Default::default()
});

View file

@ -26,7 +26,7 @@ fn setup(
.spawn(PbrComponents {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(Color::rgb(0.5, 0.4, 0.3).into()),
translation: Translation::new(0.0, 1.0, 0.0),
transform: Transform::from_translation(Vec3::new(0.0, 1.0, 0.0)),
..Default::default()
})
// sphere
@ -36,17 +36,17 @@ fn setup(
radius: 0.5,
})),
material: materials.add(Color::rgb(0.1, 0.4, 0.8).into()),
translation: Translation::new(1.5, 1.5, 1.5),
transform: Transform::from_translation(Vec3::new(1.5, 1.5, 1.5)),
..Default::default()
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 8.0, 4.0),
transform: Transform::from_translation(Vec3::new(4.0, 8.0, 4.0)),
..Default::default()
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(-3.0, 5.0, 8.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -23,7 +23,7 @@ fn setup(
.unwrap(),
// create a material for the mesh
material: materials.add(Color::rgb(0.5, 0.4, 0.3).into()),
translation: Translation::new(-1.5, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(-1.5, 0.0, 0.0)),
..Default::default()
})
// mesh
@ -34,17 +34,17 @@ fn setup(
.unwrap(),
// create a material for the mesh
material: materials.add(Color::rgb(0.5, 0.4, 0.3).into()),
translation: Translation::new(1.5, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(1.5, 0.0, 0.0)),
..Default::default()
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 5.0, 4.0),
transform: Transform::from_translation(Vec3::new(4.0, 5.0, 4.0)),
..Default::default()
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(-2.0, 2.0, 6.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -27,12 +27,12 @@ fn setup(
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 8.0, 4.0),
transform: Transform::from_translation(Vec3::new(4.0, 8.0, 4.0)),
..Default::default()
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(-3.0, 3.0, 5.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -15,9 +15,9 @@ fn main() {
struct Rotator;
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, mut query: Query<(&Rotator, &mut Rotation)>) {
for (_rotator, mut rotation) in &mut query.iter() {
rotation.0 *= Quat::from_rotation_x(3.0 * time.delta_seconds);
fn rotator_system(time: Res<Time>, mut query: Query<(&Rotator, &mut Transform)>) {
for (_rotator, mut transform) in &mut query.iter() {
transform.rotate(Quat::from_rotation_x(3.0 * time.delta_seconds));
}
}
@ -38,7 +38,7 @@ fn setup(
.spawn(PbrComponents {
mesh: cube_handle,
material: cube_material_handle,
translation: Translation::new(0.0, 0.0, 1.0),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)),
..Default::default()
})
.with(Rotator)
@ -47,18 +47,18 @@ fn setup(
parent.spawn(PbrComponents {
mesh: cube_handle,
material: cube_material_handle,
translation: Translation::new(0.0, 0.0, 3.0),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 3.0)),
..Default::default()
});
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 5.0, -4.0),
transform: Transform::from_translation(Vec3::new(4.0, 5.0, -4.0)),
..Default::default()
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(5.0, 10.0, 10.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -21,11 +21,11 @@ fn main() {
fn move_cubes(
time: Res<Time>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut query: Query<(&mut Translation, &Handle<StandardMaterial>)>,
mut query: Query<(&mut Transform, &Handle<StandardMaterial>)>,
) {
for (mut translation, material_handle) in &mut query.iter() {
for (mut transform, material_handle) in &mut query.iter() {
let material = materials.get_mut(&material_handle).unwrap();
translation.0 += Vec3::new(1.0, 0.0, 0.0) * time.delta_seconds;
transform.translate(Vec3::new(1.0, 0.0, 0.0) * time.delta_seconds);
material.albedo =
Color::BLUE * Vec3::splat((3.0 * time.seconds_since_startup as f32).sin());
}
@ -39,12 +39,12 @@ fn setup(
commands
// light
.spawn(LightComponents {
translation: Translation::new(4.0, -4.0, 5.0),
transform: Transform::from_translation(Vec3::new(4.0, -4.0, 5.0)),
..Default::default()
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(0.0, 15.0, 150.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
@ -65,11 +65,11 @@ fn setup(
),
..Default::default()
}),
translation: Translation::new(
transform: Transform::from_translation(Vec3::new(
rng.gen_range(-50.0, 50.0),
rng.gen_range(-50.0, 50.0),
0.0,
),
)),
..Default::default()
});
}

View file

@ -59,8 +59,8 @@ fn setup(
.spawn(PbrComponents {
mesh: quad_handle,
material: material_handle,
translation: Translation::new(0.0, 0.0, 1.5),
rotation: Rotation(Quat::from_rotation_x(-std::f32::consts::PI / 5.0)),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 1.5))
.with_rotation(Quat::from_rotation_x(-std::f32::consts::PI / 5.0)),
draw: Draw {
is_transparent: true,
..Default::default()
@ -71,8 +71,8 @@ fn setup(
.spawn(PbrComponents {
mesh: quad_handle,
material: red_material_handle,
translation: Translation::new(0.0, 0.0, 0.0),
rotation: Rotation(Quat::from_rotation_x(-std::f32::consts::PI / 5.0)),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0))
.with_rotation(Quat::from_rotation_x(-std::f32::consts::PI / 5.0)),
draw: Draw {
is_transparent: true,
..Default::default()
@ -83,8 +83,8 @@ fn setup(
.spawn(PbrComponents {
mesh: quad_handle,
material: blue_material_handle,
translation: Translation::new(0.0, 0.0, -1.5),
rotation: Rotation(Quat::from_rotation_x(-std::f32::consts::PI / 5.0)),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, -1.5))
.with_rotation(Quat::from_rotation_x(-std::f32::consts::PI / 5.0)),
draw: Draw {
is_transparent: true,
..Default::default()
@ -93,7 +93,7 @@ fn setup(
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(3.0, 5.0, 8.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -19,9 +19,9 @@ fn main() {
struct Rotator;
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, mut query: Query<(&Rotator, &mut Rotation)>) {
for (_rotator, mut rotation) in &mut query.iter() {
rotation.0 *= Quat::from_rotation_x(3.0 * time.delta_seconds);
fn rotator_system(time: Res<Time>, mut query: Query<(&Rotator, &mut Transform)>) {
for (_rotator, mut transform) in &mut query.iter() {
transform.rotate(Quat::from_rotation_x(3.0 * time.delta_seconds));
}
}
@ -57,7 +57,7 @@ fn setup(
shaded: false,
..Default::default()
}),
translation: Translation::new(0.0, 0.0, 1.0),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)),
..Default::default()
})
.with(Rotator)
@ -70,7 +70,7 @@ fn setup(
shaded: false,
..Default::default()
}),
translation: Translation::new(0.0, 3.0, 0.0),
transform: Transform::from_translation(Vec3::new(0.0, 3.0, 0.0)),
..Default::default()
})
.spawn(PbrComponents {
@ -79,13 +79,13 @@ fn setup(
shaded: false,
..Default::default()
}),
translation: Translation::new(0.0, -3.0, 0.0),
transform: Transform::from_translation(Vec3::new(0.0, -3.0, 0.0)),
..Default::default()
});
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(5.0, 10.0, 10.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -49,31 +49,31 @@ fn setup(
.spawn(PbrComponents {
mesh: monkey_handle,
material: material_handle,
translation: Translation::new(-3.0, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(-3.0, 0.0, 0.0)),
..Default::default()
})
// cube
.spawn(PbrComponents {
mesh: cube_handle,
material: material_handle,
translation: Translation::new(0.0, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
..Default::default()
})
// sphere
.spawn(PbrComponents {
mesh: sphere_handle,
material: material_handle,
translation: Translation::new(3.0, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(3.0, 0.0, 0.0)),
..Default::default()
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 5.0, 4.0),
transform: Transform::from_translation(Vec3::new(4.0, 5.0, 4.0)),
..Default::default()
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(0.0, 3.0, 10.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -42,12 +42,12 @@ fn setup(
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 5.0, 4.0),
transform: Transform::from_translation(Vec3::new(4.0, 5.0, 4.0)),
..Default::default()
})
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(2.0, 2.0, 6.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -15,8 +15,7 @@ fn spawn_system(
commands
.spawn(SpriteComponents {
material,
translation: Translation::new(0.0, 0.0, 0.0),
scale: Scale(0.1),
transform: Transform::from_scale(Vec3::one() * 0.1),
..Default::default()
})
.with(Velocity(
@ -26,7 +25,7 @@ fn spawn_system(
}
// Move sprites according to their velocity
fn move_system(pool: Res<ComputeTaskPool>, mut sprites: Query<(&mut Translation, &Velocity)>) {
fn move_system(pool: Res<ComputeTaskPool>, mut sprites: Query<(&mut Transform, &Velocity)>) {
// Compute the new location of each sprite in parallel on the
// ComputeTaskPool using batches of 32 sprties
//
@ -35,16 +34,19 @@ fn move_system(pool: Res<ComputeTaskPool>, mut sprites: Query<(&mut Translation,
// elements will not typically be faster than just using a normal Iterator.
// See the ParallelIterator documentation for more information on when
// to use or not use ParallelIterator over a normal Iterator.
sprites.iter().par_iter(32).for_each(&pool, |(mut t, v)| {
t.0 += v.0.extend(0.0);
});
sprites
.iter()
.par_iter(32)
.for_each(&pool, |(mut transform, velocity)| {
transform.translate(velocity.0.extend(0.0));
});
}
// Bounce sprties outside the window
fn bounce_system(
pool: Res<ComputeTaskPool>,
windows: Res<Windows>,
mut sprites: Query<(&Translation, &mut Velocity)>,
mut sprites: Query<(&Transform, &mut Velocity)>,
) {
let Window { width, height, .. } = windows.get_primary().expect("No primary window");
let left = *width as f32 / -2.0;
@ -57,7 +59,12 @@ fn bounce_system(
// ParallelIterator, since negating a vector is very inexpensive.
.par_iter(32)
// Filter out sprites that don't need to be bounced
.filter(|(t, _)| !(left < t.x() && t.x() < right && bottom < t.y() && t.y() < top))
.filter(|(transform, _)| {
!(left < transform.translation().x()
&& transform.translation().x() < right
&& bottom < transform.translation().y()
&& transform.translation().y() < top)
})
// For simplicity, just reverse the velocity; don't use realistic bounces
.for_each(&pool, |(_, mut v)| {
v.0 = -v.0;

View file

@ -48,7 +48,7 @@ fn setup(
// paddle
.spawn(SpriteComponents {
material: materials.add(Color::rgb(0.2, 0.2, 0.8).into()),
translation: Translation(Vec3::new(0.0, -215.0, 0.0)),
transform: Transform::from_translation(Vec3::new(0.0, -215.0, 0.0)),
sprite: Sprite::new(Vec2::new(120.0, 30.0)),
..Default::default()
})
@ -57,7 +57,7 @@ fn setup(
// ball
.spawn(SpriteComponents {
material: materials.add(Color::rgb(0.8, 0.2, 0.2).into()),
translation: Translation(Vec3::new(0.0, -50.0, 1.0)),
transform: Transform::from_translation(Vec3::new(0.0, -50.0, 1.0)),
sprite: Sprite::new(Vec2::new(30.0, 30.0)),
..Default::default()
})
@ -95,7 +95,7 @@ fn setup(
// left
.spawn(SpriteComponents {
material: wall_material,
translation: Translation(Vec3::new(-bounds.x() / 2.0, 0.0, 0.0)),
transform: Transform::from_translation(Vec3::new(-bounds.x() / 2.0, 0.0, 0.0)),
sprite: Sprite::new(Vec2::new(wall_thickness, bounds.y() + wall_thickness)),
..Default::default()
})
@ -103,7 +103,7 @@ fn setup(
// right
.spawn(SpriteComponents {
material: wall_material,
translation: Translation(Vec3::new(bounds.x() / 2.0, 0.0, 0.0)),
transform: Transform::from_translation(Vec3::new(bounds.x() / 2.0, 0.0, 0.0)),
sprite: Sprite::new(Vec2::new(wall_thickness, bounds.y() + wall_thickness)),
..Default::default()
})
@ -111,7 +111,7 @@ fn setup(
// bottom
.spawn(SpriteComponents {
material: wall_material,
translation: Translation(Vec3::new(0.0, -bounds.y() / 2.0, 0.0)),
transform: Transform::from_translation(Vec3::new(0.0, -bounds.y() / 2.0, 0.0)),
sprite: Sprite::new(Vec2::new(bounds.x() + wall_thickness, wall_thickness)),
..Default::default()
})
@ -119,7 +119,7 @@ fn setup(
// top
.spawn(SpriteComponents {
material: wall_material,
translation: Translation(Vec3::new(0.0, bounds.y() / 2.0, 0.0)),
transform: Transform::from_translation(Vec3::new(0.0, bounds.y() / 2.0, 0.0)),
sprite: Sprite::new(Vec2::new(bounds.x() + wall_thickness, wall_thickness)),
..Default::default()
})
@ -147,7 +147,7 @@ fn setup(
.spawn(SpriteComponents {
material: materials.add(Color::rgb(0.2, 0.2, 0.8).into()),
sprite: Sprite::new(brick_size),
translation: Translation(brick_position),
transform: Transform::from_translation(brick_position),
..Default::default()
})
.with(Collider::Scorable);
@ -158,9 +158,9 @@ fn setup(
fn paddle_movement_system(
time: Res<Time>,
keyboard_input: Res<Input<KeyCode>>,
mut query: Query<(&Paddle, &mut Translation)>,
mut query: Query<(&Paddle, &mut Transform)>,
) {
for (paddle, mut translation) in &mut query.iter() {
for (paddle, mut transform) in &mut query.iter() {
let mut direction = 0.0;
if keyboard_input.pressed(KeyCode::Left) {
direction -= 1.0;
@ -170,19 +170,24 @@ fn paddle_movement_system(
direction += 1.0;
}
*translation.0.x_mut() += time.delta_seconds * direction * paddle.speed;
transform.translate(Vec3::unit_x() * time.delta_seconds * direction * paddle.speed);
// bound the paddle within the walls
*translation.0.x_mut() = f32::max(-380.0, f32::min(380.0, translation.0.x()));
let translation = transform.translation();
transform.set_translation(Vec3::new(
f32::max(-380.0, f32::min(380.0, translation.x())),
translation.y(),
translation.z(),
));
}
}
fn ball_movement_system(time: Res<Time>, mut ball_query: Query<(&Ball, &mut Translation)>) {
fn ball_movement_system(time: Res<Time>, mut ball_query: Query<(&Ball, &mut Transform)>) {
// clamp the timestep to stop the ball from escaping when the game starts
let delta_seconds = f32::min(0.2, time.delta_seconds);
for (ball, mut translation) in &mut ball_query.iter() {
translation.0 += ball.velocity * delta_seconds;
for (ball, mut transform) in &mut ball_query.iter() {
transform.translate(ball.velocity * delta_seconds);
}
}
@ -195,16 +200,21 @@ fn scoreboard_system(scoreboard: Res<Scoreboard>, mut query: Query<&mut Text>) {
fn ball_collision_system(
mut commands: Commands,
mut scoreboard: ResMut<Scoreboard>,
mut ball_query: Query<(&mut Ball, &Translation, &Sprite)>,
mut collider_query: Query<(Entity, &Collider, &Translation, &Sprite)>,
mut ball_query: Query<(&mut Ball, &Transform, &Sprite)>,
mut collider_query: Query<(Entity, &Collider, &Transform, &Sprite)>,
) {
for (mut ball, ball_translation, sprite) in &mut ball_query.iter() {
for (mut ball, ball_transform, sprite) in &mut ball_query.iter() {
let ball_size = sprite.size;
let velocity = &mut ball.velocity;
// check collision with walls
for (collider_entity, collider, translation, sprite) in &mut collider_query.iter() {
let collision = collide(ball_translation.0, ball_size, translation.0, sprite.size);
for (collider_entity, collider, transform, sprite) in &mut collider_query.iter() {
let collision = collide(
ball_transform.translation(),
ball_size,
transform.translation(),
sprite.size,
);
if let Some(collision) = collision {
// scorable colliders should be despawned and increment the scoreboard on collision
if let Collider::Scorable = *collider {

View file

@ -102,13 +102,13 @@ fn setup(
..Default::default()
},
)]),
translation: Translation::new(0.0, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
..Default::default()
})
.with(material)
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(3.0, 5.0, -8.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -123,7 +123,7 @@ fn setup(
..Default::default()
},
)]),
translation: Translation::new(-2.0, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(-2.0, 0.0, 0.0)),
..Default::default()
})
.with(green_material)
@ -149,13 +149,13 @@ fn setup(
..Default::default()
},
)]),
translation: Translation::new(2.0, 0.0, 0.0),
transform: Transform::from_translation(Vec3::new(2.0, 0.0, 0.0)),
..Default::default()
})
.with(blue_material)
// camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(3.0, 5.0, -8.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),

View file

@ -173,12 +173,12 @@ fn setup(
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 5.0, 4.0),
transform: Transform::from_translation(Vec3::new(4.0, 5.0, 4.0)),
..Default::default()
})
// main camera
.spawn(Camera3dComponents {
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(0.0, 0.0, 6.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
@ -192,7 +192,7 @@ fn setup(
window: window_id,
..Default::default()
},
transform: Transform::new_sync_disabled(Mat4::face_toward(
transform: Transform::new(Mat4::face_toward(
Vec3::new(6.0, 0.0, 0.0),
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),