From 9f3622850bae75900b7e394b05782b3e1eb18cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Lescaudey=20de=20Maneville?= Date: Wed, 25 Oct 2023 10:15:21 +0200 Subject: [PATCH] Mesh can be scaled --- CHANGELOG.md | 3 +++ examples/3d_columns.rs | 4 ++-- examples/a_star.rs | 7 +++++-- examples/chunks.rs | 7 +++++-- examples/field_of_movement.rs | 2 +- examples/field_of_view.rs | 7 +++++-- examples/hex_grid.rs | 7 +++++-- examples/mesh_builder.rs | 3 +++ src/mesh/column_builder.rs | 33 ++++++++++++++++++++++++++++----- src/mesh/plane_builder.rs | 31 +++++++++++++++++++++++++++---- 10 files changed, 84 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56b2efc..f772a34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +* Added `PlaneMeshBuilder::with_scale` option to scale generated mesh (#121) +* Added `ColumnMeshBuilder::with_scale` option to scale generated mesh (#121) + ## 0.11.0 * Reduced vertice and tri count of mesh generation (#119) diff --git a/examples/3d_columns.rs b/examples/3d_columns.rs index 5fcfbe5..bfc13eb 100644 --- a/examples/3d_columns.rs +++ b/examples/3d_columns.rs @@ -75,8 +75,7 @@ fn setup_grid( let pos = layout.hex_to_world_pos(hex); let id = commands .spawn(PbrBundle { - transform: Transform::from_xyz(pos.x, hex.length() as f32 / 2.0, pos.y) - .with_scale(Vec3::splat(0.9)), + transform: Transform::from_xyz(pos.x, hex.length() as f32 / 2.0, pos.y), mesh: mesh_handle.clone(), material: default_material.clone(), ..default() @@ -124,6 +123,7 @@ fn animate_rings( fn hexagonal_column(hex_layout: &HexLayout) -> Mesh { let mesh_info = ColumnMeshBuilder::new(hex_layout, COLUMN_HEIGHT) .without_bottom_face() + .with_scale(Vec3::splat(0.9)) .build(); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices); diff --git a/examples/a_star.rs b/examples/a_star.rs index d985b84..60cae00 100644 --- a/examples/a_star.rs +++ b/examples/a_star.rs @@ -71,7 +71,7 @@ fn setup_grid( .spawn(ColorMesh2dBundle { mesh: mesh.clone().into(), material, - transform: Transform::from_xyz(pos.x, pos.y, 0.0).with_scale(Vec3::splat(0.9)), + transform: Transform::from_xyz(pos.x, pos.y, 0.0), ..default() }) .id(); @@ -151,7 +151,10 @@ fn handle_input( /// Compute a bevy mesh from the layout fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh { - let mesh_info = PlaneMeshBuilder::new(hex_layout).facing(Vec3::Z).build(); + let mesh_info = PlaneMeshBuilder::new(hex_layout) + .facing(Vec3::Z) + .with_scale(Vec3::splat(0.9)) + .build(); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals); diff --git a/examples/chunks.rs b/examples/chunks.rs index c19fed3..e4e507a 100644 --- a/examples/chunks.rs +++ b/examples/chunks.rs @@ -48,7 +48,7 @@ fn setup_grid( let hex_mod = hex.to_lower_res(CHUNK_SIZE); let color_index = (hex_mod.x - hex_mod.y).rem_euclid(3); commands.spawn(ColorMesh2dBundle { - transform: Transform::from_xyz(pos.x, pos.y, 0.0).with_scale(Vec3::splat(0.9)), + transform: Transform::from_xyz(pos.x, pos.y, 0.0), mesh: mesh_handle.clone().into(), material: materials[color_index as usize].clone(), ..default() @@ -58,7 +58,10 @@ fn setup_grid( /// Compute a bevy mesh from the layout fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh { - let mesh_info = PlaneMeshBuilder::new(hex_layout).facing(Vec3::Z).build(); + let mesh_info = PlaneMeshBuilder::new(hex_layout) + .with_scale(Vec3::splat(0.9)) + .facing(Vec3::Z) + .build(); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals); diff --git a/examples/field_of_movement.rs b/examples/field_of_movement.rs index 7995ae6..6d64d29 100644 --- a/examples/field_of_movement.rs +++ b/examples/field_of_movement.rs @@ -119,7 +119,7 @@ fn setup_grid( .spawn(ColorMesh2dBundle { mesh: mesh.clone().into(), material, - transform: Transform::from_xyz(pos.x, pos.y, 0.0).with_scale(Vec3::splat(1.)), + transform: Transform::from_xyz(pos.x, pos.y, 0.0), ..default() }) .id(); diff --git a/examples/field_of_view.rs b/examples/field_of_view.rs index f6c06a2..2492677 100644 --- a/examples/field_of_view.rs +++ b/examples/field_of_view.rs @@ -70,7 +70,7 @@ fn setup_grid( .spawn(ColorMesh2dBundle { mesh: mesh.clone().into(), material, - transform: Transform::from_xyz(pos.x, pos.y, 0.0).with_scale(Vec3::splat(0.9)), + transform: Transform::from_xyz(pos.x, pos.y, 0.0), ..default() }) .id(); @@ -141,7 +141,10 @@ fn handle_input( /// Compute a bevy mesh from the layout fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh { - let mesh_info = PlaneMeshBuilder::new(hex_layout).facing(Vec3::Z).build(); + let mesh_info = PlaneMeshBuilder::new(hex_layout) + .facing(Vec3::Z) + .with_scale(Vec3::splat(0.9)) + .build(); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals); diff --git a/examples/hex_grid.rs b/examples/hex_grid.rs index 1fee56c..dd4a049 100644 --- a/examples/hex_grid.rs +++ b/examples/hex_grid.rs @@ -81,7 +81,7 @@ fn setup_grid( let pos = layout.hex_to_world_pos(hex); let id = commands .spawn(ColorMesh2dBundle { - transform: Transform::from_xyz(pos.x, pos.y, 0.0).with_scale(Vec3::splat(0.95)), + transform: Transform::from_xyz(pos.x, pos.y, 0.0), mesh: mesh_handle.clone().into(), material: default_material.clone(), ..default() @@ -199,7 +199,10 @@ fn handle_input( /// Compute a bevy mesh from the layout fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh { - let mesh_info = PlaneMeshBuilder::new(hex_layout).facing(Vec3::Z).build(); + let mesh_info = PlaneMeshBuilder::new(hex_layout) + .facing(Vec3::Z) + .with_scale(Vec3::splat(0.95)) + .build(); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals); diff --git a/examples/mesh_builder.rs b/examples/mesh_builder.rs index f2d6b54..4d1505f 100644 --- a/examples/mesh_builder.rs +++ b/examples/mesh_builder.rs @@ -26,6 +26,7 @@ struct BuilderParams { pub bottom_face: bool, pub sides_uvs: UVOptions, pub caps_uvs: UVOptions, + pub scale: Vec3, } pub fn main() { @@ -111,6 +112,7 @@ fn update_mesh(params: Res, info: Res, mut meshes: ResMu let mut new_mesh = ColumnMeshBuilder::new(&info.layout, params.height) .with_subdivisions(params.subdivisions) .with_offset(Vec3::NEG_Y * params.height / 2.0) + .with_scale(params.scale) .with_caps_uv_options(params.caps_uvs.clone()) .with_sides_uv_options(params.sides_uvs.clone()); if !params.top_face { @@ -144,6 +146,7 @@ impl Default for BuilderParams { bottom_face: true, sides_uvs: UVOptions::quad_default().with_scale_factor(vec2(1.0, 0.3)), caps_uvs: UVOptions::cap_default().with_scale_factor(vec2(0.5, 0.5)), + scale: Vec3::ONE, } } } diff --git a/src/mesh/column_builder.rs b/src/mesh/column_builder.rs index 6f8c781..37226fe 100644 --- a/src/mesh/column_builder.rs +++ b/src/mesh/column_builder.rs @@ -36,9 +36,13 @@ pub struct ColumnMeshBuilder<'l> { pub pos: Hex, /// Optional custom offset for the mesh vertex positions pub offset: Option, + /// Optional custom scale factor for the mesh vertex positions + pub scale: Option, /// Optional custom facing direction, useful to have the mesh already /// rotated /// + /// Note that the `scale` factor will be applied before the rotation + /// /// By default the mesh is *facing* up (**Y** axis) pub facing: Option, /// Amount of quads to be generated on the sides of the column @@ -64,6 +68,7 @@ impl<'l> ColumnMeshBuilder<'l> { facing: None, subdivisions: None, offset: None, + scale: None, top_face: true, bottom_face: true, sides_uv_options: UVOptions::quad_default(), @@ -86,6 +91,8 @@ impl<'l> ColumnMeshBuilder<'l> { /// Specify a custom *facing* direction for the mesh, by default the column /// is vertical (facing up) + /// + /// Note that the `scale` factor will be applied before the rotation #[must_use] #[inline] pub const fn facing(mut self, facing: Vec3) -> Self { @@ -116,6 +123,13 @@ impl<'l> ColumnMeshBuilder<'l> { self } + /// Specify a custom scale factor for the whole mesh + #[must_use] + pub const fn with_scale(mut self, scale: Vec3) -> Self { + self.scale = Some(scale); + self + } + /// Defines the column side quads amount #[must_use] #[inline] @@ -164,19 +178,21 @@ impl<'l> ColumnMeshBuilder<'l> { #[allow(clippy::many_single_char_names)] /// Comsumes the builder to return the computed mesh data pub fn build(self) -> MeshInfo { + // We compute the mesh at the origin to allow scaling let cap_mesh = PlaneMeshBuilder::new(self.layout) - .at(self.pos) .with_uv_options(self.caps_uv_options) .build(); + // We store the offset to match the `self.pos` + let mut offset = self.layout.hex_to_world_pos(self.pos).extend(0.0); + // We create the final mesh let mut mesh = MeshInfo::default(); // Column sides let subidivisions = self.subdivisions.unwrap_or(0).max(1); let delta = self.height / subidivisions as f32; - let center = self.layout.hex_to_world_pos(self.pos); let [a, b, c, d, e, f] = self.layout.hex_corners(self.pos); let corners = [[a, b], [b, c], [c, d], [d, e], [e, f], [f, a]]; for [left, right] in corners { - let normal = (left - center + right - center).normalize(); + let normal = (left + right).normalize(); for div in 0..subidivisions { let height = delta * div as f32; let left = Vec3::new(left.x, height, left.y); @@ -194,14 +210,21 @@ impl<'l> ColumnMeshBuilder<'l> { let bottom_face = cap_mesh.rotated(rotation); mesh.merge_with(bottom_face); } - if let Some(offset) = self.offset { - mesh = mesh.with_offset(offset); + // We apply optional scale + if let Some(scale) = self.scale { + mesh.vertices.iter_mut().for_each(|p| *p *= scale); } + // We rotate the mesh to face the given direction if let Some(facing) = self.facing { let facing = facing.normalize(); let rotation = Quat::from_rotation_arc(BASE_FACING, facing); mesh = mesh.rotated(rotation); } + // We offset the vertex positions after scaling and rotating + if let Some(custom_offset) = self.offset { + offset += custom_offset; + } + mesh = mesh.with_offset(offset); mesh } } diff --git a/src/mesh/plane_builder.rs b/src/mesh/plane_builder.rs index 96f7c05..0ff57dc 100644 --- a/src/mesh/plane_builder.rs +++ b/src/mesh/plane_builder.rs @@ -15,8 +15,12 @@ pub struct PlaneMeshBuilder<'l> { pub pos: Hex, /// Optional custom offset for the mesh vertex positions pub offset: Option, + /// Optional custom scale factor for the mesh vertex positions + pub scale: Option, /// Optional custom facing direction, useful to have the mesh already - /// rotated + /// rotated. + /// + /// Note that the `scale` factor will be applied before the rotation /// /// By default the mesh is *facing* up (**Y** axis) pub facing: Option, @@ -33,6 +37,7 @@ impl<'l> PlaneMeshBuilder<'l> { pos: Hex::ZERO, facing: None, offset: None, + scale: None, uv_options: UVOptions::cap_default(), } } @@ -51,6 +56,8 @@ impl<'l> PlaneMeshBuilder<'l> { /// Specify a custom *facing* direction for the mesh, by default the column /// is vertical (facing up) + /// + /// Note that the `scale` factor will be applied before the rotation #[must_use] pub const fn facing(mut self, facing: Vec3) -> Self { self.facing = Some(facing); @@ -64,6 +71,13 @@ impl<'l> PlaneMeshBuilder<'l> { self } + /// Specify a custom scale factor for the whole mesh + #[must_use] + pub const fn with_scale(mut self, scale: Vec3) -> Self { + self.scale = Some(scale); + self + } + /// Specify custom UV mapping options #[must_use] pub const fn with_uv_options(mut self, uv_options: UVOptions) -> Self { @@ -74,10 +88,19 @@ impl<'l> PlaneMeshBuilder<'l> { /// Comsumes the builder to return the computed mesh data #[must_use] pub fn build(self) -> MeshInfo { - let mut mesh = MeshInfo::hexagonal_plane(self.layout, self.pos); - if let Some(offset) = self.offset { - mesh = mesh.with_offset(offset); + // We compute the mesh at the origin to allow scaling + let mut mesh = MeshInfo::hexagonal_plane(self.layout, Hex::ZERO); + // We store the offset to match the `self.pos` + let mut offset = self.layout.hex_to_world_pos(self.pos).extend(0.0); + // We apply optional scale + if let Some(scale) = self.scale { + mesh.vertices.iter_mut().for_each(|p| *p *= scale); + } + // We offset the vertex positions + if let Some(custom_offset) = self.offset { + offset += custom_offset; } + mesh = mesh.with_offset(offset); if let Some(facing) = self.facing { let facing = facing.normalize(); let rotation = Quat::from_rotation_arc(BASE_FACING, facing);