terrain modification

improved responsiveness of terrain edits
added hexselect methods
add create crater function
This commit is contained in:
2024-05-30 22:28:22 -04:00
parent d25d0de02d
commit f491739ee8
4 changed files with 142 additions and 21 deletions

View File

@@ -52,7 +52,7 @@ pub fn tile_to_world_distance(dist: u32) -> f32 {
return dist as f32 * (2. * INNER_RADIUS); return dist as f32 * (2. * INNER_RADIUS);
} }
pub fn get_tile_count(radius: u32) -> u32 { pub fn get_tile_count(radius: usize) -> usize {
return 1 + 3 * (radius + 1) * radius; return 1 + 3 * (radius + 1) * radius;
} }
@@ -220,4 +220,46 @@ impl HexCoord {
self.get_neighbor(5), self.get_neighbor(5),
]; ];
} }
pub fn hex_select(&self, radius: usize, include_center: bool) -> Vec<HexCoord> {
assert!(radius != 0, "Radius cannot be zero");
let mut result = Vec::with_capacity(get_tile_count(radius));
if include_center {
result.push(*self);
}
for k in 0..(radius + 1) {
let mut p = self.scale(4, k);
for i in 0..6 {
for _j in 0..k {
p = p.get_neighbor(i);
result.push(p);
}
}
}
return result;
}
pub fn select_ring(&self, radius: usize) -> Vec<HexCoord> {
assert!(radius != 0, "Radius cannot be zero");
let mut result = Vec::with_capacity(radius * 6);
let mut p = self.scale(4, radius);
if radius == 1 {
result.push(*self);
return result;
}
for i in 0..6 {
for _j in 0..radius {
result.push(p);
p = p.get_neighbor(i);
}
}
return result;
}
} }

View File

@@ -8,7 +8,7 @@ pub mod tile_manager;
pub mod tile_mapper; pub mod tile_mapper;
pub mod prelude { pub mod prelude {
use crate::hex_utils::{HexCoord, INNER_RADIUS, OUTER_RADIUS, SHORT_DIAGONAL}; use crate::hex_utils::{get_tile_count, HexCoord, INNER_RADIUS, OUTER_RADIUS, SHORT_DIAGONAL};
use bevy::math::{IVec2, UVec2, Vec2, Vec3}; use bevy::math::{IVec2, UVec2, Vec2, Vec3};
use bevy::prelude::Resource; use bevy::prelude::Resource;
use bevy::prelude::*; use bevy::prelude::*;
@@ -205,6 +205,11 @@ pub mod prelude {
return chunk.heights[pos.to_chunk_local_index()]; return chunk.heights[pos.to_chunk_local_index()];
} }
pub fn sample_height_mut(&mut self, pos: &HexCoord) -> &mut f32 {
let chunk = &mut self.chunks[pos.to_chunk_index(self.width)];
return &mut chunk.heights[pos.to_chunk_local_index()];
}
pub fn is_in_bounds(&self, pos: &HexCoord) -> bool { pub fn is_in_bounds(&self, pos: &HexCoord) -> bool {
return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE); return pos.is_in_bounds(self.height * Chunk::SIZE, self.width * Chunk::SIZE);
} }
@@ -239,6 +244,90 @@ pub mod prelude {
pub fn set_height(&mut self, pos: &HexCoord, height: f32) { pub fn set_height(&mut self, pos: &HexCoord, height: f32) {
self.chunks[pos.to_chunk_index(self.width)].heights[pos.to_chunk_local_index()] = height; self.chunks[pos.to_chunk_index(self.width)].heights[pos.to_chunk_local_index()] = height;
} }
pub fn create_crater(&mut self, pos: &HexCoord, radius: usize, depth: f32) -> Vec<usize> {
assert!(radius != 0, "Radius cannot be zero");
let width = self.width;
let mut chunks = self.hex_select_mut(pos, radius, true, |h, r| {
let d = (r as f32) / (radius as f32);
let cur = h.clone();
let h2 = cur - depth;
*h = h2.lerp(cur, d * d);
return pos.to_chunk_index(width);
});
chunks.dedup();
return chunks;
}
pub fn hex_select<OP, Ret>(
&mut self,
center: &HexCoord,
radius: usize,
include_center: bool,
op: OP,
) -> Vec<Ret>
where
OP: Fn(f32, usize) -> Ret + Sync + Send,
{
assert!(radius != 0, "Radius cannot be zero");
if include_center {
let h = self.sample_height(center);
(op)(h, 0);
}
let mut result = Vec::with_capacity(get_tile_count(radius));
for k in 0..(radius + 1) {
let mut p = center.scale(4, k);
for i in 0..6 {
for _j in 0..k {
p = p.get_neighbor(i);
let h = self.sample_height(&p);
result.push((op)(h, k));
}
}
}
return result;
}
pub fn hex_select_mut<OP, Ret>(
&mut self,
center: &HexCoord,
radius: usize,
include_center: bool,
op: OP,
) -> Vec<Ret>
where
OP: Fn(&mut f32, usize) -> Ret + Sync + Send,
{
assert!(radius != 0, "Radius cannot be zero");
if include_center {
let h = self.sample_height_mut(center);
(op)(h, 0);
}
let mut result = Vec::with_capacity(get_tile_count(radius));
for k in 0..(radius + 1) {
let mut p = center.scale(4, k);
for i in 0..6 {
for _j in 0..k {
p = p.get_neighbor(i);
let h = self.sample_height_mut(&p);
result.push((op)(h, k));
}
}
}
return result;
}
} }
pub const ATTRIBUTE_PACKED_VERTEX_DATA: MeshVertexAttribute = pub const ATTRIBUTE_PACKED_VERTEX_DATA: MeshVertexAttribute =
MeshVertexAttribute::new("PackedVertexData", 988540817, VertexFormat::Uint32); MeshVertexAttribute::new("PackedVertexData", 988540817, VertexFormat::Uint32);

