-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Mesh
transformation
#11454
Add Mesh
transformation
#11454
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this, but maybe it would be better to implement this as Add<Transform>
and AddAssign<Transform>
?
Not sure, maybe? But it'd probably be I could at least add a |
I ended up adding let transform = Transform::from_xyz(0.0, 2.0, 0.0);
// These are equivalent
let cuboid = Mesh::from(shape::Box::default()).transformed_by(transform);
let cuboid = transform * Mesh::from(shape::Box::default());
let mut cuboid = Mesh::from(shape::Box::default());
cuboid.transform_by(transform); Feel free to suggest further improvements/changes to the API |
Why did you implement let mut cuboid = Mesh::from(shape::Box::default());
cuboid *= transform;
// or
let cuboid = Mesh::from(shape::Box::default()) * transform; |
For consistency. You can't do For me, the current |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see equivalent helpers for the translate/rotate/scale operations alone (that skip the unneeded ops and take in a type-safe arg), but that can be a second PR.
I don't personally think that transformed_by
is worth duplicating the API over, but I won't block over it.
Here to mention that I ran into exactly this use-case (wanted a xz plane circle). I do think the Mul<> operator not being symmetric is a bit weird, and I will most likely used the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I agree that separate translate/rotate/scale version later would be a nice followup PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't transform normals and tangents correctly. Test non-uniform scaling, they need to be component-wise divided by scale in the original unrotated basis and then normalized, because together they make up a bivector.
alternatively to avoid a division, |
If any component of scale is zero it will cause a division by zero and result in infinite-length normals, maybe the division-skipping version that uses only multiplications is a better option |
Won't the multiplication version similarly fail when we attempt to normalize the product? Or is |
I can use |
No, the multiplication-only version will work fine, i'm talking about only one component being zero. |
Thanks for the patience and the fixes |
# Objective It can sometimes be useful to transform actual `Mesh` data without needing to change the `Transform` of an entity. For example, one might want to spawn a circle mesh facing up instead of facing Z, or to spawn a mesh slightly offset without needing child entities. ## Solution Add `transform_by` and `transformed_by` methods to `Mesh`. They take a `Transform` and apply the translation, rotation, and scale to vertex positions, and the rotation to normals and tangents. In the `load_gltf` example, with this system: ```rust fn transform(time: Res<Time>, mut q: Query<&mut Handle<Mesh>>, mut meshes: ResMut<Assets<Mesh>>) { let sin = 0.0025 * time.elapsed_seconds().sin(); for mesh_handle in &mut q { if let Some(mesh) = meshes.get_mut(mesh_handle.clone_weak()) { let transform = Transform::from_rotation(Quat::from_rotation_y(0.75 * time.delta_seconds())) .with_scale(Vec3::splat(1.0 + sin)); mesh.transform_by(transform); } } } ``` it looks like this: https://github.com/bevyengine/bevy/assets/57632562/60432456-6d28-4d06-9c94-2f4148f5acd5
# Objective It can sometimes be useful to transform actual `Mesh` data without needing to change the `Transform` of an entity. For example, one might want to spawn a circle mesh facing up instead of facing Z, or to spawn a mesh slightly offset without needing child entities. ## Solution Add `transform_by` and `transformed_by` methods to `Mesh`. They take a `Transform` and apply the translation, rotation, and scale to vertex positions, and the rotation to normals and tangents. In the `load_gltf` example, with this system: ```rust fn transform(time: Res<Time>, mut q: Query<&mut Handle<Mesh>>, mut meshes: ResMut<Assets<Mesh>>) { let sin = 0.0025 * time.elapsed_seconds().sin(); for mesh_handle in &mut q { if let Some(mesh) = meshes.get_mut(mesh_handle.clone_weak()) { let transform = Transform::from_rotation(Quat::from_rotation_y(0.75 * time.delta_seconds())) .with_scale(Vec3::splat(1.0 + sin)); mesh.transform_by(transform); } } } ``` it looks like this: https://github.com/bevyengine/bevy/assets/57632562/60432456-6d28-4d06-9c94-2f4148f5acd5
# Objective It can sometimes be useful to combine several meshes into one. This allows constructing more complex meshes out of simple primitives without needing to use a 3D modeling program or entity hierarchies. This could also be used internally to increase code reuse by using existing mesh generation logic for e.g. circles and using that in cylinder mesh generation logic to add the top and bottom of the cylinder. **Note**: This is *not* implementing CSGs (Constructive Solid Geometry) or any boolean operations, as that is much more complex. This is simply adding the mesh data of another mesh to a mesh. ## Solution Add a `merge` method to `Mesh`. It appends the vertex attributes and indices of `other` to `self`, resulting in a `Mesh` that is the combination of the two. For example, you could do this: ```rust let mut cuboid = Mesh::from(shape::Box::default()); let mut cylinder = Mesh::from(shape::Cylinder::default()); let mut torus = Mesh::from(shape::Torus::default()); cuboid.merge(cylinder); cuboid.merge(torus); ``` This would result in `cuboid` being a `Mesh` that also has the cylinder mesh and torus mesh. In this case, they would just be placed on top of each other, but by utilizing #11454 we can transform the cylinder and torus to get a result like this: https://github.com/bevyengine/bevy/assets/57632562/557402c6-b896-4aba-bd95-312e7d1b5238 This is just a single entity and a single mesh.
# Objective Make it straightforward to translate and rotate bounding volumes. ## Solution Add `translate_by`/`translated_by`, `rotate_by`/`rotated_by`, `transform_by`/`transformed_by` methods to the `BoundingVolume` trait. This follows the naming used for mesh transformations (see #11454 and #11675). --- ## Changelog - Added `translate_by`/`translated_by`, `rotate_by`/`rotated_by`, `transform_by`/`transformed_by` methods to the `BoundingVolume` trait and implemented them for the bounding volumes - Renamed `Position` associated type to `Translation` --------- Co-authored-by: Mateusz Wachowiak <[email protected]>
# Objective Make it straightforward to translate and rotate bounding volumes. ## Solution Add `translate_by`/`translated_by`, `rotate_by`/`rotated_by`, `transform_by`/`transformed_by` methods to the `BoundingVolume` trait. This follows the naming used for mesh transformations (see bevyengine#11454 and bevyengine#11675). --- ## Changelog - Added `translate_by`/`translated_by`, `rotate_by`/`rotated_by`, `transform_by`/`transformed_by` methods to the `BoundingVolume` trait and implemented them for the bounding volumes - Renamed `Position` associated type to `Translation` --------- Co-authored-by: Mateusz Wachowiak <[email protected]>
# Objective Make it straightforward to translate and rotate bounding volumes. ## Solution Add `translate_by`/`translated_by`, `rotate_by`/`rotated_by`, `transform_by`/`transformed_by` methods to the `BoundingVolume` trait. This follows the naming used for mesh transformations (see bevyengine#11454 and bevyengine#11675). --- ## Changelog - Added `translate_by`/`translated_by`, `rotate_by`/`rotated_by`, `transform_by`/`transformed_by` methods to the `BoundingVolume` trait and implemented them for the bounding volumes - Renamed `Position` associated type to `Translation` --------- Co-authored-by: Mateusz Wachowiak <[email protected]>
Objective
It can sometimes be useful to transform actual
Mesh
data without needing to change theTransform
of an entity. For example, one might want to spawn a circle mesh facing up instead of facing Z, or to spawn a mesh slightly offset without needing child entities.Solution
Add
transform_by
andtransformed_by
methods toMesh
. They take aTransform
and apply the translation, rotation, and scale to vertex positions, and the rotation to normals and tangents.In the
load_gltf
example, with this system:it looks like this:
2024-01-21.15-05-43.mp4