mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-11-22 06:48:43 -06:00
commit
0802e4806c
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -27,3 +27,10 @@ _visualizer.json
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.rlib
|
*.rlib
|
||||||
|
|
||||||
|
#but keep the dxcompiler.dll/dxil.dll
|
||||||
|
!dxcompiler.dll
|
||||||
|
!dxil.dll
|
||||||
|
|
||||||
|
# blender backup files
|
||||||
|
*.blend1
|
||||||
|
|
872
Cargo.lock
generated
872
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
13
Cargo.toml
13
Cargo.toml
|
@ -21,10 +21,13 @@ opt-level = 1
|
||||||
[profile.dev.package.uflow]
|
[profile.dev.package.uflow]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
[profile.dev.package.glium]
|
[profile.dev.package.wgpu]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
[profile.dev.package.bracket-noise]
|
[profile.dev.package.wgpu-core]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
|
[profile.dev.package.wgpu-hal]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
[profile.dev.package.fastnoise-lite]
|
[profile.dev.package.fastnoise-lite]
|
||||||
|
@ -38,5 +41,7 @@ opt-level = 3
|
||||||
[profile.dev.package.android-activity]
|
[profile.dev.package.android-activity]
|
||||||
debug-assertions = false
|
debug-assertions = false
|
||||||
|
|
||||||
[patch.crates-io]
|
# [patch.'https://github.com/griffi-gh/hui']
|
||||||
glium = { git = "https://github.com/glium/glium", rev = "a352c667" }
|
# hui = { path = "X:/Projects/hui/hui" }
|
||||||
|
# hui-winit = { path = "X:/Projects/hui/hui-winit" }
|
||||||
|
# hui-wgpu = { path = "X:/Projects/hui/hui-wgpu" }
|
||||||
|
|
BIN
assets-src/playermodel1.blend
Normal file
BIN
assets-src/playermodel1.blend
Normal file
Binary file not shown.
64
assets/playermodel1.obj
Normal file
64
assets/playermodel1.obj
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# Blender 4.0.0 Beta
|
||||||
|
# www.blender.org
|
||||||
|
o Cube
|
||||||
|
v -0.500000 0.708333 -0.666667
|
||||||
|
v -0.500000 -0.291667 -0.666667
|
||||||
|
v 0.500000 0.708333 -0.666667
|
||||||
|
v 0.500000 -0.291667 -0.666667
|
||||||
|
v -0.500000 0.708333 0.333333
|
||||||
|
v -0.500000 -0.291667 0.333333
|
||||||
|
v 0.500000 0.708333 0.333333
|
||||||
|
v 0.500000 -0.291667 0.333333
|
||||||
|
v -0.500000 0.958333 -0.666667
|
||||||
|
v 0.500000 0.958333 -0.666667
|
||||||
|
v -0.166667 0.708333 -0.666667
|
||||||
|
v 0.166667 0.708333 -0.666667
|
||||||
|
vn -0.0000 1.0000 -0.0000
|
||||||
|
vn 1.0000 -0.0000 -0.0000
|
||||||
|
vn -0.0000 -0.0000 1.0000
|
||||||
|
vn -0.0000 -1.0000 -0.0000
|
||||||
|
vn -0.0000 -0.0000 -1.0000
|
||||||
|
vn -1.0000 -0.0000 -0.0000
|
||||||
|
vt 0.204555 0.291387
|
||||||
|
vt 0.043204 0.462923
|
||||||
|
vt 0.043204 0.291387
|
||||||
|
vt 0.259623 0.207472
|
||||||
|
vt 0.024467 0.457472
|
||||||
|
vt 0.024467 0.207472
|
||||||
|
vt 0.177715 0.183914
|
||||||
|
vt 0.010921 0.538561
|
||||||
|
vt 0.010921 0.183914
|
||||||
|
vt 0.246583 0.218979
|
||||||
|
vt 0.011426 0.468979
|
||||||
|
vt 0.011426 0.218979
|
||||||
|
vt 0.896961 0.811182
|
||||||
|
vt 0.168955 0.037222
|
||||||
|
vt 0.896961 0.037221
|
||||||
|
vt 0.177715 0.538561
|
||||||
|
vt 0.010921 0.361238
|
||||||
|
vt 0.168955 0.811182
|
||||||
|
vt 0.411624 0.811182
|
||||||
|
vt 0.204555 0.462923
|
||||||
|
vt 0.259623 0.457472
|
||||||
|
vt 0.246583 0.468979
|
||||||
|
vt 0.177715 0.361238
|
||||||
|
vt 0.896961 0.990308
|
||||||
|
vt 0.654292 0.811182
|
||||||
|
vt 0.168955 0.990308
|
||||||
|
s 0
|
||||||
|
f 5/1/1 3/2/1 1/3/1
|
||||||
|
f 3/4/2 8/5/2 4/6/2
|
||||||
|
f 7/7/3 6/8/3 8/9/3
|
||||||
|
f 2/10/4 8/11/4 6/12/4
|
||||||
|
f 1/13/5 4/14/5 2/15/5
|
||||||
|
f 5/16/6 2/17/6 6/8/6
|
||||||
|
f 3/18/3 1/13/3 12/19/3
|
||||||
|
f 5/1/1 7/20/1 3/2/1
|
||||||
|
f 3/4/2 7/21/2 8/5/2
|
||||||
|
f 7/7/3 5/16/3 6/8/3
|
||||||
|
f 2/10/4 4/22/4 8/11/4
|
||||||
|
f 1/13/5 3/18/5 4/14/5
|
||||||
|
f 5/16/6 1/23/6 2/17/6
|
||||||
|
f 1/13/5 9/24/5 11/25/5
|
||||||
|
f 12/19/5 10/26/5 3/18/5
|
||||||
|
f 1/13/5 11/25/5 12/19/5
|
BIN
assets/playermodel1.png
Normal file
BIN
assets/playermodel1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 137 KiB |
|
@ -9,7 +9,7 @@ pub fn init() {
|
||||||
use env_logger::{fmt::Color, Builder, Env};
|
use env_logger::{fmt::Color, Builder, Env};
|
||||||
|
|
||||||
let env = Env::default()
|
let env = Env::default()
|
||||||
.filter_or("RUST_LOG", "trace,gilrs=warn,rusty_xinput=warn");
|
.filter_or("RUST_LOG", "trace,gilrs=warn,rusty_xinput=warn,wgpu=warn,wgpu_core=warn,wgpu_hal=warn,hui=info,hui-winit=info,hui-glium=info,hui-wgpu=info,naga=warn");
|
||||||
Builder::from_env(env)
|
Builder::from_env(env)
|
||||||
.format(|buf, record| {
|
.format(|buf, record| {
|
||||||
let mut level_style = buf.style();
|
let mut level_style = buf.style();
|
||||||
|
|
|
@ -15,7 +15,7 @@ glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
|
||||||
hashbrown = "0.14"
|
hashbrown = "0.14"
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
rayon = "1.7"
|
rayon = "1.10"
|
||||||
flume = "0.11"
|
flume = "0.11"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
uflow = "0.7"
|
uflow = "0.7"
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use fastnoise_lite::FastNoiseLite;
|
|
||||||
use glam::ivec3;
|
use glam::ivec3;
|
||||||
use crate::{block::Block, chunk::CHUNK_SIZE, worldgen::SeedThingy};
|
use crate::{block::Block, chunk::CHUNK_SIZE, worldgen::SeedThingy};
|
||||||
use super::{
|
use super::{
|
||||||
|
|
|
@ -11,21 +11,21 @@ crate-type = ["lib", "cdylib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kubi-shared = { path = "../kubi-shared" }
|
kubi-shared = { path = "../kubi-shared" }
|
||||||
kubi-logging = { path = "../kubi-logging" }
|
kubi-logging = { path = "../kubi-logging" }
|
||||||
hui = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
hui = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4" }
|
||||||
hui-glium = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
hui-wgpu = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4" }
|
||||||
hui-winit = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
hui-winit = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4", features = ["winit_30"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
glium = { git = "https://github.com/glium/glium", rev = "a352c667" }
|
wgpu = { version = "0.20", features = ["webgl"] }
|
||||||
glutin = "0.31"
|
pollster = "0.3"
|
||||||
winit = { version = "0.29", features = ["android-native-activity"] }
|
bytemuck = { version = "1.15", features = ["derive"] }
|
||||||
glutin-winit = "0.4"
|
winit = { version = "0.30", features = ["android-native-activity"] }
|
||||||
raw-window-handle = "=0.5.*"
|
raw-window-handle = "0.6"
|
||||||
glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
|
glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
|
||||||
image = { version = "0.25", default_features = false, features = ["png"] }
|
image = { version = "0.25", default_features = false, features = ["png"] }
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
hashbrown = "0.14"
|
hashbrown = "0.14"
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
rayon = "1.7"
|
rayon = "1.10"
|
||||||
shipyard = { git = "https://github.com/leudz/shipyard", rev = "aacf3b1df5", default-features = false, features = ["std", "proc", "thread_local"] }
|
shipyard = { git = "https://github.com/leudz/shipyard", rev = "aacf3b1df5", default-features = false, features = ["std", "proc", "thread_local"] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
flume = "0.11"
|
flume = "0.11"
|
||||||
|
@ -38,10 +38,11 @@ tinyset = "0.4"
|
||||||
serde_json = { version = "1.0", optional = true } #only used for `generate_visualizer_data`
|
serde_json = { version = "1.0", optional = true } #only used for `generate_visualizer_data`
|
||||||
rand = { version = "0.8", features = ["alloc", "small_rng"]}
|
rand = { version = "0.8", features = ["alloc", "small_rng"]}
|
||||||
atomic = "0.6"
|
atomic = "0.6"
|
||||||
|
tobj = "4.0"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android-activity = "^0.5.2"
|
android-activity = "0.6"
|
||||||
ndk = "0.8"
|
ndk = "0.9"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = { version = "0.3", features = ["wincon"] }
|
winapi = { version = "0.3", features = ["wincon"] }
|
||||||
|
|
14
kubi/shaders/c2d.wgsl
Normal file
14
kubi/shaders/c2d.wgsl
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var<uniform> color: vec4<f32>;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
@location(0) position: vec2<f32>,
|
||||||
|
) -> @builtin(position) vec4<f32> {
|
||||||
|
return vec4<f32>(position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main() -> @location(0) vec4<f32> {
|
||||||
|
return color;
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
|
|
||||||
out vec4 out_color;
|
|
||||||
uniform vec4 color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// discard fully transparent pixels
|
|
||||||
if (color.w <= 0.) discard;
|
|
||||||
out_color = color;
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
|
|
||||||
in vec3 position;
|
|
||||||
uniform mat4 model;
|
|
||||||
uniform mat4 perspective;
|
|
||||||
uniform mat4 view;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
mat4 modelview = view * model;
|
|
||||||
gl_Position = perspective * modelview * vec4(position, 1.);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
|
|
||||||
out vec4 out_color;
|
|
||||||
uniform vec4 color;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
out_color = color;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
|
|
||||||
in vec2 position;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
|
||||||
}
|
|
50
kubi/shaders/entities.wgsl
Normal file
50
kubi/shaders/entities.wgsl
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
struct CameraUniform {
|
||||||
|
view_proj: mat4x4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(1) @binding(0)
|
||||||
|
var<uniform> camera: CameraUniform;
|
||||||
|
|
||||||
|
struct VertexInput {
|
||||||
|
@location(0) uv: vec2<f32>,
|
||||||
|
@location(1) position: vec3<f32>,
|
||||||
|
@location(2) normal: vec3<f32>,
|
||||||
|
@location(3) mat_row0: vec4<f32>,
|
||||||
|
@location(4) mat_row1: vec4<f32>,
|
||||||
|
@location(5) mat_row2: vec4<f32>,
|
||||||
|
@location(6) mat_row3: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) uv: vec2<f32>,
|
||||||
|
@location(1) normal: vec3<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
in: VertexInput,
|
||||||
|
) -> VertexOutput {
|
||||||
|
let inst_mat = mat4x4<f32>(
|
||||||
|
in.mat_row0,
|
||||||
|
in.mat_row1,
|
||||||
|
in.mat_row2,
|
||||||
|
in.mat_row3,
|
||||||
|
);
|
||||||
|
var out: VertexOutput;
|
||||||
|
out.clip_position = camera.view_proj * (inst_mat * vec4<f32>(in.position, 1.0));
|
||||||
|
out.uv = in.uv;
|
||||||
|
out.normal = in.normal;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var t_diffuse: texture_2d<f32>;
|
||||||
|
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return textureSample(t_diffuse, s_diffuse, in.uv);
|
||||||
|
}
|
27
kubi/shaders/selection_box.wgsl
Normal file
27
kubi/shaders/selection_box.wgsl
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
struct CameraUniform {
|
||||||
|
view_proj: mat4x4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var<uniform> camera: CameraUniform;
|
||||||
|
|
||||||
|
struct SboxUniform {
|
||||||
|
position: vec3<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(1) @binding(0)
|
||||||
|
var<uniform> sbox: SboxUniform;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
@location(0) position: vec3<f32>,
|
||||||
|
) -> @builtin(position) vec4<f32> {
|
||||||
|
return camera.view_proj * vec4<f32>(position + sbox.position, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(
|
||||||
|
@builtin(position) in: vec4<f32>,
|
||||||
|
) -> @location(0) vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 0.0, 0.0, 0.5);
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision lowp sampler2DArray;
|
|
||||||
|
|
||||||
in vec3 v_normal;
|
|
||||||
in vec2 v_uv;
|
|
||||||
flat in uint v_tex_index;
|
|
||||||
out vec4 color;
|
|
||||||
uniform sampler2DArray tex;
|
|
||||||
uniform bool discard_alpha;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// base color from texture
|
|
||||||
color = texture(tex, vec3(v_uv, v_tex_index));
|
|
||||||
|
|
||||||
// discard fully transparent pixels
|
|
||||||
if (discard_alpha ? (color.w < 0.5) : (color.w == 0.)) discard;
|
|
||||||
|
|
||||||
//basic "lighting"
|
|
||||||
float light = abs(v_normal.x) + .8 * abs(v_normal.y) + .6 * abs(v_normal.z);
|
|
||||||
color *= vec4(vec3(light), 1.);
|
|
||||||
|
|
||||||
//discard alpha
|
|
||||||
if (discard_alpha) color.w = 1.;
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#version 300 es
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
|
|
||||||
//TODO pack this data:
|
|
||||||
// uint position_normal_uv
|
|
||||||
// XXYYZZNU
|
|
||||||
// wehere Normal and Uv are enums
|
|
||||||
// maybe somehow pack in tex index too
|
|
||||||
|
|
||||||
in vec3 position;
|
|
||||||
in vec3 normal;
|
|
||||||
in vec2 uv;
|
|
||||||
in uint tex_index;
|
|
||||||
out vec2 v_uv;
|
|
||||||
out vec3 v_normal;
|
|
||||||
flat out uint v_tex_index;
|
|
||||||
uniform vec3 position_offset;
|
|
||||||
uniform mat4 perspective;
|
|
||||||
uniform mat4 view;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
v_normal = normal;
|
|
||||||
v_tex_index = tex_index;
|
|
||||||
v_uv = uv;
|
|
||||||
gl_Position = perspective * view * vec4(position + position_offset, 1.);
|
|
||||||
}
|
|
62
kubi/shaders/world.wgsl
Normal file
62
kubi/shaders/world.wgsl
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
struct CameraUniform {
|
||||||
|
view_proj: mat4x4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(1) @binding(0)
|
||||||
|
var<uniform> camera: CameraUniform;
|
||||||
|
|
||||||
|
struct VertexInput {
|
||||||
|
@location(0) position: vec3<f32>,
|
||||||
|
@location(1) normal: vec3<f32>,
|
||||||
|
@location(2) uv: vec2<f32>,
|
||||||
|
@location(3) tex_index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) uv: vec2<f32>,
|
||||||
|
@location(1) normal: vec3<f32>,
|
||||||
|
@location(2) @interpolate(flat)tex_index: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
in: VertexInput,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
out.uv = in.uv;
|
||||||
|
out.normal = in.normal;
|
||||||
|
out.tex_index = in.tex_index;
|
||||||
|
out.clip_position = camera.view_proj * vec4<f32>(in.position, 1.0);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var t_diffuse: texture_2d_array<f32>;
|
||||||
|
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
// not actual lighting; makes it easier to distinguish unlit faces
|
||||||
|
let light: f32 =
|
||||||
|
abs(in.normal.x) + .85 *
|
||||||
|
abs(in.normal.y) + .65 *
|
||||||
|
abs(in.normal.z);
|
||||||
|
|
||||||
|
let color: vec4<f32> =
|
||||||
|
textureSample(t_diffuse, s_diffuse, in.uv, in.tex_index)
|
||||||
|
* vec4<f32>(light, light, light, 1.0);
|
||||||
|
|
||||||
|
if (color.a < 0.5) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main_trans(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return textureSample(t_diffuse, s_diffuse, in.uv, in.tex_index);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use glam::{Vec3, Mat4};
|
use glam::{Vec3, Mat4};
|
||||||
use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator};
|
use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator};
|
||||||
use crate::{transform::Transform, rendering::WindowSize, events::WindowResizedEvent};
|
use crate::{transform::Transform, rendering::Renderer, events::WindowResizedEvent};
|
||||||
use super::Camera;
|
use super::Camera;
|
||||||
|
|
||||||
//maybe parallelize these two?
|
//maybe parallelize these two?
|
||||||
|
@ -18,12 +18,13 @@ fn update_view_matrix(
|
||||||
|
|
||||||
fn update_perspective_matrix(
|
fn update_perspective_matrix(
|
||||||
mut vm_camera: ViewMut<Camera>,
|
mut vm_camera: ViewMut<Camera>,
|
||||||
size: UniqueView<WindowSize>,
|
ren: UniqueView<Renderer>,
|
||||||
) {
|
) {
|
||||||
|
let sz = ren.size_vec2();
|
||||||
for mut camera in (&mut vm_camera).iter() {
|
for mut camera in (&mut vm_camera).iter() {
|
||||||
camera.perspective_matrix = Mat4::perspective_rh_gl(
|
camera.perspective_matrix = Mat4::perspective_rh(
|
||||||
camera.fov,
|
camera.fov,
|
||||||
size.0.x as f32 / size.0.y as f32,
|
sz.x / sz.y,
|
||||||
camera.z_near,
|
camera.z_near,
|
||||||
camera.z_far,
|
camera.z_far,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use kubi_shared::networking::client::ClientId;
|
use kubi_shared::networking::client::ClientId;
|
||||||
use shipyard::{AllStoragesView, Unique, UniqueViewMut};
|
use shipyard::{AllStoragesView, Unique};
|
||||||
|
|
||||||
pub enum ChatMessage {
|
pub enum ChatMessage {
|
||||||
PlayerMessage {
|
PlayerMessage {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//TODO client-side physics
|
//TODO client-side physics
|
||||||
//TODO move this to shared
|
//TODO move this to shared
|
||||||
use glam::{vec3, IVec3, Mat4, Vec3, Vec3Swizzles};
|
use glam::{vec3, Mat4, Vec3, Vec3Swizzles};
|
||||||
use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, View, ViewMut};
|
use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, ViewMut};
|
||||||
use kubi_shared::{block::{Block, CollisionType}, transform::Transform};
|
use kubi_shared::{block::{Block, CollisionType}, transform::Transform};
|
||||||
use crate::{delta_time::DeltaTime, world::ChunkStorage};
|
use crate::{delta_time::DeltaTime, world::ChunkStorage};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
use shipyard::{AllStoragesView, IntoIter, Unique, UniqueView, UniqueViewMut, View};
|
||||||
use crate::{events::InputDeviceEvent, rendering::{Renderer, WindowSize}};
|
use crate::{events::InputDeviceEvent, rendering::Renderer};
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::PhysicalPosition, event::{DeviceEvent, ElementState, RawKeyEvent}, keyboard::{KeyCode, PhysicalKey}, window::CursorGrabMode
|
dpi::PhysicalPosition, event::{DeviceEvent, ElementState, RawKeyEvent}, keyboard::{KeyCode, PhysicalKey}, window::CursorGrabMode
|
||||||
};
|
};
|
||||||
|
@ -9,14 +9,14 @@ pub struct CursorLock(pub bool);
|
||||||
|
|
||||||
pub fn update_cursor_lock_state(
|
pub fn update_cursor_lock_state(
|
||||||
lock: UniqueView<CursorLock>,
|
lock: UniqueView<CursorLock>,
|
||||||
display: NonSendSync<UniqueView<Renderer>>
|
display: UniqueView<Renderer>
|
||||||
) {
|
) {
|
||||||
if cfg!(target_os = "android") {
|
if cfg!(target_os = "android") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if lock.is_inserted_or_modified() {
|
if lock.is_inserted_or_modified() {
|
||||||
//TODO MIGRATION
|
//TODO MIGRATION
|
||||||
let window = &display.window;
|
let window = display.window();
|
||||||
window.set_cursor_grab(match lock.0 {
|
window.set_cursor_grab(match lock.0 {
|
||||||
true => CursorGrabMode::Confined,
|
true => CursorGrabMode::Confined,
|
||||||
false => CursorGrabMode::None,
|
false => CursorGrabMode::None,
|
||||||
|
@ -41,8 +41,7 @@ pub fn lock_cursor_now(
|
||||||
pub fn debug_toggle_lock(
|
pub fn debug_toggle_lock(
|
||||||
mut lock: UniqueViewMut<CursorLock>,
|
mut lock: UniqueViewMut<CursorLock>,
|
||||||
device_events: View<InputDeviceEvent>,
|
device_events: View<InputDeviceEvent>,
|
||||||
ren: NonSendSync<UniqueView<Renderer>>,
|
ren: UniqueView<Renderer>,
|
||||||
size: UniqueView<WindowSize>,
|
|
||||||
) {
|
) {
|
||||||
for evt in device_events.iter() {
|
for evt in device_events.iter() {
|
||||||
if let DeviceEvent::Key(RawKeyEvent {
|
if let DeviceEvent::Key(RawKeyEvent {
|
||||||
|
@ -51,8 +50,8 @@ pub fn debug_toggle_lock(
|
||||||
}) = evt.event {
|
}) = evt.event {
|
||||||
lock.0 = !lock.0;
|
lock.0 = !lock.0;
|
||||||
if !lock.0 {
|
if !lock.0 {
|
||||||
let center = PhysicalPosition::new(size.0.x as f64 / 2., size.0.y as f64 / 2.);
|
let center = PhysicalPosition::new(ren.size().width as f64 / 2., ren.size().height as f64 / 2.);
|
||||||
let _ = ren.window.set_cursor_position(center);
|
let _ = ren.window().set_cursor_position(center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub fn process_winit_events(world: &mut World, event: &Event<()>) {
|
||||||
EventComponent,
|
EventComponent,
|
||||||
InputDeviceEvent {
|
InputDeviceEvent {
|
||||||
device_id: *device_id,
|
device_id: *device_id,
|
||||||
event: DeviceEvent::Key(RawKeyEvent {
|
event: DeviceEvent::Key(winit::event::RawKeyEvent {
|
||||||
physical_key: event.physical_key,
|
physical_key: event.physical_key,
|
||||||
state: event.state,
|
state: event.state,
|
||||||
})
|
})
|
||||||
|
@ -86,18 +86,18 @@ pub fn process_winit_events(world: &mut World, event: &Event<()>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initial_resize_event(
|
// pub fn initial_resize_event(
|
||||||
mut storages: AllStoragesViewMut,
|
// mut storages: AllStoragesViewMut,
|
||||||
) {
|
// ) {
|
||||||
let (w, h) = {
|
// let (w, h) = {
|
||||||
let renderer = storages.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap();
|
// let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
renderer.display.get_framebuffer_dimensions()
|
// (renderer.size().width, renderer.size().height)
|
||||||
};
|
// };
|
||||||
storages.add_entity((
|
// storages.add_entity((
|
||||||
EventComponent,
|
// EventComponent,
|
||||||
WindowResizedEvent(UVec2::new(w, h))
|
// WindowResizedEvent(UVec2::new(w, h))
|
||||||
));
|
// ));
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn clear_events(
|
pub fn clear_events(
|
||||||
mut all_storages: AllStoragesViewMut,
|
mut all_storages: AllStoragesViewMut,
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
use hui::UiInstance;
|
use hui::UiInstance;
|
||||||
use hui_glium::GliumUiRenderer;
|
use hui_wgpu::WgpuUiRenderer;
|
||||||
|
//use hui_glium::GliumUiRenderer;
|
||||||
use shipyard::{AllStoragesView, Unique, UniqueView, NonSendSync, UniqueViewMut};
|
use shipyard::{AllStoragesView, Unique, UniqueView, NonSendSync, UniqueViewMut};
|
||||||
use crate::rendering::{RenderTarget, Renderer, WindowSize};
|
use crate::rendering::{RenderCtx, Renderer};
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
pub struct UiState {
|
pub struct UiState {
|
||||||
pub hui: UiInstance,
|
pub hui: UiInstance,
|
||||||
pub renderer: GliumUiRenderer
|
pub renderer: WgpuUiRenderer,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kubi_ui_init(
|
pub fn kubi_ui_init(
|
||||||
storages: AllStoragesView
|
storages: AllStoragesView
|
||||||
) {
|
) {
|
||||||
let renderer = storages.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap();
|
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
storages.add_unique_non_send_sync(UiState {
|
storages.add_unique_non_send_sync(UiState {
|
||||||
hui: UiInstance::new(),
|
hui: UiInstance::new(),
|
||||||
renderer: GliumUiRenderer::new(&renderer.display),
|
renderer: WgpuUiRenderer::new(renderer.device(), renderer.surface_config().format),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,20 +27,20 @@ pub fn kubi_ui_begin(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kubi_ui_end(
|
pub fn kubi_ui_end(
|
||||||
mut ui: NonSendSync<UniqueViewMut<UiState>>
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
) {
|
) {
|
||||||
let ui: &mut UiState = &mut ui;
|
let ui: &mut UiState = &mut ui;
|
||||||
let UiState { hui, renderer, .. } = ui;
|
let UiState { hui, renderer: ui_renderer } = ui;
|
||||||
hui.end();
|
hui.end();
|
||||||
renderer.update(hui);
|
ui_renderer.update(hui, renderer.queue(), renderer.device(), renderer.size_vec2());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kubi_ui_draw(
|
pub fn kubi_ui_draw(
|
||||||
|
ctx: &mut RenderCtx,
|
||||||
ui: NonSendSync<UniqueView<UiState>>,
|
ui: NonSendSync<UniqueView<UiState>>,
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
|
||||||
size: UniqueView<WindowSize>
|
|
||||||
) {
|
) {
|
||||||
ui.renderer.draw(&mut target.0, size.0.as_vec2());
|
ui.renderer.draw(ctx.encoder, ctx.surface_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hui_process_winit_events(
|
pub fn hui_process_winit_events(
|
||||||
|
|
|
@ -10,7 +10,7 @@ use nohash_hasher::BuildNoHashHasher;
|
||||||
use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync};
|
use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::{InputDeviceEvent, TouchEvent},
|
events::{InputDeviceEvent, TouchEvent},
|
||||||
rendering::WindowSize
|
rendering::Renderer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Unique, Clone, Copy, Default, Debug)]
|
#[derive(Unique, Clone, Copy, Default, Debug)]
|
||||||
|
@ -211,10 +211,10 @@ fn update_input_state_gamepad (
|
||||||
|
|
||||||
fn update_input_state_touch (
|
fn update_input_state_touch (
|
||||||
touch_state: UniqueView<RawTouchState>,
|
touch_state: UniqueView<RawTouchState>,
|
||||||
win_size: UniqueView<WindowSize>,
|
renderer: UniqueView<Renderer>,
|
||||||
mut inputs: UniqueViewMut<Inputs>,
|
mut inputs: UniqueViewMut<Inputs>,
|
||||||
) {
|
) {
|
||||||
let w = win_size.0.as_dvec2();
|
let w = renderer.size_uvec2().as_dvec2();
|
||||||
|
|
||||||
//Movement
|
//Movement
|
||||||
if let Some(finger) = touch_state.query_area(
|
if let Some(finger) = touch_state.query_area(
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
use shipyard::{
|
use shipyard::{
|
||||||
World, Workload, IntoWorkload,
|
World, Workload, IntoWorkload,
|
||||||
UniqueView, UniqueViewMut,
|
UniqueView, UniqueViewMut,
|
||||||
NonSendSync, WorkloadModificator,
|
WorkloadModificator,
|
||||||
SystemModificator
|
SystemModificator
|
||||||
};
|
};
|
||||||
use winit::{
|
use winit::{
|
||||||
|
@ -67,27 +67,10 @@ use player::{spawn_player, MainPlayer};
|
||||||
use prefabs::load_prefabs;
|
use prefabs::load_prefabs;
|
||||||
use settings::{load_settings, GameSettings};
|
use settings::{load_settings, GameSettings};
|
||||||
use camera::compute_cameras;
|
use camera::compute_cameras;
|
||||||
use events::{
|
use events::{clear_events, process_winit_events, player_actions::generate_move_events};
|
||||||
clear_events,
|
|
||||||
process_winit_events,
|
|
||||||
initial_resize_event,
|
|
||||||
player_actions::generate_move_events,
|
|
||||||
};
|
|
||||||
use input::{init_input, process_inputs};
|
use input::{init_input, process_inputs};
|
||||||
use player_controller::{debug_switch_ctl_type, update_player_controllers};
|
use player_controller::{debug_switch_ctl_type, update_player_controllers};
|
||||||
use rendering::{
|
use rendering::{BackgroundColor, Renderer, init_rendering, render_master, update_rendering_early, update_rendering_late};
|
||||||
Renderer,
|
|
||||||
RenderTarget,
|
|
||||||
BackgroundColor,
|
|
||||||
clear_background,
|
|
||||||
init_window_size,
|
|
||||||
update_window_size,
|
|
||||||
primitives::init_primitives,
|
|
||||||
world::{init_trans_chunk_queue, draw_world, draw_world_trans, draw_current_chunk_border},
|
|
||||||
selection_box::render_selection_box,
|
|
||||||
entities::render_entities,
|
|
||||||
sumberge::render_submerged_view,
|
|
||||||
};
|
|
||||||
use block_placement::update_block_placement;
|
use block_placement::update_block_placement;
|
||||||
use delta_time::{DeltaTime, init_delta_time};
|
use delta_time::{DeltaTime, init_delta_time};
|
||||||
use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state};
|
use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state};
|
||||||
|
@ -95,7 +78,7 @@ use control_flow::{exit_on_esc, insert_control_flow_unique, RequestExit};
|
||||||
use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting};
|
use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting};
|
||||||
use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
|
use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
|
||||||
use init::initialize_from_args;
|
use init::initialize_from_args;
|
||||||
use hui_integration::{kubi_ui_begin, kubi_ui_draw, kubi_ui_end, kubi_ui_init};
|
use hui_integration::{kubi_ui_begin, /*kubi_ui_draw,*/ kubi_ui_end, kubi_ui_init};
|
||||||
use loading_screen::update_loading_screen;
|
use loading_screen::update_loading_screen;
|
||||||
use connecting_screen::update_connecting_screen;
|
use connecting_screen::update_connecting_screen;
|
||||||
use fixed_timestamp::init_fixed_timestamp_storage;
|
use fixed_timestamp::init_fixed_timestamp_storage;
|
||||||
|
@ -105,8 +88,7 @@ use chat_ui::render_chat;
|
||||||
use chat::init_chat_manager;
|
use chat::init_chat_manager;
|
||||||
use crosshair_ui::{init_crosshair_image, draw_crosshair};
|
use crosshair_ui::{init_crosshair_image, draw_crosshair};
|
||||||
use settings_ui::render_settings_ui;
|
use settings_ui::render_settings_ui;
|
||||||
|
use hui_integration::hui_process_winit_events;
|
||||||
use crate::hui_integration::hui_process_winit_events;
|
|
||||||
|
|
||||||
/// stuff required to init the renderer and other basic systems
|
/// stuff required to init the renderer and other basic systems
|
||||||
fn pre_startup() -> Workload {
|
fn pre_startup() -> Workload {
|
||||||
|
@ -118,11 +100,9 @@ fn pre_startup() -> Workload {
|
||||||
fn startup() -> Workload {
|
fn startup() -> Workload {
|
||||||
(
|
(
|
||||||
init_fixed_timestamp_storage,
|
init_fixed_timestamp_storage,
|
||||||
initial_resize_event,
|
|
||||||
init_window_size,
|
|
||||||
kubi_ui_init,
|
kubi_ui_init,
|
||||||
load_prefabs,
|
load_prefabs,
|
||||||
init_primitives,
|
init_rendering,
|
||||||
insert_lock_state,
|
insert_lock_state,
|
||||||
init_state,
|
init_state,
|
||||||
initialize_from_args,
|
initialize_from_args,
|
||||||
|
@ -133,14 +113,13 @@ fn startup() -> Workload {
|
||||||
init_client_physics,
|
init_client_physics,
|
||||||
init_chat_manager,
|
init_chat_manager,
|
||||||
init_crosshair_image,
|
init_crosshair_image,
|
||||||
init_trans_chunk_queue,
|
|
||||||
).into_sequential_workload()
|
).into_sequential_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update() -> Workload {
|
fn update() -> Workload {
|
||||||
(
|
(
|
||||||
|
update_rendering_early,
|
||||||
debug_toggle_lock,
|
debug_toggle_lock,
|
||||||
update_window_size,
|
|
||||||
update_cursor_lock_state,
|
update_cursor_lock_state,
|
||||||
process_inputs,
|
process_inputs,
|
||||||
kubi_ui_begin,
|
kubi_ui_begin,
|
||||||
|
@ -179,25 +158,26 @@ fn update() -> Workload {
|
||||||
update_state,
|
update_state,
|
||||||
exit_on_esc,
|
exit_on_esc,
|
||||||
disconnect_on_exit.run_if(is_multiplayer),
|
disconnect_on_exit.run_if(is_multiplayer),
|
||||||
|
update_rendering_late,
|
||||||
).into_sequential_workload()
|
).into_sequential_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render() -> Workload {
|
// fn render() -> Workload {
|
||||||
(
|
// (
|
||||||
clear_background,
|
// clear_background,
|
||||||
(
|
// (
|
||||||
draw_world,
|
// draw_world,
|
||||||
draw_current_chunk_border,
|
// draw_current_chunk_border,
|
||||||
render_selection_box,
|
// render_selection_box,
|
||||||
render_entities,
|
// render_entities,
|
||||||
draw_world_trans,
|
// draw_world_trans,
|
||||||
render_submerged_view,
|
// render_submerged_view,
|
||||||
).into_sequential_workload().run_if(is_ingame),
|
// ).into_sequential_workload().run_if(is_ingame),
|
||||||
kubi_ui_draw,
|
// kubi_ui_draw,
|
||||||
).into_sequential_workload()
|
// ).into_sequential_workload()
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn after_frame_end() -> Workload {
|
fn after_render() -> Workload {
|
||||||
(
|
(
|
||||||
clear_events,
|
clear_events,
|
||||||
).into_sequential_workload()
|
).into_sequential_workload()
|
||||||
|
@ -245,8 +225,8 @@ pub fn kubi_main(
|
||||||
world.add_workload(pre_startup);
|
world.add_workload(pre_startup);
|
||||||
world.add_workload(startup);
|
world.add_workload(startup);
|
||||||
world.add_workload(update);
|
world.add_workload(update);
|
||||||
world.add_workload(render);
|
//world.add_workload(render);
|
||||||
world.add_workload(after_frame_end);
|
world.add_workload(after_render);
|
||||||
|
|
||||||
//Save _visualizer.json
|
//Save _visualizer.json
|
||||||
#[cfg(feature = "generate_visualizer_data")]
|
#[cfg(feature = "generate_visualizer_data")]
|
||||||
|
@ -286,7 +266,7 @@ pub fn kubi_main(
|
||||||
let settings = world.borrow::<UniqueView<GameSettings>>().unwrap();
|
let settings = world.borrow::<UniqueView<GameSettings>>().unwrap();
|
||||||
world.add_unique_non_send_sync(Renderer::init(window_target, &settings));
|
world.add_unique_non_send_sync(Renderer::init(window_target, &settings));
|
||||||
}
|
}
|
||||||
world.add_unique(BackgroundColor(vec3(0.5, 0.5, 1.)));
|
world.add_unique(BackgroundColor(vec3(0.21, 0.21, 1.)));
|
||||||
|
|
||||||
//Run startup systems
|
//Run startup systems
|
||||||
world.run_workload(startup).unwrap();
|
world.run_workload(startup).unwrap();
|
||||||
|
@ -326,22 +306,24 @@ pub fn kubi_main(
|
||||||
//Run update workflows
|
//Run update workflows
|
||||||
world.run_workload(update).unwrap();
|
world.run_workload(update).unwrap();
|
||||||
|
|
||||||
|
world.run(render_master);
|
||||||
|
|
||||||
//Start rendering (maybe use custom views for this?)
|
//Start rendering (maybe use custom views for this?)
|
||||||
let target = {
|
// let target = {
|
||||||
let renderer = world.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap();
|
// let renderer = world.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
renderer.display.draw()
|
// renderer.display.draw()
|
||||||
};
|
// };
|
||||||
world.add_unique_non_send_sync(RenderTarget(target));
|
// world.add_unique_non_send_sync(RenderTarget(target));
|
||||||
|
|
||||||
//Run render workflow
|
//Run render workflow
|
||||||
world.run_workload(render).unwrap();
|
//world.run_workload(render).unwrap();
|
||||||
|
|
||||||
//Finish rendering
|
//Finish rendering
|
||||||
let target = world.remove_unique::<RenderTarget>().unwrap();
|
// let target = world.remove_unique::<RenderTarget>().unwrap();
|
||||||
target.0.finish().unwrap();
|
// target.0.finish().unwrap();
|
||||||
|
|
||||||
//After frame end
|
//After frame end
|
||||||
world.run_workload(after_frame_end).unwrap();
|
world.run_workload(after_render).unwrap();
|
||||||
|
|
||||||
//Process control flow changes
|
//Process control flow changes
|
||||||
if world.borrow::<UniqueView<RequestExit>>().unwrap().0 {
|
if world.borrow::<UniqueView<RequestExit>>().unwrap().0 {
|
||||||
|
|
|
@ -1,15 +1,35 @@
|
||||||
use std::{io::{BufReader, Read}, path::Path};
|
use std::{io::{BufReader, Read}, path::{Path, PathBuf}};
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
use hui::text::FontHandle;
|
use hui::text::FontHandle;
|
||||||
use shipyard::{AllStoragesView, NonSendSync, Unique, UniqueView, UniqueViewMut};
|
use shipyard::{AllStoragesView, NonSendSync, Unique, UniqueView, UniqueViewMut};
|
||||||
use glium::{texture::{SrgbTexture2dArray, MipmapsOption}, Program};
|
|
||||||
use kubi_shared::block::BlockTexture;
|
use kubi_shared::block::BlockTexture;
|
||||||
use crate::{filesystem::AssetManager, hui_integration::UiState, rendering::Renderer};
|
use crate::{filesystem::AssetManager, hui_integration::UiState, rendering::{BufferPair, Renderer}};
|
||||||
|
|
||||||
mod texture;
|
//TODO move to rendering module
|
||||||
mod shaders;
|
|
||||||
|
|
||||||
use texture::load_texture2darray_prefab;
|
mod loader;
|
||||||
use shaders::include_shader_prefab;
|
use loader::{load_texture2darray_prefab, load_texture2d_prefab, load_obj_prefab};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct ModelVertex {
|
||||||
|
pub tex_coords: [f32; 2],
|
||||||
|
pub position: [f32; 3],
|
||||||
|
pub _padding: u32,
|
||||||
|
pub normal: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelVertex {
|
||||||
|
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<ModelVertex>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &wgpu::vertex_attr_array![
|
||||||
|
0 => Float32x2,
|
||||||
|
1 => Float32x3,
|
||||||
|
2 => Float32x3,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub trait AssetPaths {
|
pub trait AssetPaths {
|
||||||
fn file_name(self) -> &'static str;
|
fn file_name(self) -> &'static str;
|
||||||
|
@ -40,20 +60,15 @@ impl AssetPaths for BlockTexture {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
#[repr(transparent)]
|
pub struct GpuPrefabs {
|
||||||
pub struct BlockTexturesPrefab(pub SrgbTexture2dArray);
|
pub block_diffuse_texture: wgpu::Texture,
|
||||||
|
pub block_diffuse_bind_group_layout: wgpu::BindGroupLayout,
|
||||||
#[derive(Unique)]
|
pub block_diffuse_bind_group: wgpu::BindGroup,
|
||||||
#[repr(transparent)]
|
pub player_model_diffuse_texture: wgpu::Texture,
|
||||||
pub struct ChunkShaderPrefab(pub Program);
|
pub player_model_diffuse_bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
pub player_model_diffuse_bind_group: wgpu::BindGroup,
|
||||||
#[derive(Unique)]
|
pub player_model: BufferPair,
|
||||||
#[repr(transparent)]
|
}
|
||||||
pub struct ColoredShaderPrefab(pub Program);
|
|
||||||
|
|
||||||
#[derive(Unique)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Colored2ShaderPrefab(pub Program);
|
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -61,19 +76,132 @@ pub struct UiFontPrefab(pub FontHandle);
|
||||||
|
|
||||||
pub fn load_prefabs(
|
pub fn load_prefabs(
|
||||||
storages: AllStoragesView,
|
storages: AllStoragesView,
|
||||||
renderer: NonSendSync<UniqueView<Renderer>>,
|
renderer: UniqueView<Renderer>,
|
||||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
assman: UniqueView<AssetManager>
|
assman: UniqueView<AssetManager>
|
||||||
) {
|
) {
|
||||||
log::info!("Loading textures...");
|
log::info!("Loading textures...");
|
||||||
storages.add_unique_non_send_sync(BlockTexturesPrefab(
|
let block_diffuse_texture = load_texture2darray_prefab::<BlockTexture>(
|
||||||
load_texture2darray_prefab::<BlockTexture, _>(
|
&renderer,
|
||||||
&assman,
|
&assman,
|
||||||
"blocks".into(),
|
"blocks".into(),
|
||||||
&renderer.display,
|
);
|
||||||
MipmapsOption::AutoGeneratedMipmaps
|
|
||||||
)
|
log::info!("Creating bing groups");
|
||||||
));
|
let block_diffuse_view = block_diffuse_texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
|
label: Some("block_texture_view"),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let block_diffuse_sampler = renderer.device().create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
label: Some("block_diffuse_sampler"),
|
||||||
|
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::Linear,
|
||||||
|
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let block_diffuse_bind_group_layout = renderer.device()
|
||||||
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("block_diffuse_bind_group_layout"),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2Array,
|
||||||
|
multisampled: false,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
let block_diffuse_bind_group = renderer.device().create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("block_diffuse_bind_group"),
|
||||||
|
layout: &block_diffuse_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&block_diffuse_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&block_diffuse_sampler),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let player_model_diffuse_texture = load_texture2d_prefab(&renderer, &assman, &PathBuf::from("playermodel1.png"));
|
||||||
|
let player_model_diffuse_view = player_model_diffuse_texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
|
label: Some("player_model_texture_view"),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let player_model_diffuse_sampler = renderer.device().create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
label: Some("player_model_sampler"),
|
||||||
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
|
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||||
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
|
min_filter: wgpu::FilterMode::Linear,
|
||||||
|
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let player_model_diffuse_bind_group_layout = renderer.device()
|
||||||
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("player_model_bind_group_layout"),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
multisampled: false,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
let player_model_diffuse_bind_group = renderer.device().create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("player_model_bind_group"),
|
||||||
|
layout: &player_model_diffuse_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&player_model_diffuse_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&player_model_diffuse_sampler),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
let player_model = load_obj_prefab(&renderer, &assman, &PathBuf::from("playermodel1.obj"));
|
||||||
|
|
||||||
|
storages.add_unique_non_send_sync(GpuPrefabs {
|
||||||
|
block_diffuse_texture,
|
||||||
|
block_diffuse_bind_group_layout,
|
||||||
|
block_diffuse_bind_group,
|
||||||
|
player_model_diffuse_texture,
|
||||||
|
player_model_diffuse_bind_group_layout,
|
||||||
|
player_model_diffuse_bind_group,
|
||||||
|
player_model,
|
||||||
|
});
|
||||||
|
|
||||||
log::info!("Loading the UI stuff...");
|
log::info!("Loading the UI stuff...");
|
||||||
{
|
{
|
||||||
|
@ -85,33 +213,33 @@ pub fn load_prefabs(
|
||||||
storages.add_unique(UiFontPrefab(font_handle));
|
storages.add_unique(UiFontPrefab(font_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Compiling shaders...");
|
//log::info!("Compiling shaders...");
|
||||||
storages.add_unique_non_send_sync(ChunkShaderPrefab(
|
// storages.add_unique_non_send_sync(ChunkShaderPrefab(
|
||||||
include_shader_prefab!(
|
// include_shader_prefab!(
|
||||||
"world",
|
// "world",
|
||||||
"../shaders/world.vert",
|
// "../shaders/world.vert",
|
||||||
"../shaders/world.frag",
|
// "../shaders/world.frag",
|
||||||
&renderer.display
|
// &renderer.display
|
||||||
)
|
// )
|
||||||
));
|
// ));
|
||||||
storages.add_unique_non_send_sync(ColoredShaderPrefab(
|
// storages.add_unique_non_send_sync(ColoredShaderPrefab(
|
||||||
include_shader_prefab!(
|
// include_shader_prefab!(
|
||||||
"colored",
|
// "colored",
|
||||||
"../shaders/colored.vert",
|
// "../shaders/colored.vert",
|
||||||
"../shaders/colored.frag",
|
// "../shaders/colored.frag",
|
||||||
&renderer.display
|
// &renderer.display
|
||||||
)
|
// )
|
||||||
));
|
// ));
|
||||||
storages.add_unique_non_send_sync(Colored2ShaderPrefab(
|
// storages.add_unique_non_send_sync(Colored2ShaderPrefab(
|
||||||
include_shader_prefab!(
|
// include_shader_prefab!(
|
||||||
"colored",
|
// "colored",
|
||||||
"../shaders/colored2.vert",
|
// "../shaders/colored2.vert",
|
||||||
"../shaders/colored2.frag",
|
// "../shaders/colored2.frag",
|
||||||
&renderer.display
|
// &renderer.display
|
||||||
)
|
// )
|
||||||
));
|
// ));
|
||||||
|
|
||||||
log::info!("releasing shader compiler");
|
//log::info!("releasing shader compiler");
|
||||||
|
|
||||||
renderer.display.release_shader_compiler();
|
//renderer.display.release_shader_compiler();
|
||||||
}
|
}
|
||||||
|
|
168
kubi/src/prefabs/loader.rs
Normal file
168
kubi/src/prefabs/loader.rs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use glam::UVec2;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use wgpu::util::{DeviceExt, TextureDataOrder};
|
||||||
|
use std::{io::{BufReader, Read}, path::{Path, PathBuf}};
|
||||||
|
use crate::{filesystem::AssetManager, prefabs::ModelVertex, rendering::{BufferPair, Renderer}};
|
||||||
|
use super::AssetPaths;
|
||||||
|
|
||||||
|
pub fn load_texture2darray_prefab<T: AssetPaths + IntoEnumIterator>(
|
||||||
|
renderer: &Renderer,
|
||||||
|
assman: &AssetManager,
|
||||||
|
directory: PathBuf,
|
||||||
|
) -> wgpu::Texture {
|
||||||
|
log::info!("started loading {}", directory.as_os_str().to_str().unwrap());
|
||||||
|
|
||||||
|
//Load raw images
|
||||||
|
let tex_files: Vec<&'static str> = T::iter().map(|x| x.file_name()).collect();
|
||||||
|
let raw_images: Vec<(Vec<u8>, UVec2)> = tex_files.par_iter().map(|&file_name| {
|
||||||
|
log::info!("loading texture {}", file_name);
|
||||||
|
|
||||||
|
//Get path to the image and open the file
|
||||||
|
let reader = {
|
||||||
|
let path = directory.join(file_name);
|
||||||
|
BufReader::new(assman.open_asset(&path).expect("Failed to open texture file"))
|
||||||
|
};
|
||||||
|
|
||||||
|
//Parse image data
|
||||||
|
let (image_data, dimensions) = {
|
||||||
|
let image = image::load(
|
||||||
|
reader,
|
||||||
|
image::ImageFormat::Png
|
||||||
|
).unwrap().to_rgba8();
|
||||||
|
let dimensions = image.dimensions();
|
||||||
|
(image.into_raw(), dimensions)
|
||||||
|
};
|
||||||
|
(image_data, UVec2::from(dimensions))
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
assert!(!raw_images.is_empty(), "no images loaded");
|
||||||
|
//TODO: check same size
|
||||||
|
|
||||||
|
log::info!("done loading texture files, uploading to the gpu");
|
||||||
|
|
||||||
|
let size = raw_images[0].1;
|
||||||
|
let layers = raw_images.len() as u32;
|
||||||
|
|
||||||
|
//Concat data into a single vec
|
||||||
|
let mut data = Vec::with_capacity((size.x * size.y * layers * 4) as usize);
|
||||||
|
for (layer_data, _) in raw_images {
|
||||||
|
data.extend_from_slice(&layer_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Upload images to the GPU
|
||||||
|
let label = format!("texture2darray_prefab_{}", directory.as_os_str().to_str().unwrap());
|
||||||
|
let desc = &wgpu::TextureDescriptor {
|
||||||
|
label: Some(&label),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: size.x,
|
||||||
|
height: size.y,
|
||||||
|
depth_or_array_layers: layers,
|
||||||
|
},
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
view_formats: &[],
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer.device().create_texture_with_data(
|
||||||
|
renderer.queue(),
|
||||||
|
desc,
|
||||||
|
TextureDataOrder::MipMajor,
|
||||||
|
&data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_texture2d_prefab(
|
||||||
|
renderer: &Renderer,
|
||||||
|
assman: &AssetManager,
|
||||||
|
path: &Path,
|
||||||
|
) -> wgpu::Texture {
|
||||||
|
log::info!("loading texture2d: {path:?}");
|
||||||
|
|
||||||
|
let image = image::load(
|
||||||
|
BufReader::new(assman.open_asset(path).expect("Failed to open texture file")),
|
||||||
|
image::ImageFormat::Png
|
||||||
|
).unwrap().to_rgba8();
|
||||||
|
let size = image.dimensions();
|
||||||
|
let data = image.into_raw();
|
||||||
|
|
||||||
|
let label = format!("texture2d_prefab_{}", path.file_name().unwrap().to_str().unwrap());
|
||||||
|
let desc = wgpu::TextureDescriptor {
|
||||||
|
label: Some(&label),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: size.0,
|
||||||
|
height: size.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
view_formats: &[],
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer.device().create_texture_with_data(
|
||||||
|
renderer.queue(),
|
||||||
|
&desc,
|
||||||
|
TextureDataOrder::MipMajor,
|
||||||
|
&data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_obj_prefab(
|
||||||
|
renderer: &Renderer,
|
||||||
|
assman: &AssetManager,
|
||||||
|
path: &Path,
|
||||||
|
) -> BufferPair {
|
||||||
|
log::info!("loading obj prefab: {path:?}");
|
||||||
|
|
||||||
|
let mut reader = BufReader::new(
|
||||||
|
assman.open_asset(path).expect("Failed to open texture file")
|
||||||
|
);
|
||||||
|
|
||||||
|
let (model, _) = tobj::load_obj_buf(
|
||||||
|
&mut reader,
|
||||||
|
&tobj::GPU_LOAD_OPTIONS,
|
||||||
|
|_| unimplemented!()
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(model.len(), 1, "only single model supported at the moment, sowwy :3");
|
||||||
|
let mesh = &model[0].mesh;
|
||||||
|
debug_assert!(mesh.normal_indices.is_empty() && mesh.texcoord_indices.is_empty(), "forgor single_index");
|
||||||
|
|
||||||
|
let tex_coords = bytemuck::cast_slice::<f32, [f32; 2]>(&mesh.texcoords);
|
||||||
|
let positions = bytemuck::cast_slice::<f32, [f32; 3]>(&mesh.positions);
|
||||||
|
let normals = bytemuck::cast_slice::<f32, [f32; 3]>(&mesh.normals);
|
||||||
|
|
||||||
|
let vertex_buffer: Vec<_> = (0..positions.len()).map(|i| {
|
||||||
|
ModelVertex {
|
||||||
|
tex_coords: [tex_coords[i][0], 1. - tex_coords[i][1]],
|
||||||
|
position: positions[i],
|
||||||
|
_padding: 0,
|
||||||
|
normal: normals[i],
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let vertex_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("obj_vertex_buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&vertex_buffer),
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let index_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("obj_index_buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&mesh.indices),
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
BufferPair {
|
||||||
|
vertex: vertex_buffer,
|
||||||
|
vertex_len: positions.len() as u32,
|
||||||
|
index: index_buffer,
|
||||||
|
index_len: mesh.indices.len() as u32,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +0,0 @@
|
||||||
macro_rules! include_shader_prefab {
|
|
||||||
($name: literal, $vert: literal, $frag: literal, $facade: expr) => {
|
|
||||||
{
|
|
||||||
use ::glium::{Program, program::ProgramCreationInput};
|
|
||||||
log::info!("compiling shader {}", $name);
|
|
||||||
Program::new(&*$facade, ProgramCreationInput::SourceCode {
|
|
||||||
vertex_shader: include_str!($vert),
|
|
||||||
fragment_shader: include_str!($frag),
|
|
||||||
geometry_shader: None,
|
|
||||||
tessellation_control_shader: None,
|
|
||||||
tessellation_evaluation_shader: None,
|
|
||||||
transform_feedback_varyings: None,
|
|
||||||
outputs_srgb: false,
|
|
||||||
uses_point_size: false,
|
|
||||||
}).expect("Failed to compile gpu program")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use include_shader_prefab;
|
|
|
@ -1,46 +0,0 @@
|
||||||
use strum::IntoEnumIterator;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::{path::PathBuf, io::BufReader};
|
|
||||||
use glium::{texture::{SrgbTexture2dArray, RawImage2d, MipmapsOption}, backend::Facade};
|
|
||||||
use crate::filesystem::AssetManager;
|
|
||||||
use super::AssetPaths;
|
|
||||||
|
|
||||||
pub fn load_texture2darray_prefab<
|
|
||||||
T: AssetPaths + IntoEnumIterator,
|
|
||||||
E: Facade
|
|
||||||
>(
|
|
||||||
assman: &AssetManager,
|
|
||||||
directory: PathBuf,
|
|
||||||
facade: &E,
|
|
||||||
mipmaps: MipmapsOption,
|
|
||||||
) -> SrgbTexture2dArray {
|
|
||||||
log::info!("started loading {}", directory.as_os_str().to_str().unwrap());
|
|
||||||
//Load raw images
|
|
||||||
let tex_files: Vec<&'static str> = T::iter().map(|x| x.file_name()).collect();
|
|
||||||
let raw_images: Vec<RawImage2d<u8>> = tex_files.par_iter().map(|&file_name| {
|
|
||||||
log::info!("loading texture {}", file_name);
|
|
||||||
//Get path to the image and open the file
|
|
||||||
let reader = {
|
|
||||||
let path = directory.join(file_name);
|
|
||||||
BufReader::new(assman.open_asset(&path).expect("Failed to open texture file"))
|
|
||||||
};
|
|
||||||
//Parse image data
|
|
||||||
let (image_data, dimensions) = {
|
|
||||||
let image = image::load(
|
|
||||||
reader,
|
|
||||||
image::ImageFormat::Png
|
|
||||||
).unwrap().to_rgba8();
|
|
||||||
let dimensions = image.dimensions();
|
|
||||||
(image.into_raw(), dimensions)
|
|
||||||
};
|
|
||||||
//Create a glium RawImage
|
|
||||||
RawImage2d::from_raw_rgba_reversed(
|
|
||||||
&image_data,
|
|
||||||
dimensions
|
|
||||||
)
|
|
||||||
}).collect();
|
|
||||||
log::info!("done loading texture files, uploading to the gpu");
|
|
||||||
//Upload images to the GPU
|
|
||||||
SrgbTexture2dArray::with_mipmaps(facade, raw_images, mipmaps)
|
|
||||||
.expect("Failed to upload texture array to GPU")
|
|
||||||
}
|
|
|
@ -1,191 +1,106 @@
|
||||||
use std::num::NonZeroU32;
|
use shipyard::{AllStoragesViewMut, IntoIter, IntoWorkload, SystemModificator, Unique, UniqueView, UniqueViewMut, View, Workload, WorkloadModificator};
|
||||||
use raw_window_handle::HasRawWindowHandle;
|
use winit::dpi::PhysicalSize;
|
||||||
use shipyard::{Unique, NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, AllStoragesView};
|
use glam::Vec3;
|
||||||
use winit::{
|
use crate::{events::WindowResizedEvent, hui_integration::kubi_ui_draw, state::is_ingame};
|
||||||
event_loop::EventLoopWindowTarget,
|
|
||||||
window::{WindowBuilder, Fullscreen, Window},
|
|
||||||
dpi::PhysicalSize
|
|
||||||
};
|
|
||||||
use glium::{Display, Surface, Version, Api as GlApiTy};
|
|
||||||
use glutin::{
|
|
||||||
config::{Api, ConfigTemplateBuilder}, context::{ContextAttributesBuilder, GlProfile}, display::GetGlDisplay, prelude::*, surface::{SurfaceAttributesBuilder, WindowSurface}
|
|
||||||
};
|
|
||||||
use glutin_winit::DisplayBuilder;
|
|
||||||
use glam::{Vec3, UVec2};
|
|
||||||
use crate::{events::WindowResizedEvent, settings::{GameSettings, FullscreenMode}};
|
|
||||||
|
|
||||||
pub mod primitives;
|
mod renderer;
|
||||||
|
mod primitives;
|
||||||
|
mod selection_box;
|
||||||
|
mod entities;
|
||||||
|
pub use renderer::Renderer;
|
||||||
|
|
||||||
|
pub mod background;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
pub mod selection_box;
|
pub mod camera_uniform;
|
||||||
pub mod entities;
|
pub mod depth;
|
||||||
pub mod sumberge;
|
pub mod smoverlay;
|
||||||
|
|
||||||
|
pub struct BufferPair {
|
||||||
|
pub index: wgpu::Buffer,
|
||||||
|
pub index_len: u32,
|
||||||
|
pub vertex: wgpu::Buffer,
|
||||||
|
pub vertex_len: u32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct RenderTarget(pub glium::Frame);
|
|
||||||
|
|
||||||
#[derive(Unique)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct BackgroundColor(pub Vec3);
|
pub struct BackgroundColor(pub Vec3);
|
||||||
|
|
||||||
#[derive(Unique, Clone, Copy)]
|
pub struct RenderCtx<'a> {
|
||||||
#[repr(transparent)]
|
//pub renderer: &'a Renderer,
|
||||||
pub struct WindowSize(pub UVec2);
|
pub encoder: &'a mut wgpu::CommandEncoder,
|
||||||
|
pub surface_view: &'a wgpu::TextureView,
|
||||||
#[derive(Unique)]
|
|
||||||
pub struct Renderer {
|
|
||||||
pub window: Window,
|
|
||||||
pub display: Display<WindowSurface>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
//TODO run init_world_render_state, init_selection_box_state, etc. only once ingame?
|
||||||
pub fn init(event_loop: &EventLoopWindowTarget<()>, settings: &GameSettings) -> Self {
|
|
||||||
log::info!("initializing display");
|
|
||||||
|
|
||||||
let wb = WindowBuilder::new()
|
pub fn init_rendering() -> Workload {
|
||||||
.with_title("kubi")
|
(
|
||||||
.with_maximized(true)
|
depth::init_depth_texture,
|
||||||
.with_min_inner_size(PhysicalSize::new(640, 480))
|
camera_uniform::init_camera_uniform_buffer,
|
||||||
.with_fullscreen({
|
primitives::init_primitives,
|
||||||
//this has no effect on android, so skip this pointless stuff
|
world::init_world_render_state, //req: depth, camera
|
||||||
#[cfg(target_os = "android")] {
|
entities::init_entities_render_state, //req: depth, camera
|
||||||
None
|
selection_box::init_selection_box_render_state, //req: depth, camera, primitives
|
||||||
}
|
smoverlay::init_smoverlay_render_state, //req: primitives
|
||||||
#[cfg(not(target_os = "android"))]
|
).into_sequential_workload()
|
||||||
if let Some(fs_settings) = &settings.fullscreen {
|
}
|
||||||
let monitor = event_loop.primary_monitor().or_else(|| {
|
|
||||||
event_loop.available_monitors().next()
|
pub fn update_rendering_early() -> Workload {
|
||||||
});
|
(
|
||||||
if let Some(monitor) = monitor {
|
resize_renderer,
|
||||||
log::info!("monitor: {}", monitor.name().unwrap_or_else(|| "generic".into()));
|
depth::resize_depth_texture,
|
||||||
match fs_settings.mode {
|
).into_sequential_workload()
|
||||||
FullscreenMode::Borderless => {
|
}
|
||||||
log::info!("starting in borderless fullscreen mode");
|
|
||||||
Some(Fullscreen::Borderless(Some(monitor)))
|
pub fn update_rendering_late() -> Workload {
|
||||||
},
|
(
|
||||||
FullscreenMode::Exclusive => {
|
camera_uniform::update_camera_uniform_buffer,
|
||||||
log::warn!("exclusive fullscreen mode is experimental");
|
(
|
||||||
log::info!("starting in exclusive fullscreen mode");
|
selection_box::update_selection_box_render_state,
|
||||||
//TODO: grabbing the first video mode is probably not the best idea...
|
entities::update_entities_render_state,
|
||||||
monitor.video_modes().next()
|
smoverlay::update_smoverlay_render_state,
|
||||||
.map(|vmode| {
|
).into_workload().run_if(is_ingame),
|
||||||
log::info!("video mode: {}", vmode.to_string());
|
).into_workload()
|
||||||
Some(Fullscreen::Exclusive(vmode))
|
}
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
pub fn render_master(storages: AllStoragesViewMut) {
|
||||||
log::warn!("no valid video modes found, falling back to windowed mode instead");
|
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
None
|
|
||||||
})
|
let mut encoder = renderer.device().create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
}
|
label: Some("main_encoder"),
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("no monitors found, falling back to windowed mode");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::info!("starting in windowed mode");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
let surface_texture = renderer.surface().get_current_texture().unwrap();
|
||||||
|
let surface_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
let display_builder = DisplayBuilder::new()
|
let mut data = RenderCtx {
|
||||||
.with_window_builder(Some(wb));
|
encoder: &mut encoder,
|
||||||
|
surface_view: &surface_view,
|
||||||
let config_template_builder = ConfigTemplateBuilder::new()
|
|
||||||
.with_api(Api::GLES3)
|
|
||||||
.prefer_hardware_accelerated(Some(true))
|
|
||||||
.with_depth_size(24)
|
|
||||||
.with_multisampling(settings.msaa.unwrap_or_default());
|
|
||||||
|
|
||||||
let (window, gl_config) = display_builder
|
|
||||||
.build(event_loop, config_template_builder, |mut configs| {
|
|
||||||
configs.next().unwrap()
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let window = window.expect("no window");
|
|
||||||
|
|
||||||
// Now we get the window size to use as the initial size of the Surface
|
|
||||||
let (width, height): (u32, u32) = window.inner_size().into();
|
|
||||||
let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
|
|
||||||
window.raw_window_handle(),
|
|
||||||
NonZeroU32::new(width).unwrap(),
|
|
||||||
NonZeroU32::new(height).unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Finally we can create a Surface, use it to make a PossiblyCurrentContext and create the glium Display
|
|
||||||
let surface = unsafe {
|
|
||||||
gl_config.display().create_window_surface(&gl_config, &attrs).unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let context_attributes = ContextAttributesBuilder::new()
|
storages.run_with_data(background::clear_bg, &mut data);
|
||||||
.with_debug(cfg!(debug_assertions))
|
if storages.run(is_ingame) {
|
||||||
.with_context_api(glutin::context::ContextApi::Gles(None))
|
storages.run_with_data(world::draw_world, &mut data);
|
||||||
.with_profile(GlProfile::Core)
|
storages.run_with_data(selection_box::draw_selection_box, &mut data);
|
||||||
.build(Some(window.raw_window_handle()));
|
storages.run_with_data(entities::render_entities, &mut data);
|
||||||
|
storages.run_with_data(world::rpass_submit_trans_bundle, &mut data);
|
||||||
|
storages.run_with_data(smoverlay::render_submerged_view, &mut data);
|
||||||
|
}
|
||||||
|
storages.run_with_data(kubi_ui_draw, &mut data);
|
||||||
|
|
||||||
let current_context = unsafe {
|
renderer.queue().submit([encoder.finish()]);
|
||||||
gl_config.display()
|
surface_texture.present();
|
||||||
.create_context(&gl_config, &context_attributes)
|
}
|
||||||
.expect("failed to create context")
|
|
||||||
}.make_current(&surface).unwrap();
|
|
||||||
|
|
||||||
let display = Display::from_context_surface(current_context, surface).unwrap();
|
/// Resize the renderer when the window is resized
|
||||||
|
pub fn resize_renderer(
|
||||||
//TODO MIGRATION
|
mut renderer: UniqueViewMut<Renderer>,
|
||||||
// let cb = ContextBuilder::new()
|
resize: View<WindowResizedEvent>,
|
||||||
// //.with_srgb(false)
|
) {
|
||||||
// .with_depth_buffer(24)
|
if let Some(size) = resize.iter().last() {
|
||||||
// .with_multisampling(settings.msaa.unwrap_or_default())
|
renderer.resize(PhysicalSize::new(size.0.x, size.0.y));
|
||||||
// .with_vsync(settings.vsync)
|
|
||||||
// .with_gl_profile(GlProfile::Core)
|
|
||||||
// .with_gl(GlRequest::Latest);
|
|
||||||
|
|
||||||
// let display = Display::new(wb, cb)
|
|
||||||
// .expect("Failed to create a glium Display");
|
|
||||||
|
|
||||||
log::info!("Vendor: {}", display.get_opengl_vendor_string());
|
|
||||||
log::info!("Renderer: {}", display.get_opengl_renderer_string());
|
|
||||||
log::info!("OpenGL: {}", display.get_opengl_version_string());
|
|
||||||
log::info!("Supports GLSL: {:?}", display.get_supported_glsl_version());
|
|
||||||
log::info!("Framebuffer dimensions: {:?}", display.get_framebuffer_dimensions());
|
|
||||||
if display.is_context_loss_possible() { log::warn!("OpenGL context loss possible") }
|
|
||||||
if display.is_robust() { log::warn!("OpenGL implementation is not robust") }
|
|
||||||
if display.is_debug() { log::info!("OpenGL context is in debug mode") }
|
|
||||||
|
|
||||||
assert!(display.is_glsl_version_supported(&Version(GlApiTy::GlEs, 3, 0)), "GLSL ES 3.0 is not supported");
|
|
||||||
|
|
||||||
Self { window, display }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_background(
|
// pub fn if_resized (resize: View<WindowResizedEvent>,) -> bool {
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
// resize.len() > 0
|
||||||
color: UniqueView<BackgroundColor>,
|
// }
|
||||||
) {
|
|
||||||
target.0.clear_color_srgb_and_depth((color.0.x, color.0.y, color.0.z, 1.), 1.);
|
|
||||||
}
|
|
||||||
|
|
||||||
//not sure if this belongs here
|
|
||||||
|
|
||||||
pub fn init_window_size(
|
|
||||||
storages: AllStoragesView,
|
|
||||||
) {
|
|
||||||
let size = storages.borrow::<View<WindowResizedEvent>>().unwrap().iter().next().unwrap().0;
|
|
||||||
storages.add_unique(WindowSize(size))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_window_size(
|
|
||||||
mut win_size: UniqueViewMut<WindowSize>,
|
|
||||||
resize: View<WindowResizedEvent>,
|
|
||||||
) {
|
|
||||||
if let Some(resize) = resize.iter().next() {
|
|
||||||
win_size.0 = resize.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn if_resized (
|
|
||||||
resize: View<WindowResizedEvent>,
|
|
||||||
) -> bool {
|
|
||||||
resize.len() > 0
|
|
||||||
}
|
|
||||||
|
|
25
kubi/src/rendering/background.rs
Normal file
25
kubi/src/rendering/background.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use shipyard::UniqueView;
|
||||||
|
use super::{BackgroundColor, RenderCtx};
|
||||||
|
|
||||||
|
pub fn clear_bg(
|
||||||
|
ctx: &mut RenderCtx,
|
||||||
|
bg: UniqueView<BackgroundColor>,
|
||||||
|
) {
|
||||||
|
let _rpass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("clear_bg"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: ctx.surface_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: bg.0.x as f64,
|
||||||
|
g: bg.0.y as f64,
|
||||||
|
b: bg.0.z as f64,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
81
kubi/src/rendering/camera_uniform.rs
Normal file
81
kubi/src/rendering/camera_uniform.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use shipyard::{AllStoragesView, IntoIter, Unique, UniqueView, View};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use crate::camera::Camera;
|
||||||
|
use super::Renderer;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct CameraUniformData {
|
||||||
|
pub view_proj: [f32; 4 * 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO if multiple cameras, buffer per camera
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct CameraUniformBuffer {
|
||||||
|
pub camera_uniform_buffer: wgpu::Buffer,
|
||||||
|
pub camera_bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
pub camera_bind_group: wgpu::BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraUniformBuffer {
|
||||||
|
pub fn init(renderer: &Renderer, data: CameraUniformData) -> Self {
|
||||||
|
let camera_uniform_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("camera_uniform_buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&[data]),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let camera_bind_group_layout = renderer.device().create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("camera_bind_group_layout"),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let camera_bind_group = renderer.device().create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("camera_bind_group"),
|
||||||
|
layout: &camera_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: camera_uniform_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self { camera_uniform_buffer, camera_bind_group_layout, camera_bind_group }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_default(renderer: &Renderer) -> Self {
|
||||||
|
Self::init(renderer, CameraUniformData::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self, renderer: &Renderer, data: CameraUniformData) {
|
||||||
|
renderer.queue().write_buffer(&self.camera_uniform_buffer, 0, bytemuck::cast_slice(&[data]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_camera_uniform_buffer(storages: AllStoragesView) {
|
||||||
|
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
|
storages.add_unique(CameraUniformBuffer::init_default(&renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_camera_uniform_buffer(
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
camera_uniform_buffer: UniqueView<CameraUniformBuffer>,
|
||||||
|
camera: View<Camera>,
|
||||||
|
) {
|
||||||
|
let Some(camera) = camera.iter().next() else { return };
|
||||||
|
let proj = camera.perspective_matrix * camera.view_matrix;
|
||||||
|
camera_uniform_buffer.update(&renderer, CameraUniformData { view_proj: proj.to_cols_array() });
|
||||||
|
}
|
72
kubi/src/rendering/depth.rs
Normal file
72
kubi/src/rendering/depth.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use glam::{uvec2, UVec2};
|
||||||
|
use shipyard::{AllStoragesView, Unique, UniqueView, UniqueViewMut};
|
||||||
|
|
||||||
|
use super::Renderer;
|
||||||
|
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct DepthTexture {
|
||||||
|
pub depth_texture: wgpu::Texture,
|
||||||
|
pub depth_view: wgpu::TextureView,
|
||||||
|
pub depth_sampler: wgpu::Sampler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DepthTexture {
|
||||||
|
fn desc(size: UVec2) -> wgpu::TextureDescriptor<'static> {
|
||||||
|
wgpu::TextureDescriptor {
|
||||||
|
label: Some("depth_texture"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: size.x,
|
||||||
|
height: size.y,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
|
||||||
|
view_formats: &[wgpu::TextureFormat::Depth32Float],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(renderer: &Renderer) -> Self {
|
||||||
|
let size = uvec2(renderer.size().width, renderer.size().height);
|
||||||
|
let depth_texture_desc = Self::desc(size);
|
||||||
|
let depth_texture = renderer.device().create_texture(&depth_texture_desc);
|
||||||
|
let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let depth_sampler = renderer.device().create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
label: Some("depth_sampler"),
|
||||||
|
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,
|
||||||
|
compare: Some(wgpu::CompareFunction::LessEqual),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
Self { depth_texture, depth_view, depth_sampler }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, renderer: &Renderer) {
|
||||||
|
let old_size = uvec2(self.depth_texture.size().width, self.depth_texture.size().height);
|
||||||
|
let new_size = uvec2(renderer.size().width, renderer.size().height);
|
||||||
|
if old_size == new_size { return }
|
||||||
|
let depth_texture_desc = Self::desc(new_size);
|
||||||
|
self.depth_texture = renderer.device().create_texture(&depth_texture_desc);
|
||||||
|
self.depth_view = self.depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_depth_texture(
|
||||||
|
storages: AllStoragesView,
|
||||||
|
) {
|
||||||
|
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
|
storages.add_unique(DepthTexture::init(&renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize_depth_texture(
|
||||||
|
mut depth_texture: UniqueViewMut<DepthTexture>,
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
) {
|
||||||
|
depth_texture.resize(&renderer);
|
||||||
|
}
|
|
@ -1,58 +1,67 @@
|
||||||
use shipyard::{NonSendSync, UniqueViewMut, UniqueView, View, IntoIter, IntoWithId};
|
use shipyard::{AllStoragesView, IntoIter, IntoWithId, Unique, UniqueView, View};
|
||||||
use glium::{DepthTest, Depth, PolygonMode, BackfaceCullingMode, DrawParameters, Surface, uniform};
|
|
||||||
use kubi_shared::{entity::Entity, transform::Transform};
|
use kubi_shared::{entity::Entity, transform::Transform};
|
||||||
use crate::{
|
use crate::{
|
||||||
prefabs::ColoredShaderPrefab,
|
camera::Camera, prefabs::GpuPrefabs, settings::GameSettings
|
||||||
camera::Camera,
|
|
||||||
settings::GameSettings
|
|
||||||
};
|
|
||||||
use super::{
|
|
||||||
RenderTarget,
|
|
||||||
primitives::cube::CenteredCubePrimitive
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, RenderCtx};
|
||||||
|
|
||||||
|
mod instance;
|
||||||
|
mod pipeline;
|
||||||
|
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct EntitiesRenderState {
|
||||||
|
pub pipeline: wgpu::RenderPipeline,
|
||||||
|
pub instance_buffer: instance::InstanceBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_entities_render_state(storages: AllStoragesView) {
|
||||||
|
storages.add_unique(EntitiesRenderState {
|
||||||
|
pipeline: storages.run(pipeline::init_entities_pipeline),
|
||||||
|
instance_buffer: storages.run(instance::create_instance_buffer),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use instance::update_instance_buffer as update_entities_render_state;
|
||||||
|
|
||||||
// TODO: entity models
|
// TODO: entity models
|
||||||
pub fn render_entities(
|
pub fn render_entities(
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
ctx: &mut RenderCtx,
|
||||||
buffers: NonSendSync<UniqueView<CenteredCubePrimitive>>,
|
state: UniqueView<EntitiesRenderState>,
|
||||||
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
|
depth: UniqueView<DepthTexture>,
|
||||||
camera: View<Camera>,
|
prefabs: UniqueView<GpuPrefabs>,
|
||||||
settings: UniqueView<GameSettings>,
|
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||||
entities: View<Entity>,
|
|
||||||
transform: View<Transform>,
|
|
||||||
) {
|
) {
|
||||||
let (camera_id, camera) = camera.iter().with_id().next().expect("No cameras in the scene");
|
if state.instance_buffer.count == 0 {
|
||||||
|
return
|
||||||
let draw_parameters = DrawParameters {
|
|
||||||
depth: Depth {
|
|
||||||
test: DepthTest::IfLess,
|
|
||||||
write: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
multisampling: settings.msaa.is_some(),
|
|
||||||
polygon_mode: PolygonMode::Fill,
|
|
||||||
backface_culling: BackfaceCullingMode::CullClockwise,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let view = camera.view_matrix.to_cols_array_2d();
|
|
||||||
let perspective = camera.perspective_matrix.to_cols_array_2d();
|
|
||||||
|
|
||||||
for (entity_id, (_, trans)) in (&entities, &transform).iter().with_id() {
|
|
||||||
//skip rendering camera holder (as the entity would block the view)
|
|
||||||
if entity_id == camera_id { continue }
|
|
||||||
|
|
||||||
//render entity
|
|
||||||
target.0.draw(
|
|
||||||
&buffers.0,
|
|
||||||
&buffers.1,
|
|
||||||
&program.0,
|
|
||||||
&uniform! {
|
|
||||||
color: [1.0, 1.0, 1.0, 1.0_f32],
|
|
||||||
model: trans.0.to_cols_array_2d(),
|
|
||||||
view: view,
|
|
||||||
perspective: perspective,
|
|
||||||
},
|
|
||||||
&draw_parameters
|
|
||||||
).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut rpass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("rpass_draw_entities"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: ctx.surface_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||||
|
view: &depth.depth_view,
|
||||||
|
depth_ops: Some(wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
}),
|
||||||
|
stencil_ops: None,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
rpass.set_pipeline(&state.pipeline);
|
||||||
|
rpass.set_bind_group(0, &prefabs.player_model_diffuse_bind_group, &[]);
|
||||||
|
rpass.set_bind_group(1, &camera_ubo.camera_bind_group, &[]);
|
||||||
|
rpass.set_vertex_buffer(0, prefabs.player_model.vertex.slice(..));
|
||||||
|
rpass.set_vertex_buffer(1, state.instance_buffer.buffer.slice(..));
|
||||||
|
rpass.set_index_buffer(prefabs.player_model.index.slice(..), wgpu::IndexFormat::Uint32);
|
||||||
|
rpass.draw_indexed(0..prefabs.player_model.index_len, 0, 0..state.instance_buffer.count);
|
||||||
}
|
}
|
||||||
|
|
78
kubi/src/rendering/entities/instance.rs
Normal file
78
kubi/src/rendering/entities/instance.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use kubi_shared::{entity::Entity, transform::Transform};
|
||||||
|
use renderer::Renderer;
|
||||||
|
use shipyard::{EntityId, IntoIter, IntoWithId, UniqueView, UniqueViewMut, View};
|
||||||
|
|
||||||
|
use crate::{camera::Camera, rendering::renderer};
|
||||||
|
|
||||||
|
use super::EntitiesRenderState;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct InstanceData {
|
||||||
|
pub mat: [f32; 4 * 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstanceData {
|
||||||
|
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<InstanceData>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Instance,
|
||||||
|
attributes: &wgpu::vertex_attr_array![
|
||||||
|
3 => Float32x4,
|
||||||
|
4 => Float32x4,
|
||||||
|
5 => Float32x4,
|
||||||
|
6 => Float32x4,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InstanceBuffer {
|
||||||
|
pub count: u32,
|
||||||
|
pub buffer: wgpu::Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_instance_buffer(
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
) -> InstanceBuffer {
|
||||||
|
log::info!("entities: create_instance_buffer");
|
||||||
|
let buffer = renderer.device().create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("instance_buffer"),
|
||||||
|
size: 255 * std::mem::size_of::<InstanceData>() as u64,
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
InstanceBuffer { count: 0, buffer }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_instance_buffer(
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
mut state: UniqueViewMut<EntitiesRenderState>,
|
||||||
|
entities: View<Entity>,
|
||||||
|
transforms: View<Transform>,
|
||||||
|
camera: View<Camera>,
|
||||||
|
) {
|
||||||
|
//Get id of the camera entity (this assumes a single camera entity)
|
||||||
|
let cam_id = (&camera)
|
||||||
|
.iter().with_id().next()
|
||||||
|
.map(|(x, _)| x)
|
||||||
|
.unwrap_or(EntityId::dead());
|
||||||
|
|
||||||
|
// Create a list of instance data for all entities except ones that have camera attached
|
||||||
|
let mut instances = Vec::with_capacity(entities.len() - 1);
|
||||||
|
for (id, (_, trans)) in (&entities, &transforms).iter().with_id() {
|
||||||
|
if id == cam_id { continue }
|
||||||
|
instances.push(InstanceData {
|
||||||
|
mat: trans.0.to_cols_array(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
state.instance_buffer.count = instances.len() as u32;
|
||||||
|
|
||||||
|
if !instances.is_empty() {
|
||||||
|
renderer.queue().write_buffer(
|
||||||
|
&state.instance_buffer.buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&instances)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
66
kubi/src/rendering/entities/pipeline.rs
Normal file
66
kubi/src/rendering/entities/pipeline.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use shipyard::UniqueView;
|
||||||
|
use wgpu::include_wgsl;
|
||||||
|
use crate::{prefabs::{GpuPrefabs, ModelVertex}, rendering::{camera_uniform::CameraUniformBuffer, Renderer}};
|
||||||
|
|
||||||
|
use super::instance::InstanceData;
|
||||||
|
|
||||||
|
pub fn init_entities_pipeline(
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
prefabs: UniqueView<GpuPrefabs>,
|
||||||
|
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||||
|
) -> wgpu::RenderPipeline {
|
||||||
|
log::info!("init_entities_pipeline");
|
||||||
|
|
||||||
|
let module = renderer.device().create_shader_module(include_wgsl!("../../../shaders/entities.wgsl"));
|
||||||
|
|
||||||
|
let pipeline_layout = renderer.device().create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("entities_pipeline_layout"),
|
||||||
|
bind_group_layouts: &[
|
||||||
|
&prefabs.player_model_diffuse_bind_group_layout,
|
||||||
|
&camera_ubo.camera_bind_group_layout,
|
||||||
|
],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
renderer.device().create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("entities_pipeline"),
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &module,
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[
|
||||||
|
ModelVertex::LAYOUT,
|
||||||
|
InstanceData::LAYOUT,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &module,
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: renderer.surface_config().format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::COLOR,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: None, // Some(wgpu::Face::Back), //XXX: this culls their majestic ears! :(
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
conservative: false,
|
||||||
|
unclipped_depth: false,
|
||||||
|
},
|
||||||
|
depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
depth_write_enabled: true,
|
||||||
|
depth_compare: wgpu::CompareFunction::Less,
|
||||||
|
bias: wgpu::DepthBiasState::default(),
|
||||||
|
stencil: wgpu::StencilState::default(),
|
||||||
|
}),
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,30 +1,42 @@
|
||||||
use shipyard::{Workload, IntoWorkload};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use glium::implement_vertex;
|
use shipyard::{IntoWorkload, Workload};
|
||||||
|
|
||||||
pub mod cube;
|
mod cube;
|
||||||
pub mod rect;
|
mod fstri;
|
||||||
pub mod stri;
|
pub use cube::CubePrimitive;
|
||||||
|
pub use fstri::FstriPrimitive;
|
||||||
use cube::init_cube_primitive;
|
|
||||||
use rect::init_rect_primitive;
|
|
||||||
use stri::init_stri_primitive;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
pub struct PositionOnlyVertex {
|
|
||||||
pub position: [f32; 3],
|
|
||||||
}
|
|
||||||
implement_vertex!(PositionOnlyVertex, position);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
|
||||||
pub struct PositionOnlyVertex2d {
|
|
||||||
pub position: [f32; 2],
|
|
||||||
}
|
|
||||||
implement_vertex!(PositionOnlyVertex2d, position);
|
|
||||||
|
|
||||||
pub fn init_primitives() -> Workload {
|
pub fn init_primitives() -> Workload {
|
||||||
(
|
(
|
||||||
init_cube_primitive,
|
cube::init_cube_primitive,
|
||||||
init_rect_primitive,
|
fstri::init_fstri_primitive,
|
||||||
init_stri_primitive,
|
|
||||||
).into_workload()
|
).into_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct PrimitiveVertex {
|
||||||
|
pub position: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimitiveVertex {
|
||||||
|
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<PrimitiveVertex>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &wgpu::vertex_attr_array![0 => Float32x3],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct PrimitiveVertex2 {
|
||||||
|
pub position: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimitiveVertex2 {
|
||||||
|
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<PrimitiveVertex2>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &wgpu::vertex_attr_array![0 => Float32x2],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,85 +1,50 @@
|
||||||
use shipyard::{AllStoragesView, NonSendSync, UniqueView, Unique};
|
use shipyard::{AllStoragesView, Unique, UniqueView};
|
||||||
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
use wgpu::util::DeviceExt;
|
||||||
use crate::rendering::Renderer;
|
use crate::rendering::{BufferPair, Renderer};
|
||||||
use super::PositionOnlyVertex;
|
use super::PrimitiveVertex;
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
pub struct CubePrimitive(pub VertexBuffer<PositionOnlyVertex>, pub IndexBuffer<u16>);
|
pub struct CubePrimitive(pub BufferPair);
|
||||||
|
|
||||||
#[derive(Unique)]
|
/// Vertices for a centered cube with a side length of 1
|
||||||
pub struct CenteredCubePrimitive(pub VertexBuffer<PositionOnlyVertex>, pub IndexBuffer<u16>);
|
const CUBE_VERTICES: &[PrimitiveVertex] = &[
|
||||||
|
// front
|
||||||
|
PrimitiveVertex { position: [-0.5, -0.5, 0.5] },
|
||||||
|
PrimitiveVertex { position: [ 0.5, -0.5, 0.5] },
|
||||||
|
PrimitiveVertex { position: [ 0.5, 0.5, 0.5] },
|
||||||
|
PrimitiveVertex { position: [-0.5, 0.5, 0.5] },
|
||||||
|
// back
|
||||||
|
PrimitiveVertex { position: [-0.5, -0.5, -0.5] },
|
||||||
|
PrimitiveVertex { position: [ 0.5, -0.5, -0.5] },
|
||||||
|
PrimitiveVertex { position: [ 0.5, 0.5, -0.5] },
|
||||||
|
PrimitiveVertex { position: [-0.5, 0.5, -0.5] },
|
||||||
|
];
|
||||||
|
|
||||||
const CENTERED_CUBE_VERTICES: &[PositionOnlyVertex] = &[
|
/// Indices for a cube primitive
|
||||||
// front
|
|
||||||
PositionOnlyVertex { position: [-0.5, -0.5, 0.5] },
|
|
||||||
PositionOnlyVertex { position: [ 0.5, -0.5, 0.5] },
|
|
||||||
PositionOnlyVertex { position: [ 0.5, 0.5, 0.5] },
|
|
||||||
PositionOnlyVertex { position: [-0.5, 0.5, 0.5] },
|
|
||||||
// back
|
|
||||||
PositionOnlyVertex { position: [-0.5, -0.5, -0.5] },
|
|
||||||
PositionOnlyVertex { position: [ 0.5, -0.5, -0.5] },
|
|
||||||
PositionOnlyVertex { position: [ 0.5, 0.5, -0.5] },
|
|
||||||
PositionOnlyVertex { position: [-0.5, 0.5, -0.5] },
|
|
||||||
];
|
|
||||||
const CUBE_VERTICES: &[PositionOnlyVertex] = &[
|
|
||||||
// front
|
|
||||||
PositionOnlyVertex { position: [0.0, 0.0, 1.0] },
|
|
||||||
PositionOnlyVertex { position: [1.0, 0.0, 1.0] },
|
|
||||||
PositionOnlyVertex { position: [1.0, 1.0, 1.0] },
|
|
||||||
PositionOnlyVertex { position: [0.0, 1.0, 1.0] },
|
|
||||||
// back
|
|
||||||
PositionOnlyVertex { position: [0.0, 0.0, 0.0] },
|
|
||||||
PositionOnlyVertex { position: [1.0, 0.0, 0.0] },
|
|
||||||
PositionOnlyVertex { position: [1.0, 1.0, 0.0] },
|
|
||||||
PositionOnlyVertex { position: [0.0, 1.0, 0.0] },
|
|
||||||
];
|
|
||||||
const CUBE_INDICES: &[u16] = &[
|
const CUBE_INDICES: &[u16] = &[
|
||||||
// front
|
0, 1, 2, 2, 3, 0, // front
|
||||||
0, 1, 2,
|
1, 5, 6, 6, 2, 1, // right
|
||||||
2, 3, 0,
|
7, 6, 5, 5, 4, 7, // back
|
||||||
// right
|
4, 0, 3, 3, 7, 4, // left
|
||||||
1, 5, 6,
|
4, 5, 1, 1, 0, 4, // bottom
|
||||||
6, 2, 1,
|
3, 2, 6, 6, 7, 3, // top
|
||||||
// back
|
|
||||||
7, 6, 5,
|
|
||||||
5, 4, 7,
|
|
||||||
// left
|
|
||||||
4, 0, 3,
|
|
||||||
3, 7, 4,
|
|
||||||
// bottom
|
|
||||||
4, 5, 1,
|
|
||||||
1, 0, 4,
|
|
||||||
// top
|
|
||||||
3, 2, 6,
|
|
||||||
6, 7, 3
|
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(super) fn init_cube_primitive(
|
pub fn init_cube_primitive(storages: AllStoragesView) {
|
||||||
storages: AllStoragesView,
|
log::info!("init_cube_primitive");
|
||||||
display: NonSendSync<UniqueView<Renderer>>
|
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
) {
|
storages.add_unique(CubePrimitive(BufferPair {
|
||||||
{
|
index: renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
let vert = VertexBuffer::immutable(
|
label: Some("cube_index_buffer"),
|
||||||
&display.display,
|
contents: bytemuck::cast_slice(CUBE_INDICES),
|
||||||
CUBE_VERTICES
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
|
||||||
).unwrap();
|
}),
|
||||||
let index = IndexBuffer::immutable(
|
index_len: CUBE_INDICES.len() as u32,
|
||||||
&display.display,
|
vertex: renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
PrimitiveType::TrianglesList,
|
label: Some("cube_vertex_buffer"),
|
||||||
CUBE_INDICES
|
contents: bytemuck::cast_slice(CUBE_VERTICES),
|
||||||
).unwrap();
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||||
storages.add_unique_non_send_sync(CubePrimitive(vert, index));
|
}),
|
||||||
}
|
vertex_len: CUBE_VERTICES.len() as u32,
|
||||||
{
|
}));
|
||||||
let vert = VertexBuffer::immutable(
|
|
||||||
&display.display,
|
|
||||||
CENTERED_CUBE_VERTICES
|
|
||||||
).unwrap();
|
|
||||||
let index = IndexBuffer::immutable(
|
|
||||||
&display.display,
|
|
||||||
PrimitiveType::TrianglesList,
|
|
||||||
CUBE_INDICES
|
|
||||||
).unwrap();
|
|
||||||
storages.add_unique_non_send_sync(CenteredCubePrimitive(vert, index));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
24
kubi/src/rendering/primitives/fstri.rs
Normal file
24
kubi/src/rendering/primitives/fstri.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use shipyard::{AllStoragesView, Unique, UniqueView};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use crate::rendering::Renderer;
|
||||||
|
use super::PrimitiveVertex2;
|
||||||
|
|
||||||
|
pub const FSTRI_VERTICES: &[PrimitiveVertex2] = &[
|
||||||
|
PrimitiveVertex2 { position: [-1.0, -1.0] },
|
||||||
|
PrimitiveVertex2 { position: [ 3.0, -1.0] },
|
||||||
|
PrimitiveVertex2 { position: [-1.0, 3.0] },
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct FstriPrimitive(pub wgpu::Buffer);
|
||||||
|
|
||||||
|
pub fn init_fstri_primitive(storages: AllStoragesView) {
|
||||||
|
log::info!("init_fstri_primitive");
|
||||||
|
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||||
|
let buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("fstri_vertex_buffer"),
|
||||||
|
contents: bytemuck::cast_slice(FSTRI_VERTICES),
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
storages.add_unique(FstriPrimitive(buffer));
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
use shipyard::{Unique, AllStoragesView, NonSendSync, UniqueView};
|
|
||||||
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
|
||||||
use crate::rendering::Renderer;
|
|
||||||
use super::PositionOnlyVertex2d;
|
|
||||||
|
|
||||||
#[derive(Unique)]
|
|
||||||
pub struct RectPrimitive(pub VertexBuffer<PositionOnlyVertex2d>, pub IndexBuffer<u16>);
|
|
||||||
|
|
||||||
const RECT_VERTEX: &[PositionOnlyVertex2d] = &[
|
|
||||||
PositionOnlyVertex2d { position: [0., 0.] },
|
|
||||||
PositionOnlyVertex2d { position: [1., 0.] },
|
|
||||||
PositionOnlyVertex2d { position: [0., 1.] },
|
|
||||||
PositionOnlyVertex2d { position: [1., 1.] },
|
|
||||||
];
|
|
||||||
const RECT_INDEX: &[u16] = &[0, 1, 2, 1, 3, 2];
|
|
||||||
|
|
||||||
pub(super) fn init_rect_primitive(
|
|
||||||
storages: AllStoragesView,
|
|
||||||
display: NonSendSync<UniqueView<Renderer>>
|
|
||||||
) {
|
|
||||||
let vert = VertexBuffer::immutable(
|
|
||||||
&display.display,
|
|
||||||
RECT_VERTEX
|
|
||||||
).unwrap();
|
|
||||||
let index = IndexBuffer::immutable(
|
|
||||||
&display.display,
|
|
||||||
PrimitiveType::TrianglesList,
|
|
||||||
RECT_INDEX
|
|
||||||
).unwrap();
|
|
||||||
storages.add_unique_non_send_sync(RectPrimitive(vert, index));
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
use shipyard::{Unique, AllStoragesView, NonSendSync, UniqueView};
|
|
||||||
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
|
||||||
use crate::rendering::Renderer;
|
|
||||||
use super::PositionOnlyVertex2d;
|
|
||||||
|
|
||||||
#[derive(Unique)]
|
|
||||||
pub struct STriPrimitive(pub VertexBuffer<PositionOnlyVertex2d>, pub IndexBuffer<u16>);
|
|
||||||
|
|
||||||
const STRI_VERTEX: &[PositionOnlyVertex2d] = &[
|
|
||||||
PositionOnlyVertex2d { position: [-1., -1.] },
|
|
||||||
PositionOnlyVertex2d { position: [ 3., -1.] },
|
|
||||||
PositionOnlyVertex2d { position: [-1., 3.] },
|
|
||||||
];
|
|
||||||
const STRI_INDEX: &[u16] = &[0, 1, 2];
|
|
||||||
|
|
||||||
pub(super) fn init_stri_primitive(
|
|
||||||
storages: AllStoragesView,
|
|
||||||
display: NonSendSync<UniqueView<Renderer>>
|
|
||||||
) {
|
|
||||||
let vert = VertexBuffer::immutable(
|
|
||||||
&display.display,
|
|
||||||
STRI_VERTEX
|
|
||||||
).unwrap();
|
|
||||||
let index = IndexBuffer::immutable(
|
|
||||||
&display.display,
|
|
||||||
PrimitiveType::TrianglesList,
|
|
||||||
STRI_INDEX
|
|
||||||
).unwrap();
|
|
||||||
storages.add_unique_non_send_sync(STriPrimitive(vert, index));
|
|
||||||
}
|
|
177
kubi/src/rendering/renderer.rs
Normal file
177
kubi/src/rendering/renderer.rs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
use pollster::FutureExt;
|
||||||
|
use shipyard::Unique;
|
||||||
|
use winit::{
|
||||||
|
event_loop::ActiveEventLoop,
|
||||||
|
window::{Fullscreen, Window},
|
||||||
|
dpi::PhysicalSize
|
||||||
|
};
|
||||||
|
use crate::settings::{GameSettings, FullscreenMode};
|
||||||
|
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct Renderer {
|
||||||
|
window: Arc<Window>,
|
||||||
|
instance: wgpu::Instance,
|
||||||
|
surface: wgpu::Surface<'static>,
|
||||||
|
device: wgpu::Device,
|
||||||
|
queue: wgpu::Queue,
|
||||||
|
surface_config: wgpu::SurfaceConfiguration,
|
||||||
|
size: PhysicalSize<u32>,
|
||||||
|
// pub depth_texture: wgpu::Texture,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderer {
|
||||||
|
pub fn init(event_loop: &ActiveEventLoop, settings: &GameSettings) -> Self {
|
||||||
|
log::info!("initializing display");
|
||||||
|
|
||||||
|
let window_attributes = Window::default_attributes()
|
||||||
|
.with_title("kubi")
|
||||||
|
.with_maximized(true)
|
||||||
|
.with_min_inner_size(PhysicalSize::new(640, 480))
|
||||||
|
.with_fullscreen({
|
||||||
|
//this has no effect on android, so skip this pointless stuff
|
||||||
|
#[cfg(target_os = "android")] {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
if let Some(fs_settings) = &settings.fullscreen {
|
||||||
|
let monitor = event_loop.primary_monitor().or_else(|| {
|
||||||
|
event_loop.available_monitors().next()
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(monitor) = monitor {
|
||||||
|
log::info!("monitor: {}", monitor.name().unwrap_or_else(|| "generic".into()));
|
||||||
|
match fs_settings.mode {
|
||||||
|
FullscreenMode::Borderless => {
|
||||||
|
log::info!("starting in borderless fullscreen mode");
|
||||||
|
Some(Fullscreen::Borderless(Some(monitor)))
|
||||||
|
},
|
||||||
|
FullscreenMode::Exclusive => {
|
||||||
|
log::warn!("exclusive fullscreen mode is experimental");
|
||||||
|
log::info!("starting in exclusive fullscreen mode");
|
||||||
|
//TODO: grabbing the first video mode is probably not the best idea...
|
||||||
|
monitor.video_modes().next()
|
||||||
|
.map(|vmode| {
|
||||||
|
log::info!("video mode: {}", vmode.to_string());
|
||||||
|
Some(Fullscreen::Exclusive(vmode))
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
log::warn!("no valid video modes found, falling back to windowed mode instead");
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("no monitors found, falling back to windowed mode");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::info!("starting in windowed mode");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
|
||||||
|
|
||||||
|
let size = window.inner_size();
|
||||||
|
|
||||||
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||||
|
backends: wgpu::Backends::all(),
|
||||||
|
//Disable validation layer
|
||||||
|
flags: wgpu::InstanceFlags::default() & !wgpu::InstanceFlags::VALIDATION,
|
||||||
|
//we're using vulkan on windows
|
||||||
|
// #[cfg(all(target_os = "windows", target_arch = "x86_64"))]
|
||||||
|
// dx12_shader_compiler: wgpu::Dx12Compiler::Dxc {
|
||||||
|
// dxil_path: Some("./dxil.dll".into()),
|
||||||
|
// dxc_path: Some("./dxcompiler.dll".into()),
|
||||||
|
// },
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a surface with `create_surface_unsafe` to get a surface with 'static lifetime
|
||||||
|
// It should never outlive the window it's created from
|
||||||
|
// let surface = unsafe {
|
||||||
|
// let target = wgpu::SurfaceTargetUnsafe::from_window(&window).unwrap();
|
||||||
|
// instance.create_surface_unsafe(target).unwrap()
|
||||||
|
// };
|
||||||
|
let surface = instance.create_surface(Arc::clone(&window)).unwrap();
|
||||||
|
|
||||||
|
let adapter = instance.request_adapter(
|
||||||
|
&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||||
|
compatible_surface: Some(&surface),
|
||||||
|
force_fallback_adapter: false,
|
||||||
|
},
|
||||||
|
).block_on().unwrap();
|
||||||
|
|
||||||
|
log::info!("Adapter: {:?}", adapter.get_info());
|
||||||
|
log::info!("Features: {:?}", adapter.features());
|
||||||
|
log::info!("Limits: {:?}", adapter.limits());
|
||||||
|
|
||||||
|
let (device, queue) = adapter.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
required_features: wgpu::Features::empty(),
|
||||||
|
required_limits: wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
).block_on().unwrap();
|
||||||
|
|
||||||
|
let surface_config = surface.get_default_config(&adapter, size.width, size.height).unwrap();
|
||||||
|
surface.configure(&device, &surface_config);
|
||||||
|
|
||||||
|
Self { window, instance, surface, device, queue, surface_config, size }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, size: PhysicalSize<u32>) {
|
||||||
|
if size.width == 0 || size.height == 0 {
|
||||||
|
log::warn!("Ignoring resize event with zero width or height");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.size == size {
|
||||||
|
log::warn!("Ignoring resize event with same size");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log::debug!("resizing surface to {:?}", size);
|
||||||
|
self.size = size;
|
||||||
|
self.surface_config.width = size.width;
|
||||||
|
self.surface_config.height = size.height;
|
||||||
|
self.surface.configure(&self.device, &self.surface_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reconfigure(&self) {
|
||||||
|
self.surface.configure(&self.device, &self.surface_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
//getters:
|
||||||
|
pub fn size(&self) -> PhysicalSize<u32> {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size_uvec2(&self) -> glam::UVec2 {
|
||||||
|
glam::UVec2::new(self.size.width, self.size.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size_vec2(&self) -> glam::Vec2 {
|
||||||
|
glam::Vec2::new(self.size.width as f32, self.size.height as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window(&self) -> &Window {
|
||||||
|
&self.window
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn surface(&self) -> &wgpu::Surface<'static> {
|
||||||
|
&self.surface
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device(&self) -> &wgpu::Device {
|
||||||
|
&self.device
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue(&self) -> &wgpu::Queue {
|
||||||
|
&self.queue
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn surface_config(&self) -> &wgpu::SurfaceConfiguration {
|
||||||
|
&self.surface_config
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,59 +1,65 @@
|
||||||
use glam::{Mat4, Vec3, Quat};
|
use shipyard::{AllStoragesView, IntoIter, Unique, UniqueView, View};
|
||||||
use shipyard::{View, IntoIter, NonSendSync, UniqueViewMut, UniqueView};
|
use wgpu::RenderPassDescriptor;
|
||||||
use glium::{
|
use crate::{player::MainPlayer, world::raycast::LookingAtBlock};
|
||||||
Surface,
|
use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, primitives::CubePrimitive, RenderCtx};
|
||||||
DrawParameters,
|
|
||||||
BackfaceCullingMode,
|
|
||||||
Blend, Depth, DepthTest,
|
|
||||||
uniform,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
world::raycast::LookingAtBlock,
|
|
||||||
camera::Camera,
|
|
||||||
prefabs::ColoredShaderPrefab
|
|
||||||
};
|
|
||||||
use super::{
|
|
||||||
RenderTarget,
|
|
||||||
primitives::cube::CubePrimitive,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SMOL: f32 = 0.0025;
|
mod pipeline;
|
||||||
|
mod uniform;
|
||||||
|
|
||||||
pub fn render_selection_box(
|
use uniform::SelectionBoxUniform;
|
||||||
lookat: View<LookingAtBlock>,
|
|
||||||
camera: View<Camera>,
|
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
|
||||||
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
|
|
||||||
buffers: NonSendSync<UniqueView<CubePrimitive>>,
|
|
||||||
) {
|
|
||||||
let camera = camera.iter().next().unwrap();
|
|
||||||
let Some(lookat) = lookat.iter().next() else { return };
|
|
||||||
let Some(lookat) = lookat.0 else { return };
|
|
||||||
|
|
||||||
//Darken block
|
#[derive(Unique)]
|
||||||
target.0.draw(
|
pub struct SboxRenderState {
|
||||||
&buffers.0,
|
pipeline: wgpu::RenderPipeline,
|
||||||
&buffers.1,
|
uniform: SelectionBoxUniform,
|
||||||
&program.0,
|
}
|
||||||
&uniform! {
|
|
||||||
color: [0., 0., 0., 0.5_f32],
|
pub fn init_selection_box_render_state(storages: AllStoragesView) {
|
||||||
model: Mat4::from_scale_rotation_translation(
|
let uniform = storages.run(uniform::init_selection_box_uniform);
|
||||||
Vec3::splat(1. + SMOL * 2.),
|
let pipeline = storages.run_with_data(pipeline::init_selection_box_pipeline, &uniform);
|
||||||
Quat::default(),
|
storages.add_unique(SboxRenderState { pipeline, uniform });
|
||||||
lookat.block_position.as_vec3() - Vec3::splat(SMOL)
|
}
|
||||||
).to_cols_array_2d(),
|
|
||||||
perspective: camera.perspective_matrix.to_cols_array_2d(),
|
pub use uniform::update_selection_box_uniform
|
||||||
view: camera.view_matrix.to_cols_array_2d(),
|
as update_selection_box_render_state;
|
||||||
},
|
|
||||||
&DrawParameters {
|
pub fn draw_selection_box(
|
||||||
backface_culling: BackfaceCullingMode::CullClockwise,
|
ctx: &mut RenderCtx,
|
||||||
blend: Blend::alpha_blending(),
|
state: UniqueView<SboxRenderState>,
|
||||||
depth: Depth {
|
depth: UniqueView<DepthTexture>,
|
||||||
//this may be unreliable... unless scale is applied! hacky...
|
cube: UniqueView<CubePrimitive>,
|
||||||
test: DepthTest::IfLessOrEqual,
|
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||||
..Default::default()
|
lookat: View<LookingAtBlock>,
|
||||||
},
|
player: View<MainPlayer>,
|
||||||
..Default::default()
|
) {
|
||||||
}
|
let Some((LookingAtBlock(Some(_)), _)) = (&lookat, &player).iter().next() else {
|
||||||
).unwrap();
|
return
|
||||||
|
};
|
||||||
|
let mut rpass = ctx.encoder.begin_render_pass(&RenderPassDescriptor {
|
||||||
|
label: Some("rpass_selection_box"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: ctx.surface_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||||
|
view: &depth.depth_view,
|
||||||
|
depth_ops: Some(wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
}),
|
||||||
|
stencil_ops: None,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
rpass.set_pipeline(&state.pipeline);
|
||||||
|
rpass.set_bind_group(0, &camera_ubo.camera_bind_group, &[]);
|
||||||
|
rpass.set_bind_group(1, &state.uniform.bind_group, &[]);
|
||||||
|
rpass.set_index_buffer(cube.0.index.slice(..), wgpu::IndexFormat::Uint16);
|
||||||
|
rpass.set_vertex_buffer(0, cube.0.vertex.slice(..));
|
||||||
|
rpass.draw_indexed(0..cube.0.index_len, 0, 0..1);
|
||||||
}
|
}
|
||||||
|
|
70
kubi/src/rendering/selection_box/pipeline.rs
Normal file
70
kubi/src/rendering/selection_box/pipeline.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use shipyard::UniqueView;
|
||||||
|
use crate::rendering::{
|
||||||
|
camera_uniform::CameraUniformBuffer,
|
||||||
|
depth::DepthTexture,
|
||||||
|
primitives::PrimitiveVertex,
|
||||||
|
Renderer
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init_selection_box_pipeline(
|
||||||
|
uniform: &super::uniform::SelectionBoxUniform,
|
||||||
|
ren: UniqueView<Renderer>,
|
||||||
|
depth: UniqueView<DepthTexture>,
|
||||||
|
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||||
|
) -> wgpu::RenderPipeline {
|
||||||
|
log::info!("init_selection_box_pipeline");
|
||||||
|
|
||||||
|
let shader = ren.device().create_shader_module(
|
||||||
|
wgpu::include_wgsl!("../../../shaders/selection_box.wgsl")
|
||||||
|
);
|
||||||
|
|
||||||
|
let selection_box_pipeline_layout = ren.device().create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("selection_box_pipeline_layout"),
|
||||||
|
bind_group_layouts: &[
|
||||||
|
&camera_ubo.camera_bind_group_layout,
|
||||||
|
&uniform.bind_group_layout,
|
||||||
|
],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
ren.device().create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("selection_box_pipeline"),
|
||||||
|
layout: Some(&selection_box_pipeline_layout),
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: ren.surface_config().format,
|
||||||
|
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
|
||||||
|
write_mask: wgpu::ColorWrites::COLOR,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
buffers: &[
|
||||||
|
PrimitiveVertex::LAYOUT,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
unclipped_depth: false,
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
|
format: depth.depth_texture.format(),
|
||||||
|
depth_write_enabled: false,
|
||||||
|
depth_compare: wgpu::CompareFunction::LessEqual,
|
||||||
|
stencil: wgpu::StencilState::default(),
|
||||||
|
bias: wgpu::DepthBiasState::default(),
|
||||||
|
}),
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
})
|
||||||
|
}
|
83
kubi/src/rendering/selection_box/uniform.rs
Normal file
83
kubi/src/rendering/selection_box/uniform.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use glam::Vec3;
|
||||||
|
use shipyard::{IntoIter, UniqueView, View};
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use crate::{
|
||||||
|
player::MainPlayer,
|
||||||
|
rendering::Renderer,
|
||||||
|
world::raycast::LookingAtBlock,
|
||||||
|
};
|
||||||
|
use super::SboxRenderState;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct SelectionBoxUniformData {
|
||||||
|
pub position: [f32; 3],
|
||||||
|
pub _padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SelectionBoxUniform {
|
||||||
|
pub buffer: wgpu::Buffer,
|
||||||
|
pub bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
pub bind_group: wgpu::BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_selection_box_uniform(
|
||||||
|
renderer: UniqueView<Renderer>
|
||||||
|
) -> SelectionBoxUniform {
|
||||||
|
log::info!("init_selection_box_uniform");
|
||||||
|
|
||||||
|
let buffer = renderer.device().create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("selection_box_uniform"),
|
||||||
|
size: std::mem::size_of::<SelectionBoxUniformData>() as u64,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group_layout = renderer.device().create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("selection_box_bind_group_layout"),
|
||||||
|
entries: &[wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = renderer.device().create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("selection_box_bind_group"),
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
entries: &[wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
SelectionBoxUniform {
|
||||||
|
buffer,
|
||||||
|
bind_group_layout,
|
||||||
|
bind_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_selection_box_uniform(
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
state: UniqueView<SboxRenderState>,
|
||||||
|
lookat: View<LookingAtBlock>,
|
||||||
|
player: View<MainPlayer>,
|
||||||
|
) {
|
||||||
|
//TODO: only update if changed
|
||||||
|
if let Some((LookingAtBlock(Some(lookat)), _)) = (&lookat, &player).iter().next() {
|
||||||
|
renderer.queue().write_buffer(
|
||||||
|
&state.uniform.buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&[SelectionBoxUniformData {
|
||||||
|
position: (lookat.position.floor() + Vec3::splat(0.5)).to_array(),
|
||||||
|
_padding: 0,
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
48
kubi/src/rendering/smoverlay.rs
Normal file
48
kubi/src/rendering/smoverlay.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use shipyard::{AllStoragesView, Unique, UniqueView};
|
||||||
|
use super::{primitives::FstriPrimitive, RenderCtx, Renderer};
|
||||||
|
|
||||||
|
mod uniform;
|
||||||
|
mod pipeline;
|
||||||
|
use uniform::SmUniform;
|
||||||
|
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct SmOverlayRenderState {
|
||||||
|
pub uniform: SmUniform,
|
||||||
|
pub pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_smoverlay_render_state(storages: AllStoragesView) {
|
||||||
|
let uniform = storages.run(uniform::init_sm_uniform);
|
||||||
|
let pipeline = storages.run_with_data(pipeline::init_smoverlay_pipeline, &uniform);
|
||||||
|
storages.add_unique(SmOverlayRenderState { uniform, pipeline });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use uniform::update_sm_uniform as update_smoverlay_render_state;
|
||||||
|
|
||||||
|
pub fn render_submerged_view(
|
||||||
|
ctx: &mut RenderCtx,
|
||||||
|
state: UniqueView<SmOverlayRenderState>,
|
||||||
|
buf: UniqueView<FstriPrimitive>,
|
||||||
|
) {
|
||||||
|
if state.uniform.stored_data.is_none() { return }
|
||||||
|
|
||||||
|
let mut rpass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("smoverlay_render_pass"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: ctx.surface_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
rpass.set_pipeline(&state.pipeline);
|
||||||
|
rpass.set_bind_group(0, &state.uniform.bind_group, &[]);
|
||||||
|
rpass.set_vertex_buffer(0, buf.0.slice(..));
|
||||||
|
rpass.draw(0..3, 0..1);
|
||||||
|
}
|
57
kubi/src/rendering/smoverlay/pipeline.rs
Normal file
57
kubi/src/rendering/smoverlay/pipeline.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use shipyard::UniqueView;
|
||||||
|
use crate::rendering::{primitives::PrimitiveVertex2, Renderer};
|
||||||
|
use super::uniform::SmUniform;
|
||||||
|
|
||||||
|
pub fn init_smoverlay_pipeline(
|
||||||
|
uniform: &SmUniform,
|
||||||
|
renderer: UniqueView<Renderer>
|
||||||
|
) -> wgpu::RenderPipeline {
|
||||||
|
let module = renderer.device().create_shader_module(
|
||||||
|
wgpu::include_wgsl!("../../../shaders/c2d.wgsl")
|
||||||
|
);
|
||||||
|
|
||||||
|
let rp_layout = renderer.device().create_pipeline_layout(
|
||||||
|
&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("smoverlay_pipeline_layout"),
|
||||||
|
bind_group_layouts: &[
|
||||||
|
&uniform.bind_group_layout,
|
||||||
|
],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
renderer.device().create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("smoverlay_pipeline"),
|
||||||
|
layout: Some(&rp_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &module,
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[
|
||||||
|
PrimitiveVertex2::LAYOUT,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &module,
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: renderer.surface_config().format,
|
||||||
|
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
conservative: false,
|
||||||
|
unclipped_depth: false,
|
||||||
|
},
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
})
|
||||||
|
}
|
94
kubi/src/rendering/smoverlay/uniform.rs
Normal file
94
kubi/src/rendering/smoverlay/uniform.rs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use kubi_shared::transform::Transform;
|
||||||
|
use shipyard::{IntoIter, UniqueView, UniqueViewMut, View};
|
||||||
|
use crate::{player::MainPlayer, rendering::Renderer, world::ChunkStorage};
|
||||||
|
use super::SmOverlayRenderState;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct SmUniformData {
|
||||||
|
pub color: [f32; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SmUniform {
|
||||||
|
pub stored_data: Option<SmUniformData>,
|
||||||
|
pub buffer: wgpu::Buffer,
|
||||||
|
pub bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
pub bind_group: wgpu::BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_sm_uniform(
|
||||||
|
renderer: UniqueView<Renderer>
|
||||||
|
) -> SmUniform {
|
||||||
|
log::info!("init_sm_uniform");
|
||||||
|
|
||||||
|
let buffer = renderer.device().create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("smoverlay_uniform"),
|
||||||
|
size: std::mem::size_of::<SmUniformData>() as u64,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group_layout = renderer.device().create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("smoverlay_bind_group_layout"),
|
||||||
|
entries: &[wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = renderer.device().create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("smoverlay_bind_group"),
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
entries: &[wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
SmUniform {
|
||||||
|
stored_data: None,
|
||||||
|
buffer,
|
||||||
|
bind_group_layout,
|
||||||
|
bind_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_sm_uniform(
|
||||||
|
mut state: UniqueViewMut<SmOverlayRenderState>,
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
plr: View<MainPlayer>,
|
||||||
|
trans: View<Transform>,
|
||||||
|
world: UniqueView<ChunkStorage>,
|
||||||
|
) {
|
||||||
|
state.uniform.stored_data = None;
|
||||||
|
|
||||||
|
let (_, plr_trans) = (&plr, &trans).iter().next().expect("Main player MIA");
|
||||||
|
let plr_pos = plr_trans.0.to_scale_rotation_translation().2;
|
||||||
|
let block_at_pos = world.get_block(plr_pos.floor().as_ivec3());
|
||||||
|
let Some(block_at_pos) = block_at_pos else { return };
|
||||||
|
let Some(color) = block_at_pos.descriptor().submerge else { return };
|
||||||
|
|
||||||
|
let new_data = SmUniformData {
|
||||||
|
color: color.to_array()
|
||||||
|
};
|
||||||
|
|
||||||
|
if state.uniform.stored_data == Some(new_data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.uniform.stored_data = Some(new_data);
|
||||||
|
|
||||||
|
log::debug!("update_sm_uniform: {:?}", new_data);
|
||||||
|
|
||||||
|
renderer.queue().write_buffer(
|
||||||
|
&state.uniform.buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&[new_data]),
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,39 +0,0 @@
|
||||||
use glium::{uniform, Blend, DrawParameters, Surface};
|
|
||||||
use kubi_shared::transform::Transform;
|
|
||||||
use shipyard::{IntoIter, NonSendSync, UniqueView, UniqueViewMut, View};
|
|
||||||
use crate::{
|
|
||||||
player::MainPlayer,
|
|
||||||
prefabs::Colored2ShaderPrefab,
|
|
||||||
rendering::primitives::stri::STriPrimitive,
|
|
||||||
world::ChunkStorage,
|
|
||||||
};
|
|
||||||
use super::RenderTarget;
|
|
||||||
|
|
||||||
pub fn render_submerged_view(
|
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
|
||||||
primitive: NonSendSync<UniqueView<STriPrimitive>>,
|
|
||||||
program: NonSendSync<UniqueView<Colored2ShaderPrefab>>,
|
|
||||||
plr: View<MainPlayer>,
|
|
||||||
trans: View<Transform>,
|
|
||||||
world: UniqueView<ChunkStorage>,
|
|
||||||
) {
|
|
||||||
let (_, plr_trans) = (&plr, &trans).iter().next().expect("Main player MIA");
|
|
||||||
let plr_pos = plr_trans.0.to_scale_rotation_translation().2;
|
|
||||||
let block_at_pos = world.get_block(plr_pos.floor().as_ivec3());
|
|
||||||
let Some(block_at_pos) = block_at_pos else { return };
|
|
||||||
let Some(color) = block_at_pos.descriptor().submerge else { return };
|
|
||||||
|
|
||||||
let draw_parameters = DrawParameters {
|
|
||||||
blend: Blend::alpha_blending(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
target.0.draw(
|
|
||||||
&primitive.0,
|
|
||||||
&primitive.1,
|
|
||||||
&program.0,
|
|
||||||
&uniform! {
|
|
||||||
color: color.to_array(),
|
|
||||||
},
|
|
||||||
&draw_parameters,
|
|
||||||
).unwrap();
|
|
||||||
}
|
|
|
@ -1,248 +1,161 @@
|
||||||
use glam::{ivec3, IVec3, Mat4, Quat, Vec3};
|
use glam::Vec3;
|
||||||
use shipyard::{track, AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||||
use glium::{
|
use kubi_shared::chunk::CHUNK_SIZE;
|
||||||
draw_parameters::{
|
|
||||||
BackfaceCullingMode, Depth, DepthTest, PolygonMode
|
|
||||||
}, implement_vertex, uniform, uniforms::{
|
|
||||||
MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction
|
|
||||||
}, Blend, DrawParameters, Smooth, Surface
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
player::MainPlayer,
|
prefabs::GpuPrefabs,
|
||||||
transform::Transform,
|
world::{ChunkMeshStorage, ChunkStorage},
|
||||||
prefabs::{
|
|
||||||
ChunkShaderPrefab,
|
|
||||||
BlockTexturesPrefab,
|
|
||||||
ColoredShaderPrefab,
|
|
||||||
},
|
|
||||||
world::{
|
|
||||||
ChunkStorage,
|
|
||||||
ChunkMeshStorage,
|
|
||||||
chunk::CHUNK_SIZE,
|
|
||||||
}, settings::GameSettings,
|
|
||||||
};
|
};
|
||||||
use super::{RenderTarget, primitives::cube::CubePrimitive};
|
use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, RenderCtx, Renderer};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
mod pipeline;
|
||||||
#[repr(C)]
|
mod vertex;
|
||||||
pub struct ChunkVertex {
|
pub use vertex::ChunkVertex;
|
||||||
pub position: [f32; 3],
|
|
||||||
pub normal: [f32; 3],
|
|
||||||
pub uv: [f32; 2],
|
|
||||||
pub tex_index: u8,
|
|
||||||
}
|
|
||||||
implement_vertex!(ChunkVertex, position, normal, uv, tex_index);
|
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
pub struct TransChunkQueue(pub Vec<IVec3>);
|
pub struct WorldRenderState {
|
||||||
|
pub pipeline: wgpu::RenderPipeline,
|
||||||
pub fn init_trans_chunk_queue(storages: AllStoragesView) {
|
pub pipeline_trans: wgpu::RenderPipeline,
|
||||||
storages.add_unique(TransChunkQueue(Vec::with_capacity(512)));
|
pub trans_bundle: Option<wgpu::RenderBundle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_params(settings: &GameSettings) -> DrawParameters {
|
pub fn init_world_render_state(storages: AllStoragesView) {
|
||||||
DrawParameters {
|
let (pipeline, pipeline_trans) = storages.run(pipeline::init_world_pipeline);
|
||||||
depth: Depth {
|
storages.add_unique(WorldRenderState {
|
||||||
test: DepthTest::IfLess,
|
pipeline, pipeline_trans,
|
||||||
write: true,
|
trans_bundle: None,
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
multisampling: settings.msaa.is_some(),
|
|
||||||
polygon_mode: PolygonMode::Fill, //Change to Line for wireframe
|
|
||||||
backface_culling: BackfaceCullingMode::CullClockwise,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn texture_sampler<'a, T>(texture: &'a T, settings: &GameSettings) -> Sampler<'a, T> {
|
|
||||||
Sampler(texture, SamplerBehavior {
|
|
||||||
minify_filter: MinifySamplerFilter::LinearMipmapLinear,
|
|
||||||
magnify_filter: MagnifySamplerFilter::Nearest,
|
|
||||||
max_anisotropy: settings.max_anisotropy.unwrap_or_default(),
|
|
||||||
wrap_function: (SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp),
|
|
||||||
depth_texture_comparison: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_world(
|
pub fn draw_world(
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
ctx: &mut RenderCtx,
|
||||||
|
mut state: UniqueViewMut<WorldRenderState>,
|
||||||
|
renderer: UniqueView<Renderer>,
|
||||||
|
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||||
|
depth: UniqueView<DepthTexture>,
|
||||||
|
textures: UniqueView<GpuPrefabs>,
|
||||||
|
camera: View<Camera>,
|
||||||
chunks: UniqueView<ChunkStorage>,
|
chunks: UniqueView<ChunkStorage>,
|
||||||
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
|
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
|
||||||
program: NonSendSync<UniqueView<ChunkShaderPrefab>>,
|
//settings: UniqueView<GameSettings>,
|
||||||
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
|
|
||||||
transform: View<Transform>,
|
|
||||||
camera: View<Camera>,
|
|
||||||
settings: UniqueView<GameSettings>,
|
|
||||||
mut trans_queue: UniqueViewMut<TransChunkQueue>,
|
|
||||||
) {
|
) {
|
||||||
// let (camera, transform) = (&camera, &transform).iter().next().expect("No cameras in the scene");
|
|
||||||
// let camera_position = transform.0.to_scale_rotation_translation().2;
|
|
||||||
|
|
||||||
let camera = camera.iter().next().expect("No cameras in the scene");
|
let camera = camera.iter().next().expect("No cameras in the scene");
|
||||||
let view = camera.view_matrix.to_cols_array_2d();
|
|
||||||
let perspective = camera.perspective_matrix.to_cols_array_2d();
|
|
||||||
|
|
||||||
let draw_parameters = draw_params(&settings);
|
let mut render_pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
let texture_sampler = texture_sampler(&texture.0, &settings);
|
label: Some("rpass_draw_world"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: ctx.surface_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||||
|
view: &depth.depth_view,
|
||||||
|
depth_ops: Some(wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(1.0),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
}),
|
||||||
|
stencil_ops: None,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
render_pass.set_pipeline(&state.pipeline);
|
||||||
|
render_pass.set_bind_group(0, &textures.block_diffuse_bind_group, &[]);
|
||||||
|
render_pass.set_bind_group(1, &camera_ubo.camera_bind_group, &[]);
|
||||||
|
|
||||||
|
let mut trans_bundle_used = false;
|
||||||
|
let mut trans_bundle = renderer.device().create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
|
||||||
|
label: Some("trans_bundle_encoder"),
|
||||||
|
color_formats: &[Some(renderer.surface_config().format)],
|
||||||
|
depth_stencil: Some(wgpu::RenderBundleDepthStencil {
|
||||||
|
format: depth.depth_texture.format(),
|
||||||
|
depth_read_only: true,
|
||||||
|
stencil_read_only: true,
|
||||||
|
}),
|
||||||
|
sample_count: 1,
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
trans_bundle.set_pipeline(&state.pipeline_trans);
|
||||||
|
trans_bundle.set_bind_group(0, &textures.block_diffuse_bind_group, &[]);
|
||||||
|
trans_bundle.set_bind_group(1, &camera_ubo.camera_bind_group, &[]);
|
||||||
|
|
||||||
for (&position, chunk) in &chunks.chunks {
|
for (&position, chunk) in &chunks.chunks {
|
||||||
if let Some(key) = chunk.mesh_index {
|
if let Some(key) = chunk.mesh_index {
|
||||||
let mesh = meshes.get(key).expect("Mesh index pointing to nothing");
|
let mesh = meshes.get(key).expect("Mesh index pointing to nothing");
|
||||||
let world_position = position.as_vec3() * CHUNK_SIZE as f32;
|
let world_position = position.as_vec3() * CHUNK_SIZE as f32;
|
||||||
|
|
||||||
//Skip mesh if its empty
|
//Skip if mesh is empty
|
||||||
if mesh.index_buffer.len() == 0 && mesh.trans_index_buffer.len() == 0 {
|
if mesh.main.index.size() == 0 && mesh.trans.index.size() == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
//Frustum culling
|
//Frustum culling
|
||||||
{
|
|
||||||
let minp = world_position;
|
let minp = world_position;
|
||||||
let maxp = world_position + Vec3::splat(CHUNK_SIZE as f32);
|
let maxp = world_position + Vec3::splat(CHUNK_SIZE as f32);
|
||||||
if !camera.frustum.is_box_visible(minp, maxp) {
|
if !camera.frustum.is_box_visible(minp, maxp) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//Draw chunk mesh
|
//Draw chunk mesh
|
||||||
if mesh.index_buffer.len() > 0 {
|
if mesh.main.index_len > 0 {
|
||||||
target.0.draw(
|
render_pass.set_index_buffer(mesh.main.index.slice(..), wgpu::IndexFormat::Uint32);
|
||||||
&mesh.vertex_buffer,
|
render_pass.set_vertex_buffer(0, mesh.main.vertex.slice(..));
|
||||||
&mesh.index_buffer,
|
render_pass.draw_indexed(0..mesh.main.index_len, 0, 0..1);
|
||||||
&program.0,
|
|
||||||
&uniform! {
|
|
||||||
position_offset: world_position.to_array(),
|
|
||||||
view: view,
|
|
||||||
perspective: perspective,
|
|
||||||
tex: texture_sampler,
|
|
||||||
discard_alpha: true,
|
|
||||||
},
|
|
||||||
&draw_parameters
|
|
||||||
).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if mesh.trans_index_buffer.len() > 0 {
|
//Draw transparent chunk mesh
|
||||||
trans_queue.0.push(position);
|
if mesh.trans.index_len > 0 {
|
||||||
|
trans_bundle_used = true;
|
||||||
|
trans_bundle.set_index_buffer(mesh.trans.index.slice(..), wgpu::IndexFormat::Uint32);
|
||||||
|
trans_bundle.set_vertex_buffer(0, mesh.trans.vertex.slice(..));
|
||||||
|
trans_bundle.draw_indexed(0..mesh.trans.index_len, 0, 0..1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const HALF_CHUNK_SIZE: IVec3 = IVec3::splat((CHUNK_SIZE >> 1) as i32);
|
drop(render_pass);
|
||||||
// trans_queue.0.sort_by_cached_key(|&pos| -(
|
|
||||||
// (pos + HALF_CHUNK_SIZE).distance_squared(camera_position.as_ivec3())
|
if trans_bundle_used {
|
||||||
// ));
|
let bundle = trans_bundle.finish(&wgpu::RenderBundleDescriptor {
|
||||||
|
label: Some("trans_bundle"),
|
||||||
|
});
|
||||||
|
state.trans_bundle = Some(bundle);
|
||||||
|
} else {
|
||||||
|
state.trans_bundle = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_world_trans(
|
pub fn rpass_submit_trans_bundle(
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
ctx: &mut RenderCtx,
|
||||||
chunks: UniqueView<ChunkStorage>,
|
state: UniqueView<WorldRenderState>,
|
||||||
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
|
depth: UniqueView<DepthTexture>,
|
||||||
program: NonSendSync<UniqueView<ChunkShaderPrefab>>,
|
|
||||||
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
|
|
||||||
camera: View<Camera>,
|
|
||||||
settings: UniqueView<GameSettings>,
|
|
||||||
mut trans_queue: UniqueViewMut<TransChunkQueue>,
|
|
||||||
) {
|
) {
|
||||||
let camera = camera.iter().next().expect("No cameras in the scene");
|
let Some(bundle) = state.trans_bundle.as_ref() else {
|
||||||
let view = camera.view_matrix.to_cols_array_2d();
|
|
||||||
let perspective = camera.perspective_matrix.to_cols_array_2d();
|
|
||||||
|
|
||||||
let mut draw_parameters = draw_params(&settings);
|
|
||||||
draw_parameters.blend = Blend::alpha_blending();
|
|
||||||
draw_parameters.backface_culling = BackfaceCullingMode::CullingDisabled;
|
|
||||||
draw_parameters.smooth = Some(Smooth::Fastest);
|
|
||||||
|
|
||||||
let texture_sampler = texture_sampler(&texture.0, &settings);
|
|
||||||
|
|
||||||
for position in trans_queue.0.drain(..).rev() {
|
|
||||||
let world_position = position.as_vec3() * CHUNK_SIZE as f32;
|
|
||||||
let mesh_idx = chunks.chunks[&position].mesh_index.expect("No mesh index");
|
|
||||||
let mesh = meshes.get(mesh_idx).expect("Mesh index pointing to nothing");
|
|
||||||
target.0.draw(
|
|
||||||
&mesh.trans_vertex_buffer,
|
|
||||||
&mesh.trans_index_buffer,
|
|
||||||
&program.0,
|
|
||||||
&uniform! {
|
|
||||||
position_offset: world_position.to_array(),
|
|
||||||
view: view,
|
|
||||||
perspective: perspective,
|
|
||||||
tex: texture_sampler,
|
|
||||||
discard_alpha: false,
|
|
||||||
},
|
|
||||||
&draw_parameters
|
|
||||||
).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_current_chunk_border(
|
|
||||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
|
||||||
player: View<MainPlayer>,
|
|
||||||
transforms: View<Transform, track::All>,
|
|
||||||
buffers: NonSendSync<UniqueView<CubePrimitive>>,
|
|
||||||
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
|
|
||||||
camera: View<Camera>,
|
|
||||||
settings: UniqueView<GameSettings>,
|
|
||||||
) {
|
|
||||||
if cfg!(target_os = "android") {
|
|
||||||
return
|
return
|
||||||
}
|
};
|
||||||
if !settings.debug_draw_current_chunk_border {
|
let mut rpass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
return
|
label: Some("rpass_submit_trans_bundle"),
|
||||||
}
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
let camera = camera.iter().next().expect("No cameras in the scene");
|
view: ctx.surface_view,
|
||||||
let view = camera.view_matrix.to_cols_array_2d();
|
resolve_target: None,
|
||||||
let perspective = camera.perspective_matrix.to_cols_array_2d();
|
ops: wgpu::Operations {
|
||||||
let (_, &player_transform) = (&player, &transforms).iter().next().expect("No player");
|
load: wgpu::LoadOp::Load,
|
||||||
let (_, _, player_position) = player_transform.0.to_scale_rotation_translation();
|
store: wgpu::StoreOp::Store,
|
||||||
let player_in_chunk = ivec3(
|
|
||||||
(player_position.x as i32).div_euclid(CHUNK_SIZE as i32),
|
|
||||||
(player_position.y as i32).div_euclid(CHUNK_SIZE as i32),
|
|
||||||
(player_position.z as i32).div_euclid(CHUNK_SIZE as i32),
|
|
||||||
);
|
|
||||||
let world_position = player_in_chunk.as_vec3() * CHUNK_SIZE as f32;
|
|
||||||
target.0.draw(
|
|
||||||
&buffers.0,
|
|
||||||
&buffers.1,
|
|
||||||
&program.0,
|
|
||||||
&uniform! {
|
|
||||||
model: Mat4::from_scale_rotation_translation(
|
|
||||||
Vec3::splat(CHUNK_SIZE as f32),
|
|
||||||
Quat::default(),
|
|
||||||
world_position
|
|
||||||
).to_cols_array_2d(),
|
|
||||||
color: [0.25f32; 4],
|
|
||||||
view: view,
|
|
||||||
perspective: perspective,
|
|
||||||
},
|
},
|
||||||
&DrawParameters {
|
})],
|
||||||
depth: Depth {
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||||
test: DepthTest::IfLess,
|
view: &depth.depth_view,
|
||||||
|
depth_ops: Some(wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
}),
|
||||||
|
stencil_ops: None,
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
});
|
||||||
blend: Blend::alpha_blending(),
|
rpass.execute_bundles(Some(bundle));
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
).unwrap();
|
|
||||||
target.0.draw(
|
|
||||||
&buffers.0,
|
|
||||||
&buffers.1,
|
|
||||||
&program.0,
|
|
||||||
&uniform! {
|
|
||||||
model: Mat4::from_scale_rotation_translation(
|
|
||||||
Vec3::splat(CHUNK_SIZE as f32),
|
|
||||||
Quat::default(),
|
|
||||||
world_position
|
|
||||||
).to_cols_array_2d(),
|
|
||||||
color: [0.0f32; 4],
|
|
||||||
view: view,
|
|
||||||
perspective: perspective,
|
|
||||||
},
|
|
||||||
&DrawParameters {
|
|
||||||
polygon_mode: PolygonMode::Point,
|
|
||||||
line_width: Some(2.),
|
|
||||||
point_size: Some(5.),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
117
kubi/src/rendering/world/pipeline.rs
Normal file
117
kubi/src/rendering/world/pipeline.rs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
use shipyard::UniqueView;
|
||||||
|
use crate::{
|
||||||
|
prefabs::GpuPrefabs,
|
||||||
|
rendering::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, world::ChunkVertex, Renderer}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn init_world_pipeline(
|
||||||
|
ren: UniqueView<Renderer>,
|
||||||
|
depth: UniqueView<DepthTexture>,
|
||||||
|
textures: UniqueView<GpuPrefabs>,
|
||||||
|
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||||
|
) -> (wgpu::RenderPipeline, wgpu::RenderPipeline) {
|
||||||
|
log::info!("init_world_pipeline: creating shader module");
|
||||||
|
|
||||||
|
let shader = ren.device().create_shader_module(
|
||||||
|
wgpu::include_wgsl!("../../../shaders/world.wgsl")
|
||||||
|
);
|
||||||
|
|
||||||
|
log::info!("init_world_pipeline: creating pipeline layout");
|
||||||
|
|
||||||
|
let world_pipeline_layout = ren.device().create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("world_pipeline_layout"),
|
||||||
|
bind_group_layouts: &[
|
||||||
|
&textures.block_diffuse_bind_group_layout,
|
||||||
|
&camera_ubo.camera_bind_group_layout,
|
||||||
|
],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
log::info!("init_world_pipeline: create main pipeline");
|
||||||
|
|
||||||
|
let pipeline_main = ren.device().create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("world_pipeline"),
|
||||||
|
layout: Some(&world_pipeline_layout),
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: ren.surface_config().format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::COLOR,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
buffers: &[
|
||||||
|
ChunkVertex::LAYOUT,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
unclipped_depth: false,
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
|
format: depth.depth_texture.format(),
|
||||||
|
depth_write_enabled: true,
|
||||||
|
depth_compare: wgpu::CompareFunction::Less,
|
||||||
|
stencil: wgpu::StencilState::default(),
|
||||||
|
bias: wgpu::DepthBiasState::default(),
|
||||||
|
}),
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
log::info!("init_world_pipeline: create trans pipeline");
|
||||||
|
|
||||||
|
let pipeline_trans = ren.device().create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("world_pipeline_trans"),
|
||||||
|
layout: Some(&world_pipeline_layout),
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "fs_main_trans",
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: ren.surface_config().format,
|
||||||
|
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
|
||||||
|
write_mask: wgpu::ColorWrites::COLOR,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
buffers: &[
|
||||||
|
ChunkVertex::LAYOUT,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
cull_mode: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
unclipped_depth: false,
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
|
format: depth.depth_texture.format(),
|
||||||
|
depth_write_enabled: false,
|
||||||
|
depth_compare: wgpu::CompareFunction::Less,
|
||||||
|
stencil: wgpu::StencilState::default(),
|
||||||
|
bias: wgpu::DepthBiasState::default(),
|
||||||
|
}),
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
(pipeline_main, pipeline_trans)
|
||||||
|
}
|
23
kubi/src/rendering/world/vertex.rs
Normal file
23
kubi/src/rendering/world/vertex.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct ChunkVertex {
|
||||||
|
pub position: [f32; 3],
|
||||||
|
pub normal: [f32; 3],
|
||||||
|
pub uv: [f32; 2],
|
||||||
|
pub tex_index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChunkVertex {
|
||||||
|
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
||||||
|
array_stride: std::mem::size_of::<ChunkVertex>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &wgpu::vertex_attr_array![
|
||||||
|
0 => Float32x3,
|
||||||
|
1 => Float32x3,
|
||||||
|
2 => Float32x2,
|
||||||
|
3 => Uint32,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
|
@ -11,10 +11,10 @@ pub struct FullscreenSettings {
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
pub struct GameSettings {
|
pub struct GameSettings {
|
||||||
pub vsync: bool,
|
// pub vsync: bool,
|
||||||
pub fullscreen: Option<FullscreenSettings>,
|
pub fullscreen: Option<FullscreenSettings>,
|
||||||
pub msaa: Option<u8>,
|
// pub msaa: Option<u8>,
|
||||||
pub max_anisotropy: Option<u16>,
|
// pub max_anisotropy: Option<u16>,
|
||||||
/// there's a 1 chunk border of loaded but invisible around this
|
/// there's a 1 chunk border of loaded but invisible around this
|
||||||
pub render_distance: u8,
|
pub render_distance: u8,
|
||||||
pub mouse_sensitivity: f32,
|
pub mouse_sensitivity: f32,
|
||||||
|
@ -24,10 +24,10 @@ pub struct GameSettings {
|
||||||
impl Default for GameSettings {
|
impl Default for GameSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
vsync: false,
|
// vsync: false,
|
||||||
fullscreen: None,
|
fullscreen: None,
|
||||||
msaa: Some(4),
|
// msaa: Some(4),
|
||||||
max_anisotropy: Some(16),
|
// max_anisotropy: Some(16),
|
||||||
render_distance: match true {
|
render_distance: match true {
|
||||||
cfg!(debug_assertions) => 5,
|
cfg!(debug_assertions) => 5,
|
||||||
cfg!(target_os = "android") => 6,
|
cfg!(target_os = "android") => 6,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use hui::{color, element::{container::Container, text::Text, UiElementExt}, layout::Alignment, size};
|
use hui::{color, element::{container::Container, text::Text, UiElementExt}, layout::Alignment, size};
|
||||||
use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
|
use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
|
||||||
use crate::{chat::{ChatHistory, ChatMessage}, hui_integration::UiState, rendering::WindowSize};
|
use crate::{chat::{ChatHistory, ChatMessage}, hui_integration::UiState, rendering::Renderer};
|
||||||
|
|
||||||
pub fn render_chat(
|
pub fn render_chat(
|
||||||
mut hui: NonSendSync<UniqueViewMut<UiState>>,
|
mut hui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
size: UniqueView<WindowSize>,
|
ren: UniqueView<Renderer>,
|
||||||
chat: UniqueView<ChatHistory>,
|
chat: UniqueView<ChatHistory>,
|
||||||
) {
|
) {
|
||||||
let messages = chat.get_messages();
|
let messages = chat.get_messages();
|
||||||
|
@ -39,5 +39,5 @@ pub fn render_chat(
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.add_root(&mut hui.hui, size.0.as_vec2());
|
.add_root(&mut hui.hui, ren.size_vec2());
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
hui_integration::UiState,
|
hui_integration::UiState,
|
||||||
loading_screen::loading_screen_base,
|
loading_screen::loading_screen_base,
|
||||||
networking::{ConnectionRejectionReason, ServerAddress},
|
networking::{ConnectionRejectionReason, ServerAddress},
|
||||||
rendering::WindowSize,
|
rendering::Renderer,
|
||||||
state::{GameState, NextState}
|
state::{GameState, NextState}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ fn render_connecting_ui(
|
||||||
rejection: Option<UniqueView<ConnectionRejectionReason>>,
|
rejection: Option<UniqueView<ConnectionRejectionReason>>,
|
||||||
join_state: UniqueView<ClientJoinState>,
|
join_state: UniqueView<ClientJoinState>,
|
||||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
size: UniqueView<WindowSize>,
|
ren: UniqueView<Renderer>,
|
||||||
) {
|
) {
|
||||||
let text = match (rejection, *join_state) {
|
let text = match (rejection, *join_state) {
|
||||||
(Some(err), _) => {
|
(Some(err), _) => {
|
||||||
|
@ -32,7 +32,7 @@ fn render_connecting_ui(
|
||||||
Text::new(text)
|
Text::new(text)
|
||||||
.with_text_size(16)
|
.with_text_size(16)
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
}).add_root(&mut ui.hui, size.0.as_vec2())
|
}).add_root(&mut ui.hui, ren.size_vec2())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch_to_loading_if_connected(
|
fn switch_to_loading_if_connected(
|
||||||
|
|
|
@ -6,7 +6,7 @@ use hui::{
|
||||||
size
|
size
|
||||||
};
|
};
|
||||||
use shipyard::{AllStoragesViewMut, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
use shipyard::{AllStoragesViewMut, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||||
use crate::{hui_integration::UiState, player::MainPlayer, rendering::WindowSize, settings::GameSettings, world::raycast::LookingAtBlock};
|
use crate::{hui_integration::UiState, player::MainPlayer, rendering::Renderer, settings::GameSettings, world::raycast::LookingAtBlock};
|
||||||
|
|
||||||
const CROSSHAIR_SIZE: usize = 9;
|
const CROSSHAIR_SIZE: usize = 9;
|
||||||
const CROSSHAIR: &[u8] = &[
|
const CROSSHAIR: &[u8] = &[
|
||||||
|
@ -45,7 +45,7 @@ pub fn init_crosshair_image(storages: AllStoragesViewMut) {
|
||||||
pub fn draw_crosshair(
|
pub fn draw_crosshair(
|
||||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
crosshair: UniqueView<CrosshairImage>,
|
crosshair: UniqueView<CrosshairImage>,
|
||||||
size: UniqueView<WindowSize>,
|
ren: UniqueView<Renderer>,
|
||||||
player: View<MainPlayer>,
|
player: View<MainPlayer>,
|
||||||
raycast: View<LookingAtBlock>,
|
raycast: View<LookingAtBlock>,
|
||||||
settings: UniqueView<GameSettings>,
|
settings: UniqueView<GameSettings>,
|
||||||
|
@ -57,6 +57,9 @@ pub fn draw_crosshair(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let size = ren.size_uvec2();
|
||||||
|
let size_rounded = uvec2(size.x & !1, size.y & !1).as_vec2();
|
||||||
|
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(100%))
|
.with_size(size!(100%))
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
|
@ -66,5 +69,5 @@ pub fn draw_crosshair(
|
||||||
.with_size(size!((CROSSHAIR_SIZE * 2)))
|
.with_size(size!((CROSSHAIR_SIZE * 2)))
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
})
|
})
|
||||||
.add_root(&mut ui.hui, uvec2(size.0.x & !1, size.0.y & !1).as_vec2());
|
.add_root(&mut ui.hui, size_rounded);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use hui::{
|
||||||
UiElementExt,
|
UiElementExt,
|
||||||
},
|
},
|
||||||
layout::{Alignment, Direction},
|
layout::{Alignment, Direction},
|
||||||
frame_rect, size,
|
rect_frame, size,
|
||||||
};
|
};
|
||||||
use shipyard::{UniqueView, UniqueViewMut, Workload, NonSendSync, IntoWorkload};
|
use shipyard::{UniqueView, UniqueViewMut, Workload, NonSendSync, IntoWorkload};
|
||||||
use winit::keyboard::KeyCode;
|
use winit::keyboard::KeyCode;
|
||||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
hui_integration::UiState,
|
hui_integration::UiState,
|
||||||
input::RawKbmInputState,
|
input::RawKbmInputState,
|
||||||
networking::ServerAddress,
|
networking::ServerAddress,
|
||||||
rendering::WindowSize,
|
rendering::Renderer,
|
||||||
state::{GameState, NextState},
|
state::{GameState, NextState},
|
||||||
world::ChunkStorage,
|
world::ChunkStorage,
|
||||||
};
|
};
|
||||||
|
@ -28,7 +28,7 @@ pub fn loading_screen_base(bg_alpha: f32, xui: impl FnOnce(&mut ElementList)) ->
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(400, auto))
|
.with_size(size!(400, auto))
|
||||||
.with_background(frame_rect! {
|
.with_background(rect_frame! {
|
||||||
color: (0.2, 0.2, 0.2),
|
color: (0.2, 0.2, 0.2),
|
||||||
corner_radius: 8.
|
corner_radius: 8.
|
||||||
})
|
})
|
||||||
|
@ -43,7 +43,7 @@ fn render_loading_ui(
|
||||||
addr: Option<UniqueView<ServerAddress>>,
|
addr: Option<UniqueView<ServerAddress>>,
|
||||||
world: UniqueView<ChunkStorage>,
|
world: UniqueView<ChunkStorage>,
|
||||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
size: UniqueView<WindowSize>
|
ren: UniqueView<Renderer>,
|
||||||
) {
|
) {
|
||||||
let loaded = world.chunks.iter().fold(0, |acc, (&_, chunk)| {
|
let loaded = world.chunks.iter().fold(0, |acc, (&_, chunk)| {
|
||||||
acc + chunk.desired_state.matches_current(chunk.current_state) as usize
|
acc + chunk.desired_state.matches_current(chunk.current_state) as usize
|
||||||
|
@ -63,11 +63,11 @@ fn render_loading_ui(
|
||||||
ProgressBar::default()
|
ProgressBar::default()
|
||||||
.with_value(value)
|
.with_value(value)
|
||||||
.with_size(size!(100%, 15))
|
.with_size(size!(100%, 15))
|
||||||
.with_background(frame_rect! {
|
.with_background(rect_frame! {
|
||||||
color: (0.1, 0.1, 0.1),
|
color: (0.1, 0.1, 0.1),
|
||||||
corner_radius: 2.
|
corner_radius: 2.
|
||||||
})
|
})
|
||||||
.with_foreground(frame_rect! {
|
.with_foreground(rect_frame! {
|
||||||
color: (0.4, 0.4, 1.0),
|
color: (0.4, 0.4, 1.0),
|
||||||
corner_radius: 2.
|
corner_radius: 2.
|
||||||
})
|
})
|
||||||
|
@ -83,7 +83,7 @@ fn render_loading_ui(
|
||||||
.add_child(ui)
|
.add_child(ui)
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
}).add_root(&mut ui.hui, size.0.as_vec2());
|
}).add_root(&mut ui.hui, ren.size_vec2());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch_to_ingame_if_loaded(
|
fn switch_to_ingame_if_loaded(
|
||||||
|
|
|
@ -2,11 +2,11 @@ use hui::{
|
||||||
element::{br::Break, container::Container, slider::Slider, text::Text, UiElementExt},
|
element::{br::Break, container::Container, slider::Slider, text::Text, UiElementExt},
|
||||||
layout::{Alignment, Direction},
|
layout::{Alignment, Direction},
|
||||||
signal::Signal,
|
signal::Signal,
|
||||||
frame_rect, size,
|
rect_frame, size,
|
||||||
};
|
};
|
||||||
use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
|
use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
|
||||||
use winit::keyboard::KeyCode;
|
use winit::keyboard::KeyCode;
|
||||||
use crate::{hui_integration::UiState, input::RawKbmInputState, rendering::WindowSize, settings::GameSettings};
|
use crate::{hui_integration::UiState, input::RawKbmInputState, rendering::Renderer, settings::GameSettings};
|
||||||
|
|
||||||
#[derive(Signal)]
|
#[derive(Signal)]
|
||||||
enum SettingsSignal {
|
enum SettingsSignal {
|
||||||
|
@ -18,7 +18,7 @@ enum SettingsSignal {
|
||||||
|
|
||||||
pub fn render_settings_ui(
|
pub fn render_settings_ui(
|
||||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
size: UniqueView<WindowSize>,
|
ren: UniqueView<Renderer>,
|
||||||
mut settings: UniqueViewMut<GameSettings>,
|
mut settings: UniqueViewMut<GameSettings>,
|
||||||
kbd: UniqueView<RawKbmInputState>,
|
kbd: UniqueView<RawKbmInputState>,
|
||||||
) {
|
) {
|
||||||
|
@ -34,7 +34,7 @@ pub fn render_settings_ui(
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_background(frame_rect! {
|
.with_background(rect_frame! {
|
||||||
color: (0.2, 0.2, 0.2),
|
color: (0.2, 0.2, 0.2),
|
||||||
corner_radius: 8.
|
corner_radius: 8.
|
||||||
})
|
})
|
||||||
|
@ -99,7 +99,7 @@ pub fn render_settings_ui(
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
})
|
})
|
||||||
.add_root(&mut ui.hui, size.0.as_vec2());
|
.add_root(&mut ui.hui, ren.size_vec2());
|
||||||
|
|
||||||
ui.hui.process_signals(|signal: SettingsSignal| match signal {
|
ui.hui.process_signals(|signal: SettingsSignal| match signal {
|
||||||
SettingsSignal::SetRenderDistance(value) => settings.render_distance = value,
|
SettingsSignal::SetRenderDistance(value) => settings.render_distance = value,
|
||||||
|
|
|
@ -102,6 +102,7 @@ impl ChunkMeshStorage {
|
||||||
pub fn init_game_world(
|
pub fn init_game_world(
|
||||||
storages: AllStoragesView,
|
storages: AllStoragesView,
|
||||||
) {
|
) {
|
||||||
|
log::info!("init_game_world called");
|
||||||
storages.add_unique_non_send_sync(ChunkMeshStorage::new());
|
storages.add_unique_non_send_sync(ChunkMeshStorage::new());
|
||||||
storages.add_unique(ChunkStorage::new());
|
storages.add_unique(ChunkStorage::new());
|
||||||
storages.add_unique(ChunkTaskManager::new());
|
storages.add_unique(ChunkTaskManager::new());
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use glam::IVec3;
|
use glam::IVec3;
|
||||||
use atomic::Atomic;
|
use atomic::Atomic;
|
||||||
use glium::{VertexBuffer, IndexBuffer};
|
|
||||||
use kubi_shared::worldgen::AbortState;
|
use kubi_shared::worldgen::AbortState;
|
||||||
use crate::rendering::world::ChunkVertex;
|
use crate::rendering::{world::ChunkVertex, BufferPair};
|
||||||
|
|
||||||
pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData};
|
pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData};
|
||||||
|
|
||||||
|
@ -18,10 +17,8 @@ impl ChunkData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChunkMesh {
|
pub struct ChunkMesh {
|
||||||
pub vertex_buffer: VertexBuffer<ChunkVertex>,
|
pub main: BufferPair,
|
||||||
pub index_buffer: IndexBuffer<u32>,
|
pub trans: BufferPair,
|
||||||
pub trans_vertex_buffer: VertexBuffer<ChunkVertex>,
|
|
||||||
pub trans_index_buffer: IndexBuffer<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use atomic::{Atomic, Ordering};
|
use atomic::{Atomic, Ordering};
|
||||||
use glam::{IVec3, ivec3};
|
use glam::{IVec3, ivec3};
|
||||||
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
|
||||||
use kubi_shared::{networking::{channels::Channel, messages::ClientToServerMessage}, worldgen::AbortState};
|
use kubi_shared::{networking::{channels::Channel, messages::ClientToServerMessage}, worldgen::AbortState};
|
||||||
use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync, track};
|
use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync, track};
|
||||||
use uflow::SendMode;
|
use uflow::SendMode;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
use crate::{
|
use crate::{
|
||||||
player::MainPlayer,
|
|
||||||
transform::Transform,
|
|
||||||
settings::GameSettings,
|
|
||||||
rendering::Renderer,
|
|
||||||
state::GameState,
|
|
||||||
networking::UdpClient,
|
networking::UdpClient,
|
||||||
|
player::MainPlayer,
|
||||||
|
rendering::{world::ChunkVertex, BufferPair, Renderer},
|
||||||
|
settings::GameSettings,
|
||||||
|
state::GameState,
|
||||||
|
transform::Transform,
|
||||||
};
|
};
|
||||||
use super::{
|
use super::{
|
||||||
ChunkStorage, ChunkMeshStorage,
|
ChunkStorage, ChunkMeshStorage,
|
||||||
|
@ -266,7 +266,7 @@ fn process_completed_tasks(
|
||||||
task_manager: UniqueView<ChunkTaskManager>,
|
task_manager: UniqueView<ChunkTaskManager>,
|
||||||
mut world: UniqueViewMut<ChunkStorage>,
|
mut world: UniqueViewMut<ChunkStorage>,
|
||||||
mut meshes: NonSendSync<UniqueViewMut<ChunkMeshStorage>>,
|
mut meshes: NonSendSync<UniqueViewMut<ChunkMeshStorage>>,
|
||||||
renderer: NonSendSync<UniqueView<Renderer>>,
|
renderer: UniqueView<Renderer>,
|
||||||
state: UniqueView<GameState>,
|
state: UniqueView<GameState>,
|
||||||
mut queue: UniqueViewMut<BlockUpdateQueue>,
|
mut queue: UniqueViewMut<BlockUpdateQueue>,
|
||||||
) {
|
) {
|
||||||
|
@ -327,12 +327,51 @@ fn process_completed_tasks(
|
||||||
|
|
||||||
//apply the mesh
|
//apply the mesh
|
||||||
//TODO: Skip if mesh is empty? (i.e. set to None)
|
//TODO: Skip if mesh is empty? (i.e. set to None)
|
||||||
let mesh = ChunkMesh {
|
//TODO
|
||||||
vertex_buffer: VertexBuffer::immutable(&renderer.display, &vertices).unwrap(),
|
|
||||||
index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &indices).unwrap(),
|
let vtx_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
trans_vertex_buffer: VertexBuffer::immutable(&renderer.display, &trans_vertices).unwrap(),
|
label: Some("chunk_vertex_buffer"),
|
||||||
trans_index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &trans_indices).unwrap(),
|
contents: bytemuck::cast_slice(&vertices),
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let idx_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("chunk_vertex_buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&indices),
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let vtx_buffer_trans = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("chunk_trans_vertex_buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&trans_vertices),
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let idx_buffer_trans = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("chunk_trans_index_buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&trans_indices),
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let main_buffer_pair = BufferPair {
|
||||||
|
vertex: vtx_buffer,
|
||||||
|
vertex_len: vertices.len() as u32,
|
||||||
|
index: idx_buffer,
|
||||||
|
index_len: indices.len() as u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let trans_buffer_pair = BufferPair {
|
||||||
|
vertex: vtx_buffer_trans,
|
||||||
|
vertex_len: trans_vertices.len() as u32,
|
||||||
|
index: idx_buffer_trans,
|
||||||
|
index_len: trans_indices.len() as u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mesh = ChunkMesh {
|
||||||
|
main: main_buffer_pair,
|
||||||
|
trans: trans_buffer_pair,
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(index) = chunk.mesh_index {
|
if let Some(index) = chunk.mesh_index {
|
||||||
meshes.update(index, mesh).expect("Mesh update failed");
|
meshes.update(index, mesh).expect("Mesh update failed");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use glam::{IVec3, ivec3};
|
use glam::{ivec3, IVec3, Vec3};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use kubi_shared::block::{Block, RenderType, Transparency};
|
use kubi_shared::block::{Block, RenderType, Transparency};
|
||||||
use crate::world::chunk::CHUNK_SIZE;
|
use crate::world::chunk::CHUNK_SIZE;
|
||||||
|
@ -10,7 +10,7 @@ mod builder;
|
||||||
use data::MeshGenData;
|
use data::MeshGenData;
|
||||||
use builder::{MeshBuilder, CubeFace, DiagonalFace};
|
use builder::{MeshBuilder, CubeFace, DiagonalFace};
|
||||||
|
|
||||||
pub fn generate_mesh(data: MeshGenData) -> (
|
pub fn generate_mesh(position: IVec3, data: MeshGenData) -> (
|
||||||
(Vec<ChunkVertex>, Vec<u32>),
|
(Vec<ChunkVertex>, Vec<u32>),
|
||||||
(Vec<ChunkVertex>, Vec<u32>),
|
(Vec<ChunkVertex>, Vec<u32>),
|
||||||
) {
|
) {
|
||||||
|
@ -32,8 +32,8 @@ pub fn generate_mesh(data: MeshGenData) -> (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut builder = MeshBuilder::new();
|
let mut builder = MeshBuilder::new_with_offset((position * CHUNK_SIZE as i32).as_vec3());
|
||||||
let mut trans_builder = MeshBuilder::new();
|
let mut trans_builder = MeshBuilder::new_with_offset((position * CHUNK_SIZE as i32).as_vec3());
|
||||||
|
|
||||||
for x in 0..CHUNK_SIZE as i32 {
|
for x in 0..CHUNK_SIZE as i32 {
|
||||||
for y in 0..CHUNK_SIZE as i32 {
|
for y in 0..CHUNK_SIZE as i32 {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use strum::EnumIter;
|
use strum::EnumIter;
|
||||||
use glam::{Vec3, vec3, IVec3, ivec3};
|
use glam::{ivec3, vec3, IVec3, Vec3};
|
||||||
use std::f32::consts::FRAC_1_SQRT_2;
|
use std::f32::consts::FRAC_1_SQRT_2;
|
||||||
use crate::rendering::world::ChunkVertex;
|
use crate::rendering::world::ChunkVertex;
|
||||||
|
|
||||||
|
@ -79,14 +79,15 @@ const CROSS_FACE_INDICES: [u32; 12] = [
|
||||||
|
|
||||||
|
|
||||||
const UV_COORDS: [[f32; 2]; 4] = [
|
const UV_COORDS: [[f32; 2]; 4] = [
|
||||||
[0., 0.],
|
|
||||||
[0., 1.],
|
[0., 1.],
|
||||||
[1., 0.],
|
[0., 0.],
|
||||||
[1., 1.],
|
[1., 1.],
|
||||||
|
[1., 0.],
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MeshBuilder {
|
pub struct MeshBuilder {
|
||||||
|
offset: Vec3,
|
||||||
vertex_buffer: Vec<ChunkVertex>,
|
vertex_buffer: Vec<ChunkVertex>,
|
||||||
index_buffer: Vec<u32>,
|
index_buffer: Vec<u32>,
|
||||||
idx_counter: u32,
|
idx_counter: u32,
|
||||||
|
@ -96,6 +97,10 @@ impl MeshBuilder {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_with_offset(offset: Vec3) -> Self {
|
||||||
|
Self { offset, ..Self::new() }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_face(&mut self, face: CubeFace, coord: IVec3, texture: u8) {
|
pub fn add_face(&mut self, face: CubeFace, coord: IVec3, texture: u8) {
|
||||||
let coord = coord.as_vec3();
|
let coord = coord.as_vec3();
|
||||||
let face_index = face as usize;
|
let face_index = face as usize;
|
||||||
|
@ -106,10 +111,10 @@ impl MeshBuilder {
|
||||||
self.vertex_buffer.reserve(4);
|
self.vertex_buffer.reserve(4);
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
self.vertex_buffer.push(ChunkVertex {
|
self.vertex_buffer.push(ChunkVertex {
|
||||||
position: (coord + vert[i]).to_array(),
|
position: (coord + vert[i] + self.offset).to_array(),
|
||||||
normal: norm.to_array(),
|
normal: norm.to_array(),
|
||||||
uv: UV_COORDS[i],
|
uv: UV_COORDS[i],
|
||||||
tex_index: texture
|
tex_index: texture as u32
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,18 +134,18 @@ impl MeshBuilder {
|
||||||
self.vertex_buffer.reserve(8);
|
self.vertex_buffer.reserve(8);
|
||||||
for i in 0..4 { //push front vertices
|
for i in 0..4 { //push front vertices
|
||||||
self.vertex_buffer.push(ChunkVertex {
|
self.vertex_buffer.push(ChunkVertex {
|
||||||
position: (coord.as_vec3() + vertices[i]).to_array(),
|
position: (coord.as_vec3() + vertices[i] + self.offset).to_array(),
|
||||||
normal: normal_front,
|
normal: normal_front,
|
||||||
uv: UV_COORDS[i],
|
uv: UV_COORDS[i],
|
||||||
tex_index: front_texture
|
tex_index: front_texture as u32
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for i in 0..4 { //push back vertices
|
for i in 0..4 { //push back vertices
|
||||||
self.vertex_buffer.push(ChunkVertex {
|
self.vertex_buffer.push(ChunkVertex {
|
||||||
position: (coord.as_vec3() + vertices[i]).to_array(),
|
position: (coord.as_vec3() + vertices[i] + self.offset).to_array(),
|
||||||
normal: normal_back,
|
normal: normal_back,
|
||||||
uv: UV_COORDS[i],
|
uv: UV_COORDS[i],
|
||||||
tex_index: back_texture
|
tex_index: back_texture as u32
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,24 +156,25 @@ impl MeshBuilder {
|
||||||
self.idx_counter += 8;
|
self.idx_counter += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_model(&mut self, position: Vec3, vertices: &[ChunkVertex], indices: Option<&[u32]>) {
|
//XXX: needs offset supprt
|
||||||
//push vertices
|
// pub fn add_model(&mut self, position: Vec3, vertices: &[ChunkVertex], indices: Option<&[u32]>) {
|
||||||
self.vertex_buffer.extend(vertices.iter().map(|vertex| {
|
// //push vertices
|
||||||
let mut vertex = *vertex;
|
// self.vertex_buffer.extend(vertices.iter().map(|vertex| {
|
||||||
vertex.position[0] += position.x;
|
// let mut vertex = *vertex;
|
||||||
vertex.position[0] += position.y;
|
// vertex.position[0] += position.x;
|
||||||
vertex.position[0] += position.z;
|
// vertex.position[0] += position.y;
|
||||||
vertex
|
// vertex.position[0] += position.z;
|
||||||
}));
|
// vertex
|
||||||
//push indices
|
// }));
|
||||||
if let Some(indices) = indices {
|
// //push indices
|
||||||
self.index_buffer.extend(indices.iter().map(|x| x + self.idx_counter));
|
// if let Some(indices) = indices {
|
||||||
} else {
|
// self.index_buffer.extend(indices.iter().map(|x| x + self.idx_counter));
|
||||||
self.index_buffer.extend(0..(self.vertex_buffer.len() as u32));
|
// } else {
|
||||||
}
|
// self.index_buffer.extend(0..(self.vertex_buffer.len() as u32));
|
||||||
//increment idx counter
|
// }
|
||||||
self.idx_counter += vertices.len() as u32;
|
// //increment idx counter
|
||||||
}
|
// self.idx_counter += vertices.len() as u32;
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn finish(self) -> (Vec<ChunkVertex>, Vec<u32>) {
|
pub fn finish(self) -> (Vec<ChunkVertex>, Vec<u32>) {
|
||||||
(self.vertex_buffer, self.index_buffer)
|
(self.vertex_buffer, self.index_buffer)
|
||||||
|
|
|
@ -63,7 +63,7 @@ impl ChunkTaskManager {
|
||||||
let (
|
let (
|
||||||
(vertices, indices),
|
(vertices, indices),
|
||||||
(trans_vertices, trans_indices),
|
(trans_vertices, trans_indices),
|
||||||
) = generate_mesh(data);
|
) = generate_mesh(position, data);
|
||||||
ChunkTaskResponse::GeneratedMesh {
|
ChunkTaskResponse::GeneratedMesh {
|
||||||
position,
|
position,
|
||||||
vertices, indices,
|
vertices, indices,
|
||||||
|
|
Loading…
Reference in a new issue