Skip to content
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

Mesh can be scaled #121

Merged
merged 5 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## [Unreleased]

* Added `PlaneMeshBuilder::with_scale` option to scale generated mesh (#121)
* Added `PlaneMeshBuilder::with_rotation` option to rotate generated mesh (#121)
* Added `ColumnMeshBuilder::with_scale` option to scale generated mesh (#121)
* Added `ColumnMeshBuilder::with_rotation` option to rotate generated mesh (#121)
* Mesh transformation follow the SRT order of operations (#121)

## 0.11.0

* Reduced vertice and tri count of mesh generation (#119)
Expand Down
4 changes: 2 additions & 2 deletions examples/3d_columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions examples/a_star.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions examples/chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion examples/field_of_movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
7 changes: 5 additions & 2 deletions examples/field_of_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions examples/hex_grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 5 additions & 1 deletion examples/merged_columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ fn setup_grid(
// We compute the merged mesh with all children columns
let mesh = children.fold(MeshInfo::default(), |mut mesh, c| {
let [min, max] = settings.column_heights;
let height = rng.gen_range(min..=max);
let height = if min < max {
rng.gen_range(min..=max)
} else {
min
};
let info = ColumnMeshBuilder::new(&layout, height)
.at(c)
.without_bottom_face()
Expand Down
3 changes: 3 additions & 0 deletions examples/mesh_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct BuilderParams {
pub bottom_face: bool,
pub sides_uvs: UVOptions,
pub caps_uvs: UVOptions,
pub scale: Vec3,
}

pub fn main() {
Expand Down Expand Up @@ -111,6 +112,7 @@ fn update_mesh(params: Res<BuilderParams>, info: Res<HexInfo>, 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 {
Expand Down Expand Up @@ -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,
}
}
}
67 changes: 53 additions & 14 deletions src/mesh/column_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ use crate::{Hex, HexLayout, PlaneMeshBuilder, UVOptions};
/// .without_top_face()
/// .build();
/// ```
///
/// # Note
///
/// Transform operations (Scale, Rotate, Translate) through the methods
///
/// - Scale: [`Self::with_scale`]
/// - Rotate: [`Self::with_rotation`], [`Self::facing`]
/// - Translate: [`Self::with_offset`], [`Self::at`]
///
/// Are executed in that order, or **SRT**
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
pub struct ColumnMeshBuilder<'l> {
Expand All @@ -36,11 +46,13 @@ pub struct ColumnMeshBuilder<'l> {
pub pos: Hex,
/// Optional custom offset for the mesh vertex positions
pub offset: Option<Vec3>,
/// Optional custom facing direction, useful to have the mesh already
/// Optional custom scale factor for the mesh vertex positions
pub scale: Option<Vec3>,
/// Optional rotation quaternion, useful to have the mesh already
/// rotated
///
/// By default the mesh is *facing* up (**Y** axis)
pub facing: Option<Vec3>,
pub rotation: Option<Quat>,
/// Amount of quads to be generated on the sides of the column
pub subdivisions: Option<usize>,
/// Should the top hexagonal face be present
Expand All @@ -61,9 +73,10 @@ impl<'l> ColumnMeshBuilder<'l> {
layout,
height,
pos: Hex::ZERO,
facing: None,
rotation: None,
subdivisions: None,
offset: None,
scale: None,
top_face: true,
bottom_face: true,
sides_uv_options: UVOptions::quad_default(),
Expand All @@ -86,10 +99,21 @@ impl<'l> ColumnMeshBuilder<'l> {

/// Specify a custom *facing* direction for the mesh, by default the column
/// is vertical (facing up)
///
/// # Panics
///
/// Will panic if `facing` is zero length
#[must_use]
#[inline]
pub const fn facing(mut self, facing: Vec3) -> Self {
self.facing = Some(facing);
pub fn facing(mut self, facing: Vec3) -> Self {
self.rotation = Some(Quat::from_rotation_arc(BASE_FACING, facing));
self
}

/// Specify a custom rotation for the whole mesh
#[must_use]
pub const fn with_rotation(mut self, rotation: Quat) -> Self {
self.rotation = Some(rotation);
self
}

Expand All @@ -116,6 +140,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]
Expand Down Expand Up @@ -164,19 +195,22 @@ 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 pos = self.layout.hex_to_world_pos(self.pos);
let mut offset = Vec3::new(pos.x, 0.0, pos.y);
// 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 [a, b, c, d, e, f] = self.layout.hex_corners(Hex::ZERO);
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);
Expand All @@ -194,14 +228,19 @@ 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);
// **S** - We apply optional scale
if let Some(scale) = self.scale {
mesh.vertices.iter_mut().for_each(|p| *p *= scale);
}
if let Some(facing) = self.facing {
let facing = facing.normalize();
let rotation = Quat::from_rotation_arc(BASE_FACING, facing);
// **R** - We rotate the mesh to face the given direction
if let Some(rotation) = self.rotation {
mesh = mesh.rotated(rotation);
}
// **T** - 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
}
}
Loading