mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-22 11:58:21 -06:00
rendering chunks
This commit is contained in:
parent
3cfd6448cc
commit
213075c5f9
22
src/game.rs
22
src/game.rs
|
@ -1,6 +1,5 @@
|
|||
use glam::Vec2;
|
||||
use glium::{Surface, uniform};
|
||||
use glium::uniforms::{Sampler, MinifySamplerFilter, MagnifySamplerFilter};
|
||||
use glium::Surface;
|
||||
use glium::glutin::{
|
||||
event::{Event, WindowEvent, DeviceEvent},
|
||||
event_loop::{EventLoop, ControlFlow},
|
||||
|
@ -19,7 +18,7 @@ mod options;
|
|||
|
||||
use assets::Assets;
|
||||
use display::init_display;
|
||||
use shaders::{Programs, chunk::Vertex as ChunkVertex};
|
||||
use shaders::Programs;
|
||||
use camera::Camera;
|
||||
use controller::Controls;
|
||||
use world::World;
|
||||
|
@ -53,15 +52,15 @@ pub fn run() {
|
|||
let options = GameOptions::default();
|
||||
log::info!("init game state");
|
||||
let mut state = State::init();
|
||||
state.camera.position = [0., 0., -1.];
|
||||
state.camera.position = [0., 260., -1.];
|
||||
log::info!("game loaded");
|
||||
|
||||
//=======================
|
||||
let vertex1 = ChunkVertex { position: [-0.5, -0.5, 0.], uv: [0., 0.], normal: [0., 1., 0.] };
|
||||
let vertex2 = ChunkVertex { position: [ 0.0, 0.5, 0.], uv: [0., 1.], normal: [0., 1., 0.] };
|
||||
let vertex3 = ChunkVertex { position: [ 0.5, -0.5, 0.], uv: [1., 1.], normal: [0., 1., 0.] };
|
||||
let shape = vec![vertex1, vertex2, vertex3];
|
||||
let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();
|
||||
// let vertex1 = ChunkVertex { position: [-0.5, -0.5, 0.], uv: [0., 0.], normal: [0., 1., 0.] };
|
||||
// let vertex2 = ChunkVertex { position: [ 0.0, 0.5, 0.], uv: [0., 1.], normal: [0., 1., 0.] };
|
||||
// let vertex3 = ChunkVertex { position: [ 0.5, -0.5, 0.], uv: [1., 1.], normal: [0., 1., 0.] };
|
||||
// let shape = vec![vertex1, vertex2, vertex3];
|
||||
// let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();
|
||||
//=======================
|
||||
|
||||
let mut last_render = Instant::now();
|
||||
|
@ -122,12 +121,11 @@ pub fn run() {
|
|||
let target_dimensions = target.get_dimensions();
|
||||
let perspective = state.camera.perspective_matrix(target_dimensions);
|
||||
let view = state.camera.view_matrix();
|
||||
|
||||
//Draw example triangle
|
||||
|
||||
//Draw chunks
|
||||
state.world.render(&mut target, &programs, &assets, perspective, view);
|
||||
|
||||
|
||||
//Draw example triangle
|
||||
// target.draw(
|
||||
// &vertex_buffer,
|
||||
// glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList),
|
||||
|
|
|
@ -109,7 +109,7 @@ impl Default for Controls {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
inputs: Default::default(),
|
||||
speed: 1.,
|
||||
speed: 10.,
|
||||
sensitivity: 2.,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use glam::{Vec2, IVec2};
|
||||
use glium::{
|
||||
Display, Frame, Surface, uniform,
|
||||
Display, Frame, Surface,
|
||||
DrawParameters, Depth,
|
||||
DepthTest, uniform,
|
||||
uniforms::{
|
||||
Sampler, SamplerBehavior,
|
||||
MinifySamplerFilter, MagnifySamplerFilter,
|
||||
|
@ -24,6 +26,8 @@ const NEGATIVE_X_NEIGHBOR: usize = 1;
|
|||
const POSITIVE_Z_NEIGHBOR: usize = 2;
|
||||
const NEGATIVE_Z_NEIGHBOR: usize = 3;
|
||||
|
||||
const MAX_TASKS: usize = 8;
|
||||
|
||||
pub struct World {
|
||||
pub chunks: HashMap<IVec2, Chunk>,
|
||||
pub thread: WorldThreading,
|
||||
|
@ -37,12 +41,14 @@ impl World {
|
|||
self.chunks.get(&(position - IVec2::new(0, 1))),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
chunks: HashMap::new(),
|
||||
thread: WorldThreading::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
target: &mut Frame,
|
||||
|
@ -56,28 +62,39 @@ impl World {
|
|||
magnify_filter: MagnifySamplerFilter::Nearest,
|
||||
..Default::default()
|
||||
};
|
||||
let draw_parameters = DrawParameters {
|
||||
depth: Depth {
|
||||
test: DepthTest::IfLess,
|
||||
write: true,
|
||||
..Default::default()
|
||||
},
|
||||
//backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
|
||||
..Default::default()
|
||||
};
|
||||
for (&position, chunk) in &self.chunks {
|
||||
if let Some((_, vertex, index)) = &chunk.mesh {
|
||||
if let Some(mesh) = &chunk.mesh {
|
||||
target.draw(
|
||||
vertex,
|
||||
index,
|
||||
&mesh.vertex_buffer,
|
||||
&mesh.index_buffer,
|
||||
&programs.chunk,
|
||||
&uniform! {
|
||||
model: [
|
||||
[1., 0., 0., 0.],
|
||||
[0., 1., 0., 0.],
|
||||
[0., 0., 1., 0.],
|
||||
//[0., 0., 0., 1.0_f32]
|
||||
[(position.x * CHUNK_SIZE as i32) as f32, 0., (position.y * CHUNK_SIZE as i32) as f32, 1.0_f32]
|
||||
],
|
||||
view: view,
|
||||
persperctive: perspective,
|
||||
perspective: perspective,
|
||||
tex: Sampler(&assets.textures.block_atlas, sampler)
|
||||
},
|
||||
&Default::default()
|
||||
&draw_parameters
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -98,43 +115,49 @@ impl World {
|
|||
{
|
||||
//we only need mutable reference here:
|
||||
let chunk = self.chunks.get_mut(&position).unwrap();
|
||||
if x == 0 || z == 0 || x == render_dist || z == render_dist {
|
||||
if x == -render_dist || z == -render_dist || x == render_dist || z == render_dist {
|
||||
chunk.desired = ChunkState::Loaded;
|
||||
} else {
|
||||
chunk.desired = ChunkState::Rendered;
|
||||
}
|
||||
}
|
||||
//borrow chunk immutably
|
||||
let chunk = self.chunks.get(&position).unwrap();
|
||||
if matches!(chunk.state, ChunkState::Nothing) && matches!(chunk.desired, ChunkState::Loaded | ChunkState::Rendered) {
|
||||
self.thread.queue_load(position);
|
||||
} else if matches!(chunk.state, ChunkState::Loaded) && matches!(chunk.desired, ChunkState::Rendered) {
|
||||
fn all_some<'a>(x: [Option<&'a Chunk>; 4]) -> Option<[&'a Chunk; 4]> {
|
||||
Some([x[0]?, x[1]?, x[2]?, x[3]?])
|
||||
}
|
||||
if let Some(neighbors) = all_some(self.chunk_neighbors(chunk.position)) {
|
||||
if {
|
||||
neighbors[0].block_data.is_some() &&
|
||||
neighbors[1].block_data.is_some() &&
|
||||
neighbors[2].block_data.is_some() &&
|
||||
neighbors[3].block_data.is_some()
|
||||
} {
|
||||
self.thread.queue_mesh(
|
||||
position,
|
||||
chunk.block_data.clone().unwrap(),
|
||||
[
|
||||
neighbors[0].block_data.clone().unwrap(),
|
||||
neighbors[1].block_data.clone().unwrap(),
|
||||
neighbors[2].block_data.clone().unwrap(),
|
||||
neighbors[3].block_data.clone().unwrap(),
|
||||
]
|
||||
);
|
||||
if self.thread.task_amount() < MAX_TASKS {
|
||||
if matches!(chunk.state, ChunkState::Nothing) && matches!(chunk.desired, ChunkState::Loaded | ChunkState::Rendered) {
|
||||
self.thread.queue_load(position);
|
||||
self.chunks.get_mut(&position).unwrap().state = ChunkState::Loading;
|
||||
} else if matches!(chunk.state, ChunkState::Loaded) && matches!(chunk.desired, ChunkState::Rendered) {
|
||||
let mut state_changed = false;
|
||||
fn all_some<'a>(x: [Option<&'a Chunk>; 4]) -> Option<[&'a Chunk; 4]> {
|
||||
Some([x[0]?, x[1]?, x[2]?, x[3]?])
|
||||
}
|
||||
if let Some(neighbors) = all_some(self.chunk_neighbors(chunk.position)) {
|
||||
if {
|
||||
neighbors[0].block_data.is_some() &&
|
||||
neighbors[1].block_data.is_some() &&
|
||||
neighbors[2].block_data.is_some() &&
|
||||
neighbors[3].block_data.is_some()
|
||||
} {
|
||||
self.thread.queue_mesh(
|
||||
position,
|
||||
chunk.block_data.clone().unwrap(),
|
||||
[
|
||||
neighbors[0].block_data.clone().unwrap(),
|
||||
neighbors[1].block_data.clone().unwrap(),
|
||||
neighbors[2].block_data.clone().unwrap(),
|
||||
neighbors[3].block_data.clone().unwrap(),
|
||||
]
|
||||
);
|
||||
state_changed = true;
|
||||
}
|
||||
}
|
||||
if state_changed {
|
||||
self.chunks.get_mut(&position).unwrap().state = ChunkState::Rendering;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Unloads and state downgrades
|
||||
self.chunks.retain(|_, chunk| {
|
||||
match chunk.desired {
|
||||
|
|
|
@ -19,10 +19,16 @@ pub enum ChunkState {
|
|||
|
||||
pub type ChunkData = Box<[[[Block; CHUNK_SIZE]; CHUNK_HEIGHT]; CHUNK_SIZE]>;
|
||||
|
||||
pub struct ChunkMesh {
|
||||
pub is_dirty: bool,
|
||||
pub vertex_buffer: VertexBuffer<ChunkVertex>,
|
||||
pub index_buffer: IndexBuffer<u32>,
|
||||
}
|
||||
|
||||
pub struct Chunk {
|
||||
pub position: IVec2,
|
||||
pub block_data: Option<ChunkData>,
|
||||
pub mesh: Option<(bool, VertexBuffer<ChunkVertex>, IndexBuffer<u16>)>,
|
||||
pub mesh: Option<ChunkMesh>,
|
||||
pub state: ChunkState,
|
||||
pub desired: ChunkState,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use glium::{Display, VertexBuffer, IndexBuffer, index::PrimitiveType};
|
|||
use std::{mem, thread::{self, JoinHandle}};
|
||||
use hashbrown::HashMap;
|
||||
use super::chunk::{Chunk, ChunkData, ChunkState};
|
||||
use crate::game::shaders::chunk::Vertex as ChunkVertex;
|
||||
use crate::game::{shaders::chunk::Vertex as ChunkVertex, world::chunk::ChunkMesh};
|
||||
|
||||
mod world_gen;
|
||||
mod mesh_gen;
|
||||
|
@ -14,7 +14,7 @@ pub struct WorldThreading {
|
|||
//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<(Vec<ChunkVertex>, Vec<u16>)>>>,
|
||||
pub mesh_tasks: HashMap<IVec2, Option<JoinHandle<(Vec<ChunkVertex>, Vec<u32>)>>>,
|
||||
}
|
||||
impl WorldThreading {
|
||||
pub fn new() -> Self {
|
||||
|
@ -84,11 +84,11 @@ impl WorldThreading {
|
|||
let handle = mem::take(handle).unwrap();
|
||||
let (shape, index) = handle.join().unwrap();
|
||||
let chunk = chunks.get_mut(position).unwrap();
|
||||
chunk.mesh = Some((
|
||||
true,
|
||||
VertexBuffer::immutable(display, &shape).expect("Failed to build VertexBuffer"),
|
||||
IndexBuffer::immutable(display, PrimitiveType::TrianglesList, &index).expect("Failed to build IndexBuffer")
|
||||
));
|
||||
chunk.mesh = Some(ChunkMesh {
|
||||
is_dirty: false,
|
||||
vertex_buffer: VertexBuffer::new(display, &shape).expect("Failed to build VertexBuffer"),
|
||||
index_buffer: IndexBuffer::new(display, PrimitiveType::TrianglesList, &index).expect("Failed to build IndexBuffer")
|
||||
});
|
||||
chunk.state = ChunkState::Rendered;
|
||||
false
|
||||
});
|
||||
|
|
|
@ -1,15 +1,126 @@
|
|||
use glam::IVec2;
|
||||
use crate::game::world::chunk::ChunkData;
|
||||
use crate::game::shaders::chunk::Vertex as ChunkVertex;
|
||||
use glam::{IVec2, IVec3, Vec2, Vec3A, vec3a, vec2, ivec3};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use crate::game::{
|
||||
world::{
|
||||
POSITIVE_X_NEIGHBOR,
|
||||
NEGATIVE_X_NEIGHBOR,
|
||||
POSITIVE_Z_NEIGHBOR,
|
||||
NEGATIVE_Z_NEIGHBOR,
|
||||
chunk::{ChunkData, CHUNK_SIZE, CHUNK_HEIGHT}
|
||||
},
|
||||
shaders::chunk::Vertex,
|
||||
blocks::Block
|
||||
};
|
||||
|
||||
pub fn generate_mesh(position: IVec2, chunk_data: ChunkData, neighbors: [ChunkData; 4]) -> (Vec<ChunkVertex>, Vec<u16>) {
|
||||
let mut vertex = Vec::new();
|
||||
let mut index = Vec::new();
|
||||
vertex.push(ChunkVertex { position: [-0.5, -0.5, 0.], uv: [0., 0.], normal: [0., 1., 0.] });
|
||||
vertex.push(ChunkVertex { position: [ 0.0, 0.5, 0.], uv: [0., 1.], normal: [0., 1., 0.] });
|
||||
vertex.push(ChunkVertex { position: [ 0.5, -0.5, 0.], uv: [1., 1.], normal: [0., 1., 0.] });
|
||||
index.push(0);
|
||||
index.push(1);
|
||||
index.push(2);
|
||||
(vertex, index)
|
||||
#[repr(usize)]
|
||||
#[derive(Clone, Copy, Debug, EnumIter)]
|
||||
pub enum CubeFace {
|
||||
Top = 0,
|
||||
Front = 1,
|
||||
Left = 2,
|
||||
Right = 3,
|
||||
Back = 4,
|
||||
Bottom = 5,
|
||||
}
|
||||
const CUBE_FACE_VERTICES: [[Vec3A; 4]; 6] = [
|
||||
[vec3a(0., 1., 0.), vec3a(0., 1., 1.), vec3a(1., 1., 0.), vec3a(1., 1., 1.)],
|
||||
[vec3a(0., 0., 0.), vec3a(0., 1., 0.), vec3a(1., 0., 0.), vec3a(1., 1., 0.)],
|
||||
[vec3a(0., 0., 1.), vec3a(0., 1., 1.), vec3a(0., 0., 0.), vec3a(0., 1., 0.)],
|
||||
[vec3a(1., 0., 0.), vec3a(1., 1., 0.), vec3a(1., 0., 1.), vec3a(1., 1., 1.)],
|
||||
[vec3a(1., 0., 1.), vec3a(1., 1., 1.), vec3a(0., 0., 1.), vec3a(0., 1., 1.)],
|
||||
[vec3a(0., 0., 1.), vec3a(0., 0., 0.), vec3a(1., 0., 1.), vec3a(1., 0., 0.)]
|
||||
];
|
||||
pub const CUBE_FACE_NORMALS: [[f32; 3]; 6] = [
|
||||
[0., 1., 0.],
|
||||
[0., 0., -1.],
|
||||
[-1., 0., 0.],
|
||||
[1., 0., 0.],
|
||||
[0., 0., 1.],
|
||||
[0., -1., 0.]
|
||||
];
|
||||
pub const CUBE_FACE_INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3];
|
||||
|
||||
#[derive(Default)]
|
||||
struct MeshBuilder {
|
||||
vertex_buffer: Vec<Vertex>,
|
||||
index_buffer: Vec<u32>,
|
||||
idx_counter: u32,
|
||||
}
|
||||
impl MeshBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn add_face(&mut self, face: CubeFace, coord: IVec3, uvs: [Vec2; 4]) {
|
||||
let coord = coord.as_vec3a();
|
||||
let face_index = face as usize;
|
||||
|
||||
//Push vertexes
|
||||
let norm = CUBE_FACE_NORMALS[face_index];
|
||||
let vert = CUBE_FACE_VERTICES[face_index];
|
||||
self.vertex_buffer.reserve(4);
|
||||
for i in 0..4 {
|
||||
self.vertex_buffer.push(Vertex {
|
||||
position: (coord + vert[i]).to_array(),
|
||||
normal: norm,
|
||||
uv: uvs[i].to_array()
|
||||
});
|
||||
}
|
||||
|
||||
//Push indices
|
||||
self.index_buffer.extend_from_slice(&CUBE_FACE_INDICES.map(|x| x + self.idx_counter));
|
||||
self.idx_counter += 4;
|
||||
}
|
||||
|
||||
pub fn finish(self) -> (Vec<Vertex>, Vec<u32>) {
|
||||
(self.vertex_buffer, self.index_buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_mesh(position: IVec2, chunk_data: ChunkData, neighbors: [ChunkData; 4]) -> (Vec<Vertex>, Vec<u32>) {
|
||||
let get_block = |pos: IVec3| -> Block {
|
||||
if pos.x < 0 {
|
||||
neighbors[NEGATIVE_X_NEIGHBOR][(CHUNK_SIZE as i32 + pos.x) as usize][pos.y as usize][pos.z as usize]
|
||||
} else if pos.x >= CHUNK_SIZE as i32 {
|
||||
neighbors[POSITIVE_X_NEIGHBOR][pos.x as usize - CHUNK_SIZE as usize][pos.y as usize][pos.z as usize]
|
||||
} else if pos.z < 0 {
|
||||
neighbors[NEGATIVE_Z_NEIGHBOR][pos.x as usize][pos.y as usize][(CHUNK_SIZE as i32 + pos.z) as usize]
|
||||
} else if pos.z >= CHUNK_SIZE as i32 {
|
||||
neighbors[POSITIVE_Z_NEIGHBOR][pos.x as usize][pos.y as usize][pos.z as usize - CHUNK_SIZE as usize]
|
||||
} else {
|
||||
chunk_data[pos.x as usize][pos.y as usize][pos.z as usize]
|
||||
}
|
||||
};
|
||||
|
||||
let mut builer = MeshBuilder::new();
|
||||
|
||||
for x in 0..CHUNK_SIZE {
|
||||
for y in 0..CHUNK_HEIGHT {
|
||||
for z in 0..CHUNK_SIZE {
|
||||
let coord = ivec3(x as i32, y as i32, z as i32);
|
||||
if get_block(coord).descriptor().render.is_none() {
|
||||
continue
|
||||
}
|
||||
for face in CubeFace::iter() {
|
||||
let facing = Vec3A::from_array(CUBE_FACE_NORMALS[face as usize]).as_ivec3();
|
||||
let facing_coord = coord + facing;
|
||||
let show = {
|
||||
(facing_coord.y < 0) ||
|
||||
(facing_coord.y >= CHUNK_HEIGHT as i32) ||
|
||||
get_block(facing_coord).descriptor().render.is_none()
|
||||
};
|
||||
if show {
|
||||
builer.add_face(face, coord, [
|
||||
vec2(0., 0.),
|
||||
vec2(0., 1.),
|
||||
vec2(1., 0.),
|
||||
vec2(1., 1.),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builer.finish()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue