Compare commits
56
Commits
main
..
729daa1503
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
729daa1503 | ||
|
|
dac7c52912 | ||
|
|
7daec14ba7 | ||
|
|
a5d109fb0a | ||
|
|
e9632a20d2 | ||
|
|
c4daecae6f | ||
|
|
15e40a941c | ||
|
|
3718c2145e | ||
|
|
10182730a5 | ||
|
|
9fb88c634b | ||
|
|
5a62d8c70d | ||
|
|
688405a579 | ||
|
|
65bacd745e | ||
|
|
5266b32823 | ||
|
|
7359b6549c | ||
|
|
00d1fa427a | ||
|
|
0487f4acf3 | ||
|
|
eecedbf4f4 | ||
|
|
67bb4fddd0 | ||
|
|
76e952995d | ||
|
|
57e1a9134e | ||
|
|
77c74be02f | ||
|
|
f8760e876c | ||
|
|
e6a8712de0 | ||
|
|
fa72ea7da4 | ||
|
|
16edbfea42 | ||
|
|
00c518b9c1 | ||
|
|
e6329d568c | ||
|
|
05b62d4b90 | ||
|
|
c1f3d0f398 | ||
|
|
120049d9f7 | ||
|
|
98736c39a0 | ||
|
|
57782a88ab | ||
|
|
ab9f4c83d0 | ||
|
|
4acf8ea816 | ||
|
|
6acdd51b05 | ||
|
|
0c7f07bc7b | ||
|
|
71e30abb62 | ||
|
|
e110e99053 | ||
|
|
a09f446ecd | ||
|
|
4eaa0fab56 | ||
|
|
3bd9fa942e | ||
|
|
3a45d4e0de | ||
|
|
0e53810fbd | ||
|
|
466f98a42c | ||
|
|
bf14d7c578 | ||
|
|
23d702ad6c | ||
|
|
d0b7a781b4 | ||
|
|
547f66d214 | ||
|
|
989b75f54d | ||
|
|
c089afbcbe | ||
|
|
c16ad4f4a0 | ||
|
|
45362afbf4 | ||
|
|
5b1ca38d75 | ||
|
|
cb2379786c | ||
|
|
d34196a6bd |
Generated
+193
-15
@@ -1,5 +1,15 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ab_glyph"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a933731feda8b460bdad9a9e43bb386baba6ec593d2bc19716ef3c75c09085c"
|
||||
dependencies = [
|
||||
"ab_glyph_rasterizer",
|
||||
"owned_ttf_parser 0.12.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ab_glyph_rasterizer"
|
||||
version = "0.1.4"
|
||||
@@ -434,6 +444,26 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch"
|
||||
version = "0.2.0"
|
||||
@@ -816,6 +846,45 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glyph_brush"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e3f00b8574a76fb6c50890c48da03946ca50e4372a2778737922666a2238221"
|
||||
dependencies = [
|
||||
"glyph_brush_draw_cache",
|
||||
"glyph_brush_layout",
|
||||
"log",
|
||||
"ordered-float",
|
||||
"rustc-hash",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glyph_brush_draw_cache"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac2c82074cafb68b9e459c50c655f7eedcb92d6ee7166813802934bc6fc29fa3"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"linked-hash-map",
|
||||
"rayon",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glyph_brush_layout"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10bc06d530bf20c1902f1b02799ab7372ff43f6119770c49b0bc3f21bd148820"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"approx",
|
||||
"xi-unicode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gpu-alloc"
|
||||
version = "0.3.0"
|
||||
@@ -1025,6 +1094,12 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.3"
|
||||
@@ -1043,6 +1118,15 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzzzz"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6d891cedd3b1659c206a60ff8afd15bccd7c2754b157f8a164861989e042b88"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
@@ -1373,13 +1457,31 @@ version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8919aecb97e5ee9aceef27e24f39c46b11831130f4a6b7b091ec5de0de12"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owned_ttf_parser"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3"
|
||||
dependencies = [
|
||||
"ttf-parser",
|
||||
"ttf-parser 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owned_ttf_parser"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a3c7a20e3f122223e68eef6ca58e39bc1ea8a1d83418ba4c2c1ba189d2ee355"
|
||||
dependencies = [
|
||||
"ttf-parser 0.12.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1637,6 +1739,22 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom 0.2.2",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rusttype"
|
||||
version = "0.9.2"
|
||||
@@ -1644,7 +1762,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59"
|
||||
dependencies = [
|
||||
"ab_glyph_rasterizer",
|
||||
"owned_ttf_parser",
|
||||
"owned_ttf_parser 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1679,6 +1797,20 @@ name = "serde"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shaderc"
|
||||
@@ -1758,6 +1890,12 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "storage-map"
|
||||
version = "0.3.0"
|
||||
@@ -1868,6 +2006,23 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85e00391c1f3d171490a3f8bd79999b0002ae38d3da0d6a3a306c754b053d71b"
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"rand 0.7.3",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
@@ -1880,6 +2035,30 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "voxel_game"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"cgmath",
|
||||
"dirs",
|
||||
"fs_extra",
|
||||
"futures",
|
||||
"glob",
|
||||
"lzzzz",
|
||||
"noise",
|
||||
"num_cpus",
|
||||
"rand 0.8.3",
|
||||
"serde",
|
||||
"shaderc",
|
||||
"toml",
|
||||
"wgpu",
|
||||
"wgpu_glyph",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
@@ -2118,22 +2297,15 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wgpufun"
|
||||
version = "0.1.0"
|
||||
name = "wgpu_glyph"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "354c1f79e09923724a6006a6953c3946522b84f7a9ec202b7e8001f274fd4bd5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"cgmath",
|
||||
"fs_extra",
|
||||
"futures",
|
||||
"glob",
|
||||
"image",
|
||||
"noise",
|
||||
"rand 0.8.3",
|
||||
"shaderc",
|
||||
"glyph_brush",
|
||||
"log",
|
||||
"wgpu",
|
||||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2256,6 +2428,12 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
|
||||
|
||||
[[package]]
|
||||
name = "xi-unicode"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.3"
|
||||
|
||||
+26
-8
@@ -1,22 +1,40 @@
|
||||
[package]
|
||||
name = "wgpufun"
|
||||
name = "voxel_game"
|
||||
version = "0.1.0"
|
||||
authors = ["Piotrek <piotrek54pl@gmail.com>"]
|
||||
authors = ["Piotrek <hello@pbaja.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Rendering and window management
|
||||
winit = "0.24"
|
||||
wgpu = "0.7"
|
||||
futures = "0.3"
|
||||
bytemuck = { version = "1.5", features = [ "derive" ] }
|
||||
byteorder = "1.4"
|
||||
image = "0.23.14"
|
||||
wgpu_glyph = "0.11"
|
||||
|
||||
# Math
|
||||
cgmath = "0.18"
|
||||
rand = "0.8"
|
||||
noise = "0.7"
|
||||
|
||||
# Saving and loading to and from files
|
||||
dirs = "3.0" # Retrieving common paths (%APPDATA%)
|
||||
serde = { version = "1.0", features = ["derive"] } # Serialization and deserialization (world.cfg)
|
||||
toml = "0.5" # Toml file format (used with serde)
|
||||
lzzzz = "0.8" # Lz4 compression
|
||||
|
||||
# Other
|
||||
futures = "0.3"
|
||||
bytemuck = { version = "1.5", features = [ "derive" ] }
|
||||
byteorder = "1.4"
|
||||
num_cpus = "1.13"
|
||||
|
||||
# Deprecated
|
||||
#image = "0.23.14"
|
||||
#flate2 = "1.0"
|
||||
#yaml-rust = "0.4"
|
||||
#linked-hash-map = "0.5"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0"
|
||||
fs_extra = "1.1"
|
||||
|
||||
Binary file not shown.
@@ -1,53 +0,0 @@
|
||||
use winit::event::Event;
|
||||
use noise::Perlin;
|
||||
|
||||
use crate::renderer::{camera::CameraTransform, RenderSpace};
|
||||
|
||||
mod camera_controller;
|
||||
use camera_controller::CameraController;
|
||||
|
||||
mod world;
|
||||
use world::World;
|
||||
|
||||
pub enum GameState {
|
||||
Paused,
|
||||
Running
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
camera_controller: CameraController,
|
||||
world: World
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(renderer: &mut dyn RenderSpace) -> Self {
|
||||
Self {
|
||||
camera_controller: CameraController::new(),
|
||||
world: World::new("default".to_string())
|
||||
}
|
||||
//instance.world_gen.generate(renderer, &Perlin::new());
|
||||
}
|
||||
|
||||
pub fn input(&mut self, event: &Event<()>) {
|
||||
// Update camera controller
|
||||
self.camera_controller.handle_input(event);
|
||||
}
|
||||
|
||||
pub fn update_state(&mut self, requested_state: GameState) -> GameState {
|
||||
match requested_state {
|
||||
GameState::Paused => {
|
||||
self.camera_controller.set_enabled(false);
|
||||
}
|
||||
GameState::Running => {
|
||||
self.camera_controller.set_enabled(true);
|
||||
}
|
||||
};
|
||||
|
||||
// Currently we are not preventing any state changes, just return requested state
|
||||
requested_state
|
||||
}
|
||||
|
||||
pub fn update_camera(&mut self, delta: f32, camera_transform: &mut dyn CameraTransform) {
|
||||
self.camera_controller.update(delta, camera_transform);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
struct Player {
|
||||
camera_controller: CameraController
|
||||
}
|
||||
|
||||
impl Player {
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WorldBlock {
|
||||
pub data: [u8;32*32*32]
|
||||
}
|
||||
|
||||
impl WorldBlock {
|
||||
pub fn new() -> Self {
|
||||
let data = [0_u8; 32*32*32];
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
use cgmath::Point3;
|
||||
use crate::app::world::WorldBlock;
|
||||
|
||||
pub struct WorldChunk {
|
||||
pos: Point3<u32>,
|
||||
blocks: Box<[WorldBlock]>
|
||||
}
|
||||
|
||||
impl WorldChunk {
|
||||
pub fn new(pos: Point3<u32>) -> Self {
|
||||
let blocks = vec![WorldBlock::new(); 16*16*16].into_boxed_slice();
|
||||
Self { pos, blocks }
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
use cgmath::Point3;
|
||||
use noise::{Perlin, NoiseFn};
|
||||
use rand::{Rng, prelude::ThreadRng};
|
||||
|
||||
use crate::renderer::RenderSpace;
|
||||
use crate::renderer::buffers::BLOCK_SIZE;
|
||||
|
||||
const SCALE: f64 = 150.0;
|
||||
|
||||
|
||||
pub struct WorldGen {
|
||||
|
||||
}
|
||||
|
||||
impl WorldGen {
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self{}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate height map for given block position
|
||||
*/
|
||||
fn gen_height_map(x: usize, z: usize, rand: &mut ThreadRng, noise: &Perlin) -> ([[usize;BLOCK_SIZE]; BLOCK_SIZE], usize) {
|
||||
let x = x * BLOCK_SIZE;
|
||||
let z = z * BLOCK_SIZE;
|
||||
|
||||
// Generate map
|
||||
let mut max: usize = 0;
|
||||
let mut map = [[0_usize; BLOCK_SIZE]; BLOCK_SIZE];
|
||||
for lx in 0..BLOCK_SIZE {
|
||||
for lz in 0..BLOCK_SIZE {
|
||||
let value = (noise.get([(x + lx) as f64 / SCALE, (z + lz) as f64 /SCALE]) + 1.0) / 2.0;
|
||||
let mut h = (value * 30.0) as usize;
|
||||
h += (rand.gen::<f32>() * 2.0) as usize;
|
||||
if h == 0 { h = 1; }
|
||||
if h > max { max = h; }
|
||||
map[lx][lz] = h;
|
||||
}
|
||||
}
|
||||
(map, max)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate world
|
||||
*/
|
||||
pub fn generate(&mut self, renderer: &mut dyn RenderSpace, noise: &Perlin) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut dirty: Vec<usize> = Vec::new();
|
||||
|
||||
for bx in 0..16 {
|
||||
for bz in 0..16 {
|
||||
// Generate height map
|
||||
let (map, max) = WorldGen::gen_height_map(bx, bz, &mut rng, noise);
|
||||
let bh = (max / BLOCK_SIZE) + 1;
|
||||
|
||||
// Generate blocks
|
||||
for by in 0..bh {
|
||||
// Get
|
||||
let b = renderer.block_get(Point3{x:bx, y:by, z:bz});
|
||||
dirty.push(b.id);
|
||||
// Fill
|
||||
for x in 0..BLOCK_SIZE {
|
||||
for z in 0..BLOCK_SIZE {
|
||||
// TODO
|
||||
let h = map[x][z];
|
||||
for y in 0..h {
|
||||
b.set(Point3{x, y, z}, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for id in dirty {
|
||||
renderer.block_dirty(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
mod generator;
|
||||
use generator::WorldGen;
|
||||
mod chunk;
|
||||
use chunk::WorldChunk;
|
||||
mod block;
|
||||
use block::WorldBlock;
|
||||
|
||||
pub struct World {
|
||||
name: String,
|
||||
chunks: Vec<WorldChunk> // Loaded chunks
|
||||
}
|
||||
|
||||
impl World {
|
||||
|
||||
pub fn new(name: String) -> Self {
|
||||
Self { name, chunks: Vec::new() }
|
||||
}
|
||||
|
||||
fn load(&mut self) {
|
||||
|
||||
}
|
||||
|
||||
fn save(&mut self) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -42,12 +42,16 @@ impl CameraController {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
movement: CameraMovement { speed: 2.0, .. CameraMovement::default()},
|
||||
movement: CameraMovement { speed: 3.0, .. CameraMovement::default()},
|
||||
rotation: CameraRotation { yaw: 135.0, sensitivity: 0.2, .. CameraRotation::default() },
|
||||
cam_position: (-1.0, 2.0, -1.0).into(),
|
||||
cam_position: (1.0, 2.0, 1.0).into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_yaw(&self) -> f32 {
|
||||
self.rotation.yaw
|
||||
}
|
||||
|
||||
pub fn set_enabled(&mut self, enabled: bool) { self.enabled = enabled; }
|
||||
|
||||
pub fn handle_input(&mut self, event: &Event<()>) -> bool {
|
||||
@@ -105,7 +109,7 @@ impl CameraController {
|
||||
|
||||
// Movement amount
|
||||
let mut amount = delta_time * self.movement.speed;
|
||||
if self.movement.shift { amount *= 2.0; }
|
||||
if self.movement.shift { amount *= 8.0; }
|
||||
|
||||
// Forward and backwards
|
||||
if self.movement.fwd && cam_forward.magnitude() > amount { self.cam_position += cam_forward.normalize() * amount; }
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
mod camera_controller;
|
||||
mod player;
|
||||
pub mod world;
|
||||
use std::time::Instant;
|
||||
use crate::renderer::renderer_view::RendererView;
|
||||
use cgmath::Vector3;
|
||||
use winit::event::Event;
|
||||
use player::Player;
|
||||
use world::World;
|
||||
use world::chunk::{WorldChunk, ChunkState};
|
||||
|
||||
pub const RENDER_DIST: i32 = 3;
|
||||
|
||||
pub enum GameState {
|
||||
Paused,
|
||||
Running
|
||||
}
|
||||
|
||||
pub struct Game {
|
||||
state: GameState,
|
||||
player: Player,
|
||||
#[allow(unused)]
|
||||
world: World,
|
||||
|
||||
prev_pos: Option<Vector3<i32>>,
|
||||
prev_chunk_pos: Option<Vector3<i32>>,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new(renderer: &mut impl RendererView) -> Self {
|
||||
let mut instance = Self {
|
||||
state: GameState::Paused,
|
||||
player: Player::new(),
|
||||
world: World::init("hello".to_string()),
|
||||
prev_pos: None,
|
||||
prev_chunk_pos: None,
|
||||
};
|
||||
|
||||
// Preload chunks around player
|
||||
for x in -RENDER_DIST..RENDER_DIST {
|
||||
for z in -RENDER_DIST..RENDER_DIST {
|
||||
instance.world.load(Vector3{x, y: 0, z});
|
||||
}
|
||||
}
|
||||
|
||||
// Return generated instance
|
||||
instance
|
||||
}
|
||||
|
||||
fn debug_info(&mut self, renderer: &mut impl RendererView) {
|
||||
// Update pointing direction
|
||||
let yaw = self.player.camera_controller.get_yaw();
|
||||
let mut yaw_str = "";
|
||||
if yaw >= 315.0 || yaw <= 45.0 { yaw_str = "+z"; }
|
||||
else if yaw >= 45.0 && yaw < 135.0 { yaw_str = "-x"; }
|
||||
else if yaw >= 135.0 && yaw < 225.0 { yaw_str = "-z"; }
|
||||
else if yaw >= 225.0 && yaw < 315.0 { yaw_str = "+x"; }
|
||||
renderer.get_ui().set_text("World", 0, format!("Direction: {}", yaw_str));
|
||||
}
|
||||
|
||||
pub fn update(&mut self, delta: f32, renderer: &mut impl RendererView) {
|
||||
// Update camera
|
||||
let camera = renderer.get_camera_transform();
|
||||
self.player.camera_controller.update(delta, camera);
|
||||
|
||||
// Get positions
|
||||
let cam_pos = camera.get_position();
|
||||
let cam_pos = Vector3{x: cam_pos.x as i32, y: 0/*cam_pos.y as i32*/, z: cam_pos.z as i32};
|
||||
let chunk_pos = cam_pos / world::chunk::SIZE as i32;
|
||||
if self.prev_pos == None || self.prev_pos != Some(cam_pos) {
|
||||
renderer.get_ui().set_text("World", 1, format!("Position: {}, {}, {}", cam_pos.x, cam_pos.y, cam_pos.z));
|
||||
self.prev_pos = Some(cam_pos);
|
||||
}
|
||||
|
||||
// Debug
|
||||
self.debug_info(renderer);
|
||||
|
||||
// Receives chunks from workers
|
||||
self.world.update();
|
||||
|
||||
// Load, unload or write chunks to gpu
|
||||
if !renderer.is_busy() {
|
||||
|
||||
// If chunk position changed
|
||||
if self.prev_chunk_pos == None || self.prev_chunk_pos != Some(chunk_pos) {
|
||||
renderer.get_ui().set_text("World", 2, format!("Chunk: {}, {}, {}", chunk_pos.x, chunk_pos.y, chunk_pos.z));
|
||||
|
||||
// Shift world
|
||||
if self.prev_chunk_pos == None { self.prev_chunk_pos = Some(chunk_pos); }
|
||||
let prev_chunk_pos = self.prev_chunk_pos.unwrap();
|
||||
let mut dir = chunk_pos - prev_chunk_pos;
|
||||
|
||||
if dir.x != 0 { dir = Vector3{x: dir.x.clamp(-1, 1), y:0, z:0 } }
|
||||
else if dir.y != 0 { dir = Vector3{x:0, y:dir.y.clamp(-1, 1), z:0 } }
|
||||
else if dir.z != 0 { dir = Vector3{x:0, y:0, z:dir.z.clamp(-1, 1) } }
|
||||
else { return; }
|
||||
|
||||
renderer.shift(dir * world::chunk::SIZE as i32);
|
||||
|
||||
// Save previous
|
||||
self.prev_chunk_pos = Some(prev_chunk_pos+dir);
|
||||
}
|
||||
|
||||
// Load chunks in radius
|
||||
//TODO: y direction
|
||||
for ox in -RENDER_DIST..RENDER_DIST+1 {
|
||||
for oz in -RENDER_DIST..RENDER_DIST+1 {
|
||||
// Load chunk
|
||||
let pos = Vector3{x: chunk_pos.x+ox, y: 0, z: chunk_pos.z+oz};
|
||||
self.world.load(pos).and_then(|ch| -> Option<()> {
|
||||
// Chunk is ready, schedule it for writing
|
||||
if ch.state == ChunkState::Ready || ch.state == ChunkState::Stale {
|
||||
ch.state = ChunkState::Waiting;
|
||||
}
|
||||
None
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Send waiting chunks to gpu
|
||||
for (pos, chunk) in self.world.all() {
|
||||
if let Some(chunk) = chunk {
|
||||
// Skip stale chunks
|
||||
if chunk.state == ChunkState::Stale {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate if chunk is within render distance
|
||||
let (ox, oy, oz) = (pos.x - chunk_pos.x, pos.y - chunk_pos.y, pos.z - chunk_pos.z);
|
||||
let within_distance = ox > -RENDER_DIST && ox < RENDER_DIST
|
||||
&& oy > -RENDER_DIST && oy < RENDER_DIST
|
||||
&& oz > -RENDER_DIST && oz < RENDER_DIST;
|
||||
|
||||
// Within render distance and waiting
|
||||
if within_distance && chunk.state == ChunkState::Waiting {
|
||||
// Write chunk. If succeded change its state to Visible, otherwise make it Ready again.
|
||||
if renderer.write_chunk(pos, chunk) {
|
||||
chunk.state = ChunkState::Visible;
|
||||
} else {
|
||||
chunk.state = ChunkState::Ready;
|
||||
}
|
||||
}
|
||||
|
||||
// Outside render distance and visible
|
||||
if !within_distance && chunk.state == ChunkState::Visible {
|
||||
// Set chunk to stale, a.k.a. was visible but is not anymore
|
||||
chunk.state = ChunkState::Stale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input(&mut self, event: &Event<()>) {
|
||||
// Update camera controller
|
||||
self.player.camera_controller.handle_input(event);
|
||||
}
|
||||
|
||||
pub fn update_state(&mut self, requested_state: GameState) -> &GameState {
|
||||
match requested_state {
|
||||
GameState::Paused => {
|
||||
self.player.camera_controller.set_enabled(false);
|
||||
}
|
||||
GameState::Running => {
|
||||
self.player.camera_controller.set_enabled(true);
|
||||
}
|
||||
};
|
||||
|
||||
// Currently we are not preventing any state changes, just return requested state
|
||||
self.state = requested_state;
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub fn get_state(&self) -> &GameState {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use super::camera_controller::CameraController;
|
||||
|
||||
pub struct Player {
|
||||
pub camera_controller: CameraController
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
camera_controller: CameraController::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
use cgmath::Vector3;
|
||||
|
||||
pub const SIZE: usize = 32;
|
||||
pub const SIZE_QB: usize = SIZE*SIZE*SIZE;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WorldBlock {
|
||||
pub materials: Box<[u8;SIZE_QB]>
|
||||
}
|
||||
|
||||
impl WorldBlock {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
materials: Box::new([0_u8; SIZE_QB])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, voxel_pos: Vector3<usize>, value: u8) {
|
||||
self.materials[voxel_pos.x + SIZE * (voxel_pos.y + SIZE * voxel_pos.z)] = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use std::thread;
|
||||
use std::sync::mpsc;
|
||||
use crate::game::RendererView;
|
||||
use byteorder::LittleEndian;
|
||||
use byteorder::ByteOrder;
|
||||
use cgmath::Vector3;
|
||||
use lzzzz::lz4;
|
||||
use crate::game::world::{WorldBlock, block};
|
||||
|
||||
pub const SIZE: usize = 16;
|
||||
const SIZE_SQ: usize = SIZE*SIZE;
|
||||
const SIZE_QB: usize = SIZE*SIZE*SIZE;
|
||||
|
||||
// 1 chunk times on i7-5930K
|
||||
// alg | size | compr | decom
|
||||
// raw 32.89kB 0.06s 1.19s
|
||||
// deflate fast 1.23kB 1.22s 5.46s
|
||||
// lz4 1.6kB 0.07s 1.23s
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum ChunkState {
|
||||
Partial, // Not yet loaded
|
||||
Ready, // Loaded
|
||||
Waiting, // Scheduled to be uploaded
|
||||
Visible, // Visible
|
||||
Stale, // Scheduled to be unloaded
|
||||
}
|
||||
|
||||
pub struct WorldChunk {
|
||||
nodes: Box<[u32]>,
|
||||
blocks: Vec<WorldBlock>,
|
||||
pub state: ChunkState,
|
||||
}
|
||||
|
||||
impl WorldChunk {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
nodes: vec![0_u32; SIZE_QB].into_boxed_slice(),
|
||||
blocks: Vec::new(),
|
||||
state: ChunkState::Partial,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> Option<Self> {
|
||||
let mut instance = Self::new();
|
||||
|
||||
// Header
|
||||
let (header_bytes, bytes) = bytes.split_at(128);
|
||||
let file_ver = header_bytes[0];
|
||||
let num_blocks = LittleEndian::read_u32(&header_bytes[1..5]) as usize;
|
||||
assert_eq!(file_ver, 1);
|
||||
|
||||
// Nodes
|
||||
let (node_bytes, block_bytes_compressed) = bytes.split_at((SIZE_QB*4) as usize);
|
||||
for i in 0..SIZE_QB {
|
||||
let idx = (i*4) as usize;
|
||||
let val = u32::from_le_bytes([node_bytes[idx], node_bytes[idx+1], node_bytes[idx+2], node_bytes[idx+3]]);
|
||||
instance.nodes[i as usize] = val;
|
||||
}
|
||||
|
||||
// Blocks
|
||||
let mut block_bytes = vec![0_u8; num_blocks*block::SIZE_QB];
|
||||
if lz4::decompress(&block_bytes_compressed, &mut block_bytes[0..num_blocks*block::SIZE_QB]).is_err() {
|
||||
println!("Decompression failed");
|
||||
return None;
|
||||
}
|
||||
|
||||
for i in 0..num_blocks {
|
||||
let mut block = WorldBlock::new();
|
||||
let idx = i * block::SIZE_QB;
|
||||
block.materials.clone_from_slice(&block_bytes[idx..idx+block::SIZE_QB]);
|
||||
instance.blocks.push(block);
|
||||
}
|
||||
|
||||
instance.state = ChunkState::Ready;
|
||||
Some(instance)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
|
||||
// Header
|
||||
let mut bytes: Vec<u8> = vec![0_u8; 5];
|
||||
bytes[0] = 1_u8; // 1b -> File version
|
||||
LittleEndian::write_u32(&mut bytes[1..5], self.blocks.len() as u32); // 4b -> Number of blocks in this file
|
||||
bytes.extend((0..128-5).map(|_| 0)); // Reserved 128b - 5b used
|
||||
|
||||
// Nodes
|
||||
let mut node_bytes = [0_u8; (SIZE_QB*4) as usize];
|
||||
LittleEndian::write_u32_into(&self.nodes, &mut node_bytes);
|
||||
bytes.extend(node_bytes.iter());
|
||||
|
||||
// Blocks
|
||||
let mut buf = Vec::new();
|
||||
for block in self.blocks.iter() {
|
||||
buf.extend(&block.materials[..]);
|
||||
}
|
||||
lz4::compress_to_vec(&buf, &mut bytes, lz4::ACC_LEVEL_DEFAULT).unwrap();
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn all_blocks(&self) -> Vec<(Vector3<i32>, &WorldBlock)> {
|
||||
self.nodes.iter().enumerate()
|
||||
.filter(|(_, n)| **n != 0)
|
||||
.map(|(index, node)| {
|
||||
|
||||
let mut idx = index;
|
||||
let z = idx / SIZE_SQ;
|
||||
idx -= z * SIZE_SQ;
|
||||
let y = idx / SIZE;
|
||||
let x = idx % SIZE;
|
||||
|
||||
let pos = Vector3{x: x as i32, y: y as i32, z: z as i32};
|
||||
let block = &self.blocks[(*node-1) as usize];
|
||||
(pos, block)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_block_mut(&mut self, block_pos: &Vector3<i32>) -> &mut WorldBlock {
|
||||
// Get block index
|
||||
let index = (block_pos.x + SIZE as i32 * (block_pos.y + SIZE as i32 * block_pos.z)) as usize;
|
||||
let mut value = self.nodes[index];
|
||||
// Allocate new block if needed
|
||||
if value == 0 {
|
||||
value = (self.blocks.len() + 1) as u32;
|
||||
self.nodes[index] = value;
|
||||
self.blocks.push(WorldBlock::new());
|
||||
}
|
||||
// Return block reference
|
||||
&mut self.blocks[(value-1) as usize]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct WorldData {
|
||||
pub name: String,
|
||||
pub display_name: String
|
||||
}
|
||||
|
||||
impl Default for WorldData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::from("default"),
|
||||
display_name: String::from("Default")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
use std::time::Instant;
|
||||
use cgmath::Vector3;
|
||||
use noise::{Perlin, NoiseFn};
|
||||
use rand::{Rng, prelude::ThreadRng};
|
||||
|
||||
use crate::game::world::{block, block::WorldBlock};
|
||||
|
||||
const SCALE: f64 = 150.0;
|
||||
|
||||
|
||||
pub struct WorldGen {
|
||||
|
||||
}
|
||||
|
||||
impl WorldGen {
|
||||
|
||||
/*
|
||||
* Generate height map for given block position
|
||||
*/
|
||||
fn gen_height_map(x: i32, z: i32, rand: &mut ThreadRng, noise: &Perlin) -> ([[usize;block::SIZE]; block::SIZE], usize) {
|
||||
let size = block::SIZE as i32;
|
||||
let x = x * size;
|
||||
let z = z * size;
|
||||
|
||||
// Generate map
|
||||
let mut max: usize = 0;
|
||||
let mut map = [[0_usize; block::SIZE]; block::SIZE];
|
||||
for lx in 0..size {
|
||||
for lz in 0..size {
|
||||
let value = (noise.get([(x + lx) as f64 / SCALE, (z + lz) as f64 /SCALE]) + 1.0) / 2.0;
|
||||
let mut h = (value * 30.0) as usize;
|
||||
h += (rand.gen::<f32>() * 2.0) as usize;
|
||||
if h == 0 { h = 1; }
|
||||
if h > max { max = h; }
|
||||
map[lx as usize][lz as usize] = h;
|
||||
}
|
||||
}
|
||||
(map, max)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate block
|
||||
*/
|
||||
pub fn generate(block_pos: &Vector3<i32>, block: &mut WorldBlock) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let noise = Perlin::new();
|
||||
|
||||
// Generate height map
|
||||
let (map, _max) = WorldGen::gen_height_map(block_pos.x, block_pos.z, &mut rng, &noise);
|
||||
|
||||
// Fill
|
||||
for x in 0..block::SIZE {
|
||||
for z in 0..block::SIZE {
|
||||
let mut h = map[x][z];
|
||||
let mut t = 2;
|
||||
|
||||
if block_pos.x == 8 && block_pos.z == 8 {
|
||||
h = 32;
|
||||
t = 3;
|
||||
}
|
||||
|
||||
//let h = 16; // testing
|
||||
for y in 0..h {
|
||||
block.set(Vector3{x, y, z}, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use std::cmp::{min, max};
|
||||
use cgmath::Vector3;
|
||||
use std::fs;
|
||||
use std::io::{Read, Write};
|
||||
use std::thread;
|
||||
use std::sync::{mpsc, mpsc::Receiver, mpsc::Sender};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use super::{chunk, chunk::{WorldChunk, ChunkState}};
|
||||
use crate::game::world::WorldGen;
|
||||
|
||||
type ChunkPos = Vector3<i32>;
|
||||
type ChunkPosPair = (ChunkPos, WorldChunk);
|
||||
type ArcRecv<T> = Arc<Mutex<Receiver<T>>>;
|
||||
|
||||
pub struct ChunkLoader {
|
||||
receiver: Receiver<ChunkPosPair>,
|
||||
queue: Sender<ChunkPos>
|
||||
}
|
||||
|
||||
impl ChunkLoader {
|
||||
|
||||
pub fn new(world_name: String) -> Self {
|
||||
|
||||
// Create channels
|
||||
let (result_tx, result_rx) = mpsc::channel();
|
||||
let (input_tx, input_rx) = mpsc::channel();
|
||||
let input_rx = Arc::new(Mutex::new(input_rx));
|
||||
|
||||
// Figure out number of workers
|
||||
let mut n = num_cpus::get_physical();
|
||||
n = max((n as f64 * 0.66).round() as usize, 1);
|
||||
|
||||
// Spawn n workers
|
||||
for _ in 0..n {
|
||||
let q = Arc::clone(&input_rx);
|
||||
let tx = result_tx.clone();
|
||||
let name = world_name.clone();
|
||||
thread::spawn(move || { ChunkLoader::worker(q, tx, name) });
|
||||
}
|
||||
println!("Spawned {} chunk loader workers", n);
|
||||
|
||||
// Build struct
|
||||
Self {
|
||||
receiver: result_rx,
|
||||
queue: input_tx
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive loaded chunks
|
||||
*/
|
||||
pub fn receive(&mut self) -> Vec<ChunkPosPair> {
|
||||
let mut result = Vec::new();
|
||||
while let Ok((pos, chunk)) = self.receiver.try_recv() {
|
||||
result.push((pos, chunk));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule loading chunks
|
||||
*/
|
||||
pub fn load(&mut self, pos: ChunkPos) {
|
||||
self.queue.send(pos).unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
* Take position from queue and load chunk
|
||||
*/
|
||||
fn worker(queue: ArcRecv<ChunkPos>, tx: Sender<ChunkPosPair>, name: String) {
|
||||
loop {
|
||||
// Wait for job
|
||||
let pos;
|
||||
{
|
||||
// Get lock
|
||||
let rx = queue.lock();
|
||||
if rx.is_err() { break; }
|
||||
// Receive next pos
|
||||
let ps = rx.unwrap().recv();
|
||||
if ps.is_err() { break; }
|
||||
pos = ps.unwrap();
|
||||
// Release lock now (end of scope)
|
||||
}
|
||||
|
||||
// Try loading chunk from file
|
||||
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
|
||||
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").join("saves").join(&name).join(filename);
|
||||
if filepath.is_file() {
|
||||
let mut buff = Vec::new();
|
||||
let mut file = fs::File::open(&filepath).expect("Failed to open chunk file");
|
||||
file.read_to_end(&mut buff).expect("Failed to read chunk file");
|
||||
|
||||
// Create chunk from bytes
|
||||
if let Some(chunk) = WorldChunk::from_bytes(buff) {
|
||||
// Send it to channel
|
||||
if let Err(_) = tx.send((pos, chunk)) {
|
||||
break;
|
||||
}
|
||||
// Process next chunk
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate if from_bytes failed
|
||||
}
|
||||
|
||||
// Try generating chunk
|
||||
let mut chunk = WorldChunk::new();
|
||||
for x in 0..chunk::SIZE as i32 {
|
||||
for z in 0..chunk::SIZE as i32 {
|
||||
let bpos = Vector3::<i32>{x,y:0,z};
|
||||
let block = chunk.get_block_mut(&bpos);
|
||||
WorldGen::generate(&(pos*chunk::SIZE as i32 + bpos), block);
|
||||
}
|
||||
}
|
||||
chunk.state = ChunkState::Ready;
|
||||
// save
|
||||
fs::create_dir_all(&filepath.parent().unwrap()).expect("Failed to create world directory");
|
||||
let mut file = fs::File::create(filepath).expect("Failed to create chunk file");
|
||||
file.write(&chunk.to_bytes()).expect("Failed to write chunk file");
|
||||
// send
|
||||
tx.send((pos, chunk)).unwrap();
|
||||
}
|
||||
|
||||
println!("ChunkLoader: worker finished");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
mod generator;
|
||||
mod data;
|
||||
mod loader;
|
||||
pub mod chunk;
|
||||
pub mod block;
|
||||
use std::path::PathBuf;
|
||||
use std::io::Write;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
use cgmath::Vector3;
|
||||
use std::collections::HashMap;
|
||||
use generator::WorldGen;
|
||||
use chunk::WorldChunk;
|
||||
use block::WorldBlock;
|
||||
use data::WorldData;
|
||||
use loader::ChunkLoader;
|
||||
|
||||
pub struct World {
|
||||
data: WorldData,
|
||||
loader: ChunkLoader,
|
||||
chunks: HashMap<Vector3<i32>, Option<WorldChunk>>,
|
||||
}
|
||||
|
||||
impl World {
|
||||
|
||||
pub fn init(name: String) -> Self {
|
||||
let mut instance = Self {
|
||||
data: WorldData { name: name.clone(), ..Default::default() },
|
||||
loader: ChunkLoader::new(name.clone()),
|
||||
chunks: HashMap::new(),
|
||||
};
|
||||
instance.load_metadata();
|
||||
instance.save_all();
|
||||
instance
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns vec of references to all currently loaded chunks
|
||||
*/
|
||||
pub fn all(&mut self) -> Vec<(&Vector3<i32>, &mut Option<WorldChunk>)> {
|
||||
self.chunks.iter_mut().collect()
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives loaded chunks from ChunkLoader
|
||||
*/
|
||||
pub fn update(&mut self) {
|
||||
for (pos, chunk) in self.loader.receive() {
|
||||
self.chunks.insert(pos, Some(chunk));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends chunk position to the ChunkLoader if chunk is not loaded
|
||||
* Returns loaded chunk otherwise
|
||||
*/
|
||||
pub fn load(&mut self, pos: Vector3<i32>) -> Option<&mut WorldChunk> {
|
||||
let mut exists = false;
|
||||
let result : Option<&mut WorldChunk> = self.chunks.entry(pos).and_modify(|_| { exists = true; }).or_insert(None).as_mut();
|
||||
if !exists { self.loader.load(pos); }
|
||||
result
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads metadata
|
||||
*/
|
||||
fn load_metadata(&mut self, ) -> bool {
|
||||
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").join(self.data.name.clone()).join("world.dat");
|
||||
if let Ok(mut file) = fs::File::open(filepath) {
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
if let Ok(data) = toml::from_slice::<WorldData>(&buf[..]) {
|
||||
self.data = data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Saves metadata and all loaded chunks to files
|
||||
*/
|
||||
pub fn save_all(&self) {
|
||||
// Get directory
|
||||
let dirpath = dirs::data_local_dir().unwrap().join("Voxelgame").join("saves").join(&self.data.name);
|
||||
fs::create_dir_all(&dirpath).expect("Failed to create world directory");
|
||||
|
||||
// Save all chunks
|
||||
for (pos, chunk) in self.chunks.iter() {
|
||||
if let Some(chunk) = chunk {
|
||||
let filepath = dirpath.join(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z));
|
||||
let mut file = fs::File::create(filepath).expect("Failed to create chunk file");
|
||||
file.write(&chunk.to_bytes()).expect("Failed to write chunk file");
|
||||
}
|
||||
}
|
||||
|
||||
// Save metadata
|
||||
let datastr = toml::to_string(&self.data).unwrap();
|
||||
let datapath: PathBuf = dirpath.join("world.dat");
|
||||
let mut datafile = fs::File::create(datapath).expect("Failed to create world metadata file");
|
||||
datafile.write_all(datastr.as_bytes()).expect("Failed to write world metadata");
|
||||
}
|
||||
}
|
||||
+42
-19
@@ -1,3 +1,6 @@
|
||||
use std::time::Instant;
|
||||
use cgmath::Vector3;
|
||||
use crate::renderer::renderer_view::RendererView;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, MouseButton},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
@@ -8,8 +11,8 @@ use winit::{
|
||||
mod renderer;
|
||||
use renderer::Renderer;
|
||||
|
||||
mod app;
|
||||
use app::{App, GameState};
|
||||
mod game;
|
||||
use game::{Game, GameState};
|
||||
|
||||
|
||||
fn main() {
|
||||
@@ -23,8 +26,8 @@ fn main() {
|
||||
.build(&events)
|
||||
.expect("Failed to create window");
|
||||
|
||||
let mut renderer = futures::executor::block_on(Renderer::new(&window));
|
||||
let mut app = App::new(&mut renderer);
|
||||
let mut renderer = Renderer::new(&window);
|
||||
let mut game = Game::new(&mut renderer);
|
||||
|
||||
// Stats
|
||||
let mut fps_timer = std::time::Instant::now();
|
||||
@@ -34,7 +37,7 @@ fn main() {
|
||||
// Run event loop
|
||||
events.run(move |ev, _, c| {
|
||||
// App input
|
||||
app.input(&ev);
|
||||
game.input(&ev);
|
||||
|
||||
// Other
|
||||
match ev {
|
||||
@@ -43,18 +46,33 @@ fn main() {
|
||||
match event {
|
||||
// Window closed (ALT+F4,)
|
||||
WindowEvent::CloseRequested => { *c = ControlFlow::Exit; }
|
||||
// Escape key
|
||||
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. }, .. } => {
|
||||
// Request pause
|
||||
if let GameState::Paused = app.update_state(GameState::Paused) {
|
||||
window.set_cursor_grab(false).unwrap();
|
||||
window.set_cursor_visible(true);
|
||||
// Key pressed
|
||||
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, virtual_keycode, .. }, .. } => {
|
||||
if let Some(key) = virtual_keycode {
|
||||
match key {
|
||||
// Escape pressed
|
||||
VirtualKeyCode::Escape => {
|
||||
if let GameState::Paused = game.update_state(GameState::Paused) {
|
||||
window.set_cursor_grab(false).unwrap();
|
||||
window.set_cursor_visible(true);
|
||||
}
|
||||
}
|
||||
// Test key
|
||||
VirtualKeyCode::T => {
|
||||
let t = &mut renderer as &mut dyn RendererView;
|
||||
t.shift(Vector3::<i32>{ x:16, y:0, z:0 });
|
||||
println!("shifted");
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// Window clicked
|
||||
WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left, .. } => {
|
||||
// Request unpause
|
||||
if let GameState::Running = app.update_state(GameState::Running) {
|
||||
if let GameState::Running = game.update_state(GameState::Running) {
|
||||
window.set_cursor_grab(true).unwrap();
|
||||
window.set_cursor_visible(false);
|
||||
}
|
||||
@@ -72,21 +90,26 @@ fn main() {
|
||||
// Loop
|
||||
Event::MainEventsCleared => {
|
||||
// Update
|
||||
let timer = Instant::now();
|
||||
let delta = delta_timer.elapsed().as_secs_f32();
|
||||
app.update_camera(delta, &mut renderer.camera);
|
||||
delta_timer = std::time::Instant::now();
|
||||
game.update(delta, &mut renderer);
|
||||
delta_timer = Instant::now();
|
||||
renderer.ui.set_text("Performance", 1, format!("Update: {} ms", timer.elapsed().as_millis()));
|
||||
|
||||
// Redraw
|
||||
window.request_redraw();
|
||||
// Redraw if running
|
||||
//if let GameState::Running = game.get_state() {
|
||||
window.request_redraw();
|
||||
//}
|
||||
}
|
||||
// Draw
|
||||
Event::RedrawRequested(_) => {
|
||||
|
||||
// Display FPS
|
||||
let elapsed = fps_timer.elapsed().as_secs_f64();
|
||||
if elapsed >= 1.5 {
|
||||
let fps = (fps_counter as f64) / elapsed;
|
||||
println!("FPS: {}", (fps*100.0_f64).floor()/100.0);
|
||||
if elapsed >= 0.5 {
|
||||
let mut fps = (fps_counter as f64) / elapsed;
|
||||
fps = (fps*100.0_f64).floor() / 100.0;
|
||||
renderer.ui.set_text("Performance", 0, format!("FPS: {}", fps));
|
||||
fps_timer = std::time::Instant::now();
|
||||
fps_counter = 0;
|
||||
}
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
use std::convert::TryInto;
|
||||
use cgmath::Point3;
|
||||
use crate::renderer::{buffers, buffers::{BLOCK_SIZE, BLOCK_TEX_SIZE, BLOCK_TEX_LEN, BLOCK_LEN, BLOCK_ARR_LEN}};
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RenderBlock {
|
||||
pub id: usize,
|
||||
pub data: [u8;BLOCK_LEN]
|
||||
}
|
||||
|
||||
impl RenderBlock {
|
||||
pub fn new() -> Self {
|
||||
Self { id: 0, data: [0_u8;BLOCK_LEN] }
|
||||
}
|
||||
|
||||
pub fn set(&mut self, voxel_pos: Point3<usize>, value: u8) {
|
||||
self.data[voxel_pos.x + buffers::BLOCK_SIZE * (voxel_pos.y + buffers::BLOCK_SIZE * voxel_pos.z)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Blocks {
|
||||
blocks: Box<[RenderBlock]>,
|
||||
free_block: usize,
|
||||
texture: wgpu::Texture,
|
||||
pub bind_layout: wgpu::BindGroupLayout,
|
||||
pub bind_group: wgpu::BindGroup
|
||||
}
|
||||
|
||||
impl Blocks {
|
||||
|
||||
/*
|
||||
* Create buffer in memory and gpu
|
||||
*/
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
|
||||
// Create texture
|
||||
let block_tex_dim = BLOCK_TEX_SIZE * BLOCK_SIZE;
|
||||
let size = wgpu::Extent3d { width: BLOCK_SIZE as u32, height: block_tex_dim as u32, depth: block_tex_dim as u32 };
|
||||
let texture = device.create_texture(
|
||||
&wgpu::TextureDescriptor {
|
||||
size: size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D3,
|
||||
format: wgpu::TextureFormat::R8Uint,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, // STORAGE?
|
||||
label: Some("block texture"),
|
||||
}
|
||||
);
|
||||
|
||||
// Bind layout
|
||||
let bind_layout = device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture { multisampled: false, view_dimension: wgpu::TextureViewDimension::D3, sample_type: wgpu::TextureSampleType::Uint },
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler { comparison: false, filtering: false },
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("Node texture layout"),
|
||||
}
|
||||
);
|
||||
|
||||
// Bind group
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
});
|
||||
let bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&view) },
|
||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) }
|
||||
],
|
||||
label: Some("block texture group"),
|
||||
}
|
||||
);
|
||||
|
||||
// Data
|
||||
let mut blocks = vec![RenderBlock::new(); BLOCK_ARR_LEN].into_boxed_slice();
|
||||
for (i, b) in blocks.iter_mut().enumerate() { b.id = i; }
|
||||
println!("Blocks size: {} MB, max: {}", BLOCK_TEX_LEN as f32 / 1024.0 / 1024.0, BLOCK_ARR_LEN);
|
||||
|
||||
// Done
|
||||
Self { blocks, texture, bind_layout, bind_group, free_block: 0 }
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes block to gpu
|
||||
*/
|
||||
pub fn write(&mut self, queue: &wgpu::Queue, block_id: usize) {
|
||||
|
||||
// Get block from id
|
||||
let block = &self.blocks[block_id];
|
||||
|
||||
// Calculate block position in texture
|
||||
let y = (block.id%BLOCK_TEX_SIZE * BLOCK_SIZE) as u32;
|
||||
let z = (block.id/BLOCK_TEX_SIZE * BLOCK_SIZE) as u32;
|
||||
let block_origin = wgpu::Origin3d{ x:0, y, z };
|
||||
let block_size = wgpu::Extent3d{ width: BLOCK_SIZE as u32, height: BLOCK_SIZE as u32, depth: BLOCK_SIZE as u32 };
|
||||
let block_bytes : [u8;BLOCK_LEN] = block.data.try_into().unwrap();
|
||||
|
||||
// Write to texture
|
||||
queue.write_texture(
|
||||
wgpu::TextureCopyView { texture: &self.texture, mip_level: 0, origin: block_origin },
|
||||
&block_bytes,
|
||||
wgpu::TextureDataLayout { offset: 0, bytes_per_row: block_size.width, rows_per_image: block_size.height },
|
||||
block_size
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get(&mut self, block_id: usize) -> &mut RenderBlock {
|
||||
&mut self.blocks[block_id]
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates new block
|
||||
*/
|
||||
pub fn alloc(&mut self) -> &mut RenderBlock {
|
||||
// Make sure we still have some free blocks
|
||||
if self.free_block >= BLOCK_ARR_LEN { panic!("No more free blocks! we should consider reusing blocks..."); }
|
||||
|
||||
self.free_block += 1;
|
||||
&mut self.blocks[self.free_block - 1]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
use std::convert::TryInto;
|
||||
use cgmath::Vector3;
|
||||
use crate::game::world::block::WorldBlock;
|
||||
use super::nodes::{Nodes, NODE_TEX_SIZE, NODE_TEX_SIZE_QB};
|
||||
|
||||
//note: remember to update in main.frag
|
||||
pub const BLOCK_SIZE : usize = 32; // 32x32x32 block size
|
||||
pub const BLOCK_TEX_SIZE : usize = 40; // number of blocks in texture (32^3 = 1GB, 40^3 = 2GB)
|
||||
const BLOCK_SIZE_QB: usize = BLOCK_SIZE*BLOCK_SIZE*BLOCK_SIZE;
|
||||
const BLOCK_TEX_SIZE_SQ: usize = BLOCK_TEX_SIZE*BLOCK_TEX_SIZE;
|
||||
const BLOCK_TEX_SIZE_QB: usize = BLOCK_TEX_SIZE*BLOCK_TEX_SIZE*BLOCK_TEX_SIZE;
|
||||
|
||||
|
||||
pub struct ContentStats {
|
||||
pub node_tex_size: f64, // MB
|
||||
pub block_tex_size: f64, // MB
|
||||
pub block_tex_used: f64, // MB
|
||||
pub block_count: u64, // amount
|
||||
pub block_freed: u64, // amount
|
||||
pub block_used: u64, // amount
|
||||
}
|
||||
|
||||
pub struct Content {
|
||||
nodes: Nodes,
|
||||
node_texture: wgpu::Texture,
|
||||
block_freed: Vec<usize>,
|
||||
block_freeidx: usize,
|
||||
block_texture: wgpu::Texture,
|
||||
pub bind_layout: wgpu::BindGroupLayout,
|
||||
pub bind_group: wgpu::BindGroup
|
||||
}
|
||||
|
||||
impl Content {
|
||||
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
// Create textures
|
||||
let node_texture = device.create_texture(
|
||||
&wgpu::TextureDescriptor {
|
||||
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D3,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, label: Some("NodeTexture"),
|
||||
size: wgpu::Extent3d{ width: NODE_TEX_SIZE as u32, height: NODE_TEX_SIZE as u32, depth: NODE_TEX_SIZE as u32 },
|
||||
format: wgpu::TextureFormat::R32Uint,
|
||||
}
|
||||
);
|
||||
let block_texture = device.create_texture(
|
||||
&wgpu::TextureDescriptor {
|
||||
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D3,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, label: Some("BlockTexture"),
|
||||
size: wgpu::Extent3d{ width: (BLOCK_TEX_SIZE * BLOCK_SIZE) as u32, height: (BLOCK_TEX_SIZE * BLOCK_SIZE) as u32, depth: (BLOCK_TEX_SIZE * BLOCK_SIZE) as u32 },
|
||||
format: wgpu::TextureFormat::R8Uint,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// Bind layout
|
||||
let texture_entry = wgpu::BindGroupLayoutEntry {
|
||||
binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, count: None,
|
||||
ty: wgpu::BindingType::Texture { multisampled: false, view_dimension: wgpu::TextureViewDimension::D3, sample_type: wgpu::TextureSampleType::Uint },
|
||||
};
|
||||
let bind_layout = device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None, entries: &[
|
||||
wgpu::BindGroupLayoutEntry { binding: 0, ..texture_entry },
|
||||
wgpu::BindGroupLayoutEntry { binding: 1, ..texture_entry },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// Bind group
|
||||
let bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
label: None, layout: &bind_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&node_texture.create_view(&wgpu::TextureViewDescriptor::default())) },
|
||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&block_texture.create_view(&wgpu::TextureViewDescriptor::default())) },
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// Done
|
||||
let nodes = Nodes::new();
|
||||
let block_freeidx = 0;
|
||||
let block_freed = Vec::new();
|
||||
Self { nodes, block_freed, node_texture, block_freeidx, block_texture, bind_layout, bind_group }
|
||||
}
|
||||
|
||||
pub fn is_busy(&self) -> bool {
|
||||
self.nodes.is_busy()
|
||||
}
|
||||
|
||||
pub fn get_world_offset(&self) -> Vector3<i32> {
|
||||
self.nodes.get_world_offset()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, queue: &wgpu::Queue) {
|
||||
self.nodes.update(queue, &self.node_texture);
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> ContentStats {
|
||||
const TO_MB: f64 = 1.0 / (1024.0 * 1024.0);
|
||||
|
||||
let block_used = self.block_freeidx as u64;
|
||||
let block_freed = self.block_freed.len() as u64;
|
||||
let block_count = BLOCK_TEX_SIZE_QB as u64;
|
||||
|
||||
let node_tex_size = (NODE_TEX_SIZE_QB*4) as f64 * TO_MB;
|
||||
let block_tex_used = (block_used * BLOCK_SIZE_QB as u64) as f64 * TO_MB;
|
||||
let block_tex_size = (BLOCK_TEX_SIZE_QB*BLOCK_SIZE_QB) as f64 * TO_MB;
|
||||
|
||||
ContentStats { node_tex_size, block_tex_size, block_tex_used, block_count, block_freed, block_used }
|
||||
}
|
||||
|
||||
pub fn write(&mut self, queue: &wgpu::Queue, block_pos: &Vector3<i32>, block: &WorldBlock) {
|
||||
|
||||
// 0,0 at node buffer center
|
||||
let half = NODE_TEX_SIZE as i32 / 2;
|
||||
let pos = block_pos + &Vector3{x: half, y: half, z: half};
|
||||
|
||||
// Check bounds
|
||||
let size = NODE_TEX_SIZE as i32;
|
||||
if pos.x < 0 || pos.x >= size || pos.y < 0 || pos.y >= size || pos.z < 0 || pos.z >= size {
|
||||
println!("{:?} outside bounds", pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get node
|
||||
let index = (pos.x + NODE_TEX_SIZE as i32 * (pos.y + NODE_TEX_SIZE as i32 * pos.z)) as usize;
|
||||
let mut value = self.nodes.get_node(index) as usize;
|
||||
|
||||
// Allocate new block
|
||||
if value == 0 {
|
||||
// Reuse freed if available, allocate new if not
|
||||
if let Some(idx) = self.block_freed.pop() {
|
||||
value = idx;
|
||||
} else {
|
||||
value = self.block_freeidx + 1;
|
||||
}
|
||||
|
||||
// Not enough blocks left
|
||||
if value >= BLOCK_TEX_SIZE_QB {
|
||||
println!("Blocks buffer filled, no more blocks can be allocated!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign
|
||||
self.block_freeidx = value;
|
||||
self.nodes.set_node(index, value as u32);
|
||||
}
|
||||
|
||||
// Calculate position in block texture
|
||||
let mut idx = value-1;
|
||||
let z = idx / BLOCK_TEX_SIZE_SQ;
|
||||
idx -= z * BLOCK_TEX_SIZE_SQ;
|
||||
let y = idx / BLOCK_TEX_SIZE;
|
||||
let x = idx % BLOCK_TEX_SIZE;
|
||||
|
||||
let block_origin = wgpu::Origin3d{ x: (x*BLOCK_SIZE) as u32, y: (y*BLOCK_SIZE) as u32, z: (z*BLOCK_SIZE) as u32 };
|
||||
let block_size = wgpu::Extent3d{ width: BLOCK_SIZE as u32, height: BLOCK_SIZE as u32, depth: BLOCK_SIZE as u32 };
|
||||
let block_bytes : [u8;BLOCK_SIZE*BLOCK_SIZE*BLOCK_SIZE] = block.materials[..].try_into().unwrap();
|
||||
|
||||
// Write block
|
||||
queue.write_texture(
|
||||
wgpu::TextureCopyView { texture: &self.block_texture, mip_level: 0, origin: block_origin }, &block_bytes,
|
||||
wgpu::TextureDataLayout { offset: 0, bytes_per_row: block_size.width, rows_per_image: block_size.height }, block_size
|
||||
);
|
||||
}
|
||||
|
||||
pub fn shift(&mut self, offset: &Vector3<i32>) {
|
||||
|
||||
// Work in progress.
|
||||
// Note: [z is swapped with x ], [ node buffer is not equal to render distance?]
|
||||
|
||||
// if offset.x > 0 {
|
||||
// let size = NODE_TEX_SIZE as i32;
|
||||
|
||||
// for x in 0..128 {
|
||||
// for y in 0..128 {
|
||||
// for z in 0..32 {
|
||||
// let index = (x + size * (y + size * z)) as usize;
|
||||
// let value = self.nodes.get_node(index);
|
||||
// if value != 0 { self.block_freed.push(value as usize); }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// println!("Saved: {}", self.block_freed.len());
|
||||
// }
|
||||
|
||||
self.nodes.shift(offset);
|
||||
}
|
||||
}
|
||||
@@ -37,39 +37,33 @@ impl Materials {
|
||||
// Create buffer
|
||||
let buffer = device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Materials buffer"),
|
||||
contents: bytemuck::cast_slice(&materials),
|
||||
label: Some("Materials buffer"), contents: bytemuck::cast_slice(&materials),
|
||||
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
||||
}
|
||||
);
|
||||
|
||||
// Create bind group
|
||||
let bind_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
// Bind layout
|
||||
let buffer_entry = wgpu::BindGroupLayoutEntry {
|
||||
binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, count: None,
|
||||
ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None },
|
||||
};
|
||||
let bind_layout = device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None, entries: &[
|
||||
wgpu::BindGroupLayoutEntry { binding: 0, ..buffer_entry },
|
||||
],
|
||||
label: Some("Materials buffer layout"),
|
||||
});
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}
|
||||
],
|
||||
label: Some("Materials buffer group"),
|
||||
});
|
||||
|
||||
// Bind group
|
||||
let bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
label: Some("Materials buffer group"),layout: &bind_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry { binding: 0, resource: buffer.as_entire_binding() }
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// Done
|
||||
Materials { buffer, materials, bind_layout, bind_group }
|
||||
}
|
||||
|
||||
+11
-36
@@ -1,50 +1,25 @@
|
||||
|
||||
// Consts
|
||||
|
||||
pub const BLOCK_SIZE : usize = 32; // 32x32x32 block size
|
||||
pub const BLOCK_TEX_SIZE : usize = 64; // 64x64 blocks in texture
|
||||
pub const NODE_TEX_SIZE: usize = 48; // 32x32x32 nodes in texture
|
||||
|
||||
|
||||
pub const NODE_TEX_LEN: usize = NODE_TEX_SIZE*NODE_TEX_SIZE*NODE_TEX_SIZE; // Number of u32 in node texture
|
||||
pub const BLOCK_LEN : usize = BLOCK_SIZE*BLOCK_SIZE*BLOCK_SIZE; // Number of u8 in one block
|
||||
pub const BLOCK_ARR_LEN: usize = BLOCK_TEX_SIZE*BLOCK_TEX_SIZE; // Number of blocks in texture
|
||||
pub const BLOCK_TEX_LEN: usize = BLOCK_TEX_SIZE*BLOCK_TEX_SIZE * BLOCK_LEN; // Number of u8 in block texture
|
||||
|
||||
|
||||
|
||||
// Import names
|
||||
|
||||
mod nodes;
|
||||
pub use nodes::Nodes;
|
||||
|
||||
mod blocks;
|
||||
pub use blocks::Blocks;
|
||||
pub use blocks::RenderBlock;
|
||||
|
||||
mod uniform;
|
||||
pub use uniform::Uniform;
|
||||
pub use uniform::UniformValues;
|
||||
|
||||
pub mod content;
|
||||
pub use content::Content;
|
||||
mod materials;
|
||||
pub use materials::Materials;
|
||||
|
||||
// Create buffers
|
||||
pub mod nodes;
|
||||
|
||||
pub struct Buffers {
|
||||
pub uniform_buffer: Uniform,
|
||||
pub node_buffer: Nodes,
|
||||
pub brick_buffer: Blocks,
|
||||
pub material_buffer: Materials
|
||||
pub uniforms: Uniform,
|
||||
pub content: Content,
|
||||
pub materials: Materials
|
||||
}
|
||||
|
||||
impl Buffers {
|
||||
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
let uniform_buffer = Uniform::new(device);
|
||||
let node_buffer = Nodes::new(device);
|
||||
let brick_buffer = Blocks::new(device);
|
||||
let material_buffer = Materials::new(device);
|
||||
Self { uniform_buffer, node_buffer, brick_buffer, material_buffer }
|
||||
Self {
|
||||
uniforms: Uniform::new(device),
|
||||
content: Content::new(device),
|
||||
materials: Materials::new(device)
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
-79
@@ -1,91 +1,129 @@
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::cmp::{min, max};
|
||||
use std::time::Instant;
|
||||
use cgmath::Vector3;
|
||||
use std::thread;
|
||||
use std::sync::{RwLock, mpsc, mpsc::Receiver, mpsc::Sender};
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
use crate::renderer::buffers::{NODE_TEX_SIZE, NODE_TEX_LEN};
|
||||
pub const NODE_TEX_SIZE: usize = 128; // 32x32x32 nodes in texture
|
||||
pub const NODE_TEX_SIZE_SQ: usize = NODE_TEX_SIZE*NODE_TEX_SIZE;
|
||||
pub const NODE_TEX_SIZE_QB: usize = NODE_TEX_SIZE*NODE_TEX_SIZE*NODE_TEX_SIZE;
|
||||
|
||||
type NodeBuffer = (Vector3<i32>, Box<[u32]>);
|
||||
|
||||
pub struct Nodes {
|
||||
pub nodes: Box<[u32]>,
|
||||
texture: wgpu::Texture,
|
||||
size: wgpu::Extent3d,
|
||||
pub bind_layout: wgpu::BindGroupLayout,
|
||||
pub bind_group: wgpu::BindGroup
|
||||
buffer: Arc<RwLock<Box<[u32]>>>,
|
||||
offset: Vector3<i32>,
|
||||
dirty: bool,
|
||||
shift: Option<Vector3<i32>>,
|
||||
working: bool,
|
||||
channel: (Sender<NodeBuffer>, Receiver<NodeBuffer>)
|
||||
}
|
||||
|
||||
impl Nodes {
|
||||
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
// Present
|
||||
buffer: Arc::new(RwLock::new(vec![0_u32; NODE_TEX_SIZE_QB].into_boxed_slice())),
|
||||
offset: Vector3{x:0,y:0,z:0},
|
||||
dirty: false,
|
||||
|
||||
// Create texture
|
||||
let size = wgpu::Extent3d { width: NODE_TEX_SIZE as u32, height: NODE_TEX_SIZE as u32, depth: NODE_TEX_SIZE as u32 };
|
||||
let texture = device.create_texture(
|
||||
&wgpu::TextureDescriptor {
|
||||
size: size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D3,
|
||||
format: wgpu::TextureFormat::R32Uint,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, // STORAGE?
|
||||
label: Some("Node texture"),
|
||||
}
|
||||
);
|
||||
|
||||
// Bind layout
|
||||
let bind_layout = device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture { multisampled: false, view_dimension: wgpu::TextureViewDimension::D3, sample_type: wgpu::TextureSampleType::Uint },
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler { comparison: false, filtering: false },
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("Node texture layout"),
|
||||
}
|
||||
);
|
||||
|
||||
// Bind group
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
});
|
||||
let bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&view) },
|
||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) }
|
||||
],
|
||||
label: Some("Node texture group"),
|
||||
}
|
||||
);
|
||||
|
||||
// Data
|
||||
let nodes = vec![0_u32; NODE_TEX_LEN].into_boxed_slice();
|
||||
println!("Nodes size: {} MB", (nodes.len() as f32 * 4.0 / 1024.0 / 1024.0 * 100.0).round() / 100.0);
|
||||
|
||||
// Done
|
||||
Self { nodes, texture, size, bind_layout, bind_group }
|
||||
shift: None,
|
||||
working: false,
|
||||
channel: mpsc::channel()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, queue: &wgpu::Queue) {
|
||||
// Convert 32 bit values to 8 bit
|
||||
let mut bytes = [0_u8; NODE_TEX_LEN*4];
|
||||
LittleEndian::write_u32_into(&self.nodes, &mut bytes);
|
||||
pub fn get_world_offset(&self) -> Vector3<i32> {
|
||||
self.offset
|
||||
}
|
||||
|
||||
// Write
|
||||
queue.write_texture(
|
||||
wgpu::TextureCopyView { texture: &self.texture, mip_level: 0, origin: wgpu::Origin3d::ZERO },
|
||||
&bytes,
|
||||
wgpu::TextureDataLayout { offset: 0, bytes_per_row: 4*self.size.width, rows_per_image: self.size.height },
|
||||
self.size
|
||||
);
|
||||
pub fn get_node(&self, index: usize) -> u32 {
|
||||
self.buffer.read().unwrap()[index]
|
||||
}
|
||||
|
||||
pub fn is_busy(&self) -> bool {
|
||||
self.shift != None
|
||||
}
|
||||
|
||||
pub fn set_node(&mut self, index: usize, value: u32) {
|
||||
self.buffer.write().unwrap()[index] = value;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn shift(&mut self, offset: &Vector3<i32>) {
|
||||
if self.shift == None {
|
||||
self.shift = Some(*offset);
|
||||
} else {
|
||||
println!("Tried to shift while another shifing was in progress. Check RendererView.is_busy() before shifting.");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, queue: &wgpu::Queue, texture: &wgpu::Texture) {
|
||||
|
||||
// Check if there is some more work
|
||||
if !self.working {
|
||||
if let Some(offset) = self.shift {
|
||||
let source = self.buffer.clone();
|
||||
let tx = self.channel.0.clone();
|
||||
self.working = true;
|
||||
|
||||
// Spawn worker
|
||||
thread::spawn(move || {
|
||||
let source = source.read().unwrap();
|
||||
|
||||
let size = NODE_TEX_SIZE as i32;
|
||||
let mut target = vec![0_u32; NODE_TEX_SIZE_QB].into_boxed_slice();
|
||||
let (ox, oy, oz) = (offset.x, offset.y, offset.z);
|
||||
|
||||
// Copy slice of source buffer into target buffer
|
||||
for x in max(0, -ox)..min(size-ox, size) {
|
||||
for y in max(0, -oy)..min(size-oy, size) {
|
||||
for z in max(0, -oz)..min(size-oz, size) {
|
||||
let idx0 = x + size * (y + size * z);
|
||||
let idx1 = (x+ox) + size * ((y+oy) + size * (z+oz));
|
||||
target[idx1 as usize] = source[idx0 as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send result
|
||||
tx.send((offset, target)).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Receive work
|
||||
while let Ok(result) = self.channel.1.try_recv() {
|
||||
let moved_by = result.0;
|
||||
|
||||
self.offset += moved_by;
|
||||
self.buffer = Arc::new(RwLock::new(result.1));
|
||||
self.shift = None;
|
||||
self.dirty = true;
|
||||
self.working = false;
|
||||
|
||||
}
|
||||
|
||||
if self.dirty {
|
||||
self.dirty = false;
|
||||
|
||||
// Note: this takes usually 2ms, up to 9ms
|
||||
// TODO: Maybe instead of converting u32 to u8, just store u8 and convert them when writing/reading
|
||||
|
||||
// Convert u32 node buffer to u8
|
||||
let node_origin = wgpu::Origin3d{ x:0, y: 0, z: 0 };
|
||||
let node_size = wgpu::Extent3d { width: NODE_TEX_SIZE as u32, height: NODE_TEX_SIZE as u32, depth: NODE_TEX_SIZE as u32 };
|
||||
let mut node_bytes = vec![0_u8; NODE_TEX_SIZE_QB*4].into_boxed_slice();
|
||||
LittleEndian::write_u32_into(&self.buffer.read().unwrap()[..], &mut node_bytes[..]);
|
||||
|
||||
// Write nodes
|
||||
queue.write_texture(
|
||||
wgpu::TextureCopyView { texture: texture, mip_level: 0, origin: node_origin }, &node_bytes[..],
|
||||
wgpu::TextureDataLayout { offset: 0, bytes_per_row: 4*node_size.width, rows_per_image: node_size.height }, node_size
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,10 @@ impl UniformValues {
|
||||
Self { sun_dir, .. Default::default() }
|
||||
}
|
||||
|
||||
pub fn update(&mut self, camera: &Camera, time: f32) {
|
||||
pub fn update(&mut self, camera: &Camera, offset: Vector3<f32>, time: f32) {
|
||||
self.view_inv = camera.get_view().invert().unwrap().into();
|
||||
self.proj_inv = camera.get_projection().invert().unwrap().into();
|
||||
self.cam_pos = camera.get_position().into();
|
||||
self.cam_pos = (camera.get_position() + offset).into();
|
||||
self.time = time;
|
||||
}
|
||||
}
|
||||
@@ -47,39 +47,32 @@ impl Uniform {
|
||||
// Create buffer
|
||||
let buffer = device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Uniform buffer"),
|
||||
contents: bytemuck::cast_slice(&[values]),
|
||||
label: Some("Uniform buffer"), contents: bytemuck::cast_slice(&[values]),
|
||||
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
||||
}
|
||||
);
|
||||
|
||||
// Create bind group
|
||||
// Bind layout
|
||||
let buffer_entry = wgpu::BindGroupLayoutEntry {
|
||||
binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, count: None,
|
||||
ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None },
|
||||
};
|
||||
let bind_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
label: None, entries: &[
|
||||
wgpu::BindGroupLayoutEntry { binding: 0, ..buffer_entry }
|
||||
],
|
||||
label: Some("Uniform buffer layout"),
|
||||
});
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}
|
||||
],
|
||||
label: Some("Uniform buffer group"),
|
||||
});
|
||||
|
||||
// Bind group
|
||||
let bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
label: None, layout: &bind_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry { binding: 0, resource: buffer.as_entire_binding(), }
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
// Done
|
||||
Uniform { buffer, values, bind_layout, bind_group }
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ impl Camera {
|
||||
|
||||
pub fn new(aspect: f32) -> Self {
|
||||
let mut instance = Self {
|
||||
position: (2.0,1.0,2.0).into(),
|
||||
position: (1.0,2.0,1.0).into(),
|
||||
view_matrix: Matrix4::look_to_rh(
|
||||
(0.0,0.0,0.0).into(),
|
||||
Vector3{ x:0.66, y:0.0, z:0.66}.normalize(),
|
||||
@@ -31,6 +31,7 @@ impl Camera {
|
||||
|
||||
pub trait CameraTransform {
|
||||
fn update(&mut self, position: Point3<f32>, forward: Vector3<f32>, up: Vector3<f32>);
|
||||
fn get_position(&self) -> Point3<f32>;
|
||||
}
|
||||
|
||||
impl CameraTransform for Camera {
|
||||
@@ -38,4 +39,7 @@ impl CameraTransform for Camera {
|
||||
self.position = position;
|
||||
self.view_matrix = Matrix4::look_to_rh((0.0,0.0,0.0).into(), forward, up);
|
||||
}
|
||||
fn get_position(&self) -> Point3<f32> {
|
||||
self.position
|
||||
}
|
||||
}
|
||||
+68
-36
@@ -1,16 +1,15 @@
|
||||
use cgmath::Vector3;
|
||||
use std::time::Instant;
|
||||
use winit::{window::Window, dpi::PhysicalSize};
|
||||
|
||||
pub mod buffers;
|
||||
use buffers::Buffers;
|
||||
|
||||
mod passes;
|
||||
use passes::{RaytracePass, PostprocessPass};
|
||||
|
||||
pub mod camera;
|
||||
use camera::Camera;
|
||||
|
||||
pub mod renderspace;
|
||||
pub use renderspace::RenderSpace;
|
||||
pub mod renderer_view;
|
||||
mod ui;
|
||||
use ui::UserInterface;
|
||||
|
||||
pub struct Renderer {
|
||||
surface: wgpu::Surface,
|
||||
@@ -22,12 +21,12 @@ pub struct Renderer {
|
||||
texture: wgpu::TextureView,
|
||||
raytrace_pass: RaytracePass,
|
||||
postprocess_pass: PostprocessPass,
|
||||
pub ui: UserInterface,
|
||||
// Buffers
|
||||
buffers: Buffers,
|
||||
// Other
|
||||
pub camera: Camera,
|
||||
start: std::time::Instant,
|
||||
changed_bricks: Vec<usize>,
|
||||
start: std::time::Instant
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
@@ -35,18 +34,21 @@ impl Renderer {
|
||||
/*
|
||||
* Initializes renderer and all buffers
|
||||
*/
|
||||
pub async fn new(window: &Window) -> Self {
|
||||
pub fn new(window: &Window) -> Self {
|
||||
|
||||
// Create surface
|
||||
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
|
||||
let surface = unsafe { instance.create_surface(window) };
|
||||
|
||||
// Pick and initialize device
|
||||
let adapter_options = wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, compatible_surface: Some(&surface) };
|
||||
let adapter = instance.request_adapter(&adapter_options).await.unwrap();
|
||||
let device_desc = wgpu::DeviceDescriptor { features: wgpu::Features::empty(), limits: wgpu::Limits::default(), label: None };
|
||||
let (device, queue) = adapter.request_device(&device_desc, None).await.unwrap();
|
||||
|
||||
let (adapter, mut device, queue) = futures::executor::block_on(async {
|
||||
let adapter_options = wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, compatible_surface: Some(&surface) };
|
||||
let adapter = instance.request_adapter(&adapter_options).await.unwrap();
|
||||
let device_desc = wgpu::DeviceDescriptor { features: wgpu::Features::empty(), limits: wgpu::Limits::default(), label: None };
|
||||
let (device, queue) = adapter.request_device(&device_desc, None).await.unwrap();
|
||||
(adapter, device, queue)
|
||||
});
|
||||
|
||||
// Create swapchain
|
||||
let window_size = window.inner_size();
|
||||
let swapchain_desc = wgpu::SwapChainDescriptor {
|
||||
@@ -64,7 +66,7 @@ impl Renderer {
|
||||
|
||||
// Middleman texture
|
||||
let texture_descriptor = wgpu::TextureDescriptor {
|
||||
label: Some("glow_post_process_texture1"),
|
||||
label: Some("Middle texture"),
|
||||
size: wgpu::Extent3d { width: swapchain_desc.width,
|
||||
height: swapchain_desc.height,
|
||||
depth: 1,
|
||||
@@ -81,13 +83,11 @@ impl Renderer {
|
||||
let buffers = Buffers::new(&device);
|
||||
let raytrace_pass = RaytracePass::new(&device, &swapchain_desc, &buffers);
|
||||
let postprocess_pass = PostprocessPass::new(&device, &swapchain_desc, &texture);
|
||||
let ui = UserInterface::new(&mut device, swapchain_desc.format);
|
||||
|
||||
// Other
|
||||
let start = std::time::Instant::now();
|
||||
let changed_bricks: Vec<usize> = Vec::new();
|
||||
|
||||
println!("Initialized");
|
||||
Self { surface, device, queue, swapchain_desc, swapchain, texture, raytrace_pass, postprocess_pass, buffers, camera, start, changed_bricks }
|
||||
Self { surface, device, queue, swapchain_desc, swapchain, texture, raytrace_pass, postprocess_pass, ui, buffers, camera, start }
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -103,8 +103,26 @@ impl Renderer {
|
||||
// Update camera
|
||||
self.camera.set_aspect(size.width as f32 / size.height as f32);
|
||||
}
|
||||
|
||||
// Recreate swapchain
|
||||
self.swapchain = self.device.create_swap_chain(&self.surface, &self.swapchain_desc);
|
||||
|
||||
// Recreate texture
|
||||
let texture_descriptor = wgpu::TextureDescriptor {
|
||||
label: Some("Middle texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width: self.swapchain_desc.width,
|
||||
height: self.swapchain_desc.height,
|
||||
depth: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: self.swapchain_desc.format,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||
};
|
||||
self.texture = self.device.create_texture(&texture_descriptor).create_view(&wgpu::TextureViewDescriptor::default());
|
||||
self.postprocess_pass = PostprocessPass::new(&self.device, &self.swapchain_desc, &self.texture);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,36 +130,50 @@ impl Renderer {
|
||||
*/
|
||||
pub fn render(&mut self) -> Result<(), wgpu::SwapChainError> {
|
||||
|
||||
// Update content
|
||||
let timer = Instant::now();
|
||||
self.buffers.content.update(&self.queue);
|
||||
self.ui.set_text("Performance", 3, format!("Update content: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
|
||||
|
||||
// Update uniform buffer
|
||||
self.buffers.uniform_buffer.values.update(&self.camera, self.start.elapsed().as_secs_f32());
|
||||
self.queue.write_buffer(&self.buffers.uniform_buffer.buffer, 0, bytemuck::cast_slice(&[self.buffers.uniform_buffer.values]));
|
||||
let timer = Instant::now();
|
||||
let off = self.buffers.content.get_world_offset();
|
||||
let half = (buffers::nodes::NODE_TEX_SIZE / 2) as i32;
|
||||
let cam_offset = Vector3{x: (off.x + half) as f32, y: (off.y + half) as f32, z: (off.z + half) as f32 };
|
||||
|
||||
// Update changed nodes
|
||||
if self.changed_bricks.len() > 0 {
|
||||
self.buffers.node_buffer.write(&self.queue);
|
||||
for block_id in &self.changed_bricks {
|
||||
self.buffers.brick_buffer.write(&self.queue, *block_id);
|
||||
}
|
||||
|
||||
// let used = self.buffers.brick_buffer.next_block(false);
|
||||
// let total = buffers::BLOCK_TEX_SIZE * buffers::BLOCK_TEX_SIZE;
|
||||
// let changed = self.changed_bricks.len();
|
||||
// println!("Used: {}/{} Changed: {}", used, total, changed);
|
||||
|
||||
self.changed_bricks.clear();
|
||||
}
|
||||
self.buffers.uniforms.values.update(&self.camera, cam_offset, self.start.elapsed().as_secs_f32());
|
||||
self.queue.write_buffer(&self.buffers.uniforms.buffer, 0, bytemuck::cast_slice(&[self.buffers.uniforms.values]));
|
||||
self.ui.set_text("Performance", 4, format!("Update uniform: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
|
||||
|
||||
// Get next frame to render to
|
||||
let timer = Instant::now();
|
||||
let frame = self.swapchain.get_current_frame()?.output;
|
||||
self.ui.set_text("Performance", 5, format!("Swapchain get frame: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
|
||||
|
||||
// Create encoder that will build command buffer for us
|
||||
let timer = Instant::now();
|
||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Render Encoder"),});
|
||||
|
||||
self.raytrace_pass.render(&mut encoder, &mut self.buffers, &self.texture);//&frame.view);
|
||||
self.postprocess_pass.render(&mut encoder, &frame.view);
|
||||
self.ui.render(&self.device, &mut encoder, &frame.view, 1280, 720);
|
||||
self.ui.set_text("Performance", 6, format!("Build renderpass: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
|
||||
|
||||
// Submit encoder (command buffer)
|
||||
let timer = Instant::now();
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
self.ui.recall();
|
||||
self.ui.set_text("Performance", 7, format!("Submit queue: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
|
||||
|
||||
// Stats
|
||||
let content_stats = self.buffers.content.stats();
|
||||
self.ui.set_text("Renderer", 0, format!("Nodes: {}MB", content_stats.node_tex_size));
|
||||
self.ui.set_text("Renderer", 1, format!("Blocks: {} / {} MB ({} -{} / {})",
|
||||
content_stats.block_tex_used,
|
||||
content_stats.block_tex_size,
|
||||
content_stats.block_used,
|
||||
content_stats.block_freed,
|
||||
content_stats.block_count
|
||||
));
|
||||
|
||||
// Return ok
|
||||
Ok(())
|
||||
|
||||
@@ -28,10 +28,9 @@ impl RaytracePass {
|
||||
|
||||
// Bind group layouts
|
||||
let bind_group_layouts = [
|
||||
&buffers.uniform_buffer.bind_layout,
|
||||
&buffers.node_buffer.bind_layout,
|
||||
&buffers.brick_buffer.bind_layout,
|
||||
&buffers.material_buffer.bind_layout
|
||||
&buffers.uniforms.bind_layout,
|
||||
&buffers.content.bind_layout,
|
||||
&buffers.materials.bind_layout
|
||||
];
|
||||
|
||||
// Pipeline layout
|
||||
@@ -81,10 +80,9 @@ impl RaytracePass {
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
|
||||
// Bind groups
|
||||
render_pass.set_bind_group(0, &buffers.uniform_buffer.bind_group, &[]);
|
||||
render_pass.set_bind_group(1, &buffers.node_buffer.bind_group, &[]);
|
||||
render_pass.set_bind_group(2, &buffers.brick_buffer.bind_group, &[]);
|
||||
render_pass.set_bind_group(3, &buffers.material_buffer.bind_group, &[]);
|
||||
render_pass.set_bind_group(0, &buffers.uniforms.bind_group, &[]);
|
||||
render_pass.set_bind_group(1, &buffers.content.bind_group, &[]);
|
||||
render_pass.set_bind_group(2, &buffers.materials.bind_group, &[]);
|
||||
|
||||
// Draw 2 triangles
|
||||
render_pass.draw(0..6, 0..1);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::renderer::UserInterface;
|
||||
use crate::renderer::camera::CameraTransform;
|
||||
use crate::Renderer;
|
||||
use crate::{game, game::world::{chunk, chunk::WorldChunk}};
|
||||
use cgmath::Vector3;
|
||||
|
||||
pub trait RendererView {
|
||||
fn write_chunk(&mut self, chunk_pos: &Vector3<i32>, chunk: &WorldChunk) -> bool;
|
||||
fn shift(&mut self, offset: Vector3<i32>);
|
||||
fn get_camera_transform(&mut self) -> &mut dyn CameraTransform;
|
||||
fn get_ui(&mut self) -> &mut UserInterface;
|
||||
fn is_busy(&self) -> bool;
|
||||
}
|
||||
|
||||
impl RendererView for Renderer {
|
||||
|
||||
fn write_chunk(&mut self, chunk_pos: &Vector3<i32>, chunk: &WorldChunk) -> bool {
|
||||
// Calculate chunk position in renderer space
|
||||
let chunk_off = self.buffers.content.get_world_offset() / chunk::SIZE as i32;
|
||||
let pos = chunk_pos + chunk_off;
|
||||
// Make sure we are withing current world bounds
|
||||
if pos.x.abs() > game::RENDER_DIST { println!("out of bounds! x: {} off: {:?} pos: {:?}", pos.x, chunk_off, chunk_pos); return false; }
|
||||
if pos.y.abs() > game::RENDER_DIST { println!("out of bounds! y: {} off: {:?} pos: {:?}", pos.y, chunk_off, chunk_pos); return false; }
|
||||
if pos.z.abs() > game::RENDER_DIST { println!("out of bounds! z: {} off: {:?} pos: {:?}", pos.z, chunk_off, chunk_pos); return false; }
|
||||
// Write chunk to renderer
|
||||
//println!("write chunk {:?}", pos);
|
||||
let pos = pos * chunk::SIZE as i32;
|
||||
for (block_pos, block) in chunk.all_blocks() {
|
||||
self.buffers.content.write(&self.queue, &(block_pos + pos), block);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/*
|
||||
* offset: By how much to shift the world, in blocks
|
||||
*/
|
||||
fn shift(&mut self, offset: Vector3<i32>) {
|
||||
self.buffers.content.shift(&-offset);
|
||||
//println!("World shifted by {:?}", offset);
|
||||
}
|
||||
|
||||
fn get_camera_transform(&mut self) -> &mut dyn CameraTransform {
|
||||
&mut self.camera
|
||||
}
|
||||
|
||||
fn get_ui(&mut self) -> &mut UserInterface {
|
||||
&mut self.ui
|
||||
}
|
||||
|
||||
fn is_busy(&self) -> bool {
|
||||
self.buffers.content.is_busy()
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
use cgmath::Point3;
|
||||
use crate::renderer::{Renderer, buffers};
|
||||
|
||||
|
||||
pub trait RenderSpace {
|
||||
fn block_get(&mut self, block_pos: Point3<usize>) -> &mut buffers::RenderBlock;
|
||||
fn block_dirty(&mut self, id: usize);
|
||||
}
|
||||
|
||||
impl RenderSpace for Renderer {
|
||||
|
||||
/*
|
||||
* Grabs existing or allocates new block for given position
|
||||
*
|
||||
* block_pos: Position of the block in world, in block space
|
||||
* block_id: Block identifier, equal to Block.id
|
||||
*/
|
||||
fn block_get(&mut self, block_pos: Point3<usize>) -> &mut buffers::RenderBlock {
|
||||
// Get node
|
||||
let node_idx = block_pos.x + buffers::NODE_TEX_SIZE * (block_pos.y + buffers::NODE_TEX_SIZE * block_pos.z);
|
||||
let node_value = self.buffers.node_buffer.nodes[node_idx];
|
||||
|
||||
// Allocate new block
|
||||
if node_value == 0 {
|
||||
let block = self.buffers.brick_buffer.alloc();
|
||||
self.buffers.node_buffer.nodes[node_idx] = (block.id+1) as u32;
|
||||
return block;
|
||||
}
|
||||
|
||||
// Return existing block
|
||||
self.buffers.brick_buffer.get((node_value-1) as usize)
|
||||
}
|
||||
|
||||
/*
|
||||
* Marks block as dirty, will be sent to gpu at the next frame
|
||||
*
|
||||
* block_id: Block identifier, equal to Block.id
|
||||
*/
|
||||
fn block_dirty(&mut self, block_id: usize) {
|
||||
if !self.changed_bricks.contains(&block_id) {
|
||||
self.changed_bricks.push(block_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
use std::path::Path;
|
||||
use image::GenericImageView;
|
||||
|
||||
pub struct Texture {
|
||||
pub texture: wgpu::Texture,
|
||||
pub view: wgpu::TextureView,
|
||||
pub sampler: wgpu::Sampler,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
|
||||
pub fn from_file(device: &wgpu::Device, queue: &wgpu::Queue, str_path: &str) -> Self {
|
||||
let img_path = Path::new(str_path);
|
||||
let img = image::open(img_path).unwrap();
|
||||
Self::from_image(&device, &queue, &img)
|
||||
}
|
||||
|
||||
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage) -> Self {
|
||||
// Data from image
|
||||
let img_dim = img.dimensions();
|
||||
let img_data = img.as_rgba8().unwrap();
|
||||
|
||||
// Create texture on device
|
||||
let texture_size = wgpu::Extent3d { width: img_dim.0, height: img_dim.1, depth: 1, };
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
size: texture_size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
|
||||
label: None,
|
||||
});
|
||||
|
||||
// Write texture to memory
|
||||
queue.write_texture(
|
||||
wgpu::TextureCopyView { texture: &texture, mip_level: 0, origin: wgpu::Origin3d::ZERO },
|
||||
img_data,
|
||||
wgpu::TextureDataLayout { offset: 0, bytes_per_row: 4 * img_dim.0, rows_per_image: img_dim.1 },
|
||||
texture_size,
|
||||
);
|
||||
|
||||
// Create view and sampler
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(
|
||||
&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
//mag_filter: wgpu::FilterMode::Linear,
|
||||
mag_filter: wgpu::FilterMode::Nearest,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
Self { texture, view, sampler }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use std::collections::HashMap;
|
||||
use wgpu_glyph::{GlyphBrush, ab_glyph::FontArc, GlyphBrushBuilder, Section, Text};
|
||||
use wgpu::util::StagingBelt;
|
||||
use futures::executor::LocalPool;
|
||||
use futures::task::SpawnExt;
|
||||
|
||||
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
||||
//const BLUE: [f32; 4] = [0.0, 0.2, 1.0, 1.0];
|
||||
const GREEN: [f32; 4] = [0.1, 1.0, 0.0, 1.0];
|
||||
|
||||
|
||||
pub struct UserInterface {
|
||||
// Glyph drawing
|
||||
staging_belt: StagingBelt,
|
||||
local_pool: LocalPool,
|
||||
brush: GlyphBrush<()>,
|
||||
// Texts
|
||||
texts: HashMap<String, Vec<String>>
|
||||
}
|
||||
|
||||
impl UserInterface {
|
||||
|
||||
pub fn new(device: &mut wgpu::Device, render_format: wgpu::TextureFormat) -> Self {
|
||||
let font = FontArc::try_from_slice(include_bytes!("../../assets/font.ttf")).unwrap();
|
||||
Self {
|
||||
staging_belt: StagingBelt::new(1024),
|
||||
local_pool: LocalPool::new(),
|
||||
brush: GlyphBrushBuilder::using_font(font).build(device, render_format),
|
||||
texts: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn print(brush: &mut GlyphBrush<()>, line: u32, text: &str, color: [f32; 4]) {
|
||||
let t = Text::new(text).with_color(color);
|
||||
brush.queue(Section {
|
||||
screen_position: (5 as f32, (5 + line*15) as f32),
|
||||
bounds: (t.scale.x * t.text.len() as f32, t.scale.y),
|
||||
text: vec![t],
|
||||
..Section::default()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_text(&mut self, cat: &str, line: usize, text: String) {
|
||||
// Category
|
||||
let c = &cat.to_string();
|
||||
if !self.texts.contains_key(c) {self.texts.insert(c.clone(), Vec::new());}
|
||||
// Texts
|
||||
let v = self.texts.get_mut(c).unwrap();
|
||||
while v.len() < line+1 { v.push(String::new()); }
|
||||
v[line] = text;
|
||||
}
|
||||
|
||||
pub fn render(&mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, width: u32, height: u32) {
|
||||
let mut line = 0;
|
||||
for (cat, texts) in self.texts.iter_mut() {
|
||||
UserInterface::print(&mut self.brush, line, cat, GREEN);
|
||||
line += 1;
|
||||
for text in texts.iter() {
|
||||
UserInterface::print(&mut self.brush, line, text, WHITE);
|
||||
line += 1;
|
||||
}
|
||||
line += 1;
|
||||
}
|
||||
|
||||
// Draw
|
||||
self.brush.draw_queued(&device, &mut self.staging_belt, encoder, target, width, height).expect("Draw queued");
|
||||
self.staging_belt.finish();
|
||||
}
|
||||
|
||||
pub fn recall(&mut self) {
|
||||
// Reset staging belt after drawing
|
||||
self.local_pool.spawner().spawn(self.staging_belt.recall()).expect("Recall staging belt");
|
||||
self.local_pool.run_until_stalled();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -15,14 +15,14 @@ layout(set=0, binding=0) uniform Uniform {
|
||||
float _Time;
|
||||
};
|
||||
layout(set=1, binding=0) uniform usampler3D _NodesTexture;
|
||||
layout(set=2, binding=0) uniform usampler3D _BricksTexture;
|
||||
layout(set=3, binding=0) uniform Materials { Material _Materials[256]; };
|
||||
layout(set=1, binding=1) uniform usampler3D _BricksTexture;
|
||||
layout(set=2, binding=0) uniform Materials { Material _Materials[256]; };
|
||||
|
||||
// Defines
|
||||
#define EPSILON 0.00000001
|
||||
#define SCALE 1.0
|
||||
#define NODE_TEX_SIZE 48
|
||||
#define BLOCK_TEX_SIZE 64
|
||||
#define NODE_TEX_SIZE 64
|
||||
#define BLOCK_TEX_SIZE 40
|
||||
#define BLOCK_SIZE 32
|
||||
|
||||
// Includes
|
||||
@@ -36,28 +36,49 @@ vec3 solve()
|
||||
// Calculate ray direction
|
||||
vec3 view_dir = (_ProjMatrixInv * vec4(_InUV, 0, 1)).xyz;
|
||||
vec3 rayDir = normalize(_ViewMatrix * vec4(view_dir,0)).xyz;
|
||||
Ray primaryRay = Ray((_CamPos + vec3(16, 0, 16)) / SCALE, rayDir);
|
||||
Ray primaryRay = Ray(_CamPos / SCALE, rayDir);
|
||||
|
||||
// Raycast
|
||||
HitResult primary = castNodes(primaryRay, 100);
|
||||
if(primary.data > 0)
|
||||
{
|
||||
vec3 color = applyLighting(primary.pos, primaryRay.dir, primary.normal, primary.data);
|
||||
vec3 col = applyLighting(primary.pos, primaryRay.dir, primary.normal, primary.data);
|
||||
vec3 hitPos = primary.pos-primary.normal*EPSILON;
|
||||
|
||||
// Shadow ray
|
||||
Ray shadowRay = Ray(hitPos, _SunDir);
|
||||
HitResult shadow = castNodes(shadowRay, 50);
|
||||
if(shadow.data > 0) color *= 0.1;
|
||||
if(shadow.data > 0) col *= 0.1;
|
||||
|
||||
// Secondary ray
|
||||
// Ray secondRay = Ray(hitPos, randomHemisphere(primary.normal));
|
||||
// HitResult second = castNodes(secondRay, 25);
|
||||
// if(second.data > 0) color += getAlbedo(second.data) * 0.8;
|
||||
// if(second.data > 0) col += getAlbedo(second.data) * 0.8;
|
||||
|
||||
return color;
|
||||
// Sun glare
|
||||
//float sun = clamp(dot(_SunDir, primaryRay.dir), 0.0, 1.0 );
|
||||
//col += 0.25 * vec3(0.8,0.4,0.2) * pow(sun, 4.0 );
|
||||
|
||||
// Gamma
|
||||
//col = pow(clamp(col*1.1-0.02,0.0,1.0), vec3(0.4545));
|
||||
|
||||
// Contrast
|
||||
//col = col*col*(3.0-2.0*col);
|
||||
|
||||
// Color grade
|
||||
//col = pow( col, vec3(1.0,0.92,1.0) ); // soft green
|
||||
//col *= vec3(1.02,0.99,0.99); // tint red
|
||||
//col.z = (col.z+0.1)/1.1; // bias blue
|
||||
|
||||
// Fog
|
||||
// float dist = distance(primary.pos, primaryRay.origin);
|
||||
// float fog = exp2(-pow(0.015*dist, 2));
|
||||
// col = mix(vec3(0.8, 0.8, 0.75), col, clamp(fog, 0.0, 1.0));
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
// Sky
|
||||
float up = clamp(dot(primaryRay.dir, vec3(0, 1, 0)) + 0.1, 0, 1);
|
||||
return vec3(0.176, 0.592, 0.901) * up;
|
||||
}
|
||||
|
||||
@@ -22,9 +22,13 @@ HitResult castBricks(vec3 origin, vec3 start, vec3 dir, vec3 incAxis, uint addr)
|
||||
vec3 dist = (pos-origin+0.5 + raySign*0.5) * rayInv;
|
||||
|
||||
// Calculate offset
|
||||
uint y = (addr-1) % BLOCK_TEX_SIZE * BLOCK_SIZE;
|
||||
uint z = (addr-1) / BLOCK_TEX_SIZE * BLOCK_SIZE;
|
||||
ivec3 offset = ivec3(0, y, z);
|
||||
uint idx = addr-1;
|
||||
uint z = idx / (BLOCK_TEX_SIZE*BLOCK_TEX_SIZE);
|
||||
idx -= z * (BLOCK_TEX_SIZE*BLOCK_TEX_SIZE);
|
||||
uint y = idx / BLOCK_TEX_SIZE;
|
||||
uint x = idx % BLOCK_TEX_SIZE;
|
||||
|
||||
ivec3 offset = ivec3(x * BLOCK_SIZE, y * BLOCK_SIZE, z * BLOCK_SIZE);
|
||||
|
||||
for(int i=0;i<110;i++)
|
||||
{
|
||||
@@ -94,6 +98,7 @@ HitResult castNodes(Ray ray, uint maxSteps)
|
||||
incAxis = step(dist.xyz, dist.yzx) * step(dist.xyz, dist.zxy);
|
||||
dist += incAxis * raySign * rayInv;
|
||||
pos += incAxis * raySign;
|
||||
t = dot(dist, incAxis);
|
||||
|
||||
// Outside bounds
|
||||
// if(pos.x < 0 || pos.y < 0 || pos.z < 0) { break; }
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
Reference in New Issue
Block a user