+24
Cargo.lock
+24
Cargo.lock
···
254
254
"rusqlite",
255
255
"thiserror 2.0.17",
256
256
"tobj",
257
+
"tokio",
257
258
"wasm-bindgen",
258
259
"wasm-bindgen-futures",
259
260
"web-sys",
···
2591
2592
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
2592
2593
2593
2594
[[package]]
2595
+
name = "signal-hook-registry"
2596
+
version = "1.4.7"
2597
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2598
+
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
2599
+
dependencies = [
2600
+
"libc",
2601
+
]
2602
+
2603
+
[[package]]
2594
2604
name = "simd-adler32"
2595
2605
version = "0.3.8"
2596
2606
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2880
2890
"bytes",
2881
2891
"libc",
2882
2892
"mio",
2893
+
"parking_lot",
2883
2894
"pin-project-lite",
2895
+
"signal-hook-registry",
2884
2896
"socket2",
2897
+
"tokio-macros",
2885
2898
"windows-sys 0.61.2",
2899
+
]
2900
+
2901
+
[[package]]
2902
+
name = "tokio-macros"
2903
+
version = "2.6.0"
2904
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2905
+
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
2906
+
dependencies = [
2907
+
"proc-macro2",
2908
+
"quote",
2909
+
"syn",
2886
2910
]
2887
2911
2888
2912
[[package]]
+1
Cargo.toml
+1
Cargo.toml
+3
-37
src/app.rs
+3
-37
src/app.rs
···
1
1
use crate::world::chunk::Chunk;
2
+
use crate::world::map::WorldMap;
2
3
use crate::{
3
-
concurrency::GameThread,
4
4
gfx::{Gfx, GfxBuilder, MaybeGfx},
5
5
gui::EguiRenderer,
6
6
world::World,
···
32
32
window: Option<Arc<Window>>,
33
33
egui: Option<EguiRenderer>,
34
34
world: Arc<Mutex<World>>,
35
-
world_update_thread: GameThread<(), (i32, i32, i32)>,
36
35
last_render_time: instant::Instant,
37
36
conn: ConnectionOnlyOnNative,
38
37
}
···
40
39
impl Application {
41
40
pub fn new(event_loop: &EventLoop<Gfx>, title: &str, conn: Connection) -> Self {
42
41
let world = Arc::new(Mutex::new(World {
43
-
map: Default::default(),
42
+
map: WorldMap::new(),
44
43
dirty: false,
45
44
}));
46
45
Self {
···
49
48
window: None,
50
49
egui: None,
51
50
world: world.clone(),
52
-
world_update_thread: {
53
-
let (tx, rx) = channel();
54
-
55
-
let thread = spawn(move || {
56
-
let w = world;
57
-
//let mut conn = rusqlite::Connection::open("./save.sqlite").unwrap();
58
-
59
-
loop {
60
-
println!("Looping!");
61
-
62
-
// TODO: Why is this in a separate thread? Analyse.
63
-
// This has "temporarily" been disabled in gfx/camera.rs.
64
-
let Ok((x, y, z)) = rx.recv() else {
65
-
break;
66
-
};
67
-
68
-
let Ok(ref mut w) = w.lock() else {
69
-
log::error!("Poisoned mutex?");
70
-
break;
71
-
};
72
-
73
-
w.map.focus = ivec3(x, y, z);
74
-
w.dirty = true;
75
-
}
76
-
});
77
-
78
-
GameThread::new(thread, tx)
79
-
},
80
51
last_render_time: instant::Instant::now(),
81
52
conn,
82
53
}
···
256
227
) {
257
228
Ok(_) => {
258
229
// TODO CITE https://github.com/kaphula/winit-egui-wgpu-template/blob/master/src/app.rs#L3
259
-
gfx.update(
260
-
self.world.clone(),
261
-
&mut self.conn,
262
-
&mut self.world_update_thread,
263
-
dt,
264
-
);
230
+
gfx.update(self.world.clone(), &mut self.conn, dt);
265
231
}
266
232
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
267
233
gfx.resize(window.inner_size());
-17
src/concurrency.rs
-17
src/concurrency.rs
···
1
-
use std::sync::mpsc::Sender;
2
-
use std::thread::JoinHandle;
3
-
4
-
pub struct GameThread<T, U> {
5
-
pub join_handle: JoinHandle<T>,
6
-
pub tx: Sender<U>,
7
-
}
8
-
9
-
impl<T, U> GameThread<T, U> {
10
-
pub fn new(join_handle: JoinHandle<T>, tx: Sender<U>) -> Self {
11
-
GameThread { join_handle, tx }
12
-
}
13
-
14
-
// pub fn fulfil(&mut self, join_handle: JoinHandle<T>) {
15
-
// self.join_handle = Some(join_handle);
16
-
// }
17
-
}
+39
-84
src/gfx.rs
+39
-84
src/gfx.rs
···
4
4
mod resources;
5
5
mod texture;
6
6
7
-
use crate::concurrency::GameThread;
8
-
use crate::gfx::camera::CameraUniform;
7
+
use crate::gfx::camera::{Camera, CameraUniform};
9
8
use crate::gfx::model::DrawLight;
10
-
use crate::world::chunk::{sl3get, sl3get_opt, CHUNK_SIZE};
9
+
use crate::ivec3_product;
10
+
use crate::world::chunk::{sl3get, sl3get_opt, Chunk, CHUNK_SIZE};
11
11
use crate::{
12
12
app::WASM_WIN_SIZE,
13
13
gfx::model::Vertex,
···
344
344
345
345
// MAP LOAD
346
346
347
-
let mut map = crate::world::map::WorldMap::default();
347
+
let mut map = WorldMap::new();
348
348
349
-
let instances = Self::remake_instance_buf(&mut map);
349
+
let instances = Self::remake_instance_buf(&camera_state.object, &mut map);
350
350
351
351
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
352
352
let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
···
655
655
);
656
656
}
657
657
658
-
fn remake_instance_buf(map: &mut WorldMap) -> Vec<Instance> {
658
+
fn remake_instance_buf(camera: &Camera, _map: &mut WorldMap) -> Vec<Instance> {
659
659
let mut instances = vec![];
660
660
661
-
const SPACE_BETWEEN: f32 = 2.0;
661
+
// WGPU Crashes with an empty instance buffer, add a small item out of camera vfar
662
+
if instances.is_empty() {
663
+
instances.push(Instance {
664
+
position: Vec3::splat(9999.),
665
+
rotation: Quat::from_axis_angle(Vec3::Y, 0.0),
666
+
})
667
+
}
668
+
instances
669
+
}
662
670
663
-
let mut conn = rusqlite::Connection::open("./save.sqlite").unwrap();
671
+
pub fn update_instance_buf(&mut self, map: &mut WorldMap) {
672
+
let mut instances = self.object.instances.clone();
673
+
let camera = &self.camera.object;
664
674
665
-
for (coords, chunk) in map.chunks_around_focus(&mut conn, 5).iter() {
666
-
let _3diter = itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2);
675
+
const RENDER_RADIUS: i32 = 1;
667
676
668
-
let mut i = _3diter
669
-
.filter_map(|(x, y, z)| {
670
-
if let BlockKind::Air = sl3get(&chunk.blocks, x, y, z) {
671
-
return None;
672
-
}
677
+
//println!("{:?}", camera.chunk_pos());
673
678
674
-
// lookup if node is surrounded by nodes
675
-
let mut do_not_occlude = false;
676
-
let mut sum = vec![];
679
+
let lower = (camera.chunk_pos() - IVec3::splat(RENDER_RADIUS)).with_y(-1);
680
+
let higher = (camera.chunk_pos() + IVec3::splat(RENDER_RADIUS)).with_y(1);
677
681
678
-
for (x_, y_, z_) in [
679
-
(-1_i32, 0_i32, 0_i32),
680
-
(1, 0, 0),
681
-
(0, -1, 0),
682
-
(0, 1, 0),
683
-
(0, 0, -1),
684
-
(0, 0, 1),
685
-
] {
686
-
if x == 0
687
-
|| y == 0
688
-
|| z == 0
689
-
|| x == CHUNK_SIZE.0 - 1
690
-
|| y == CHUNK_SIZE.2 - 1
691
-
|| z == CHUNK_SIZE.1 - 1
692
-
{
693
-
do_not_occlude = true;
694
-
break;
695
-
}
682
+
// poke!
683
+
ivec3_product(lower, higher).for_each(|idx| map.request_chunk_load(idx));
696
684
697
-
let (x, y, z) = (x as i32 + x_, y as i32 + y_, z as i32 + z_);
698
-
if x < 0 || y < 0 || z < 0
699
-
// || x == CHUNK_SIZE.0 as i32 - 1
700
-
// || y == CHUNK_SIZE.2 as i32 - 1
701
-
// || z == CHUNK_SIZE.1 as i32 - 1
702
-
{
703
-
continue;
704
-
}
685
+
while let Ok(idx) = map.response_rx.try_recv() {
686
+
println!("GFX: got {idx}");
687
+
let chunk = map.get_chunk(idx).expect("it said it was ready!");
705
688
706
-
if let Some(block) =
707
-
sl3get_opt(&chunk.blocks, x as usize, y as usize, z as usize)
708
-
{
709
-
sum.push(block)
710
-
}
711
-
}
689
+
let mut ids = chunk.indices.clone();
690
+
instances.append(&mut ids);
691
+
}
692
+
// while let Some(idx) = map.try_chunk() {
693
+
// }
712
694
713
-
if !do_not_occlude && sum.iter().all(|b| *b == BlockKind::Brick) {
714
-
return None;
715
-
}
695
+
// for (coords, chunk) in map.chunks_around_focus(&mut conn, 5).iter() {
716
696
717
-
let chunk_offset = coords.as_vec3() * (SPACE_BETWEEN * CHUNK_SIZE.0 as f32);
718
-
719
-
let mapping = |n| SPACE_BETWEEN * (n as f32 - CHUNK_SIZE.0 as f32 / 2.0);
720
-
let position = vec3(
721
-
mapping(x) + chunk_offset.x,
722
-
mapping(y) + chunk_offset.y,
723
-
mapping(z) + chunk_offset.z,
724
-
);
725
-
726
-
let rotation = Quat::from_axis_angle(Vec3::Y, 0.0);
727
-
728
-
Some(Instance { position, rotation })
729
-
})
730
-
.collect::<Vec<_>>();
731
-
732
-
instances.append(&mut i);
733
-
}
697
+
// }
734
698
735
699
// WGPU Crashes with an empty instance buffer, add a small item out of camera vfar
736
700
if instances.is_empty() {
···
739
703
rotation: Quat::from_axis_angle(Vec3::Y, 0.0),
740
704
})
741
705
}
742
-
instances
743
-
}
744
-
745
-
pub fn update_instance_buf(&mut self, map: &mut WorldMap) {
746
-
let instances = Self::remake_instance_buf(map);
747
706
748
707
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
749
708
let instance_buffer = self
···
912
871
&mut self,
913
872
world: Arc<Mutex<World>>,
914
873
conn: &mut ConnectionOnlyOnNative,
915
-
thread: &mut GameThread<(), (i32, i32, i32)>,
916
874
dt: instant::Duration,
917
875
) {
918
876
// Camera update
919
-
self.camera.controller.update_camera(
920
-
&mut self.camera.object,
921
-
dt,
922
-
world.clone(),
923
-
conn,
924
-
thread,
925
-
);
877
+
self.camera
878
+
.controller
879
+
.update_camera(&mut self.camera.object, dt, world.clone(), conn);
926
880
self.camera
927
881
.uniform
928
882
.update_view_proj(&self.camera.object, &self.camera.projection);
···
949
903
);
950
904
951
905
let mut world = world.lock().unwrap();
906
+
self.update_instance_buf(&mut world.map);
907
+
952
908
// Object update
953
909
if world.dirty {
954
-
self.update_instance_buf(&mut world.map);
955
910
world.dirty = false;
956
911
}
957
912
}
+32
-26
src/gfx/camera.rs
+32
-26
src/gfx/camera.rs
···
1
-
use crate::concurrency::GameThread;
2
1
use crate::world::{chunk::Chunk, map::RENDER_GRID_SIZE, World};
3
2
use crate::ConnectionOnlyOnNative;
4
3
use glam::{ivec3, vec3, IVec3, Mat4, Vec2, Vec3, Vec4};
···
74
73
Vec3::Y,
75
74
)
76
75
}
76
+
pub fn chunk_pos(&self) -> IVec3 {
77
+
const BLOCK_UNIT_SIZE: i32 = 32;
78
+
IVec3::from((
79
+
self.position.x as i32 / BLOCK_UNIT_SIZE,
80
+
self.position.y as i32 / BLOCK_UNIT_SIZE,
81
+
self.position.z as i32 / BLOCK_UNIT_SIZE,
82
+
))
83
+
}
77
84
}
78
85
79
86
pub struct Projection {
···
165
172
duration: Duration,
166
173
world: Arc<Mutex<World>>,
167
174
conn: &mut ConnectionOnlyOnNative,
168
-
world_thread: &mut GameThread<(), (i32, i32, i32)>,
169
175
) {
170
176
let dt = duration.as_secs_f32();
171
177
let movement = self.movement.vec3();
···
188
194
camera.yaw += self.rotation.x * self.sensitivity * dt;
189
195
camera.pitch += -self.rotation.y * self.sensitivity * dt;
190
196
191
-
if self.load_chunks {
192
-
const BLOCK_UNIT_SIZE: i32 = 32;
193
-
let chunk_relative = IVec3::from((
194
-
camera.position.x as i32 / BLOCK_UNIT_SIZE,
195
-
camera.position.y as i32 / BLOCK_UNIT_SIZE,
196
-
camera.position.z as i32 / BLOCK_UNIT_SIZE,
197
-
)) + IVec3::splat(-(RENDER_GRID_SIZE as i32 / 2));
198
-
let mut world = world.lock().unwrap();
199
-
if chunk_relative != world.map.focus {
200
-
// This is a stupid way to do threading.
201
-
// world_thread.tx.send((x, y, z)).unwrap();
197
+
// if self.load_chunks {
198
+
// const BLOCK_UNIT_SIZE: i32 = 32;
199
+
// let chunk_relative = IVec3::from((
200
+
// camera.position.x as i32 / BLOCK_UNIT_SIZE,
201
+
// camera.position.y as i32 / BLOCK_UNIT_SIZE,
202
+
// camera.position.z as i32 / BLOCK_UNIT_SIZE,
203
+
// )) + IVec3::splat(-(RENDER_GRID_SIZE as i32 / 2));
204
+
// let mut world = world.lock().unwrap();
205
+
// if chunk_relative != world.map.focus {
206
+
// // This is a stupid way to do threading.
207
+
// // world_thread.tx.send((x, y, z)).unwrap();
202
208
203
-
world.map.focus = chunk_relative;
204
-
world.dirty = true;
205
-
}
209
+
// world.map.focus = chunk_relative;
210
+
// world.dirty = true;
211
+
// }
206
212
207
-
// if chunk_relative != world.map.chunks.offset().into() {
208
-
// world.map.chunks.reposition(
209
-
// (IVec3::from(chunk_relative)).into(),
210
-
// |_old, new, chunk| {
211
-
// *chunk = Chunk::load(ivec3(new.0, new.1, new.2), conn).unwrap();
212
-
// },
213
-
// );
214
-
// *remake = true;
215
-
// }
216
-
}
213
+
// // if chunk_relative != world.map.chunks.offset().into() {
214
+
// // world.map.chunks.reposition(
215
+
// // (IVec3::from(chunk_relative)).into(),
216
+
// // |_old, new, chunk| {
217
+
// // *chunk = Chunk::load(ivec3(new.0, new.1, new.2), conn).unwrap();
218
+
// // },
219
+
// // );
220
+
// // *remake = true;
221
+
// // }
222
+
// }
217
223
218
224
self.rotation = Vec2::ZERO;
219
225
+27
-32
src/gui.rs
+27
-32
src/gui.rs
···
158
158
) {
159
159
let mut scale_factor = self.scale_factor;
160
160
let (mut chunk_x, mut chunk_y, mut chunk_z) = self.chunk_influence;
161
-
let mut map_focus = world.map.focus;
162
161
let mut camera_load = gfx.camera.controller.load_chunks;
163
162
164
163
let dt = dt.as_secs_f32();
···
258
257
ui.heading("World toys...");
259
258
260
259
ui.checkbox(&mut camera_load, "Camera position loads chunks");
261
-
ui.label("Move chunk window... ");
262
-
ui.horizontal(|ui| {
263
-
ui.add_enabled(
264
-
!camera_load,
265
-
egui::DragValue::new(&mut map_focus.x)
266
-
.speed(0.1)
267
-
.update_while_editing(false),
268
-
);
269
-
ui.label("x ");
260
+
// ui.label("Move chunk window... ");
261
+
// ui.horizontal(|ui| {
262
+
// ui.add_enabled(
263
+
// !camera_load,
264
+
// egui::DragValue::new(&mut map_focus.x)
265
+
// .speed(0.1)
266
+
// .update_while_editing(false),
267
+
// );
268
+
// ui.label("x ");
270
269
271
-
ui.add_enabled(
272
-
!camera_load,
273
-
egui::DragValue::new(&mut map_focus.y)
274
-
.speed(0.1)
275
-
.update_while_editing(false),
276
-
);
277
-
ui.label("y ");
270
+
// ui.add_enabled(
271
+
// !camera_load,
272
+
// egui::DragValue::new(&mut map_focus.y)
273
+
// .speed(0.1)
274
+
// .update_while_editing(false),
275
+
// );
276
+
// ui.label("y ");
278
277
279
-
ui.add_enabled(
280
-
!camera_load,
281
-
egui::DragValue::new(&mut map_focus.z)
282
-
.speed(0.1)
283
-
.update_while_editing(false),
284
-
);
285
-
ui.label("z.");
286
-
});
278
+
// ui.add_enabled(
279
+
// !camera_load,
280
+
// egui::DragValue::new(&mut map_focus.z)
281
+
// .speed(0.1)
282
+
// .update_while_editing(false),
283
+
// );
284
+
// ui.label("z.");
285
+
// });
287
286
288
287
ui.separator();
289
288
ui.horizontal(|ui| {
···
314
313
};
315
314
316
315
if let Some(chunk) = c {
317
-
world.map.chunks.insert(pos, chunk);
318
-
world.dirty = true;
316
+
// world.map.chunks.insert(pos, chunk);
317
+
// world.dirty = true;
318
+
todo!("asdf")
319
319
}
320
320
}
321
321
});
···
333
333
self.chunk_influence = (chunk_x, chunk_y, chunk_z);
334
334
335
335
gfx.camera.controller.load_chunks = camera_load;
336
-
337
-
// If map focus has moved in tick, then:
338
-
if !camera_load && map_focus != world.map.focus {
339
-
world.dirty = true;
340
-
}
341
336
}
342
337
}
+16
-2
src/lib.rs
+16
-2
src/lib.rs
···
1
1
#![allow(rust_analyzer::inactive_code)]
2
2
3
3
mod app;
4
-
mod concurrency;
5
4
mod gfx;
6
5
mod gui;
7
6
mod world;
8
7
9
-
use glam::{Mat3, Quat, Vec3};
8
+
use glam::{ivec3, IVec3, Mat3, Quat, Vec3};
10
9
#[cfg(target_arch = "wasm32")]
11
10
use wasm_bindgen::prelude::*;
12
11
use wasm_bindgen::UnwrapThrowExt;
13
12
use winit::event_loop::EventLoop;
14
13
use world::chunk::preload_chunk_cache;
15
14
15
+
#[derive(Copy, Clone, Debug)]
16
16
struct Instance {
17
17
position: Vec3,
18
18
rotation: Quat,
···
125
125
let mut app = app::Application::new(&event_loop, "BL0CK", conn);
126
126
event_loop.run_app(&mut app).unwrap();
127
127
}
128
+
129
+
fn ivec3_product(lower: IVec3, higher: IVec3) -> impl Iterator<Item = IVec3> {
130
+
let IVec3 {
131
+
x: ax,
132
+
y: ay,
133
+
z: az,
134
+
} = lower;
135
+
let IVec3 {
136
+
x: bx,
137
+
y: by,
138
+
z: bz,
139
+
} = higher;
140
+
itertools::iproduct!(ax..=bx, ay..=by, az..=bz).map(|(i, j, k)| ivec3(i, j, k))
141
+
}
+3
-2
src/main.rs
+3
-2
src/main.rs
+2
-1
src/world.rs
+2
-1
src/world.rs
+178
-109
src/world/chunk.rs
+178
-109
src/world/chunk.rs
···
1
1
use super::map::BlockKind;
2
-
use crate::ConnectionOnlyOnNative;
2
+
use crate::{ConnectionOnlyOnNative, Instance};
3
3
use bincode::{Decode, Encode};
4
-
use glam::{ivec3, IVec3};
4
+
use glam::{ivec3, vec3, IVec3, Quat, Vec3};
5
5
use itertools::Itertools;
6
6
use std::sync::Mutex;
7
7
use std::{
···
38
38
Random,
39
39
}
40
40
41
-
#[derive(Copy, Clone, Encode, Decode)]
41
+
#[derive(Clone)]
42
42
pub struct Chunk {
43
43
pub blocks: Slice3,
44
+
pub indices: Vec<Instance>,
44
45
}
45
46
46
47
impl Chunk {
48
+
fn indices(blocks: Slice3, idx: IVec3) -> Vec<Instance> {
49
+
let _3diter = itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2);
50
+
51
+
_3diter
52
+
.filter_map(|(x, y, z)| {
53
+
if let BlockKind::Air = sl3get(&blocks, x, y, z) {
54
+
return None;
55
+
}
56
+
57
+
// lookup if node is surrounded by nodes
58
+
let mut do_not_occlude = false;
59
+
let mut sum = vec![];
60
+
61
+
for (x_, y_, z_) in [
62
+
(-1_i32, 0_i32, 0_i32),
63
+
(1, 0, 0),
64
+
(0, -1, 0),
65
+
(0, 1, 0),
66
+
(0, 0, -1),
67
+
(0, 0, 1),
68
+
] {
69
+
if x == 0
70
+
|| y == 0
71
+
|| z == 0
72
+
|| x == CHUNK_SIZE.0 - 1
73
+
|| y == CHUNK_SIZE.2 - 1
74
+
|| z == CHUNK_SIZE.1 - 1
75
+
{
76
+
do_not_occlude = true;
77
+
break;
78
+
}
79
+
80
+
let (x, y, z) = (x as i32 + x_, y as i32 + y_, z as i32 + z_);
81
+
if x < 0 || y < 0 || z < 0
82
+
// || x == CHUNK_SIZE.0 as i32 - 1
83
+
// || y == CHUNK_SIZE.2 as i32 - 1
84
+
// || z == CHUNK_SIZE.1 as i32 - 1
85
+
{
86
+
continue;
87
+
}
88
+
89
+
if let Some(block) = sl3get_opt(&blocks, x as usize, y as usize, z as usize) {
90
+
sum.push(block)
91
+
}
92
+
}
93
+
94
+
if !do_not_occlude && sum.iter().all(|b| *b == BlockKind::Brick) {
95
+
return None;
96
+
}
97
+
const SPACE_BETWEEN: f32 = 2.0;
98
+
99
+
let chunk_offset = idx.as_vec3() * (SPACE_BETWEEN * CHUNK_SIZE.0 as f32);
100
+
101
+
let mapping = |n| SPACE_BETWEEN * (n as f32 - CHUNK_SIZE.0 as f32 / 2.0);
102
+
let position = vec3(
103
+
mapping(x) + chunk_offset.x,
104
+
mapping(y) + chunk_offset.y,
105
+
mapping(z) + chunk_offset.z,
106
+
);
107
+
108
+
let rotation = Quat::from_axis_angle(Vec3::Y, 0.0);
109
+
110
+
Some(Instance { position, rotation })
111
+
})
112
+
.collect::<Vec<_>>()
113
+
}
114
+
47
115
fn generate_normal(world_pos: IVec3) -> Chunk {
48
116
let blocks = itertools::iproduct!(0..CHUNK_SIZE.0, 0..CHUNK_SIZE.1, 0..CHUNK_SIZE.2)
49
117
.map(|(x, z, y)| {
···
66
134
.collect_array()
67
135
.unwrap();
68
136
69
-
Chunk { blocks }
137
+
Chunk {
138
+
blocks,
139
+
indices: Chunk::indices(blocks, world_pos),
140
+
}
70
141
}
71
142
72
143
fn generate_callback(method: ChunkScramble) -> fn(IVec3) -> Chunk {
73
144
use ChunkScramble as C;
74
145
match method {
75
146
C::Normal => Chunk::generate_normal,
76
-
C::Inverse => |p| Chunk {
77
-
blocks: Chunk::generate_normal(p)
147
+
C::Inverse => |p| {
148
+
let blocks = Chunk::generate_normal(p)
78
149
.blocks
79
150
.iter()
80
151
.map(|b| match b {
···
82
153
BlockKind::Brick => BlockKind::Air,
83
154
})
84
155
.collect_array()
85
-
.unwrap(),
156
+
.unwrap();
157
+
Chunk {
158
+
blocks,
159
+
indices: Chunk::indices(blocks, p),
160
+
}
86
161
},
87
-
C::Random => |_| Chunk {
162
+
C::Random => |p| {
88
163
#[cfg(not(target_arch = "wasm32"))]
89
-
blocks: {
164
+
let blocks = {
90
165
use rand::Rng;
91
166
rand::rng().random()
92
-
},
93
-
#[cfg(target_arch = "wasm32")]
94
-
blocks: { panic!("i hate the web") },
167
+
};
168
+
169
+
Chunk {
170
+
blocks,
171
+
indices: Chunk::indices(blocks, p),
172
+
}
95
173
},
96
174
}
97
175
}
···
99
177
Chunk::generate_callback(method)(map_pos)
100
178
}
101
179
102
-
pub fn save(
103
-
&self,
104
-
map_pos: IVec3,
105
-
conn: &mut ConnectionOnlyOnNative,
106
-
) -> Result<(), Box<dyn std::error::Error>> {
107
-
let config = bincode::config::standard();
180
+
// pub fn save(
181
+
// &self,
182
+
// map_pos: IVec3,
183
+
// conn: &mut ConnectionOnlyOnNative,
184
+
// ) -> Result<(), Box<dyn std::error::Error>> {
185
+
// let config = bincode::config::standard();
108
186
109
-
#[cfg(not(target_arch = "wasm32"))]
110
-
{
111
-
let encoded = bincode::encode_to_vec(self, config)?;
187
+
// #[cfg(not(target_arch = "wasm32"))]
188
+
// {
189
+
// let encoded = bincode::encode_to_vec(self, config)?;
112
190
113
-
let mut stmt = conn.prepare_cached(
114
-
r#"
115
-
INSERT INTO chunks (x,y,z,data)
116
-
VALUES (?,?,?,?)
117
-
"#,
118
-
)?;
119
-
stmt.insert((map_pos.x, map_pos.y, map_pos.z, encoded))?;
120
-
}
191
+
// let mut stmt = conn.prepare_cached(
192
+
// r#"
193
+
// INSERT INTO chunks (x,y,z,data)
194
+
// VALUES (?,?,?,?)
195
+
// "#,
196
+
// )?;
197
+
// stmt.insert((map_pos.x, map_pos.y, map_pos.z, encoded))?;
198
+
// }
121
199
122
-
// We are going to use LocalStorage for web. I don't like it either.
123
-
#[cfg(target_arch = "wasm32")]
124
-
{
125
-
use base64::prelude::{Engine, BASE64_STANDARD};
126
-
let encoded = bincode::encode_to_vec(self, config)?;
127
-
let encoded = BASE64_STANDARD.encode(encoded);
200
+
// // We are going to use LocalStorage for web. I don't like it either.
201
+
// #[cfg(target_arch = "wasm32")]
202
+
// {
203
+
// use base64::prelude::{Engine, BASE64_STANDARD};
204
+
// let encoded = bincode::encode_to_vec(self, config)?;
205
+
// let encoded = BASE64_STANDARD.encode(encoded);
128
206
129
-
let store = web_sys::window().unwrap().local_storage().unwrap().unwrap();
130
-
store.set(&file_name, &encoded);
131
-
}
132
-
Ok(())
133
-
}
134
-
fn load_from_file(
135
-
map_pos: IVec3,
136
-
conn: &mut ConnectionOnlyOnNative,
137
-
) -> Result<Option<Chunk>, Box<dyn std::error::Error>> {
138
-
let config = bincode::config::standard();
139
-
let file_hash = calculate_hash(&map_pos);
140
-
let file_name = format!("chunk_{}.bl0ck", file_hash);
207
+
// let store = web_sys::window().unwrap().local_storage().unwrap().unwrap();
208
+
// store.set(&file_name, &encoded);
209
+
// }
210
+
// Ok(())
211
+
// }
212
+
// fn load_from_file(
213
+
// map_pos: IVec3,
214
+
// conn: &mut ConnectionOnlyOnNative,
215
+
// ) -> Result<Option<Chunk>, Box<dyn std::error::Error>> {
216
+
// let config = bincode::config::standard();
217
+
// let file_hash = calculate_hash(&map_pos);
218
+
// let file_name = format!("chunk_{}.bl0ck", file_hash);
141
219
142
-
#[cfg(not(target_arch = "wasm32"))]
143
-
{
144
-
// let file_path = Path::new("./save/chunk/").join(Path::new(&file_name));
145
-
// if file_path.exists() {
146
-
// log::warn!("Load Chunk!");
147
-
// let mut file = File::open(file_path).unwrap();
220
+
// #[cfg(not(target_arch = "wasm32"))]
221
+
// {
222
+
// // let file_path = Path::new("./save/chunk/").join(Path::new(&file_name));
223
+
// // if file_path.exists() {
224
+
// // log::warn!("Load Chunk!");
225
+
// // let mut file = File::open(file_path).unwrap();
148
226
149
-
let mut stmt = conn.prepare_cached(
150
-
r#"
151
-
SELECT (data) from chunks
152
-
WHERE (x,y,z) == (?,?,?)
153
-
"#,
154
-
)?;
155
-
let i: Vec<u8> =
156
-
match stmt.query_row((map_pos.x, map_pos.y, map_pos.z), |f| f.get("data")) {
157
-
Ok(x) => x,
158
-
Err(rusqlite::Error::QueryReturnedNoRows) => {
159
-
return Ok(None);
160
-
}
161
-
Err(e) => {
162
-
return Err(e.into());
163
-
}
164
-
};
227
+
// let mut stmt = conn.prepare_cached(
228
+
// r#"
229
+
// SELECT (data) from chunks
230
+
// WHERE (x,y,z) == (?,?,?)
231
+
// "#,
232
+
// )?;
233
+
// let i: Vec<u8> =
234
+
// match stmt.query_row((map_pos.x, map_pos.y, map_pos.z), |f| f.get("data")) {
235
+
// Ok(x) => x,
236
+
// Err(rusqlite::Error::QueryReturnedNoRows) => {
237
+
// return Ok(None);
238
+
// }
239
+
// Err(e) => {
240
+
// return Err(e.into());
241
+
// }
242
+
// };
165
243
166
-
let (decoded, _) = bincode::decode_from_slice(i.as_slice(), config)?;
244
+
// let (decoded, _) = bincode::decode_from_slice(i.as_slice(), config)?;
167
245
168
-
Ok(Some(decoded))
169
-
// } else {
170
-
// log::warn!("Chunk not loaded!");
171
-
// Ok(None)
172
-
// }
173
-
}
174
-
#[cfg(target_arch = "wasm32")]
175
-
{
176
-
use base64::prelude::{Engine, BASE64_STANDARD};
177
-
let store = web_sys::window().unwrap().local_storage().unwrap().unwrap();
178
-
if let Ok(Some(s)) = store.get(&file_name) {
179
-
let s = BASE64_STANDARD.decode(s)?;
180
-
let (decoded, _) = bincode::decode_from_slice(&s[..], config)?;
246
+
// Ok(Some(decoded))
247
+
// // } else {
248
+
// // log::warn!("Chunk not loaded!");
249
+
// // Ok(None)
250
+
// // }
251
+
// }
252
+
// #[cfg(target_arch = "wasm32")]
253
+
// {
254
+
// use base64::prelude::{Engine, BASE64_STANDARD};
255
+
// let store = web_sys::window().unwrap().local_storage().unwrap().unwrap();
256
+
// if let Ok(Some(s)) = store.get(&file_name) {
257
+
// let s = BASE64_STANDARD.decode(s)?;
258
+
// let (decoded, _) = bincode::decode_from_slice(&s[..], config)?;
181
259
182
-
Ok(Some(decoded))
183
-
} else {
184
-
Ok(None)
185
-
}
186
-
}
187
-
}
260
+
// Ok(Some(decoded))
261
+
// } else {
262
+
// Ok(None)
263
+
// }
264
+
// }
265
+
// }
188
266
189
267
pub fn load(
190
268
map_pos: IVec3,
···
195
273
#[cfg(target_arch = "wasm32")]
196
274
let cached = false;
197
275
198
-
if cached {
199
-
// log::warn!("Cache hit!");
200
-
#[cfg(not(target_arch = "wasm32"))]
201
-
return Ok(CHUNK_FILE_CACHE.lock().unwrap()[&map_pos]);
202
-
#[cfg(target_arch = "wasm32")]
203
-
return unreachable!();
204
-
} else {
205
-
// log::warn!("Cache miss!");
206
-
let chunk = match Chunk::load_from_file(map_pos, conn)? {
207
-
Some(chunk) => chunk,
208
-
None => {
209
-
let new_chunk = Chunk::generate(map_pos, ChunkScramble::Normal);
210
-
new_chunk.save(map_pos, conn)?;
211
-
new_chunk
212
-
}
213
-
};
214
-
#[cfg(not(target_arch = "wasm32"))]
215
-
CHUNK_FILE_CACHE.lock().unwrap().insert(map_pos, chunk);
216
-
Ok(chunk)
217
-
}
276
+
let chunk = Chunk::generate(map_pos, ChunkScramble::Normal);
277
+
Ok(chunk)
278
+
279
+
// if cached {
280
+
// // log::warn!("Cache hit!");
281
+
// #[cfg(not(target_arch = "wasm32"))]
282
+
// //return Ok(CHUNK_FILE_CACHE.lock().unwrap()[&map_pos]);
283
+
// #[cfg(target_arch = "wasm32")]
284
+
// return unreachable!();
285
+
// } else {
286
+
// }
218
287
}
219
288
}
220
289
+81
-34
src/world/map.rs
+81
-34
src/world/map.rs
···
1
-
use std::collections::HashMap;
1
+
use std::{
2
+
collections::HashMap,
3
+
sync::{mpsc::Receiver, Arc, RwLock},
4
+
time::Duration,
5
+
};
2
6
3
7
use crate::ConnectionOnlyOnNative;
4
8
···
11
15
Rng,
12
16
};
13
17
use rusqlite::Connection;
18
+
use tokio::{
19
+
sync::mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender},
20
+
time::sleep,
21
+
};
14
22
15
23
#[derive(Copy, Clone, Default, Encode, Decode, PartialEq)]
16
24
#[repr(u32)]
···
33
41
}
34
42
}
35
43
36
-
#[derive(Default)]
44
+
enum LoadNotify {
45
+
Loaded(IVec3),
46
+
}
47
+
37
48
pub struct WorldMap {
38
-
pub chunks: HashMap<IVec3, Chunk>,
39
-
pub focus: IVec3,
49
+
chunk_cache: Arc<RwLock<HashMap<IVec3, Chunk>>>,
50
+
request_tx: UnboundedSender<IVec3>,
51
+
pub response_rx: Receiver<IVec3>,
40
52
}
41
53
42
54
impl WorldMap {
43
-
pub fn chunks_around_focus(
44
-
&mut self,
45
-
conn: &mut Connection,
46
-
radius: i32,
47
-
) -> Vec<(IVec3, Chunk)> {
48
-
let IVec3 { x, y, z } = self.focus - radius;
49
-
let IVec3 {
50
-
x: xp,
51
-
y: yp,
52
-
z: zp,
53
-
} = self.focus + radius;
55
+
pub fn new() -> Self {
56
+
let (request_tx, mut request_rx) = unbounded_channel::<IVec3>();
57
+
let (response_tx, response_rx) = std::sync::mpsc::channel::<IVec3>();
58
+
let chunk_cache = Arc::new(RwLock::new(HashMap::<IVec3, Chunk>::new()));
59
+
60
+
let cache_handle = chunk_cache.clone();
61
+
tokio::spawn(async move {
62
+
let mut conn = rusqlite::Connection::open("./save.sqlite").unwrap();
63
+
64
+
while let Some(index) = request_rx.recv().await {
65
+
sleep(Duration::from_millis(100)).await;
66
+
//println!("idx {index}!");
67
+
68
+
// load chunk i guess.
69
+
70
+
// Already cached, don't bother.
71
+
if cache_handle.read().expect("poisoned").contains_key(&index) {
72
+
println!("MAP: cache hit");
73
+
continue;
74
+
}
75
+
println!("MAP: got {index}");
76
+
77
+
let chunk = Chunk::load(index, &mut conn).expect("oh no.");
78
+
79
+
cache_handle.write().expect("poisoned").insert(index, chunk);
80
+
response_tx.send(index).expect("channel killed");
81
+
}
82
+
});
83
+
84
+
Self {
85
+
chunk_cache,
86
+
request_tx,
87
+
response_rx,
88
+
}
89
+
}
54
90
55
-
itertools::iproduct!(x..=xp, y..=yp, z..=zp)
56
-
.map(|(i, j, k)| {
57
-
let index = ivec3(i, j, k);
58
-
(index, self.fetch_chunk(conn, index))
59
-
})
60
-
.collect()
91
+
pub fn get_chunk(&mut self, index: IVec3) -> Option<Chunk> {
92
+
self.request_chunk_load(index);
93
+
let cache = self.chunk_cache.read().expect("poisoned");
94
+
cache.get(&index).cloned()
61
95
}
62
96
63
-
fn fetch_chunk(&mut self, conn: &mut Connection, index: IVec3) -> Chunk {
64
-
let cnk = self.chunks.entry(index);
65
-
let cnk = cnk.or_insert_with(|| Chunk::load(index, conn).unwrap()); // This sucks. Asyncing this would be much nicer.
97
+
// pub fn try_chunk(&mut self) -> Option<IVec3> {
98
+
// match self.response_rx.try_recv() {
99
+
// Ok(idx) => Some(idx),
100
+
// Err(TryRecvError::Empty) => {
101
+
// println!("empy");
102
+
// None
103
+
// }
104
+
// Err(_) => panic!("aah!"),
105
+
// }
106
+
// }
66
107
67
-
cnk.clone() // This sucks.
108
+
fn poll_chunk(&mut self, index: IVec3) -> bool {
109
+
let cache = self.chunk_cache.read().expect("poisoned");
110
+
cache.contains_key(&index)
68
111
}
69
112
70
-
pub fn save(
71
-
&self,
72
-
conn: &mut ConnectionOnlyOnNative,
73
-
) -> Result<(), Box<dyn std::error::Error>> {
74
-
for (pos, chunk) in self.chunks.clone() {
75
-
// Eww a clone
76
-
chunk.save(pos, conn)?;
77
-
}
78
-
Ok(())
113
+
pub fn request_chunk_load(&self, index: IVec3) {
114
+
let _ = self.request_tx.send(index);
79
115
}
116
+
117
+
// pub fn save(
118
+
// &self,
119
+
// conn: &mut ConnectionOnlyOnNative,
120
+
// ) -> Result<(), Box<dyn std::error::Error>> {
121
+
// for (pos, chunk) in self.chunks.clone() {
122
+
// // Eww a clone
123
+
// chunk.save(pos, conn)?;
124
+
// }
125
+
// Ok(())
126
+
// }
80
127
}
81
128
82
129
pub(crate) const RENDER_GRID_SIZE: usize = 9;