mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-11-25 16:28:42 -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::{Surface, uniform};
|
||||||
use glium::uniforms::{Sampler, MinifySamplerFilter, MagnifySamplerFilter};
|
use glium::uniforms::{Sampler, MinifySamplerFilter, MagnifySamplerFilter};
|
||||||
use glium::glutin::{
|
use glium::glutin::{
|
||||||
|
@ -22,6 +23,7 @@ use shaders::{Programs, chunk::Vertex as ChunkVertex};
|
||||||
use camera::Camera;
|
use camera::Camera;
|
||||||
use controller::Controls;
|
use controller::Controls;
|
||||||
use world::World;
|
use world::World;
|
||||||
|
use options::GameOptions;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
pub camera: Camera,
|
pub camera: Camera,
|
||||||
|
@ -47,6 +49,8 @@ pub fn run() {
|
||||||
let programs = Programs::compile_all(&display);
|
let programs = Programs::compile_all(&display);
|
||||||
log::info!("loading assets");
|
log::info!("loading assets");
|
||||||
let assets = Assets::load_all_sync(&display);
|
let assets = Assets::load_all_sync(&display);
|
||||||
|
log::info!("init game options");
|
||||||
|
let options = GameOptions::default();
|
||||||
log::info!("init game state");
|
log::info!("init game state");
|
||||||
let mut state = State::init();
|
let mut state = State::init();
|
||||||
state.camera.position = [0., 0., -1.];
|
state.camera.position = [0., 0., -1.];
|
||||||
|
@ -100,18 +104,32 @@ pub fn run() {
|
||||||
_ => return
|
_ => return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Calculate delta time
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let dt = (now - last_render).as_secs_f32();
|
let dt = (now - last_render).as_secs_f32();
|
||||||
last_render = now;
|
last_render = now;
|
||||||
|
|
||||||
|
//Update controls
|
||||||
state.controls.calculate(dt).apply_to_camera(&mut state.camera);
|
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();
|
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 target_dimensions = target.get_dimensions();
|
||||||
let perspective = state.camera.perspective_matrix(target_dimensions);
|
let perspective = state.camera.perspective_matrix(target_dimensions);
|
||||||
let view = state.camera.view_matrix();
|
let view = state.camera.view_matrix();
|
||||||
target.clear_color_and_depth((0.5, 0.5, 1., 1.), 1.);
|
|
||||||
|
|
||||||
|
//Draw example triangle
|
||||||
target.draw(
|
target.draw(
|
||||||
&vertex_buffer,
|
&vertex_buffer,
|
||||||
glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
|
glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
|
||||||
|
@ -129,6 +147,8 @@ pub fn run() {
|
||||||
},
|
},
|
||||||
&Default::default()
|
&Default::default()
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
//Finish drawing
|
||||||
target.finish().unwrap();
|
target.finish().unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use glam::{Vec2, IVec2};
|
use glam::{Vec2, IVec2};
|
||||||
|
use glium::Display;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::game::options::GameOptions;
|
use crate::game::options::GameOptions;
|
||||||
|
|
||||||
mod chunk;
|
mod chunk;
|
||||||
mod thread;
|
mod thread;
|
||||||
|
|
||||||
use chunk::{Chunk, CHUNK_SIZE};
|
use chunk::{Chunk, ChunkState, CHUNK_SIZE};
|
||||||
|
use thread::WorldThreading;
|
||||||
|
|
||||||
const POSITIVE_X_NEIGHBOR: usize = 0;
|
const POSITIVE_X_NEIGHBOR: usize = 0;
|
||||||
const NEGATIVE_X_NEIGHBOR: usize = 1;
|
const NEGATIVE_X_NEIGHBOR: usize = 1;
|
||||||
|
@ -13,7 +15,8 @@ const POSITIVE_Z_NEIGHBOR: usize = 2;
|
||||||
const NEGATIVE_Z_NEIGHBOR: usize = 3;
|
const NEGATIVE_Z_NEIGHBOR: usize = 3;
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub chunks: HashMap<IVec2, Chunk>
|
pub chunks: HashMap<IVec2, Chunk>,
|
||||||
|
pub thread: WorldThreading,
|
||||||
}
|
}
|
||||||
impl World {
|
impl World {
|
||||||
pub fn chunk_neighbors(&self, position: IVec2) -> [Option<&Chunk>; 4] {
|
pub fn chunk_neighbors(&self, position: IVec2) -> [Option<&Chunk>; 4] {
|
||||||
|
@ -26,13 +29,56 @@ impl World {
|
||||||
}
|
}
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
chunks: HashMap::new()
|
chunks: HashMap::new(),
|
||||||
|
thread: WorldThreading::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update_loaded_chunks(&mut self, around_position: Vec2, game_opt: &GameOptions) {
|
pub fn update_loaded_chunks(&mut self, around_position: Vec2, options: &GameOptions, display: &Display) {
|
||||||
let render_dist = game_opt.render_distance as i32;
|
let render_dist = options.render_distance as i32 + 1;
|
||||||
let inside_chunk = (around_position / CHUNK_SIZE as f32).as_ivec2();
|
let inside_chunk = (around_position / CHUNK_SIZE as f32).as_ivec2();
|
||||||
|
//Mark all chunks for unload
|
||||||
todo!()
|
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 const CHUNK_HEIGHT: usize = 255;
|
||||||
|
|
||||||
pub enum ChunkState {
|
pub enum ChunkState {
|
||||||
|
Unload,
|
||||||
Nothing,
|
Nothing,
|
||||||
Loaded,
|
Loaded,
|
||||||
Rendered,
|
Rendered,
|
||||||
|
@ -20,7 +21,7 @@ pub type ChunkMesh = VertexBuffer<ChunkVertex>;
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
pub position: IVec2,
|
pub position: IVec2,
|
||||||
pub block_data: Option<ChunkData>,
|
pub block_data: Option<ChunkData>,
|
||||||
pub vertex_buffer: Option<ChunkMesh>,
|
pub vertex_buffer: Option<(bool, ChunkMesh)>,
|
||||||
pub state: ChunkState,
|
pub state: ChunkState,
|
||||||
pub desired: ChunkState,
|
pub desired: ChunkState,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,28 @@
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
|
use glium::{Display, VertexBuffer};
|
||||||
use std::{
|
use std::{
|
||||||
thread::{self, JoinHandle},
|
thread::{self, JoinHandle},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
mem
|
mem
|
||||||
};
|
};
|
||||||
use super::chunk::{Chunk, ChunkData, ChunkState};
|
use super::chunk::{Chunk, ChunkData, ChunkState};
|
||||||
|
use crate::game::shaders::chunk::Vertex as ChunkVertex;
|
||||||
|
|
||||||
mod world_gen;
|
mod world_gen;
|
||||||
mod mesh_gen;
|
mod mesh_gen;
|
||||||
|
|
||||||
struct WorldThreading {
|
#[derive(Default)]
|
||||||
|
pub struct WorldThreading {
|
||||||
//drain_filter is not stable yet so
|
//drain_filter is not stable yet so
|
||||||
//Options are needed here to take ownership,
|
//Options are needed here to take ownership,
|
||||||
//None values should never appear here!
|
//None values should never appear here!
|
||||||
pub load_tasks: HashMap<IVec2, Option<JoinHandle<ChunkData>>>,
|
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 {
|
impl WorldThreading {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
pub fn is_done(&self) -> bool {
|
pub fn is_done(&self) -> bool {
|
||||||
self.load_tasks.is_empty() &&
|
self.load_tasks.is_empty() &&
|
||||||
self.mesh_tasks.is_empty()
|
self.mesh_tasks.is_empty()
|
||||||
|
@ -32,7 +38,24 @@ impl WorldThreading {
|
||||||
log::warn!("load: discarded {}, reason: new task started", position);
|
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| {
|
self.load_tasks.retain(|position, handle| {
|
||||||
if !chunks.contains_key(position) {
|
if !chunks.contains_key(position) {
|
||||||
log::warn!("load: discarded {}, reason: chunk no longer exists", position);
|
log::warn!("load: discarded {}, reason: chunk no longer exists", position);
|
||||||
|
@ -54,5 +77,31 @@ impl WorldThreading {
|
||||||
chunk.state = ChunkState::Loaded;
|
chunk.state = ChunkState::Loaded;
|
||||||
false
|
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