Skip to content

Commit

Permalink
Center aligned meshes (#139)
Browse files Browse the repository at this point in the history
> Closes #136 

## What does it solve

- [x] Mesh generation was broken if used with an `HexLayout` with a non
zero `origin`, as the origin offset would be applied twice (
`hexagonal_plane` + the mesh builders)
- [x] In addition to this, if used with `bevy` the offset would be also
applied by hexagon entities transforms

## Solution

* Added a `center_aligned` builder method to all mesh builders. this
negates the `HexLayout` offset
* Fixed `PlaneMeshBuilder::build` method which was applying the offset
twice
  • Loading branch information
ManevilleF authored Jan 22, 2024
1 parent 5328691 commit f42a378
Show file tree
Hide file tree
Showing 14 changed files with 98 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ nodes instead of one, allowing for more use cases (#130, #128)
* Bumped `bevy_inspector_egui` dependency (#129)
* Added a `sprite_sheet` bevy example (#135)
* Added `HexLayout::rect_size` method (#135)
* Added `ColumnMeshBuilder::center_aligned` option (#139)
* Added `PlaneMeshBuilder::center_aligned` option (#139)
* Deprecated `MeshInfo::hexagonal_plane` in favor of `PlaneMeshBuilder` (#139)

## 0.12.0

Expand Down
1 change: 1 addition & 0 deletions examples/3d_columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ 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))
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
Expand Down
1 change: 1 addition & 0 deletions examples/a_star.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh {
let mesh_info = PlaneMeshBuilder::new(hex_layout)
.facing(Vec3::Z)
.with_scale(Vec3::splat(0.9))
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
Expand Down
1 change: 1 addition & 0 deletions examples/chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh {
let mesh_info = PlaneMeshBuilder::new(hex_layout)
.with_scale(Vec3::splat(0.9))
.facing(Vec3::Z)
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
Expand Down
5 changes: 4 additions & 1 deletion examples/field_of_movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,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)
.facing(Vec3::Z)
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals)
Expand Down
1 change: 1 addition & 0 deletions examples/field_of_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh {
let mesh_info = PlaneMeshBuilder::new(hex_layout)
.facing(Vec3::Z)
.with_scale(Vec3::splat(0.9))
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
Expand Down
1 change: 1 addition & 0 deletions examples/hex_grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ fn hexagonal_plane(hex_layout: &HexLayout) -> Mesh {
let mesh_info = PlaneMeshBuilder::new(hex_layout)
.facing(Vec3::Z)
.with_scale(Vec3::splat(0.95))
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
Expand Down
1 change: 1 addition & 0 deletions examples/merged_columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ fn setup_grid(
let info = ColumnMeshBuilder::new(&layout, height)
.at(c)
.without_bottom_face()
.center_aligned()
.build();
mesh.merge_with(info);
mesh
Expand Down
5 changes: 4 additions & 1 deletion examples/scroll_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,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)
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals)
Expand Down
5 changes: 4 additions & 1 deletion examples/wrap_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,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)
.center_aligned()
.build();
Mesh::new(PrimitiveTopology::TriangleList)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals)
Expand Down
16 changes: 12 additions & 4 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,21 @@ pub struct HexLayout {
}

impl HexLayout {
#[allow(clippy::cast_precision_loss)]
#[must_use]
/// Computes hexagonal coordinates `hex` into world/pixel coordinates
pub fn hex_to_world_pos(&self, hex: Hex) -> Vec2 {
self.hex_to_center_aligned_world_pos(hex) + self.origin
}

#[allow(clippy::cast_precision_loss)]
#[must_use]
pub(crate) fn hex_to_center_aligned_world_pos(&self, hex: Hex) -> Vec2 {
let matrix = self.orientation.forward_matrix;
Vec2::new(
matrix[0].mul_add(hex.x() as f32, matrix[1] * hex.y() as f32),
matrix[2].mul_add(hex.x() as f32, matrix[3] * hex.y() as f32),
) * self.hex_size
* self.axis_scale()
+ self.origin
}

#[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
Expand All @@ -77,15 +81,19 @@ impl HexLayout {
])
}

#[allow(clippy::cast_precision_loss)]
#[must_use]
/// Retrieves all 6 corner coordinates of the given hexagonal coordinates
/// `hex`
pub fn hex_corners(&self, hex: Hex) -> [Vec2; 6] {
let center = self.hex_to_world_pos(hex);
self.center_aligned_hex_corners().map(|c| c + center)
}

#[must_use]
pub(crate) fn center_aligned_hex_corners(&self) -> [Vec2; 6] {
Direction::ALL_DIRECTIONS.map(|dir| {
let angle = dir.angle_pointy() + self.orientation.angle_offset;
center + Vec2::new(self.hex_size.x * angle.cos(), self.hex_size.y * angle.sin())
Vec2::new(self.hex_size.x * angle.cos(), self.hex_size.y * angle.sin())
})
}

Expand Down
19 changes: 18 additions & 1 deletion src/mesh/column_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub struct ColumnMeshBuilder<'l> {
pub sides_uv_options: UVOptions,
/// UV mapping options for top and bottom faces
pub caps_uv_options: UVOptions,
/// If set to `true`, the mesh will ignore [`HexLayout::origin`]
pub center_aligned: bool,
}

impl<'l> ColumnMeshBuilder<'l> {
Expand All @@ -81,6 +83,7 @@ impl<'l> ColumnMeshBuilder<'l> {
bottom_face: true,
sides_uv_options: UVOptions::quad_default(),
caps_uv_options: UVOptions::cap_default(),
center_aligned: false,
}
}

Expand Down Expand Up @@ -190,6 +193,15 @@ impl<'l> ColumnMeshBuilder<'l> {
self
}

#[must_use]
#[inline]
/// Ignores the [`HexLayout::origin`] offset, generating a mesh centered
/// around `(0.0, 0.0)`.
pub const fn center_aligned(mut self) -> Self {
self.center_aligned = true;
self
}

#[must_use]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::many_single_char_names)]
Expand All @@ -198,9 +210,14 @@ impl<'l> ColumnMeshBuilder<'l> {
// We compute the mesh at the origin to allow scaling
let cap_mesh = PlaneMeshBuilder::new(self.layout)
.with_uv_options(self.caps_uv_options)
.center_aligned()
.build();
// We store the offset to match the `self.pos`
let pos = self.layout.hex_to_world_pos(self.pos);
let pos = if self.center_aligned {
self.layout.hex_to_center_aligned_world_pos(self.pos)
} else {
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();
Expand Down
27 changes: 27 additions & 0 deletions src/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ impl MeshInfo {
/// * rotation
/// * etc
#[must_use]
#[deprecated(since = "0.13.0", note = "Use `PlaneMeshBuilder` instead")]
pub fn hexagonal_plane(layout: &HexLayout, hex: Hex) -> Self {
let corners = layout.hex_corners(hex);
let corners_arr = corners.map(|p| Vec3::new(p.x, 0., p.y));
Expand All @@ -160,6 +161,32 @@ impl MeshInfo {
}
}

#[must_use]
pub(crate) fn center_aligned_hexagonal_plane(layout: &HexLayout) -> Self {
let corners = layout.center_aligned_hex_corners();
let corners_arr = corners.map(|p| Vec3::new(p.x, 0., p.y));
Self {
vertices: vec![
corners_arr[0],
corners_arr[1],
corners_arr[2],
corners_arr[3],
corners_arr[4],
corners_arr[5],
],
uvs: vec![
corners[0], corners[1], corners[2], corners[3], corners[4], corners[5],
],
normals: [Vec3::Y; 6].to_vec(),
indices: vec![
0, 2, 1, // Top tri
3, 5, 4, // Bot tri
0, 5, 3, // Mid Quad
3, 2, 0, // Mid Quad
],
}
}

/// Computes cheap mesh data for an hexagonal column facing `Vec3::Y`
/// without the bottom face.
///
Expand Down
23 changes: 20 additions & 3 deletions src/mesh/plane_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub struct PlaneMeshBuilder<'l> {
pub rotation: Option<Quat>,
/// UV mapping options
pub uv_options: UVOptions,
/// If set to `true`, the mesh will ignore [`HexLayout::origin`]
pub center_aligned: bool,
}

impl<'l> PlaneMeshBuilder<'l> {
Expand All @@ -47,6 +49,7 @@ impl<'l> PlaneMeshBuilder<'l> {
offset: None,
scale: None,
uv_options: UVOptions::cap_default(),
center_aligned: false,
}
}

Expand Down Expand Up @@ -102,13 +105,26 @@ impl<'l> PlaneMeshBuilder<'l> {
self
}

#[must_use]
#[inline]
/// Ignores the [`HexLayout::origin`] offset, generating a mesh centered
/// around `(0.0, 0.0)`.
pub const fn center_aligned(mut self) -> Self {
self.center_aligned = true;
self
}

/// Comsumes the builder to return the computed mesh data
#[must_use]
pub fn build(self) -> MeshInfo {
// We compute the mesh at the origin to allow scaling
let mut mesh = MeshInfo::hexagonal_plane(self.layout, Hex::ZERO);
// We compute the mesh at the origin and no offset to allow scaling
let mut mesh = MeshInfo::center_aligned_hexagonal_plane(self.layout);
// We store the offset to match the `self.pos`
let pos = self.layout.hex_to_world_pos(self.pos);
let pos = if self.center_aligned {
self.layout.hex_to_center_aligned_world_pos(self.pos)
} else {
self.layout.hex_to_world_pos(self.pos)
};
let mut offset = Vec3::new(pos.x, 0.0, pos.y);
// **S** - We apply optional scale
if let Some(scale) = self.scale {
Expand All @@ -122,6 +138,7 @@ impl<'l> PlaneMeshBuilder<'l> {
if let Some(custom_offset) = self.offset {
offset += custom_offset;
}
mesh = mesh.with_offset(offset);
self.uv_options.alter_uvs(&mut mesh.uvs);
mesh
}
Expand Down

0 comments on commit f42a378

Please sign in to comment.