Implement chunk unloading, frustrum culling and some other minor optimisations
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
206f7857fa
commit
4bba01058b
12 changed files with 230 additions and 120 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,5 +4,6 @@
|
|||
/perf.data*
|
||||
/chunks/
|
||||
|
||||
GPUCache/
|
||||
profile.txt
|
||||
callgrind.out.*
|
||||
|
|
62
src/aabb.rs
62
src/aabb.rs
|
@ -1,49 +1,23 @@
|
|||
use cgmath::Vector3;
|
||||
use std::ops::{Div, Sub};
|
||||
use cgmath::Point3;
|
||||
|
||||
pub struct Aabb<T> {
|
||||
pub min: Vector3<T>,
|
||||
pub max: Vector3<T>,
|
||||
pub struct Aabb {
|
||||
pub min: Point3<f32>,
|
||||
pub max: Point3<f32>,
|
||||
}
|
||||
|
||||
impl<T: Ord + Copy + Sub<Output = T> + Div<Output = T>> Aabb<T> {
|
||||
pub fn intersects_ray(
|
||||
&self,
|
||||
ray_origin: Vector3<T>,
|
||||
ray_direction: Vector3<T>,
|
||||
) -> Option<(T, T)> {
|
||||
let mut t_min = (self.min.x - ray_origin.x) / ray_direction.x;
|
||||
let mut t_max = (self.max.x - ray_origin.x) / ray_direction.x;
|
||||
if t_min > t_max {
|
||||
std::mem::swap(&mut t_min, &mut t_max);
|
||||
}
|
||||
|
||||
let mut ty_min = (self.min.y - ray_origin.y) / ray_direction.y;
|
||||
let mut ty_max = (self.max.y - ray_origin.y) / ray_direction.y;
|
||||
if ty_min > ty_max {
|
||||
std::mem::swap(&mut ty_min, &mut ty_max);
|
||||
}
|
||||
|
||||
if t_min > ty_max || ty_min > t_max {
|
||||
return None;
|
||||
}
|
||||
|
||||
t_min = T::max(t_min, ty_min);
|
||||
t_max = T::min(t_min, ty_max);
|
||||
|
||||
let mut tz_min = (self.min.z - ray_origin.z) / ray_direction.z;
|
||||
let mut tz_max = (self.max.z - ray_origin.z) / ray_direction.z;
|
||||
if tz_min > tz_max {
|
||||
std::mem::swap(&mut tz_min, &mut tz_max);
|
||||
}
|
||||
|
||||
if t_min > tz_max || tz_min > t_max {
|
||||
return None;
|
||||
}
|
||||
|
||||
t_min = T::max(t_min, tz_min);
|
||||
t_max = T::max(t_max, tz_max);
|
||||
|
||||
Some((t_min, t_max))
|
||||
impl Aabb {
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
(self.min.x <= other.max.x && self.max.x >= other.min.x)
|
||||
&& (self.min.y <= other.max.y && self.max.y >= other.min.y)
|
||||
&& (self.min.z <= other.max.z && self.max.z >= other.min.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Aabb {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
min: Point3::new(0.0, 0.0, 0.0),
|
||||
max: Point3::new(0.0, 0.0, 0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use cgmath::{Matrix4, Point3, Rad, Vector3};
|
||||
|
||||
use crate::aabb::Aabb;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const OPENGL_TO_WGPU_MATRIX: Matrix4<f32> = Matrix4::new(
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
|
@ -10,11 +8,6 @@ pub const OPENGL_TO_WGPU_MATRIX: Matrix4<f32> = Matrix4::new(
|
|||
0.0, 0.0, 0.5, 1.0,
|
||||
);
|
||||
|
||||
const AABB: Aabb<f32> = Aabb {
|
||||
min: Vector3::new(-0.3, 1.62, -0.3),
|
||||
max: Vector3::new(0.3, 1.8 - 1.62, 0.3),
|
||||
};
|
||||
|
||||
pub struct Camera {
|
||||
pub position: Point3<f32>,
|
||||
pub yaw: Rad<f32>,
|
||||
|
|
32
src/chunk.rs
32
src/chunk.rs
|
@ -1,6 +1,12 @@
|
|||
use std::{collections::VecDeque, usize};
|
||||
|
||||
use crate::{geometry::Geometry, quad::Quad, vertex::BlockVertex};
|
||||
use crate::{
|
||||
aabb::Aabb,
|
||||
geometry::{Geometry, GeometryBuffers},
|
||||
quad::Quad,
|
||||
vertex::BlockVertex,
|
||||
view::View,
|
||||
};
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use cgmath::{Point3, Vector3};
|
||||
use noise::utils::{NoiseMapBuilder, PlaneMapBuilder};
|
||||
|
@ -68,9 +74,18 @@ pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize;
|
|||
|
||||
type ChunkBlocks = [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE];
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Chunk {
|
||||
pub blocks: ChunkBlocks,
|
||||
pub buffers: Option<GeometryBuffers<u16>>,
|
||||
}
|
||||
|
||||
impl Default for Chunk {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
blocks: [[[None; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE],
|
||||
buffers: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Chunk {
|
||||
|
@ -179,7 +194,7 @@ impl Chunk {
|
|||
});
|
||||
}
|
||||
|
||||
if dirt_max >= 0 && dirt_max < CHUNK_ISIZE {
|
||||
if (0..CHUNK_ISIZE).contains(&dirt_max) {
|
||||
self.blocks[dirt_max as usize][z][x] = Some(Block {
|
||||
block_type: BlockType::Grass,
|
||||
});
|
||||
|
@ -410,4 +425,15 @@ impl Chunk {
|
|||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_visible(&self, position: Point3<isize>, view: &View) -> bool {
|
||||
let aabb = Aabb {
|
||||
min: position.cast().unwrap(),
|
||||
max: (position + Vector3::new(CHUNK_ISIZE, CHUNK_ISIZE, CHUNK_ISIZE))
|
||||
.cast()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
aabb.intersects(&view.aabb)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ impl<I: bytemuck::Pod> GeometryBuffers<I> {
|
|||
}
|
||||
|
||||
impl GeometryBuffers<u16> {
|
||||
pub fn set_buffers<'a>(&'a self, render_pass: &mut RenderPass<'a>) {
|
||||
pub fn apply_buffers<'a>(&'a self, render_pass: &mut RenderPass<'a>) {
|
||||
render_pass.set_vertex_buffer(0, self.vertices.slice(..));
|
||||
render_pass.set_index_buffer(self.indices.slice(..), wgpu::IndexFormat::Uint16);
|
||||
}
|
||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -2,6 +2,7 @@ mod aabb;
|
|||
mod camera;
|
||||
mod chunk;
|
||||
mod geometry;
|
||||
mod npc;
|
||||
mod quad;
|
||||
mod render_context;
|
||||
mod state;
|
||||
|
@ -11,7 +12,6 @@ mod time;
|
|||
mod vertex;
|
||||
mod view;
|
||||
mod world;
|
||||
mod npc;
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
use wgpu::SwapChainError;
|
||||
|
@ -133,9 +133,21 @@ fn main() {
|
|||
let fps_max = 1_000_000 / frametime_min.as_micros();
|
||||
let fps_min = 1_000_000 / frametime_max.as_micros();
|
||||
|
||||
print!("{:>4} frames | ", frames);
|
||||
print!(
|
||||
"frametime avg={:>5.2}ms min={:>5.2}ms max={:>5.2}ms | ",
|
||||
frametime.as_secs_f32() * 1000.0,
|
||||
frametime_min.as_secs_f32() * 1000.0,
|
||||
frametime_max.as_secs_f32() * 1000.0,
|
||||
);
|
||||
print!(
|
||||
"fps avg={:>5} min={:>5} max={:>5} | ",
|
||||
fps, fps_min, fps_max
|
||||
);
|
||||
println!(
|
||||
"{:>4} frames | frametime avg={:>5.2}ms min={:>5.2}ms max={:>5.2}ms | fps avg={:>5} min={:>5} max={:>5} | {:>8} tris",
|
||||
frames, frametime.as_secs_f32() * 1000.0, frametime_min.as_secs_f32() * 1000.0, frametime_max.as_secs_f32() * 1000.0, fps, fps_min, fps_max, triangle_count,
|
||||
"{:>8} tris | {:>5} chunks",
|
||||
triangle_count,
|
||||
state.world_state.world.chunks.len()
|
||||
);
|
||||
|
||||
elapsed = Duration::from_secs(0);
|
||||
|
@ -150,17 +162,18 @@ fn main() {
|
|||
state.update(dt);
|
||||
|
||||
match state.render() {
|
||||
Err(root_cause) =>
|
||||
match root_cause.downcast_ref::<SwapChainError>() {
|
||||
Err(root_cause) => match root_cause.downcast_ref::<SwapChainError>() {
|
||||
// Recreate the swap_chain if lost
|
||||
Some(wgpu::SwapChainError::Lost) => state.resize(state.window_size),
|
||||
// The system is out of memory, we should probably quit
|
||||
Some(wgpu::SwapChainError::OutOfMemory) => *control_flow = ControlFlow::Exit,
|
||||
Some(wgpu::SwapChainError::OutOfMemory) => {
|
||||
*control_flow = ControlFlow::Exit
|
||||
}
|
||||
// All other errors (Outdated, Timeout) should be resolved by the next frame
|
||||
Some(_) | None => eprintln!("{:?}", root_cause),
|
||||
}
|
||||
},
|
||||
|
||||
Ok(v) => triangle_count = v
|
||||
Ok(v) => triangle_count = v,
|
||||
}
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
|
|
|
@ -55,13 +55,13 @@ impl Npc {
|
|||
}
|
||||
}
|
||||
|
||||
return Self {
|
||||
Self {
|
||||
position,
|
||||
scale,
|
||||
rotation,
|
||||
geometry: Geometry::new(vertices, indices),
|
||||
geometry_buffers: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_geometry(&mut self, render_context: &RenderContext) {
|
||||
|
|
|
@ -124,19 +124,19 @@ impl HudState {
|
|||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
|
||||
// Render the HUD elements
|
||||
self.hud_geometry_buffers.set_buffers(&mut render_pass);
|
||||
self.hud_geometry_buffers.apply_buffers(&mut render_pass);
|
||||
render_pass.set_bind_group(0, &self.texture_bind_group, &[]);
|
||||
self.hud_geometry_buffers.draw_indexed(&mut render_pass);
|
||||
render_pass.draw_indexed(0..self.hud_geometry_buffers.index_count as u32, 0, 0..1);
|
||||
|
||||
// Render the FPS text
|
||||
self.fps_geometry_buffers.set_buffers(&mut render_pass);
|
||||
self.fps_geometry_buffers.apply_buffers(&mut render_pass);
|
||||
render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]);
|
||||
self.fps_geometry_buffers.draw_indexed(&mut render_pass);
|
||||
|
||||
// Render the coordinates text
|
||||
self.coordinates_geometry_buffers
|
||||
.set_buffers(&mut render_pass);
|
||||
.apply_buffers(&mut render_pass);
|
||||
render_pass.set_bind_group(0, &self.text_renderer.bind_group, &[]);
|
||||
self.coordinates_geometry_buffers
|
||||
.draw_indexed(&mut render_pass);
|
||||
|
|
|
@ -28,7 +28,7 @@ pub const PRIMITIVE_STATE: wgpu::PrimitiveState = wgpu::PrimitiveState {
|
|||
pub struct State {
|
||||
pub window_size: PhysicalSize<u32>,
|
||||
render_context: RenderContext,
|
||||
world_state: WorldState,
|
||||
pub world_state: WorldState,
|
||||
hud_state: HudState,
|
||||
|
||||
pub mouse_grabbed: bool,
|
||||
|
|
|
@ -67,7 +67,7 @@ impl WorldState {
|
|||
render_context.swap_chain_descriptor.height,
|
||||
cgmath::Deg(45.0),
|
||||
0.1,
|
||||
5000.0,
|
||||
300.0,
|
||||
);
|
||||
|
||||
(camera, projection)
|
||||
|
@ -85,7 +85,7 @@ impl WorldState {
|
|||
.device
|
||||
.create_buffer_init(&BufferInitDescriptor {
|
||||
label: Some("view_buffer"),
|
||||
contents: bytemuck::cast_slice(&[view]),
|
||||
contents: bytemuck::cast_slice(&[view.to_raw()]),
|
||||
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
||||
});
|
||||
|
||||
|
@ -324,7 +324,7 @@ impl WorldState {
|
|||
render_pass.set_bind_group(1, &self.view_bind_group, &[]);
|
||||
render_pass.set_bind_group(2, &self.time_bind_group, &[]);
|
||||
|
||||
triangle_count += self.world.render(&mut render_pass, &self.camera);
|
||||
triangle_count += self.world.render(&mut render_pass, &self.view);
|
||||
|
||||
triangle_count
|
||||
}
|
||||
|
@ -428,13 +428,15 @@ impl WorldState {
|
|||
pub fn update(&mut self, dt: Duration, render_context: &RenderContext) {
|
||||
self.update_position(dt);
|
||||
|
||||
self.world.update(render_context, &self.camera);
|
||||
self.world.update(dt, render_context, &self.camera);
|
||||
|
||||
self.view
|
||||
.update_view_projection(&self.camera, &self.projection);
|
||||
render_context
|
||||
.queue
|
||||
.write_buffer(&self.view_buffer, 0, bytemuck::cast_slice(&[self.view]));
|
||||
render_context.queue.write_buffer(
|
||||
&self.view_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[self.view.to_raw()]),
|
||||
);
|
||||
|
||||
self.time.time += dt.as_secs_f32();
|
||||
render_context.queue.write_buffer(
|
||||
|
|
69
src/view.rs
69
src/view.rs
|
@ -1,24 +1,73 @@
|
|||
use cgmath::SquareMatrix;
|
||||
use cgmath::{EuclideanSpace, Matrix4, Point3, SquareMatrix, Vector4, Zero};
|
||||
|
||||
use crate::camera::{Camera, Projection};
|
||||
use crate::{
|
||||
aabb::Aabb,
|
||||
camera::{Camera, Projection, OPENGL_TO_WGPU_MATRIX},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct View {
|
||||
view_position: [f32; 4],
|
||||
view_projection: [[f32; 4]; 4],
|
||||
view_position: Vector4<f32>,
|
||||
view_projection: Matrix4<f32>,
|
||||
pub aabb: Aabb,
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn to_raw(&self) -> ViewRaw {
|
||||
ViewRaw {
|
||||
view_position: self.view_position.into(),
|
||||
view_projection: self.view_projection.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
view_position: [0.0; 4],
|
||||
view_projection: cgmath::Matrix4::identity().into(),
|
||||
view_position: Vector4::zero(),
|
||||
view_projection: Matrix4::identity(),
|
||||
aabb: Aabb::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_view_projection(&mut self, camera: &Camera, projection: &Projection) {
|
||||
self.view_position = camera.position.to_homogeneous().into();
|
||||
self.view_projection = (projection.calculate_matrix() * camera.calculate_matrix()).into();
|
||||
self.view_position = camera.position.to_homogeneous();
|
||||
self.view_projection = projection.calculate_matrix() * camera.calculate_matrix();
|
||||
self.aabb = self.frustrum_aabb();
|
||||
}
|
||||
|
||||
fn frustrum_aabb(&self) -> Aabb {
|
||||
let projection = OPENGL_TO_WGPU_MATRIX.invert().unwrap() * self.view_projection;
|
||||
let inverse_matrix = projection.invert().unwrap();
|
||||
|
||||
let corners = &[
|
||||
Vector4::new(-1.0, -1.0, 1.0, 1.0),
|
||||
Vector4::new(-1.0, -1.0, -1.0, 1.0),
|
||||
Vector4::new(-1.0, 1.0, 1.0, 1.0),
|
||||
Vector4::new(-1.0, 1.0, -1.0, 1.0),
|
||||
Vector4::new(1.0, -1.0, 1.0, 1.0),
|
||||
Vector4::new(1.0, -1.0, -1.0, 1.0),
|
||||
Vector4::new(1.0, 1.0, 1.0, 1.0),
|
||||
Vector4::new(1.0, 1.0, -1.0, 1.0),
|
||||
];
|
||||
|
||||
let mut min = Vector4::new(f32::INFINITY, f32::INFINITY, f32::INFINITY, 1.0);
|
||||
let mut max = Vector4::new(0.0, 0.0, 0.0, 1.0);
|
||||
for &corner in corners {
|
||||
let corner = inverse_matrix * corner;
|
||||
let corner = corner / corner.w;
|
||||
|
||||
min = min.zip(corner, f32::min);
|
||||
max = max.zip(corner, f32::max);
|
||||
}
|
||||
|
||||
Aabb {
|
||||
min: Point3::from_vec(min.truncate()),
|
||||
max: Point3::from_vec(max.truncate()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct ViewRaw {
|
||||
view_position: [f32; 4],
|
||||
view_projection: [[f32; 4]; 4],
|
||||
}
|
||||
|
|
106
src/world.rs
106
src/world.rs
|
@ -1,4 +1,4 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
use std::{collections::VecDeque, time::Duration};
|
||||
|
||||
use crate::{
|
||||
camera::Camera,
|
||||
|
@ -6,30 +6,34 @@ use crate::{
|
|||
geometry::GeometryBuffers,
|
||||
npc::Npc,
|
||||
render_context::RenderContext,
|
||||
view::View,
|
||||
};
|
||||
use ahash::AHashMap;
|
||||
use cgmath::{EuclideanSpace, InnerSpace, Point3, Vector2, Vector3};
|
||||
use cgmath::{EuclideanSpace, InnerSpace, Point3, Vector3};
|
||||
use wgpu::{BufferUsage, RenderPass};
|
||||
|
||||
pub struct World {
|
||||
pub chunks: HashMap<Point3<isize>, Chunk>,
|
||||
pub npc: Npc,
|
||||
|
||||
pub chunks: AHashMap<Point3<isize>, Chunk>,
|
||||
pub chunk_database: sled::Db,
|
||||
pub chunk_save_queue: VecDeque<Point3<isize>>,
|
||||
pub chunk_save_queue: VecDeque<(Point3<isize>, bool)>,
|
||||
pub chunk_load_queue: VecDeque<Point3<isize>>,
|
||||
pub chunk_generate_queue: VecDeque<Point3<isize>>,
|
||||
pub chunk_buffers: AHashMap<Point3<isize>, GeometryBuffers<u16>>,
|
||||
|
||||
pub highlighted: Option<(Point3<isize>, Vector3<i32>)>,
|
||||
|
||||
pub unload_timer: Duration,
|
||||
}
|
||||
|
||||
pub const RENDER_DISTANCE: isize = 8;
|
||||
pub const WORLD_HEIGHT: isize = 16 * 16 / CHUNK_ISIZE;
|
||||
|
||||
const DEBUG_IO: bool = false;
|
||||
|
||||
impl World {
|
||||
pub fn new() -> Self {
|
||||
let chunks = HashMap::new();
|
||||
let chunks = AHashMap::new();
|
||||
let npc = Npc::load();
|
||||
|
||||
let chunk_database = sled::Config::new()
|
||||
|
@ -47,13 +51,14 @@ impl World {
|
|||
chunk_load_queue: VecDeque::new(),
|
||||
chunk_save_queue: VecDeque::new(),
|
||||
chunk_generate_queue: VecDeque::new(),
|
||||
chunk_buffers: AHashMap::new(),
|
||||
|
||||
highlighted: None,
|
||||
|
||||
unload_timer: Duration::new(0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, render_context: &RenderContext, camera: &Camera) {
|
||||
pub fn update(&mut self, dt: Duration, render_context: &RenderContext, camera: &Camera) {
|
||||
if let Some(position) = self.chunk_load_queue.pop_front() {
|
||||
let chunk = self.chunks.entry(position).or_default();
|
||||
match chunk.load(position, &self.chunk_database) {
|
||||
|
@ -62,20 +67,33 @@ impl World {
|
|||
}
|
||||
Ok(true) => {
|
||||
self.update_chunk_geometry(render_context, position);
|
||||
self.chunk_save_queue.push_back(position);
|
||||
// println!("Generated chunk {:?}", position);
|
||||
self.enqueue_chunk_save(position, false);
|
||||
if DEBUG_IO {
|
||||
println!("Generated chunk {:?}", position);
|
||||
}
|
||||
}
|
||||
Ok(false) => {
|
||||
self.update_chunk_geometry(render_context, position);
|
||||
// println!("Loaded chunk {:?}", position);
|
||||
if DEBUG_IO {
|
||||
println!("Loaded chunk {:?}", position);
|
||||
}
|
||||
}
|
||||
} else if let Some(position) = self.chunk_save_queue.pop_front() {
|
||||
let chunk = self.chunks.get(&position).unwrap();
|
||||
}
|
||||
} else if let Some((position, unload)) = self.chunk_save_queue.pop_front() {
|
||||
if let Some(chunk) = self.chunks.get(&position) {
|
||||
if let Err(err) = chunk.save(position, &self.chunk_database) {
|
||||
eprintln!("Failed to save chunk {:?}: {:?}", position, err);
|
||||
} else if unload {
|
||||
self.chunks.remove(&position);
|
||||
|
||||
if DEBUG_IO {
|
||||
println!("Saved and unloaded chunk {:?}", position);
|
||||
}
|
||||
} else if DEBUG_IO {
|
||||
println!("Saved chunk {:?}", position);
|
||||
}
|
||||
} else {
|
||||
// println!("Saved chunk {:?}", position);
|
||||
eprintln!("Tried to save unloaded chunk {:?}", position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,23 +120,43 @@ impl World {
|
|||
});
|
||||
|
||||
self.chunk_load_queue.extend(load_queue);
|
||||
|
||||
// Unload chunks that are far away
|
||||
self.unload_timer += dt;
|
||||
if self.unload_timer.as_secs() >= 10 {
|
||||
self.unload_timer = Duration::new(0, 0);
|
||||
|
||||
let camera_pos = camera.position.to_vec();
|
||||
let unload_distance = (RENDER_DISTANCE * CHUNK_ISIZE) as f32 * 1.5;
|
||||
|
||||
let mut unload_chunks = Vec::new();
|
||||
for point in self.chunks.keys() {
|
||||
let pos: Point3<f32> = (point * CHUNK_ISIZE).cast().unwrap();
|
||||
if (pos.x - camera_pos.x).abs() > unload_distance
|
||||
|| (pos.z - camera_pos.z).abs() > unload_distance
|
||||
{
|
||||
unload_chunks.push(*point);
|
||||
}
|
||||
}
|
||||
for point in unload_chunks {
|
||||
self.enqueue_chunk_save(point, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render<'a>(&'a self, render_pass: &mut RenderPass<'a>, camera: &Camera) -> usize {
|
||||
let camera_pos = camera.position.to_vec();
|
||||
let camera_pos = Vector2::new(camera_pos.x, camera_pos.z);
|
||||
pub fn render<'a>(&'a self, render_pass: &mut RenderPass<'a>, view: &View) -> usize {
|
||||
let mut triangle_count = 0;
|
||||
|
||||
for (position, buffers) in &self.chunk_buffers {
|
||||
let pos = (position * CHUNK_ISIZE).cast().unwrap();
|
||||
let pos = Vector2::new(pos.x, pos.z);
|
||||
if (pos - camera_pos).magnitude() > 300.0 {
|
||||
for (position, chunk) in &self.chunks {
|
||||
if !chunk.is_visible(position * CHUNK_ISIZE, &view) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buffers.set_buffers(render_pass);
|
||||
if let Some(buffers) = chunk.buffers.as_ref() {
|
||||
buffers.apply_buffers(render_pass);
|
||||
triangle_count += buffers.draw_indexed(render_pass);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let buffers = self.npc.geometry_buffers.as_ref().unwrap();
|
||||
|
@ -129,21 +167,35 @@ impl World {
|
|||
triangle_count
|
||||
}
|
||||
|
||||
pub fn enqueue_chunk_save(&mut self, position: Point3<isize>, unload: bool) {
|
||||
if let Some((_, unload_)) = self
|
||||
.chunk_save_queue
|
||||
.iter_mut()
|
||||
.find(|(pos, _)| pos == &position)
|
||||
{
|
||||
*unload_ = *unload_ || unload;
|
||||
} else {
|
||||
self.chunk_save_queue.push_back((position, unload));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_chunk_geometry(
|
||||
&mut self,
|
||||
render_context: &RenderContext,
|
||||
chunk_position: Point3<isize>,
|
||||
) {
|
||||
let chunk = &mut self.chunks.get(&chunk_position).unwrap();
|
||||
let chunk = self.chunks.get_mut(&chunk_position).unwrap();
|
||||
let offset = chunk_position * CHUNK_ISIZE;
|
||||
let geometry = chunk.to_geometry(
|
||||
offset,
|
||||
World::highlighted_for_chunk(self.highlighted, &chunk_position).as_ref(),
|
||||
);
|
||||
|
||||
let buffers =
|
||||
GeometryBuffers::from_geometry(render_context, &geometry, BufferUsage::empty());
|
||||
self.chunk_buffers.insert(chunk_position, buffers);
|
||||
chunk.buffers = Some(GeometryBuffers::from_geometry(
|
||||
render_context,
|
||||
&geometry,
|
||||
BufferUsage::empty(),
|
||||
));
|
||||
}
|
||||
|
||||
fn update_highlight(&mut self, render_context: &RenderContext, camera: &Camera) {
|
||||
|
@ -247,7 +299,7 @@ impl World {
|
|||
}
|
||||
|
||||
self.chunk_save_queue
|
||||
.push_back(chunk_position / CHUNK_ISIZE);
|
||||
.push_back((chunk_position / CHUNK_ISIZE, false));
|
||||
}
|
||||
|
||||
fn calc_scale(vector: Vector3<f32>, scalar: f32) -> f32 {
|
||||
|
|
Loading…
Reference in a new issue