mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-26 05:38:20 -06:00
world
This commit is contained in:
parent
2bc0231f22
commit
07582f968d
22
src/game.rs
22
src/game.rs
|
@ -1,3 +1,4 @@
|
|||
use glam::Vec2;
|
||||
use glium::{Surface, uniform};
|
||||
use glium::uniforms::{Sampler, MinifySamplerFilter, MagnifySamplerFilter};
|
||||
use glium::glutin::{
|
||||
|
@ -22,6 +23,7 @@ use shaders::{Programs, chunk::Vertex as ChunkVertex};
|
|||
use camera::Camera;
|
||||
use controller::Controls;
|
||||
use world::World;
|
||||
use options::GameOptions;
|
||||
|
||||
struct State {
|
||||
pub camera: Camera,
|
||||
|
@ -47,6 +49,8 @@ pub fn run() {
|
|||
let programs = Programs::compile_all(&display);
|
||||
log::info!("loading assets");
|
||||
let assets = Assets::load_all_sync(&display);
|
||||
log::info!("init game options");
|
||||
let options = GameOptions::default();
|
||||
log::info!("init game state");
|
||||
let mut state = State::init();
|
||||
state.camera.position = [0., 0., -1.];
|
||||
|
@ -100,18 +104,32 @@ pub fn run() {
|
|||
_ => return
|
||||
}
|
||||
|
||||
//Calculate delta time
|
||||
let now = Instant::now();
|
||||
let dt = (now - last_render).as_secs_f32();
|
||||
last_render = now;
|
||||
|
||||
//Update controls
|
||||
state.controls.calculate(dt).apply_to_camera(&mut state.camera);
|
||||
|
||||
//Load new chunks
|
||||
|
||||
state.world.update_loaded_chunks(
|
||||
Vec2::new(state.camera.position[0], state.camera.position[2]),
|
||||
&options,
|
||||
&display
|
||||
);
|
||||
|
||||
//Start drawing
|
||||
let mut target = display.draw();
|
||||
target.clear_color_and_depth((0.5, 0.5, 1., 1.), 1.);
|
||||
|
||||
//Compute camera
|
||||
let target_dimensions = target.get_dimensions();
|
||||
let perspective = state.camera.perspective_matrix(target_dimensions);
|
||||
let view = state.camera.view_matrix();
|
||||
target.clear_color_and_depth((0.5, 0.5, 1., 1.), 1.);
|
||||
|
||||
//Draw example triangle
|
||||
target.draw(
|
||||
&vertex_buffer,
|
||||
glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
|
||||
|
@ -129,6 +147,8 @@ pub fn run() {
|
|||
},
|
||||
&Default::default()
|
||||
).unwrap();
|
||||
|
||||
//Finish drawing
|
||||
target.finish().unwrap();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use glam::{Vec2, IVec2};
|
||||
use glium::Display;
|
||||
use std::collections::HashMap;
|
||||
use crate::game::options::GameOptions;
|
||||
|
||||
mod chunk;
|
||||
mod thread;
|
||||
|
||||
use chunk::{Chunk, CHUNK_SIZE};
|
||||
use chunk::{Chunk, ChunkState, CHUNK_SIZE};
|
||||
use thread::WorldThreading;
|
||||
|
||||
const POSITIVE_X_NEIGHBOR: usize = 0;
|
||||
const NEGATIVE_X_NEIGHBOR: usize = 1;
|
||||
|
@ -13,7 +15,8 @@ const POSITIVE_Z_NEIGHBOR: usize = 2;
|
|||
const NEGATIVE_Z_NEIGHBOR: usize = 3;
|
||||
|
||||
pub struct World {
|
||||
pub chunks: HashMap<IVec2, Chunk>
|
||||
pub chunks: HashMap<IVec2, Chunk>,
|
||||
pub thread: WorldThreading,
|
||||
}
|
||||
impl World {
|
||||
pub fn chunk_neighbors(&self, position: IVec2) -> [Option<&Chunk>; 4] {
|
||||
|
@ -26,13 +29,56 @@ impl World {
|
|||
}
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
chunks: HashMap::new()
|
||||
chunks: HashMap::new(),
|
||||
thread: WorldThreading::new(),
|
||||
}
|
||||
}
|
||||
pub fn update_loaded_chunks(&mut self, around_position: Vec2, game_opt: &GameOptions) {
|
||||
let render_dist = game_opt.render_distance as i32;
|
||||
pub fn update_loaded_chunks(&mut self, around_position: Vec2, options: &GameOptions, display: &Display) {
|
||||
let render_dist = options.render_distance as i32 + 1;
|
||||
let inside_chunk = (around_position / CHUNK_SIZE as f32).as_ivec2();
|
||||
|
||||
todo!()
|
||||
//Mark all chunks for unload
|
||||
for (_, chunk) in &mut self.chunks {
|
||||
chunk.desired = ChunkState::Unload;
|
||||
}
|
||||
//Load new/update chunks in range
|
||||
for x in -render_dist..=render_dist {
|
||||
for z in -render_dist..=render_dist {
|
||||
let offset = IVec2::new(x, z);
|
||||
let position = inside_chunk + offset;
|
||||
if !self.chunks.contains_key(&position) {
|
||||
self.chunks.insert(position, Chunk::new(position));
|
||||
}
|
||||
let chunk = self.chunks.get_mut(&position).unwrap();
|
||||
if x == 0 || z == 0 || x == render_dist || z == render_dist {
|
||||
chunk.desired = ChunkState::Loaded;
|
||||
} else {
|
||||
chunk.desired = ChunkState::Rendered;
|
||||
}
|
||||
}
|
||||
}
|
||||
//State up/downgrades are handled here!
|
||||
self.chunks.retain(|&position, chunk| {
|
||||
match chunk.desired {
|
||||
ChunkState::Unload => {
|
||||
return false
|
||||
},
|
||||
ChunkState::Nothing => {
|
||||
chunk.block_data = None;
|
||||
chunk.vertex_buffer = None;
|
||||
chunk.state = ChunkState::Nothing;
|
||||
},
|
||||
ChunkState::Loaded if matches!(chunk.state, ChunkState::Nothing) => {
|
||||
self.thread.queue_load(position);
|
||||
},
|
||||
ChunkState::Loaded if matches!(chunk.state, ChunkState::Rendered) => {
|
||||
chunk.vertex_buffer = None;
|
||||
chunk.state = ChunkState::Loaded;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
true
|
||||
});
|
||||
//Apply changes from threads
|
||||
self.thread.apply_tasks(&mut self.chunks, display);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ pub const CHUNK_SIZE: usize = 16;
|
|||
pub const CHUNK_HEIGHT: usize = 255;
|
||||
|
||||
pub enum ChunkState {
|
||||
Unload,
|
||||
Nothing,
|
||||
Loaded,
|
||||
Rendered,
|
||||
|
@ -20,7 +21,7 @@ pub type ChunkMesh = VertexBuffer<ChunkVertex>;
|
|||
pub struct Chunk {
|
||||
pub position: IVec2,
|
||||
pub block_data: Option<ChunkData>,
|
||||
pub vertex_buffer: Option<ChunkMesh>,
|
||||
pub vertex_buffer: Option<(bool, ChunkMesh)>,
|
||||
pub state: ChunkState,
|
||||
pub desired: ChunkState,
|
||||
}
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
use glam::IVec2;
|
||||
use glium::{Display, VertexBuffer};
|
||||
use std::{
|
||||
thread::{self, JoinHandle},
|
||||
collections::HashMap,
|
||||
mem
|
||||
};
|
||||
use super::chunk::{Chunk, ChunkData, ChunkState};
|
||||
use crate::game::shaders::chunk::Vertex as ChunkVertex;
|
||||
|
||||
mod world_gen;
|
||||
mod mesh_gen;
|
||||
|
||||
struct WorldThreading {
|
||||
#[derive(Default)]
|
||||
pub struct WorldThreading {
|
||||
//drain_filter is not stable yet so
|
||||
//Options are needed here to take ownership,
|
||||
//None values should never appear here!
|
||||
pub load_tasks: HashMap<IVec2, Option<JoinHandle<ChunkData>>>,
|
||||
pub mesh_tasks: HashMap<IVec2, Option<JoinHandle<ChunkData>>>,
|
||||
pub mesh_tasks: HashMap<IVec2, Option<JoinHandle<Vec<ChunkVertex>>>>,
|
||||
}
|
||||
impl WorldThreading {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.load_tasks.is_empty() &&
|
||||
self.mesh_tasks.is_empty()
|
||||
|
@ -32,7 +38,24 @@ impl WorldThreading {
|
|||
log::warn!("load: discarded {}, reason: new task started", position);
|
||||
}
|
||||
}
|
||||
pub fn apply_tasks(&mut self, chunks: &mut HashMap<IVec2, Chunk>) {
|
||||
pub fn queue_mesh(&mut self, chunk: &Chunk, neighbors: [&Chunk; 4]) {
|
||||
let position = chunk.position;
|
||||
let data = chunk.block_data.expect("Chunk has no mesh!");
|
||||
let neighbor_data = [
|
||||
neighbors[0].block_data.expect("Chunk has no mesh!"),
|
||||
neighbors[1].block_data.expect("Chunk has no mesh!"),
|
||||
neighbors[2].block_data.expect("Chunk has no mesh!"),
|
||||
neighbors[3].block_data.expect("Chunk has no mesh!"),
|
||||
];
|
||||
let handle = thread::spawn(move || {
|
||||
mesh_gen::generate_mesh(position, data, neighbor_data)
|
||||
});
|
||||
if self.mesh_tasks.insert(chunk.position, Some(handle)).is_some() {
|
||||
log::warn!("mesh: discarded {}, reason: new task started", chunk.position);
|
||||
}
|
||||
}
|
||||
pub fn apply_tasks(&mut self, chunks: &mut HashMap<IVec2, Chunk>, display: &Display) {
|
||||
//LOAD TASKS
|
||||
self.load_tasks.retain(|position, handle| {
|
||||
if !chunks.contains_key(position) {
|
||||
log::warn!("load: discarded {}, reason: chunk no longer exists", position);
|
||||
|
@ -54,5 +77,31 @@ impl WorldThreading {
|
|||
chunk.state = ChunkState::Loaded;
|
||||
false
|
||||
});
|
||||
//MESH TASKS
|
||||
self.mesh_tasks.retain(|position, handle| {
|
||||
if !chunks.contains_key(position) {
|
||||
log::warn!("mesh: discarded {}, reason: chunk no longer exists", position);
|
||||
return false
|
||||
}
|
||||
if !matches!(chunks.get(position).unwrap().desired, ChunkState::Rendered) {
|
||||
log::warn!("mesh: discarded {}, reason: state undesired", position);
|
||||
return false
|
||||
}
|
||||
if !handle.as_ref().expect("Something went terribly wrong").is_finished() {
|
||||
//task not finished yet, keep it and wait
|
||||
return true
|
||||
}
|
||||
log::info!("mesh: done {}", position);
|
||||
let handle = mem::take(handle).unwrap();
|
||||
let data = handle.join().unwrap();
|
||||
let chunk = chunks.get_mut(position).unwrap();
|
||||
chunk.vertex_buffer = Some((
|
||||
true,
|
||||
VertexBuffer::immutable(display, &data).expect("Failed to build VertexBuffer")
|
||||
));
|
||||
chunk.state = ChunkState::Rendered;
|
||||
false
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::game::world::chunk::Chunk;
|
||||
use glam::IVec2;
|
||||
use crate::game::world::chunk::{Chunk, ChunkData};
|
||||
use crate::game::shaders::chunk::Vertex as ChunkVertex;
|
||||
|
||||
pub fn generate_mesh(chunk: &Chunk, neighbors: [&Chunk; 4]) {
|
||||
|
||||
pub fn generate_mesh(position: IVec2, chunk_data: ChunkData, neighbors: [ChunkData; 4]) -> Vec<ChunkVertex> {
|
||||
vec![]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue