Skip to content

Commit

Permalink
Better UV options (#143)
Browse files Browse the repository at this point in the history
## Why

I need more options for procedurally map a texture to the column meshes.
Especially the ability to specify a UV rect for each face (Cap and
sides).

## Work done

* `ColumnMeshBuilder` now accepts custom `UvOptions` for each 6 sides, I
added a `with_multi_sides_uv_options` method
* `UVOptions` changes:
* changed `flip_x` and `flip_y` fields to `flip` BVec2, the current
builder methods are not changed
* Added `rect` field, to remap the coordinates in specific sections, and
`with_rect` builder method
* Changed the order of operations in `alter_uvs` for more logical order
* Improved `mesh_builder` example
* Fixed `MeshInfo::quad` UV generation which was upside down
  • Loading branch information
ManevilleF authored Jan 31, 2024
1 parent 6a4bbd3 commit 190a1fe
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 38 deletions.
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ nodes instead of one, allowing for more use cases (#130, #128)

### Dependencies

* Bumped `bevy_inspector_egui` dependency (#129)
* Bumped `bevy_inspector_egui` dev dependency (#129)
* Added `bevy_egui` dev dependency (#143)

### Examples

* Added a `sprite_sheet` bevy example (#135)
* Improved `mesh_builder` example (#143)

### Additions

Expand All @@ -27,6 +29,17 @@ nodes instead of one, allowing for more use cases (#130, #128)
* Added `HexOrientationData::forward` method (#141)
* Added `HexOrientationData::inverse` method (#141)

### Mesh generation

* `ColumnMeshBuilder` now accepts custom `UvOptions` for each 6 sides (#143)
* Added `ColumnMeshBuilder::with_multi_sides_uv_options` method (#143)
* `UVOptions` changes:
* (**BREAKING**) changed `flip_x` and `flip_y` fields to `flip` BVec2 (#143)
* Added `rect` field, to remap the coordinates in specific sections (#143)
* Added `with_rect` builder method (#143)
* Changed the order of operations in `alter_uvs` (#143)
* (**BREAKING**) Fixed quad generation which had upside down uvs (#143)

### Deprecation

* Deprecated `MeshInfo::hexagonal_plane` in favor of `PlaneMeshBuilder` (#139)
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ features = ["html_reports"]
[dev-dependencies]
rand = "0.8"
bevy-inspector-egui = "0.22"
bevy_egui = "0.24"

[[example]]
name = "hex_grid"
Expand Down
Binary file modified docs/mesh_builder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
103 changes: 88 additions & 15 deletions examples/mesh_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use bevy::{
prelude::*,
render::{mesh::Indices, render_resource::PrimitiveTopology},
};
use bevy_inspector_egui::{prelude::*, quick::ResourceInspectorPlugin};
use bevy_egui::{egui, EguiContext, EguiPlugin};
use bevy_inspector_egui::bevy_inspector;
use glam::vec2;
use hexx::*;

Expand All @@ -15,37 +16,105 @@ struct HexInfo {
pub mesh_handle: Handle<Mesh>,
}

#[derive(Debug, Resource, Reflect, InspectorOptions)]
#[reflect(Resource, InspectorOptions)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum SideUVMode {
Global,
Multi,
}

impl SideUVMode {
pub fn label(&self) -> &'static str {
match self {
Self::Global => "Global",
Self::Multi => "Multi",
}
}
}

#[derive(Debug, Resource)]
struct BuilderParams {
#[inspector(min = 0.0, max = 50.0)]
pub height: f32,
#[inspector(min = 1, max = 50)]
pub subdivisions: usize,
pub top_face: bool,
pub bottom_face: bool,
pub sides_uvs: UVOptions,
pub caps_uvs: UVOptions,
pub scale: Vec3,
pub sides_uvs_mode: SideUVMode,
pub sides_uvs: [UVOptions; 6],
pub caps_uvs: UVOptions,
}

pub fn main() {
App::new()
.register_type::<BuilderParams>()
.init_resource::<BuilderParams>()
.insert_resource(AmbientLight {
brightness: 0.3,
..default()
})
.add_plugins(DefaultPlugins)
.add_plugins(WireframePlugin)
.add_plugins(ResourceInspectorPlugin::<BuilderParams>::default())
.add_plugins(ResourceInspectorPlugin::<AmbientLight>::default())
.add_plugins(EguiPlugin)
.add_plugins(bevy_inspector_egui::DefaultInspectorConfigPlugin)
.add_systems(Startup, setup)
.add_systems(Update, (animate, update_mesh))
.add_systems(Update, (show_ui, animate, update_mesh))
.run();
}

fn show_ui(world: &mut World) {
world.resource_scope(|world, mut params: Mut<BuilderParams>| {
let Ok(egui_context) = world.query::<&mut EguiContext>().get_single(world) else {
return;
};
let mut egui_context = egui_context.clone();
egui::SidePanel::left("Mesh settings").show(egui_context.get_mut(), |ui| {
ui.heading("Global");
egui::Grid::new("Grid").num_columns(2).show(ui, |ui| {
ui.label("Column Height");
ui.add(egui::DragValue::new(&mut params.height).clamp_range(1.0..=50.0));
ui.end_row();
ui.label("Side Subdivisions");
ui.add(egui::DragValue::new(&mut params.subdivisions).clamp_range(0..=50));
ui.end_row();
ui.label("Top Face");
ui.add(egui::Checkbox::without_text(&mut params.top_face));
ui.end_row();
ui.label("Bottom Face");
ui.add(egui::Checkbox::without_text(&mut params.bottom_face));
ui.end_row();
ui.label("Scale");
bevy_inspector::ui_for_value(&mut params.scale, ui, world);
ui.end_row();
});
ui.separator();
ui.heading("Caps UV options");
bevy_inspector::ui_for_value(&mut params.caps_uvs, ui, world);
ui.separator();
ui.heading("Sides UV options");
ui.horizontal(|ui| {
egui::ComboBox::from_id_source("Side Uv mode")
.selected_text(params.sides_uvs_mode.label())
.show_ui(ui, |ui| {
let option = SideUVMode::Global;
ui.selectable_value(&mut params.sides_uvs_mode, option, option.label());
let option = SideUVMode::Multi;
ui.selectable_value(&mut params.sides_uvs_mode, option, option.label());
})
});
egui::ScrollArea::vertical().show(ui, |ui| match params.sides_uvs_mode {
SideUVMode::Global => {
if bevy_inspector::ui_for_value(&mut params.sides_uvs[0], ui, world) {
params.sides_uvs = [params.sides_uvs[0]; 6];
}
true
}
SideUVMode::Multi => bevy_inspector::ui_for_value(&mut params.sides_uvs, ui, world),
});
});
egui::Window::new("AmbientLight").show(egui_context.get_mut(), |ui| {
bevy_inspector::ui_for_resource::<AmbientLight>(world, ui);
});
});
}

/// 3D Orthogrpahic camera setup
fn setup(
mut commands: Commands,
Expand Down Expand Up @@ -113,8 +182,11 @@ fn update_mesh(params: Res<BuilderParams>, info: Res<HexInfo>, mut meshes: ResMu
.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());
.with_caps_uv_options(params.caps_uvs)
.with_multi_sides_uv_options(match params.sides_uvs_mode {
SideUVMode::Global => [params.sides_uvs[0]; 6],
SideUVMode::Multi => params.sides_uvs,
});
if !params.top_face {
new_mesh = new_mesh.without_top_face();
}
Expand Down Expand Up @@ -143,8 +215,9 @@ impl Default for BuilderParams {
subdivisions: 3,
top_face: true,
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)),
sides_uvs_mode: SideUVMode::Global,
sides_uvs: [UVOptions::quad_default().with_scale_factor(vec2(0.3, 1.0)); 6],
caps_uvs: UVOptions::cap_default().with_scale_factor(vec2(0.3, 0.3)),
scale: Vec3::ONE,
}
}
Expand Down
29 changes: 22 additions & 7 deletions src/mesh/column_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub struct ColumnMeshBuilder<'l> {
/// Should the bottom hexagonal face be present
pub bottom_face: bool,
/// UV mapping options for the column sides
pub sides_uv_options: UVOptions,
pub sides_uv_options: [UVOptions; 6],
/// UV mapping options for top and bottom faces
pub caps_uv_options: UVOptions,
/// If set to `true`, the mesh will ignore [`HexLayout::origin`]
Expand All @@ -81,7 +81,7 @@ impl<'l> ColumnMeshBuilder<'l> {
scale: None,
top_face: true,
bottom_face: true,
sides_uv_options: UVOptions::quad_default(),
sides_uv_options: [UVOptions::quad_default(); 6],
caps_uv_options: UVOptions::cap_default(),
center_aligned: false,
}
Expand Down Expand Up @@ -187,8 +187,21 @@ impl<'l> ColumnMeshBuilder<'l> {

#[must_use]
#[inline]
/// Specify custom uv options for the side triangles
/// Specify custom global uv options for the side quad triangles.
///
/// To customize each side quad, prefer
/// [`Self::with_multi_sides_uv_options`]
pub const fn with_sides_uv_options(mut self, uv_options: UVOptions) -> Self {
self.sides_uv_options = [uv_options; 6];
self
}

#[must_use]
#[inline]
/// Specify custom uv options for each of the side quad triangles.
///
/// For a global setting prefer [`Self::with_sides_uv_options`]
pub const fn with_multi_sides_uv_options(mut self, uv_options: [UVOptions; 6]) -> Self {
self.sides_uv_options = uv_options;
self
}
Expand Down Expand Up @@ -226,17 +239,19 @@ impl<'l> ColumnMeshBuilder<'l> {
let delta = self.height / subidivisions as f32;
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 {
(0..6).for_each(|side| {
let [left, right] = corners[side];
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);
let right = Vec3::new(right.x, height, right.y);
let quad = MeshInfo::quad([left, right], Vec3::new(normal.x, 0.0, normal.y), delta);
let mut quad =
MeshInfo::quad([left, right], Vec3::new(normal.x, 0.0, normal.y), delta);
self.sides_uv_options[side].alter_uvs(&mut quad.uvs);
mesh.merge_with(quad);
}
}
self.sides_uv_options.alter_uvs(&mut mesh.uvs);
});
if self.top_face {
mesh.merge_with(cap_mesh.clone().with_offset(Vec3::Y * self.height));
}
Expand Down
4 changes: 2 additions & 2 deletions src/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod uv_mapping;

pub use column_builder::ColumnMeshBuilder;
pub use plane_builder::PlaneMeshBuilder;
pub use uv_mapping::UVOptions;
pub use uv_mapping::{Rect, UVOptions};

use glam::{Quat, Vec2, Vec3};

Expand Down Expand Up @@ -116,7 +116,7 @@ impl MeshInfo {
Self {
vertices: vec![left, left + offset, right + offset, right],
normals: [normal; 4].to_vec(),
uvs: vec![Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y],
uvs: vec![Vec2::Y, Vec2::ZERO, Vec2::X, Vec2::ONE],
indices: vec![
1, 2, 3, // Tri 1
3, 0, 1, // Tri 2
Expand Down
Loading

0 comments on commit 190a1fe

Please sign in to comment.