Skip to content

Commit

Permalink
Picking example (#177)
Browse files Browse the repository at this point in the history
  • Loading branch information
ManevilleF authored Aug 5, 2024
1 parent 35bd74e commit b56f8a2
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 22 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

## [Unreleased]

* Added `Hex::as_u64` and `Hex::from_u64`
* Added `Hex::as_u64` and `Hex::from_u64` (#169)
* Bumped `bevy` to 0.14.x (#173)
* Bumped bevy ecosystem dependencies (#173)
* Updated examples (#173)
* Added a 3d picking exampe (#177)

## 0.17.0

Expand Down
33 changes: 19 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,21 @@ optional = true
[dev-dependencies.bevy]
version = "0.14"
features = [
"bevy_asset",
"bevy_winit",
"bevy_core_pipeline",
"bevy_pbr",
"bevy_render",
"bevy_sprite",
"bevy_text",
"default_font",
"png",
"x11",
"tonemapping_luts",
"bevy_gizmos",
# Faster compilation
"dynamic_linking",
"bevy_asset",
"bevy_winit",
"bevy_core_pipeline",
"bevy_pbr",
"bevy_render",
"bevy_sprite",
"bevy_text",
"default_font",
"png",
"x11",
"tonemapping_luts",
"bevy_gizmos",
"multi_threaded",
# Faster compilation
"dynamic_linking",
]
default-features = false

Expand Down Expand Up @@ -104,6 +105,10 @@ path = "examples/field_of_movement.rs"
name = "3d_columns"
path = "examples/3d_columns.rs"

[[example]]
name = "3d_picking"
path = "examples/3d_picking.rs"

[[example]]
name = "mesh_builder"
path = "examples/mesh_builder.rs"
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@ and how to use two layouts on the same grid.
> `cargo run --example 3d_columns`
This example showcases the 3d hexagon columns procedural generation

### 3d picking

![picking](docs/3d_picking.png "3d picking example")

> `cargo run --example 3d_picking`
This example showcases how to use the camera ray to detect hovered 3d columns

### Mesh builder

Expand Down
Binary file modified docs/3d_columns.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/3d_picking.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
139 changes: 139 additions & 0 deletions examples/3d_picking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use bevy::{
color::palettes::css::{WHITE, YELLOW},
prelude::*,
render::{mesh::Indices, render_asset::RenderAssetUsages, render_resource::PrimitiveTopology},
utils::HashMap,
window::PrimaryWindow,
};
use hexx::{shapes, *};
use light_consts::lux;

/// World size of the hexagons (outer radius)
const HEX_SIZE: Vec2 = Vec2::splat(1.0);
/// World space height of hex columns
const COLUMN_HEIGHT: f32 = 10.0;
/// Map radius
const MAP_RADIUS: u32 = 20;

pub fn main() {
App::new()
.insert_resource(AmbientLight {
brightness: lux::OFFICE,
..default()
})
.add_plugins(DefaultPlugins)
.add_systems(Startup, (setup_camera, setup_grid))
.add_systems(Update, higlight_hovered)
.run();
}

#[derive(Debug, Resource)]
struct Map {
layout: HexLayout,
entities: HashMap<Hex, Entity>,
highlighted_material: Handle<StandardMaterial>,
default_material: Handle<StandardMaterial>,
}

/// 3D Orthogrpahic camera setup
fn setup_camera(mut commands: Commands) {
let transform = Transform::from_xyz(0.0, 60.0, 60.0).looking_at(Vec3::ZERO, Vec3::Y);
commands.spawn(Camera3dBundle {
transform,
..default()
});
let transform = Transform::from_xyz(60.0, 60.0, 00.0).looking_at(Vec3::ZERO, Vec3::Y);
commands.spawn(DirectionalLightBundle {
transform,
..default()
});
}

/// Hex grid setup
fn setup_grid(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let layout = HexLayout {
hex_size: HEX_SIZE,
..default()
};
// materials
let default_material = materials.add(Color::Srgba(WHITE));
let highlighted_material = materials.add(Color::Srgba(YELLOW));
// mesh
let mesh = hexagonal_column(&layout);
let mesh_handle = meshes.add(mesh);

let entities = shapes::hexagon(Hex::ZERO, MAP_RADIUS)
.map(|hex| {
let pos = layout.hex_to_world_pos(hex);
let id = commands
.spawn(PbrBundle {
transform: Transform::from_xyz(pos.x, -COLUMN_HEIGHT, pos.y),
mesh: mesh_handle.clone(),
material: default_material.clone_weak(),
..default()
})
.id();
(hex, id)
})
.collect();
commands.insert_resource(Map {
layout,
entities,
highlighted_material,
default_material,
});
}

fn higlight_hovered(
mut commands: Commands,
map: Res<Map>,
mut highlighted: Local<Hex>,
cameras: Query<(&Camera, &GlobalTransform)>,
windows: Query<&Window, With<PrimaryWindow>>,
) {
let window = windows.single();
let (camera, cam_transform) = cameras.single();
let Some(ray) = window
.cursor_position()
.and_then(|p| camera.viewport_to_world(cam_transform, p))
else {
return;
};
let Some(distance) = ray.intersect_plane(Vec3::ZERO, InfinitePlane3d::new(Dir3::Y)) else {
return;
};
let point = ray.origin + ray.direction * distance;
let coord = map.layout.world_pos_to_hex(point.xz());
if coord != *highlighted {
let Some(entity) = map.entities.get(&coord).copied() else {
return;
};
commands
.entity(entity)
.insert(map.highlighted_material.clone_weak());
commands
.entity(map.entities[&*highlighted])
.insert(map.default_material.clone_weak());
*highlighted = coord;
}
}

/// Compute a bevy mesh from the layout
fn hexagonal_column(hex_layout: &HexLayout) -> Mesh {
let mesh_info = ColumnMeshBuilder::new(hex_layout, COLUMN_HEIGHT)
.without_bottom_face()
.center_aligned()
.build();
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::RENDER_WORLD,
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, mesh_info.vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, mesh_info.normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, mesh_info.uvs)
.with_inserted_indices(Indices::U16(mesh_info.indices))
}
2 changes: 1 addition & 1 deletion src/algorithms/pathfinding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn reconstruct_path(came_from: &HashMap<Hex, Hex>, end: Hex) -> Vec<Hex> {
/// * `start` - start node
/// * `end` - destination node
/// * `cost` - cost function taking a node pair (`a` -> `b`) and returning the
/// logical cost to go from `a` to `b`
/// logical cost to go from `a` to `b`
///
/// # Examples
///
Expand Down
6 changes: 2 additions & 4 deletions src/hex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,8 +698,7 @@ impl Hex {
/// Find in which [`VertexDirection`] wedge `rhs` is relative to `self`.
///
/// > This method can be innaccurate in case of a *tie* between directions,
/// > prefer
/// using [`Self::diagonal_way_to`] instead
/// > prefer using [`Self::diagonal_way_to`] instead
pub fn main_diagonal_to(self, rhs: Self) -> VertexDirection {
self.diagonal_way_to(rhs).unwrap()
}
Expand All @@ -723,8 +722,7 @@ impl Hex {
/// Find in which [`EdgeDirection`] wedge `rhs` is relative to `self`
///
/// > This method can be innaccurate in case of a *tie* between directions,
/// > prefer
/// using [`Self::way_to`] for accuracy
/// > prefer using [`Self::way_to`] for accuracy
#[must_use]
pub fn main_direction_to(self, rhs: Self) -> EdgeDirection {
self.way_to(rhs).unwrap()
Expand Down
3 changes: 1 addition & 2 deletions src/mesh/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@ impl<const VERTS: usize, const TRIS: usize> Face<VERTS, TRIS> {
///
/// * `mode` - the insetting behaviour mode
/// * `keep_inner_face` - If set to true the insetted face will be kept,
/// otherwise
/// it will be removed
/// otherwise it will be removed
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn inset(self, mode: InsetScaleMode, scale: f32, keep_inner_face: bool) -> MeshInfo {
Expand Down

0 comments on commit b56f8a2

Please sign in to comment.