View File

@@ -30,7 +30,7 @@ pub struct ChunkRebuildQueue {
fn chunk_rebuilder( fn chunk_rebuilder(
mut commands: Commands, mut commands: Commands,
chunk_query: Query<(Entity, &PhosChunk), With<RebuildChunk>>, chunk_query: Query<(Entity, &PhosChunk), (With<RebuildChunk>, Without<ChunkRebuildTask>)>,
heightmap: Res<Map>, heightmap: Res<Map>,
) { ) {
let pool = AsyncComputeTaskPool::get(); let pool = AsyncComputeTaskPool::get();

View File

@@ -7,8 +7,6 @@ use crate::{
prelude::{PhosChunkRegistry, RebuildChunk}, prelude::{PhosChunkRegistry, RebuildChunk},
}; };
use super::chunk_rebuild::ChunkRebuildQueue;
pub struct TerraFormingTestPlugin; pub struct TerraFormingTestPlugin;
impl Plugin for TerraFormingTestPlugin { impl Plugin for TerraFormingTestPlugin {
@@ -25,12 +23,11 @@ fn deform(
rapier_context: Res<RapierContext>, rapier_context: Res<RapierContext>,
mut heightmap: ResMut<Map>, mut heightmap: ResMut<Map>,
chunks: Res<PhosChunkRegistry>, chunks: Res<PhosChunkRegistry>,
time: Res<Time>,
) { ) {
let mut multi = 0.; let mut multi = 0.;
if mouse.pressed(MouseButton::Left) { if mouse.just_pressed(MouseButton::Left) {
multi = 1.; multi = 1.;
} else if mouse.pressed(MouseButton::Right) { } else if mouse.just_pressed(MouseButton::Right) {
multi = -1.; multi = -1.;
} }
@@ -38,6 +35,8 @@ fn deform(
return; return;
} }
#[cfg(feature = "tracing")]
let span = info_span!("Deform Mesh").entered();
let win = window.single(); let win = window.single();
let (cam_transform, camera) = cam_query.single(); let (cam_transform, camera) = cam_query.single();
let Some(cursor_pos) = win.cursor_position() else { let Some(cursor_pos) = win.cursor_position() else {
@@ -59,19 +58,10 @@ fn deform(
if let Some((e, dist)) = collision { if let Some((e, dist)) = collision {
let contact_point = cam_ray.get_point(dist); let contact_point = cam_ray.get_point(dist);
let contact_coord = HexCoord::from_world_pos(contact_point); let contact_coord = HexCoord::from_world_pos(contact_point);
let cur_height = heightmap.sample_height(&contact_coord); let modified_chunks = heightmap.create_crater(&contact_coord, 5, 5. * multi);
heightmap.set_height(&contact_coord, cur_height + 1. * time.delta_seconds() * multi); for c in modified_chunks {
let cur_chunk = contact_coord.to_chunk_index(heightmap.width);
commands.entity(e).insert(RebuildChunk);
if contact_coord.is_on_chunk_edge() {
let neighbors = contact_coord.get_neighbors();
neighbors
.iter()
.map(|c| c.to_chunk_index(heightmap.width))
.filter(|c| c != &cur_chunk)
.for_each(|c| {
commands.entity(chunks.chunks[c]).insert(RebuildChunk); commands.entity(chunks.chunks[c]).insert(RebuildChunk);
}); }
} commands.entity(e).insert(RebuildChunk);
} }
} }