Skip to content

Commit

Permalink
Store mesh handles. Simplify chunk generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Cyliann committed Dec 17, 2022
1 parent 73d6e72 commit 9f4572a
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 130 deletions.
107 changes: 76 additions & 31 deletions src/chunk.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,79 @@
use crate::mesh;
use crate::voxel_data::{CHUNK_SIZE, WORLD_SIZE_IN_CHUNKS};
use crate::voxel_map;
use crate::world::{ChunkCoord, ChunkMap};
use crate::world::{ActiveChunks, ChunkCoord, ChunkMap, ChunkToGenerateQueue, ChunkToSpawnQueue};
use crate::mesh;
use bevy::prelude::*;
use crate::voxel_map::VoxelMap;

#[derive(Default)]
pub struct MaterialHandle(pub Handle<StandardMaterial>);

#[derive(Clone, Debug)]
pub struct Chunk {
pub position: ChunkCoord,
pub is_full: bool,
pub mesh_handle: Option<Handle<Mesh>>,
}

pub fn generate_chunk(
mut chunk_to_generate_queue: ResMut<ChunkToGenerateQueue>,
mut chunk_to_spawn_queue: ResMut<ChunkToSpawnQueue>,
mut voxel_map: ResMut<VoxelMap>,
mut meshes: ResMut<Assets<Mesh>>,
mut chunk_map: ResMut<ChunkMap>,
mut active_chunks: ResMut<ActiveChunks>,
) {
while let Some(chunk_pos) = chunk_to_generate_queue.0.pop() {
let is_full;
let chunk= &mut chunk_map.0[[
(chunk_pos.x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
chunk_pos.y as usize,
(chunk_pos.z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize]].0;


if chunk.is_none()
{
is_full = voxel_map.populate_voxel_map(chunk_pos);
let mesh_handle = Some(meshes.add(mesh::create_mesh(&chunk_pos, &mut voxel_map)));

*chunk = Some(Chunk {
position: chunk_pos,
is_full,
mesh_handle,
});
} else {
is_full = chunk.as_ref().unwrap().is_full;
}
chunk_to_spawn_queue.0.push((chunk_pos, is_full));
active_chunks.0.push(chunk_pos);
}
}

impl Chunk {
pub fn new(
chunk_pos: &ChunkCoord,
commands: &mut Commands,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<StandardMaterial>>,
asset_server: &Res<AssetServer>,
voxel_map: &mut ResMut<voxel_map::VoxelMap>,
chunk_map: &mut ResMut<ChunkMap>,
is_chunk_full: bool,
) -> Self {
if !is_chunk_full {
let mesh_handle = meshes.add(mesh::create_mesh(chunk_pos, voxel_map));
let texture_handle: Handle<Image> = asset_server.load("texture_atlas.png");

let material_handle = materials.add(StandardMaterial {
base_color_texture: Some(texture_handle.clone()),
perceptual_roughness: 1.0,
metallic: 0.0,
reflectance: 0.1,
..default()
});
pub fn spawn_chunk(
mut commands: Commands,
mut chunk_map: ResMut<ChunkMap>,
mut chunk_to_spawn_queue: ResMut<ChunkToSpawnQueue>,
material_handle: Res<MaterialHandle>,
) {
while let Some((chunk_pos, is_full)) = chunk_to_spawn_queue.0.pop() {
let _span = info_span!("Chunk spawn").entered();
if !is_full {
let chunk = chunk_map.0[[
(chunk_pos.x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
chunk_pos.y as usize,
(chunk_pos.z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize]].0.as_mut().unwrap();

let mesh_handle = chunk.mesh_handle.clone().unwrap();

let _span = info_span!("Spawn mesh").entered();
chunk_map.0[[
(chunk_pos.x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
chunk_pos.y as usize,
(chunk_pos.z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
]] = Some(
]].1 = Some(
commands
.spawn_bundle(MaterialMeshBundle {
mesh: mesh_handle,
material: material_handle,
material: material_handle.0.clone(),
transform: Transform::from_xyz(
(chunk_pos.x * CHUNK_SIZE as i32) as f32,
(chunk_pos.y * CHUNK_SIZE as i32) as f32,
Expand All @@ -55,9 +88,21 @@ impl Chunk {
.id(),
);
}

Chunk {
position: *chunk_pos,
}
}
}

pub fn generate_material(
mut materials: ResMut<Assets<StandardMaterial>>,
asset_server: Res<AssetServer>,
mut material_handle: ResMut<MaterialHandle>,
) {
let texture_handle: Handle<Image> = asset_server.load("texture_atlas.png");

material_handle.0 = materials.add(StandardMaterial {
base_color_texture: Some(texture_handle.clone()),
perceptual_roughness: 1.0,
metallic: 0.0,
reflectance: 0.1,
..default()
});
}
13 changes: 5 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
use bevy::{prelude::*, render::texture::ImageSettings};
// use bevy::window::PresentMode::Immediate;
use crate::voxel_data::{WORLD_HEIGHT_IN_CHUNKS, WORLD_SIZE_IN_CHUNKS};
use bevy_atmosphere::prelude::*;
use bevy_flycam::{FlyCam, MovementSettings, NoCameraPlayerPlugin};
use bevy_inspector_egui::WorldInspectorPlugin;
Expand Down Expand Up @@ -31,16 +30,13 @@ fn main() {
//mode: WindowMode::BorderlessFullscreen,
..Default::default()
})
.init_resource::<chunk::MaterialHandle>()
.insert_resource(voxel_map::VoxelMap::new())
.insert_resource(world::ChunkMap::new())
.insert_resource(world::ChunkToGenerateQueue(Vec::new()))
.insert_resource(world::ChunkToSpawnQueue(Vec::new()))
.insert_resource(world::ActiveChunks::new())
.insert_resource(world::ActiveChunks(Vec::new()))
.insert_resource(world::PlayerLastChunk::new())
.insert_resource(world::GeneratedChunks {
chunks: [[[(false, true); WORLD_SIZE_IN_CHUNKS]; WORLD_HEIGHT_IN_CHUNKS];
WORLD_SIZE_IN_CHUNKS],
})
.insert_resource(MovementSettings {
sensitivity: 0.00012, // default: 0.00012
speed: 30.0, // default: 12.0
Expand All @@ -56,9 +52,10 @@ fn main() {
.add_startup_system(world::spawn_world)
.add_startup_system(spawn_light)
.add_startup_system(spawn_camera)
.add_startup_system(chunk::generate_material)
.add_system(world::check_render_distance)
.add_system(world::generate_chunk)
.add_system(world::spawn_chunk)
.add_system(chunk::generate_chunk)
.add_system(chunk::spawn_chunk)
.run();
}

Expand Down
2 changes: 1 addition & 1 deletion src/voxel_data.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bevy::prelude::Vec3;

pub const CHUNK_SIZE: usize = 32;
pub const WORLD_SIZE_IN_CHUNKS: usize = 32;
pub const WORLD_SIZE_IN_CHUNKS: usize = 128;
pub const WORLD_HEIGHT_IN_CHUNKS: usize = 5;
pub const RENDER_DISTANCE: usize = 8;

Expand Down
112 changes: 22 additions & 90 deletions src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::chunk::Chunk;
use crate::voxel_data::{
CHUNK_SIZE, RENDER_DISTANCE, WORLD_HEIGHT_IN_CHUNKS, WORLD_SIZE_IN_CHUNKS,
};
use crate::voxel_map::VoxelMap;
use bevy::prelude::*;
use itertools::iproduct;
use ndarray::Array3;
Expand Down Expand Up @@ -36,74 +35,18 @@ pub fn spawn_world(mut chunk_queue: ResMut<ChunkToGenerateQueue>) {
(x, z) = (x + dx, z + dz);
}

chunks.reverse();

for (x, z) in chunks {
for (x, z) in chunks.iter().rev() {
for y in 0..WORLD_HEIGHT_IN_CHUNKS {
let chunk_pos = ChunkCoord {
x: x as i32,
x: *x as i32,
y: y as i32,
z: z as i32,
z: *z as i32,
};
chunk_queue.0.push(chunk_pos);
}
}
}

pub fn generate_chunk(
mut chunk_to_generate_queue: ResMut<ChunkToGenerateQueue>,
mut chunk_to_spawn_queue: ResMut<ChunkToSpawnQueue>,
mut voxel_map: ResMut<VoxelMap>,
chunk_map: ResMut<ChunkMap>,
mut active_chunks: ResMut<ActiveChunks>,
mut generated_chunks: ResMut<GeneratedChunks>,
) {
if chunk_to_generate_queue.0.len() > 0 {
let chunk_pos = chunk_to_generate_queue.0.pop().unwrap();
if chunk_map.0[[
(chunk_pos.x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
chunk_pos.y as usize,
(chunk_pos.z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
]] == None
{
let generated_chunk = &mut generated_chunks.chunks
[(chunk_pos.x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize][chunk_pos.y as usize]
[(chunk_pos.z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize];
if !generated_chunk.0 {
(*generated_chunk).1 = voxel_map.populate_voxel_map(chunk_pos);
}

chunk_to_spawn_queue.0.push((chunk_pos, generated_chunk.1));
active_chunks.0.push(chunk_pos);
(*generated_chunk).0 = true;
}
}
}

pub fn spawn_chunk(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
asset_server: Res<AssetServer>,
mut voxel_map: ResMut<VoxelMap>,
mut chunk_map: ResMut<ChunkMap>,
mut chunk_to_spawn_queue: ResMut<ChunkToSpawnQueue>,
) {
while let Some((chunk_pos, is_full)) = chunk_to_spawn_queue.0.pop() {
let _span = info_span!("Chunk spawn").entered();
Chunk::new(
&chunk_pos,
&mut commands,
&mut meshes,
&mut materials,
&asset_server,
&mut voxel_map,
&mut chunk_map,
is_full,
);
}
}

pub fn check_render_distance(
query: Query<(&GlobalTransform, With<super::Player>)>,
mut commands: Commands,
Expand All @@ -113,20 +56,20 @@ pub fn check_render_distance(
mut player_last_chunk: ResMut<PlayerLastChunk>,
) {
let player_pos = query.single().0.translation();
let chunk_pos = get_chunk_from_player_pos(player_pos);
let player_chunk_pos = get_chunk_from_player_pos(player_pos);

if !chunk_pos.equals2d(player_last_chunk.0) {
if !player_chunk_pos.equals2d(player_last_chunk.0) {
for (x, y, z) in iproduct!(
(chunk_pos.x - RENDER_DISTANCE as i32..chunk_pos.x + RENDER_DISTANCE as i32),
(player_chunk_pos.x - RENDER_DISTANCE as i32..player_chunk_pos.x + RENDER_DISTANCE as i32),
(0..WORLD_HEIGHT_IN_CHUNKS as i32),
(chunk_pos.z - RENDER_DISTANCE as i32..chunk_pos.z + RENDER_DISTANCE as i32)
(player_chunk_pos.z - RENDER_DISTANCE as i32..player_chunk_pos.z + RENDER_DISTANCE as i32)
) {
if is_chunk_in_world(&ChunkCoord { x, y, z }) {
if chunk_map.0[[
(x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
y as usize,
(z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
]] == None
]].1 == None
{
chunk_queue.0.push(ChunkCoord { x, y, z });
}
Expand All @@ -136,29 +79,29 @@ pub fn check_render_distance(
for i in (0..active_chunks.0.len()).rev() {
let chunk_coord = active_chunks.0[i];

if chunk_coord.x < chunk_pos.x - RENDER_DISTANCE as i32
|| chunk_coord.x > chunk_pos.x + RENDER_DISTANCE as i32
|| chunk_coord.z < chunk_pos.z - RENDER_DISTANCE as i32
|| chunk_coord.z > chunk_pos.z + RENDER_DISTANCE as i32
if chunk_coord.x < player_chunk_pos.x - RENDER_DISTANCE as i32
|| chunk_coord.x > player_chunk_pos.x + RENDER_DISTANCE as i32
|| chunk_coord.z < player_chunk_pos.z - RENDER_DISTANCE as i32
|| chunk_coord.z > player_chunk_pos.z + RENDER_DISTANCE as i32
{
let chunk = chunk_map.0[[
let chunk_entity = chunk_map.0[[
(chunk_coord.x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
chunk_coord.y as usize,
(chunk_coord.z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
]];
]].1;

if chunk.is_some() {
commands.entity(chunk.unwrap()).despawn_recursive();
if chunk_entity.is_some() {
commands.entity(chunk_entity.unwrap()).despawn_recursive();
active_chunks.0.swap_remove(i);
chunk_map.0[[
(chunk_coord.x + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
chunk_coord.y as usize,
(chunk_coord.z + WORLD_SIZE_IN_CHUNKS as i32 / 2) as usize,
]] = None;
]].1 = None;
}
}
}
player_last_chunk.0 = chunk_pos;
player_last_chunk.0 = player_chunk_pos;
}
}

Expand All @@ -184,16 +127,11 @@ fn is_chunk_in_world(chunk_pos: &ChunkCoord) -> bool {
}

#[derive(Clone, Debug)]
pub struct ActiveChunks(Vec<ChunkCoord>);
pub struct ActiveChunks(pub Vec<ChunkCoord>);
pub struct PlayerLastChunk(ChunkCoord);
pub struct ChunkToGenerateQueue(pub Vec<ChunkCoord>);
pub struct ChunkToSpawnQueue(pub Vec<(ChunkCoord, bool)>);

pub struct GeneratedChunks {
pub chunks:
[[[(bool, bool); WORLD_SIZE_IN_CHUNKS]; WORLD_HEIGHT_IN_CHUNKS]; WORLD_SIZE_IN_CHUNKS],
}

#[derive(Clone, Copy, Debug)]
pub struct ChunkCoord {
pub x: i32,
Expand All @@ -202,20 +140,14 @@ pub struct ChunkCoord {
}

#[derive(Default, Debug)]
pub struct ChunkMap(pub Array3<Option<Entity>>);
pub struct ChunkMap(pub Array3<(Option<Chunk>, Option<Entity>)>);

impl PlayerLastChunk {
pub fn new() -> Self {
PlayerLastChunk(ChunkCoord { x: 0, y: 0, z: 0 })
}
}

impl ActiveChunks {
pub fn new() -> Self {
ActiveChunks(Vec::new())
}
}

impl ChunkCoord {
pub fn equals2d(self, other: ChunkCoord) -> bool {
return other.x == self.x && other.z == self.z;
Expand All @@ -224,13 +156,13 @@ impl ChunkCoord {

impl ChunkMap {
pub fn new() -> Self {
ChunkMap(Array3::<Option<Entity>>::from_elem(
ChunkMap(Array3::<(Option<Chunk>, Option<Entity>)>::from_elem(
(
WORLD_SIZE_IN_CHUNKS,
WORLD_HEIGHT_IN_CHUNKS,
WORLD_SIZE_IN_CHUNKS,
),
None,
(None, None),
))
}
}

0 comments on commit 9f4572a

Please sign in to comment.