Add Mesh transformation#11454
Conversation
doonv
left a comment
There was a problem hiding this comment.
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 |
alice-i-cecile
left a comment
There was a problem hiding this comment.
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 |
NiseVoid
left a comment
There was a problem hiding this comment.
Looks good. I agree that separate translate/rotate/scale version later would be a nice followup PR
atlv24
left a comment
There was a problem hiding this comment.
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 <mateusz_wachowiak@outlook.com>
# 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 <mateusz_wachowiak@outlook.com>
# 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 <mateusz_wachowiak@outlook.com>
Objective
It can sometimes be useful to transform actual
Meshdata without needing to change theTransformof 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_byandtransformed_bymethods toMesh. They take aTransformand apply the translation, rotation, and scale to vertex positions, and the rotation to normals and tangents.In the
load_gltfexample, with this system:it looks like this:
2024-01-21.15-05-43.mp4