optimize chunk meshing data

This commit is contained in:
2024-05-29 21:29:56 -04:00
parent 70614be759
commit 164283e152
7 changed files with 147 additions and 69 deletions

12
.vscode/launch.json vendored
View File

@@ -14,12 +14,12 @@
"args": [], "args": [],
"cwd": "${workspaceRoot}/target/debug", "cwd": "${workspaceRoot}/target/debug",
"preLaunchTask": "Build", "preLaunchTask": "Build",
"environment": [ // "environment": [
{ // {
"name": "RUST_BACKTRACE", // "name": "RUST_BACKTRACE",
"value": "1" // "value": "1"
} // }
] // ]
} }
] ]
} }

View File

@@ -5,7 +5,7 @@ use bevy::prelude::*;
const CHUNK_TOTAL: usize = Chunk::SIZE * Chunk::SIZE; const CHUNK_TOTAL: usize = Chunk::SIZE * Chunk::SIZE;
pub fn generate_chunk_collider(chunk: &Chunk, map: &Map) -> (Vec<Vec3>, Vec<[u32; 3]>) { pub fn generate_chunk_collider(chunk: &MeshChunkData) -> (Vec<Vec3>, Vec<[u32; 3]>) {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let span = info_span!("generate_chunk_collider").entered(); let span = info_span!("generate_chunk_collider").entered();
let vertex_count: usize = CHUNK_TOTAL * 6; let vertex_count: usize = CHUNK_TOTAL * 6;
@@ -14,9 +14,8 @@ pub fn generate_chunk_collider(chunk: &Chunk, map: &Map) -> (Vec<Vec3>, Vec<[u32
for z in 0..Chunk::SIZE { for z in 0..Chunk::SIZE {
for x in 0..Chunk::SIZE { for x in 0..Chunk::SIZE {
let height = chunk.heights[x + z * Chunk::SIZE]; let height = chunk.heights[x + z * Chunk::SIZE];
let coord = let coord = HexCoord::from_grid_pos(x, z);
HexCoord::from_offset(IVec2::new(x as i32, z as i32) + (chunk.chunk_offset * Chunk::SIZE as i32)); let neighbors = chunk.get_neighbors(&coord);
let neighbors = map.get_neighbors(&coord);
let off_pos = Vec3::new(x as f32, height, z as f32); let off_pos = Vec3::new(x as f32, height, z as f32);
let tile_pos = offset3d_to_world(off_pos); let tile_pos = offset3d_to_world(off_pos);
create_tile_collider(tile_pos, &mut verts, &mut indices, &neighbors); create_tile_collider(tile_pos, &mut verts, &mut indices, &neighbors);
@@ -25,7 +24,7 @@ pub fn generate_chunk_collider(chunk: &Chunk, map: &Map) -> (Vec<Vec3>, Vec<[u32
return (verts, indices); return (verts, indices);
} }
fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>, neighbors: &[Option<f32>; 6]) { fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32; 3]>, neighbors: &[f32; 6]) {
let idx = verts.len() as u32; let idx = verts.len() as u32;
for i in 0..6 { for i in 0..6 {
let p = pos + HEX_CORNERS[i]; let p = pos + HEX_CORNERS[i];
@@ -39,20 +38,15 @@ fn create_tile_collider(pos: Vec3, verts: &mut Vec<Vec3>, indices: &mut Vec<[u32
indices.push([idx + 2, idx + 3, idx + 4]); indices.push([idx + 2, idx + 3, idx + 4]);
for i in 0..neighbors.len() { for i in 0..neighbors.len() {
let cur_n = neighbors[i]; let n_height = neighbors[i];
match cur_n { if n_height < pos.y {
Some(n_height) => { create_tile_wall_collider(
if n_height < pos.y { idx,
create_tile_wall_collider( Vec3::new(pos.x, n_height.min(pos.y - OUTER_RADIUS / 2.), pos.z),
idx, i,
Vec3::new(pos.x, n_height.min(pos.y - OUTER_RADIUS / 2.), pos.z), verts,
i, indices,
verts, );
indices,
);
}
}
_ => {}
} }
} }
} }

View File

@@ -15,6 +15,7 @@ pub mod prelude {
use bevy::render::mesh::MeshVertexAttribute; use bevy::render::mesh::MeshVertexAttribute;
use bevy::render::render_resource::VertexFormat; use bevy::render::render_resource::VertexFormat;
use bevy_inspector_egui::InspectorOptions; use bevy_inspector_egui::InspectorOptions;
use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
pub const TEX_MULTI: Vec2 = Vec2::new(1000., 1.); pub const TEX_MULTI: Vec2 = Vec2::new(1000., 1.);
pub const HEX_CORNERS: [Vec3; 6] = [ pub const HEX_CORNERS: [Vec3; 6] = [
@@ -70,20 +71,20 @@ pub mod prelude {
#[derive(Clone)] #[derive(Clone)]
pub struct Chunk { pub struct Chunk {
pub heights: [f32; Chunk::SIZE * Chunk::SIZE], pub heights: [f32; Chunk::AREA],
pub textures: [[u32; 2]; Chunk::SIZE * Chunk::SIZE], pub textures: [[u32; 2]; Chunk::AREA],
pub moisture: [f32; Chunk::SIZE * Chunk::SIZE], pub moisture: [f32; Chunk::AREA],
pub temperature: [f32; Chunk::SIZE * Chunk::SIZE], pub temperature: [f32; Chunk::AREA],
pub chunk_offset: IVec2, pub chunk_offset: IVec2,
} }
impl Default for Chunk { impl Default for Chunk {
fn default() -> Self { fn default() -> Self {
Self { Self {
heights: [0.; Chunk::SIZE * Chunk::SIZE], heights: [0.; Chunk::AREA],
textures: [[0; 2]; Chunk::SIZE * Chunk::SIZE], textures: [[0; 2]; Chunk::AREA],
moisture: [0.; Chunk::SIZE * Chunk::SIZE], moisture: [0.; Chunk::AREA],
temperature: [0.; Chunk::SIZE * Chunk::SIZE], temperature: [0.; Chunk::AREA],
chunk_offset: Default::default(), chunk_offset: Default::default(),
} }
} }
@@ -91,9 +92,74 @@ pub mod prelude {
impl Chunk { impl Chunk {
pub const SIZE: usize = 64; pub const SIZE: usize = 64;
pub const AREA: usize = Chunk::SIZE * Chunk::SIZE;
pub const WORLD_WIDTH: f32 = Chunk::SIZE as f32 * SHORT_DIAGONAL; pub const WORLD_WIDTH: f32 = Chunk::SIZE as f32 * SHORT_DIAGONAL;
pub const WORLD_HEIGHT: f32 = Chunk::SIZE as f32 * 1.5; pub const WORLD_HEIGHT: f32 = Chunk::SIZE as f32 * 1.5;
pub const WORLD_SIZE: Vec2 = Vec2::new(Chunk::WORLD_WIDTH, Chunk::WORLD_HEIGHT); pub const WORLD_SIZE: Vec2 = Vec2::new(Chunk::WORLD_WIDTH, Chunk::WORLD_HEIGHT);
pub fn get_pos_z_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for x in 0..Chunk::SIZE {
let idx = x + (Chunk::SIZE - 1) * Chunk::SIZE;
data[x] = self.heights[idx];
}
return data;
}
pub fn get_neg_z_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for x in 0..Chunk::SIZE {
data[x] = self.heights[x];
}
return data;
}
pub fn get_pos_x_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for z in 0..Chunk::SIZE {
let idx = (Chunk::SIZE - 1) + z * Chunk::SIZE;
data[z] = self.heights[idx];
}
return data;
}
pub fn get_neg_x_edge(&self) -> [f32; Chunk::SIZE] {
let mut data = [0.; Chunk::SIZE];
for z in 0..Chunk::SIZE {
let idx = z * Chunk::SIZE;
data[z] = self.heights[idx];
}
return data;
}
}
pub struct MeshChunkData {
pub heights: [f32; Chunk::AREA],
pub textures: [[u32; 2]; Chunk::AREA],
}
impl MeshChunkData {
pub fn get_neighbors(&self, coord: &HexCoord) -> [f32; 6] {
let mut data = [0.; 6];
let n_tiles = coord.get_neighbors();
for i in 0..6 {
let n = n_tiles[i];
if !n.is_in_bounds(Chunk::SIZE, Chunk::SIZE) {
continue;
}
data[i] = self.heights[n.to_index(Chunk::SIZE)];
}
return data;
}
} }
#[derive(Resource, Clone)] #[derive(Resource, Clone)]
@@ -105,6 +171,17 @@ pub mod prelude {
} }
impl Map { impl Map {
pub fn get_chunk_mesh_data(&self, chunk_index: usize) -> MeshChunkData {
#[cfg(feature = "tracing")]
let _spawn_span = info_span!("Chunk Mesh Data").entered();
let chunk = &self.chunks[chunk_index];
return MeshChunkData {
heights: chunk.heights.clone(),
textures: chunk.textures.clone(),
};
}
pub fn get_neighbors(&self, pos: &HexCoord) -> [Option<f32>; 6] { pub fn get_neighbors(&self, pos: &HexCoord) -> [Option<f32>; 6] {
let mut results: [Option<f32>; 6] = [None; 6]; let mut results: [Option<f32>; 6] = [None; 6];
let w = self.width * Chunk::SIZE; let w = self.width * Chunk::SIZE;

View File

@@ -1,7 +1,4 @@
use crate::biome_painter::BiomePainterAsset;
use crate::hex_utils::HexCoord; use crate::hex_utils::HexCoord;
use crate::tile_manager::TileAsset;
use crate::tile_mapper::TileMapperAsset;
use crate::{hex_utils::offset3d_to_world, prelude::*}; use crate::{hex_utils::offset3d_to_world, prelude::*};
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
use bevy::log::*; use bevy::log::*;
@@ -13,7 +10,7 @@ use bevy::{
}, },
}; };
pub fn generate_chunk_mesh(chunk: &Chunk, map: &Map) -> Mesh { pub fn generate_chunk_mesh(chunk: &MeshChunkData) -> Mesh {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let span = info_span!("generate_chunk_mesh").entered(); let span = info_span!("generate_chunk_mesh").entered();
@@ -29,9 +26,8 @@ pub fn generate_chunk_mesh(chunk: &Chunk, map: &Map) -> Mesh {
let height = chunk.heights[idx]; let height = chunk.heights[idx];
let off_pos = Vec3::new(x as f32, height, z as f32); let off_pos = Vec3::new(x as f32, height, z as f32);
let tile_pos = offset3d_to_world(off_pos); let tile_pos = offset3d_to_world(off_pos);
let coord = let coord = HexCoord::from_grid_pos(x, z);
HexCoord::from_offset(IVec2::new(x as i32, z as i32) + (chunk.chunk_offset * Chunk::SIZE as i32)); let n = chunk.get_neighbors(&coord);
let n = map.get_neighbors(&coord);
create_tile( create_tile(
tile_pos, tile_pos,
@@ -60,7 +56,7 @@ pub fn generate_chunk_mesh(chunk: &Chunk, map: &Map) -> Mesh {
fn create_tile( fn create_tile(
pos: Vec3, pos: Vec3,
neighbors: &[Option<f32>; 6], neighbors: &[f32; 6],
verts: &mut Vec<Vec3>, verts: &mut Vec<Vec3>,
uvs: &mut Vec<Vec2>, uvs: &mut Vec<Vec2>,
indices: &mut Vec<u32>, indices: &mut Vec<u32>,
@@ -91,14 +87,9 @@ fn create_tile(
indices.push(idx + 4); indices.push(idx + 4);
for i in 0..neighbors.len() { for i in 0..neighbors.len() {
let cur_n = neighbors[i]; let n_height = neighbors[i];
match cur_n { if n_height < pos.y {
Some(n_height) => { create_tile_wall(pos, i, n_height, verts, uvs, indices, normals, side_tex_off);
if n_height < pos.y {
create_tile_wall(pos, i, n_height, verts, uvs, indices, normals, side_tex_off);
}
}
_ => {}
} }
} }
} }

View File

@@ -35,33 +35,31 @@ fn chunk_rebuilder(
) { ) {
let pool = AsyncComputeTaskPool::get(); let pool = AsyncComputeTaskPool::get();
for (chunk, idx) in &chunk_query { for (chunk_entity, idx) in &chunk_query {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _spawn_span = info_span!("Rebuild Chunk").entered(); let _spawn_span = info_span!("Rebuild Chunk").entered();
let map: Map;
{
let _clone_span = info_span!("Clone").entered();
map = heightmap.clone();
}
let chunk_index = idx.index; let chunk_index = idx.index;
let chunk_data = heightmap.get_chunk_mesh_data(chunk_index);
let chunk_offset = heightmap.chunks[chunk_index].chunk_offset;
let task = pool.spawn(async move { let task = pool.spawn(async move {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _spawn_span = info_span!("Rebuild Task").entered(); let _spawn_span = info_span!("Rebuild Task").entered();
let mut queue = CommandQueue::default(); let mut queue = CommandQueue::default();
let (mesh, collider_data, _, _) = prepare_chunk_mesh(&map.chunks[chunk_index], &map); let (mesh, collider_data, _, _) = prepare_chunk_mesh(&chunk_data, chunk_offset, chunk_index);
let c = Collider::trimesh_with_flags( let c = Collider::trimesh_with_flags(
collider_data.0, collider_data.0,
collider_data.1, collider_data.1,
TriMeshFlags::DELETE_DUPLICATE_TRIANGLES, TriMeshFlags::DELETE_DUPLICATE_TRIANGLES,
); );
queue.push(move |world: &mut World| { queue.push(move |world: &mut World| {
world.entity_mut(chunk).insert(c).remove::<ChunkRebuildTask>(); world.entity_mut(chunk_entity).insert(c).remove::<ChunkRebuildTask>();
}); });
return (queue, mesh); return (queue, mesh);
}); });
commands commands
.entity(chunk) .entity(chunk_entity)
.insert(ChunkRebuildTask { task }) .insert(ChunkRebuildTask { task })
.remove::<RebuildChunk>(); .remove::<RebuildChunk>();
} }

View File

@@ -4,7 +4,11 @@ use bevy::{asset::LoadState, pbr::ExtendedMaterial, prelude::*};
use bevy_inspector_egui::quick::ResourceInspectorPlugin; use bevy_inspector_egui::quick::ResourceInspectorPlugin;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use world_generation::{ use world_generation::{
biome_painter::*, heightmap::generate_heightmap, hex_utils::SHORT_DIAGONAL, prelude::*, tile_manager::*, biome_painter::*,
heightmap::generate_heightmap,
hex_utils::{offset_to_index, SHORT_DIAGONAL},
prelude::*,
tile_manager::*,
tile_mapper::*, tile_mapper::*,
}; };
@@ -214,7 +218,8 @@ fn spawn_map(
.chunks .chunks
.par_iter() .par_iter()
.map(|chunk: &Chunk| { .map(|chunk: &Chunk| {
return prepare_chunk_mesh_with_collider(chunk, &heightmap); let index = offset_to_index(chunk.chunk_offset, heightmap.width);
return prepare_chunk_mesh_with_collider(&heightmap.get_chunk_mesh_data(index), chunk.chunk_offset, index);
}) })
.collect(); .collect();

View File

@@ -1,6 +1,11 @@
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
use bevy::log::*; use bevy::log::*;
use bevy::{asset::Assets, ecs::system::Res, math::Vec3, render::mesh::Mesh}; use bevy::{
asset::Assets,
ecs::system::Res,
math::{IVec2, Vec3},
render::mesh::Mesh,
};
use bevy_rapier3d::geometry::{Collider, TriMeshFlags}; use bevy_rapier3d::geometry::{Collider, TriMeshFlags};
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
use world_generation::{ use world_generation::{
@@ -8,7 +13,7 @@ use world_generation::{
chunk_colliders::generate_chunk_collider, chunk_colliders::generate_chunk_collider,
hex_utils::{offset_to_index, offset_to_world}, hex_utils::{offset_to_index, offset_to_world},
mesh_generator::generate_chunk_mesh, mesh_generator::generate_chunk_mesh,
prelude::{Chunk, Map}, prelude::{Chunk, Map, MeshChunkData},
tile_manager::TileAsset, tile_manager::TileAsset,
tile_mapper::TileMapperAsset, tile_mapper::TileMapperAsset,
}; };
@@ -44,22 +49,30 @@ pub fn paint_chunk(
} }
} }
pub fn prepare_chunk_mesh(chunk: &Chunk, heightmap: &Map) -> (Mesh, (Vec<Vec3>, Vec<[u32; 3]>), Vec3, usize) { pub fn prepare_chunk_mesh(
chunk: &MeshChunkData,
chunk_offset: IVec2,
chunk_index: usize,
) -> (Mesh, (Vec<Vec3>, Vec<[u32; 3]>), Vec3, usize) {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _gen_mesh = info_span!("Generate Chunk").entered(); let _gen_mesh = info_span!("Generate Chunk").entered();
let mesh = generate_chunk_mesh(chunk, &heightmap); let mesh = generate_chunk_mesh(chunk);
let col_data = generate_chunk_collider(chunk, &heightmap); let col_data = generate_chunk_collider(chunk);
return ( return (
mesh, mesh,
col_data, col_data,
offset_to_world(chunk.chunk_offset * Chunk::SIZE as i32, 0.), offset_to_world(chunk_offset * Chunk::SIZE as i32, 0.),
offset_to_index(chunk.chunk_offset, heightmap.width), chunk_index,
); );
} }
pub fn prepare_chunk_mesh_with_collider(chunk: &Chunk, heightmap: &Map) -> (Mesh, Collider, Vec3, usize) { pub fn prepare_chunk_mesh_with_collider(
let (mesh, (col_verts, col_indicies), pos, index) = prepare_chunk_mesh(chunk, heightmap); chunk: &MeshChunkData,
chunk_offset: IVec2,
chunk_index: usize,
) -> (Mesh, Collider, Vec3, usize) {
let (mesh, (col_verts, col_indicies), pos, index) = prepare_chunk_mesh(chunk, chunk_offset, chunk_index);
let collider: Collider; let collider: Collider;
{ {
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]