mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-22 03:48:21 -06:00
commit
0802e4806c
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -27,3 +27,10 @@ _visualizer.json
|
|||
*.so
|
||||
*.dylib
|
||||
*.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]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.glium]
|
||||
[profile.dev.package.wgpu]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.bracket-noise]
|
||||
[profile.dev.package.wgpu-core]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.wgpu-hal]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.fastnoise-lite]
|
||||
|
@ -38,5 +41,7 @@ opt-level = 3
|
|||
[profile.dev.package.android-activity]
|
||||
debug-assertions = false
|
||||
|
||||
[patch.crates-io]
|
||||
glium = { git = "https://github.com/glium/glium", rev = "a352c667" }
|
||||
# [patch.'https://github.com/griffi-gh/hui']
|
||||
# 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 |
|
@ -1,64 +1,64 @@
|
|||
//! Custom env_logger options and styling
|
||||
|
||||
/// Custom env_logger options and styling
|
||||
#[inline]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub fn init() {
|
||||
use log::Level;
|
||||
use std::io::Write;
|
||||
use env_logger::{fmt::Color, Builder, Env};
|
||||
|
||||
let env = Env::default()
|
||||
.filter_or("RUST_LOG", "trace,gilrs=warn,rusty_xinput=warn");
|
||||
Builder::from_env(env)
|
||||
.format(|buf, record| {
|
||||
let mut level_style = buf.style();
|
||||
level_style.set_color(match record.level() {
|
||||
Level::Error => Color::Red,
|
||||
Level::Warn => Color::Yellow,
|
||||
Level::Debug | Level::Trace => Color::Cyan,
|
||||
_ => Color::Blue
|
||||
}).set_bold(true);
|
||||
|
||||
let mut bold_style = buf.style();
|
||||
bold_style.set_bold(true);
|
||||
|
||||
let mut location_style = buf.style();
|
||||
location_style.set_bold(true);
|
||||
location_style.set_dimmed(true);
|
||||
|
||||
let mut location_line_style = buf.style();
|
||||
location_line_style.set_dimmed(true);
|
||||
|
||||
let text = format!("{}", record.args());
|
||||
|
||||
writeln!(
|
||||
buf,
|
||||
"{} {:<50}\t{}{}{}{}",
|
||||
level_style.value(match record.level() {
|
||||
Level::Error => "[e]",
|
||||
Level::Warn => "[w]",
|
||||
Level::Info => "[i]",
|
||||
Level::Debug => "[d]",
|
||||
Level::Trace => "[t]",
|
||||
}),
|
||||
text,
|
||||
bold_style.value((text.len() > 50).then_some("\n ╰─ ").unwrap_or_default()),
|
||||
location_style.value(record.target()),
|
||||
location_line_style.value(" :"),
|
||||
location_line_style.value(record.line().unwrap_or(0))
|
||||
)
|
||||
})
|
||||
.init();
|
||||
}
|
||||
|
||||
/// Custom env_logger options and styling
|
||||
#[inline]
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn init() {
|
||||
use log::LevelFilter;
|
||||
use android_logger::Config;
|
||||
android_logger::init_once(
|
||||
Config::default().with_max_level(LevelFilter::Trace),
|
||||
);
|
||||
}
|
||||
//! Custom env_logger options and styling
|
||||
|
||||
/// Custom env_logger options and styling
|
||||
#[inline]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub fn init() {
|
||||
use log::Level;
|
||||
use std::io::Write;
|
||||
use env_logger::{fmt::Color, Builder, Env};
|
||||
|
||||
let env = Env::default()
|
||||
.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)
|
||||
.format(|buf, record| {
|
||||
let mut level_style = buf.style();
|
||||
level_style.set_color(match record.level() {
|
||||
Level::Error => Color::Red,
|
||||
Level::Warn => Color::Yellow,
|
||||
Level::Debug | Level::Trace => Color::Cyan,
|
||||
_ => Color::Blue
|
||||
}).set_bold(true);
|
||||
|
||||
let mut bold_style = buf.style();
|
||||
bold_style.set_bold(true);
|
||||
|
||||
let mut location_style = buf.style();
|
||||
location_style.set_bold(true);
|
||||
location_style.set_dimmed(true);
|
||||
|
||||
let mut location_line_style = buf.style();
|
||||
location_line_style.set_dimmed(true);
|
||||
|
||||
let text = format!("{}", record.args());
|
||||
|
||||
writeln!(
|
||||
buf,
|
||||
"{} {:<50}\t{}{}{}{}",
|
||||
level_style.value(match record.level() {
|
||||
Level::Error => "[e]",
|
||||
Level::Warn => "[w]",
|
||||
Level::Info => "[i]",
|
||||
Level::Debug => "[d]",
|
||||
Level::Trace => "[t]",
|
||||
}),
|
||||
text,
|
||||
bold_style.value((text.len() > 50).then_some("\n ╰─ ").unwrap_or_default()),
|
||||
location_style.value(record.target()),
|
||||
location_line_style.value(" :"),
|
||||
location_line_style.value(record.line().unwrap_or(0))
|
||||
)
|
||||
})
|
||||
.init();
|
||||
}
|
||||
|
||||
/// Custom env_logger options and styling
|
||||
#[inline]
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn init() {
|
||||
use log::LevelFilter;
|
||||
use android_logger::Config;
|
||||
android_logger::init_once(
|
||||
Config::default().with_max_level(LevelFilter::Trace),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
|
|||
hashbrown = "0.14"
|
||||
nohash-hasher = "0.2"
|
||||
anyhow = "1.0"
|
||||
rayon = "1.7"
|
||||
rayon = "1.10"
|
||||
flume = "0.11"
|
||||
rand = "0.8"
|
||||
uflow = "0.7"
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use fastnoise_lite::FastNoiseLite;
|
||||
use glam::ivec3;
|
||||
use crate::{block::Block, chunk::CHUNK_SIZE, worldgen::SeedThingy};
|
||||
use super::{
|
||||
|
|
|
@ -11,21 +11,21 @@ crate-type = ["lib", "cdylib"]
|
|||
[dependencies]
|
||||
kubi-shared = { path = "../kubi-shared" }
|
||||
kubi-logging = { path = "../kubi-logging" }
|
||||
hui = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
||||
hui-glium = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
||||
hui-winit = { 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-wgpu = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4" }
|
||||
hui-winit = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4", features = ["winit_30"] }
|
||||
log = "0.4"
|
||||
glium = { git = "https://github.com/glium/glium", rev = "a352c667" }
|
||||
glutin = "0.31"
|
||||
winit = { version = "0.29", features = ["android-native-activity"] }
|
||||
glutin-winit = "0.4"
|
||||
raw-window-handle = "=0.5.*"
|
||||
wgpu = { version = "0.20", features = ["webgl"] }
|
||||
pollster = "0.3"
|
||||
bytemuck = { version = "1.15", features = ["derive"] }
|
||||
winit = { version = "0.30", features = ["android-native-activity"] }
|
||||
raw-window-handle = "0.6"
|
||||
glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
|
||||
image = { version = "0.25", default_features = false, features = ["png"] }
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
hashbrown = "0.14"
|
||||
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"] }
|
||||
anyhow = "1.0"
|
||||
flume = "0.11"
|
||||
|
@ -38,10 +38,11 @@ tinyset = "0.4"
|
|||
serde_json = { version = "1.0", optional = true } #only used for `generate_visualizer_data`
|
||||
rand = { version = "0.8", features = ["alloc", "small_rng"]}
|
||||
atomic = "0.6"
|
||||
tobj = "4.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "^0.5.2"
|
||||
ndk = "0.8"
|
||||
android-activity = "0.6"
|
||||
ndk = "0.9"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
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 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;
|
||||
|
||||
//maybe parallelize these two?
|
||||
|
@ -18,14 +18,15 @@ fn update_view_matrix(
|
|||
|
||||
fn update_perspective_matrix(
|
||||
mut vm_camera: ViewMut<Camera>,
|
||||
size: UniqueView<WindowSize>,
|
||||
ren: UniqueView<Renderer>,
|
||||
) {
|
||||
let sz = ren.size_vec2();
|
||||
for mut camera in (&mut vm_camera).iter() {
|
||||
camera.perspective_matrix = Mat4::perspective_rh_gl(
|
||||
camera.perspective_matrix = Mat4::perspective_rh(
|
||||
camera.fov,
|
||||
size.0.x as f32 / size.0.y as f32,
|
||||
sz.x / sz.y,
|
||||
camera.z_near,
|
||||
camera.z_far,
|
||||
camera.z_far,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use kubi_shared::networking::client::ClientId;
|
||||
use shipyard::{AllStoragesView, Unique, UniqueViewMut};
|
||||
use shipyard::{AllStoragesView, Unique};
|
||||
|
||||
pub enum ChatMessage {
|
||||
PlayerMessage {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//TODO client-side physics
|
||||
//TODO move this to shared
|
||||
use glam::{vec3, IVec3, Mat4, Vec3, Vec3Swizzles};
|
||||
use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, View, ViewMut};
|
||||
use glam::{vec3, Mat4, Vec3, Vec3Swizzles};
|
||||
use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, ViewMut};
|
||||
use kubi_shared::{block::{Block, CollisionType}, transform::Transform};
|
||||
use crate::{delta_time::DeltaTime, world::ChunkStorage};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||
use crate::{events::InputDeviceEvent, rendering::{Renderer, WindowSize}};
|
||||
use shipyard::{AllStoragesView, IntoIter, Unique, UniqueView, UniqueViewMut, View};
|
||||
use crate::{events::InputDeviceEvent, rendering::Renderer};
|
||||
use winit::{
|
||||
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(
|
||||
lock: UniqueView<CursorLock>,
|
||||
display: NonSendSync<UniqueView<Renderer>>
|
||||
display: UniqueView<Renderer>
|
||||
) {
|
||||
if cfg!(target_os = "android") {
|
||||
return
|
||||
}
|
||||
if lock.is_inserted_or_modified() {
|
||||
//TODO MIGRATION
|
||||
let window = &display.window;
|
||||
let window = display.window();
|
||||
window.set_cursor_grab(match lock.0 {
|
||||
true => CursorGrabMode::Confined,
|
||||
false => CursorGrabMode::None,
|
||||
|
@ -41,8 +41,7 @@ pub fn lock_cursor_now(
|
|||
pub fn debug_toggle_lock(
|
||||
mut lock: UniqueViewMut<CursorLock>,
|
||||
device_events: View<InputDeviceEvent>,
|
||||
ren: NonSendSync<UniqueView<Renderer>>,
|
||||
size: UniqueView<WindowSize>,
|
||||
ren: UniqueView<Renderer>,
|
||||
) {
|
||||
for evt in device_events.iter() {
|
||||
if let DeviceEvent::Key(RawKeyEvent {
|
||||
|
@ -51,8 +50,8 @@ pub fn debug_toggle_lock(
|
|||
}) = evt.event {
|
||||
lock.0 = !lock.0;
|
||||
if !lock.0 {
|
||||
let center = PhysicalPosition::new(size.0.x as f64 / 2., size.0.y as f64 / 2.);
|
||||
let _ = ren.window.set_cursor_position(center);
|
||||
let center = PhysicalPosition::new(ren.size().width as f64 / 2., ren.size().height as f64 / 2.);
|
||||
let _ = ren.window().set_cursor_position(center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ pub fn process_winit_events(world: &mut World, event: &Event<()>) {
|
|||
EventComponent,
|
||||
InputDeviceEvent {
|
||||
device_id: *device_id,
|
||||
event: DeviceEvent::Key(RawKeyEvent {
|
||||
event: DeviceEvent::Key(winit::event::RawKeyEvent {
|
||||
physical_key: event.physical_key,
|
||||
state: event.state,
|
||||
})
|
||||
|
@ -86,18 +86,18 @@ pub fn process_winit_events(world: &mut World, event: &Event<()>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn initial_resize_event(
|
||||
mut storages: AllStoragesViewMut,
|
||||
) {
|
||||
let (w, h) = {
|
||||
let renderer = storages.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap();
|
||||
renderer.display.get_framebuffer_dimensions()
|
||||
};
|
||||
storages.add_entity((
|
||||
EventComponent,
|
||||
WindowResizedEvent(UVec2::new(w, h))
|
||||
));
|
||||
}
|
||||
// pub fn initial_resize_event(
|
||||
// mut storages: AllStoragesViewMut,
|
||||
// ) {
|
||||
// let (w, h) = {
|
||||
// let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||
// (renderer.size().width, renderer.size().height)
|
||||
// };
|
||||
// storages.add_entity((
|
||||
// EventComponent,
|
||||
// WindowResizedEvent(UVec2::new(w, h))
|
||||
// ));
|
||||
// }
|
||||
|
||||
pub fn clear_events(
|
||||
mut all_storages: AllStoragesViewMut,
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
use hui::UiInstance;
|
||||
use hui_glium::GliumUiRenderer;
|
||||
use hui_wgpu::WgpuUiRenderer;
|
||||
//use hui_glium::GliumUiRenderer;
|
||||
use shipyard::{AllStoragesView, Unique, UniqueView, NonSendSync, UniqueViewMut};
|
||||
use crate::rendering::{RenderTarget, Renderer, WindowSize};
|
||||
use crate::rendering::{RenderCtx, Renderer};
|
||||
|
||||
#[derive(Unique)]
|
||||
pub struct UiState {
|
||||
pub hui: UiInstance,
|
||||
pub renderer: GliumUiRenderer
|
||||
pub renderer: WgpuUiRenderer,
|
||||
}
|
||||
|
||||
pub fn kubi_ui_init(
|
||||
storages: AllStoragesView
|
||||
) {
|
||||
let renderer = storages.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap();
|
||||
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||
storages.add_unique_non_send_sync(UiState {
|
||||
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(
|
||||
mut ui: NonSendSync<UniqueViewMut<UiState>>
|
||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||
renderer: UniqueView<Renderer>,
|
||||
) {
|
||||
let ui: &mut UiState = &mut ui;
|
||||
let UiState { hui, renderer, .. } = ui;
|
||||
let UiState { hui, renderer: ui_renderer } = ui;
|
||||
hui.end();
|
||||
renderer.update(hui);
|
||||
ui_renderer.update(hui, renderer.queue(), renderer.device(), renderer.size_vec2());
|
||||
}
|
||||
|
||||
pub fn kubi_ui_draw(
|
||||
ctx: &mut RenderCtx,
|
||||
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(
|
||||
|
|
|
@ -10,7 +10,7 @@ use nohash_hasher::BuildNoHashHasher;
|
|||
use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync};
|
||||
use crate::{
|
||||
events::{InputDeviceEvent, TouchEvent},
|
||||
rendering::WindowSize
|
||||
rendering::Renderer,
|
||||
};
|
||||
|
||||
#[derive(Unique, Clone, Copy, Default, Debug)]
|
||||
|
@ -211,10 +211,10 @@ fn update_input_state_gamepad (
|
|||
|
||||
fn update_input_state_touch (
|
||||
touch_state: UniqueView<RawTouchState>,
|
||||
win_size: UniqueView<WindowSize>,
|
||||
renderer: UniqueView<Renderer>,
|
||||
mut inputs: UniqueViewMut<Inputs>,
|
||||
) {
|
||||
let w = win_size.0.as_dvec2();
|
||||
let w = renderer.size_uvec2().as_dvec2();
|
||||
|
||||
//Movement
|
||||
if let Some(finger) = touch_state.query_area(
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
use shipyard::{
|
||||
World, Workload, IntoWorkload,
|
||||
UniqueView, UniqueViewMut,
|
||||
NonSendSync, WorkloadModificator,
|
||||
WorkloadModificator,
|
||||
SystemModificator
|
||||
};
|
||||
use winit::{
|
||||
|
@ -58,36 +58,19 @@ pub(crate) mod chat;
|
|||
|
||||
use world::{
|
||||
init_game_world,
|
||||
loading::update_loaded_world_around_player,
|
||||
loading::update_loaded_world_around_player,
|
||||
raycast::update_raycasts,
|
||||
queue::apply_queued_blocks,
|
||||
queue::apply_queued_blocks,
|
||||
tasks::ChunkTaskManager,
|
||||
};
|
||||
use player::{spawn_player, MainPlayer};
|
||||
use prefabs::load_prefabs;
|
||||
use settings::{load_settings, GameSettings};
|
||||
use camera::compute_cameras;
|
||||
use events::{
|
||||
clear_events,
|
||||
process_winit_events,
|
||||
initial_resize_event,
|
||||
player_actions::generate_move_events,
|
||||
};
|
||||
use events::{clear_events, process_winit_events, player_actions::generate_move_events};
|
||||
use input::{init_input, process_inputs};
|
||||
use player_controller::{debug_switch_ctl_type, update_player_controllers};
|
||||
use rendering::{
|
||||
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 rendering::{BackgroundColor, Renderer, init_rendering, render_master, update_rendering_early, update_rendering_late};
|
||||
use block_placement::update_block_placement;
|
||||
use delta_time::{DeltaTime, init_delta_time};
|
||||
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 networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
|
||||
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 connecting_screen::update_connecting_screen;
|
||||
use fixed_timestamp::init_fixed_timestamp_storage;
|
||||
|
@ -105,8 +88,7 @@ use chat_ui::render_chat;
|
|||
use chat::init_chat_manager;
|
||||
use crosshair_ui::{init_crosshair_image, draw_crosshair};
|
||||
use settings_ui::render_settings_ui;
|
||||
|
||||
use crate::hui_integration::hui_process_winit_events;
|
||||
use hui_integration::hui_process_winit_events;
|
||||
|
||||
/// stuff required to init the renderer and other basic systems
|
||||
fn pre_startup() -> Workload {
|
||||
|
@ -118,11 +100,9 @@ fn pre_startup() -> Workload {
|
|||
fn startup() -> Workload {
|
||||
(
|
||||
init_fixed_timestamp_storage,
|
||||
initial_resize_event,
|
||||
init_window_size,
|
||||
kubi_ui_init,
|
||||
load_prefabs,
|
||||
init_primitives,
|
||||
init_rendering,
|
||||
insert_lock_state,
|
||||
init_state,
|
||||
initialize_from_args,
|
||||
|
@ -133,14 +113,13 @@ fn startup() -> Workload {
|
|||
init_client_physics,
|
||||
init_chat_manager,
|
||||
init_crosshair_image,
|
||||
init_trans_chunk_queue,
|
||||
).into_sequential_workload()
|
||||
}
|
||||
|
||||
fn update() -> Workload {
|
||||
(
|
||||
update_rendering_early,
|
||||
debug_toggle_lock,
|
||||
update_window_size,
|
||||
update_cursor_lock_state,
|
||||
process_inputs,
|
||||
kubi_ui_begin,
|
||||
|
@ -179,25 +158,26 @@ fn update() -> Workload {
|
|||
update_state,
|
||||
exit_on_esc,
|
||||
disconnect_on_exit.run_if(is_multiplayer),
|
||||
update_rendering_late,
|
||||
).into_sequential_workload()
|
||||
}
|
||||
|
||||
fn render() -> Workload {
|
||||
(
|
||||
clear_background,
|
||||
(
|
||||
draw_world,
|
||||
draw_current_chunk_border,
|
||||
render_selection_box,
|
||||
render_entities,
|
||||
draw_world_trans,
|
||||
render_submerged_view,
|
||||
).into_sequential_workload().run_if(is_ingame),
|
||||
kubi_ui_draw,
|
||||
).into_sequential_workload()
|
||||
}
|
||||
// fn render() -> Workload {
|
||||
// (
|
||||
// clear_background,
|
||||
// (
|
||||
// draw_world,
|
||||
// draw_current_chunk_border,
|
||||
// render_selection_box,
|
||||
// render_entities,
|
||||
// draw_world_trans,
|
||||
// render_submerged_view,
|
||||
// ).into_sequential_workload().run_if(is_ingame),
|
||||
// kubi_ui_draw,
|
||||
// ).into_sequential_workload()
|
||||
// }
|
||||
|
||||
fn after_frame_end() -> Workload {
|
||||
fn after_render() -> Workload {
|
||||
(
|
||||
clear_events,
|
||||
).into_sequential_workload()
|
||||
|
@ -245,8 +225,8 @@ pub fn kubi_main(
|
|||
world.add_workload(pre_startup);
|
||||
world.add_workload(startup);
|
||||
world.add_workload(update);
|
||||
world.add_workload(render);
|
||||
world.add_workload(after_frame_end);
|
||||
//world.add_workload(render);
|
||||
world.add_workload(after_render);
|
||||
|
||||
//Save _visualizer.json
|
||||
#[cfg(feature = "generate_visualizer_data")]
|
||||
|
@ -286,7 +266,7 @@ pub fn kubi_main(
|
|||
let settings = world.borrow::<UniqueView<GameSettings>>().unwrap();
|
||||
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
|
||||
world.run_workload(startup).unwrap();
|
||||
|
@ -326,22 +306,24 @@ pub fn kubi_main(
|
|||
//Run update workflows
|
||||
world.run_workload(update).unwrap();
|
||||
|
||||
world.run(render_master);
|
||||
|
||||
//Start rendering (maybe use custom views for this?)
|
||||
let target = {
|
||||
let renderer = world.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap();
|
||||
renderer.display.draw()
|
||||
};
|
||||
world.add_unique_non_send_sync(RenderTarget(target));
|
||||
// let target = {
|
||||
// let renderer = world.borrow::<UniqueView<Renderer>>().unwrap();
|
||||
// renderer.display.draw()
|
||||
// };
|
||||
// world.add_unique_non_send_sync(RenderTarget(target));
|
||||
|
||||
//Run render workflow
|
||||
world.run_workload(render).unwrap();
|
||||
//world.run_workload(render).unwrap();
|
||||
|
||||
//Finish rendering
|
||||
let target = world.remove_unique::<RenderTarget>().unwrap();
|
||||
target.0.finish().unwrap();
|
||||
// let target = world.remove_unique::<RenderTarget>().unwrap();
|
||||
// target.0.finish().unwrap();
|
||||
|
||||
//After frame end
|
||||
world.run_workload(after_frame_end).unwrap();
|
||||
world.run_workload(after_render).unwrap();
|
||||
|
||||
//Process control flow changes
|
||||
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 shipyard::{AllStoragesView, NonSendSync, Unique, UniqueView, UniqueViewMut};
|
||||
use glium::{texture::{SrgbTexture2dArray, MipmapsOption}, Program};
|
||||
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;
|
||||
mod shaders;
|
||||
//TODO move to rendering module
|
||||
|
||||
use texture::load_texture2darray_prefab;
|
||||
use shaders::include_shader_prefab;
|
||||
mod loader;
|
||||
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 {
|
||||
fn file_name(self) -> &'static str;
|
||||
|
@ -40,20 +60,15 @@ impl AssetPaths for BlockTexture {
|
|||
}
|
||||
|
||||
#[derive(Unique)]
|
||||
#[repr(transparent)]
|
||||
pub struct BlockTexturesPrefab(pub SrgbTexture2dArray);
|
||||
|
||||
#[derive(Unique)]
|
||||
#[repr(transparent)]
|
||||
pub struct ChunkShaderPrefab(pub Program);
|
||||
|
||||
#[derive(Unique)]
|
||||
#[repr(transparent)]
|
||||
pub struct ColoredShaderPrefab(pub Program);
|
||||
|
||||
#[derive(Unique)]
|
||||
#[repr(transparent)]
|
||||
pub struct Colored2ShaderPrefab(pub Program);
|
||||
pub struct GpuPrefabs {
|
||||
pub block_diffuse_texture: wgpu::Texture,
|
||||
pub block_diffuse_bind_group_layout: wgpu::BindGroupLayout,
|
||||
pub block_diffuse_bind_group: wgpu::BindGroup,
|
||||
pub player_model_diffuse_texture: wgpu::Texture,
|
||||
pub player_model_diffuse_bind_group_layout: wgpu::BindGroupLayout,
|
||||
pub player_model_diffuse_bind_group: wgpu::BindGroup,
|
||||
pub player_model: BufferPair,
|
||||
}
|
||||
|
||||
#[derive(Unique)]
|
||||
#[repr(transparent)]
|
||||
|
@ -61,19 +76,132 @@ pub struct UiFontPrefab(pub FontHandle);
|
|||
|
||||
pub fn load_prefabs(
|
||||
storages: AllStoragesView,
|
||||
renderer: NonSendSync<UniqueView<Renderer>>,
|
||||
renderer: UniqueView<Renderer>,
|
||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||
assman: UniqueView<AssetManager>
|
||||
) {
|
||||
log::info!("Loading textures...");
|
||||
storages.add_unique_non_send_sync(BlockTexturesPrefab(
|
||||
load_texture2darray_prefab::<BlockTexture, _>(
|
||||
&assman,
|
||||
"blocks".into(),
|
||||
&renderer.display,
|
||||
MipmapsOption::AutoGeneratedMipmaps
|
||||
)
|
||||
));
|
||||
let block_diffuse_texture = load_texture2darray_prefab::<BlockTexture>(
|
||||
&renderer,
|
||||
&assman,
|
||||
"blocks".into(),
|
||||
);
|
||||
|
||||
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...");
|
||||
{
|
||||
|
@ -85,33 +213,33 @@ pub fn load_prefabs(
|
|||
storages.add_unique(UiFontPrefab(font_handle));
|
||||
}
|
||||
|
||||
log::info!("Compiling shaders...");
|
||||
storages.add_unique_non_send_sync(ChunkShaderPrefab(
|
||||
include_shader_prefab!(
|
||||
"world",
|
||||
"../shaders/world.vert",
|
||||
"../shaders/world.frag",
|
||||
&renderer.display
|
||||
)
|
||||
));
|
||||
storages.add_unique_non_send_sync(ColoredShaderPrefab(
|
||||
include_shader_prefab!(
|
||||
"colored",
|
||||
"../shaders/colored.vert",
|
||||
"../shaders/colored.frag",
|
||||
&renderer.display
|
||||
)
|
||||
));
|
||||
storages.add_unique_non_send_sync(Colored2ShaderPrefab(
|
||||
include_shader_prefab!(
|
||||
"colored",
|
||||
"../shaders/colored2.vert",
|
||||
"../shaders/colored2.frag",
|
||||
&renderer.display
|
||||
)
|
||||
));
|
||||
//log::info!("Compiling shaders...");
|
||||
// storages.add_unique_non_send_sync(ChunkShaderPrefab(
|
||||
// include_shader_prefab!(
|
||||
// "world",
|
||||
// "../shaders/world.vert",
|
||||
// "../shaders/world.frag",
|
||||
// &renderer.display
|
||||
// )
|
||||
// ));
|
||||
// storages.add_unique_non_send_sync(ColoredShaderPrefab(
|
||||
// include_shader_prefab!(
|
||||
// "colored",
|
||||
// "../shaders/colored.vert",
|
||||
// "../shaders/colored.frag",
|
||||
// &renderer.display
|
||||
// )
|
||||
// ));
|
||||
// storages.add_unique_non_send_sync(Colored2ShaderPrefab(
|
||||
// include_shader_prefab!(
|
||||
// "colored",
|
||||
// "../shaders/colored2.vert",
|
||||
// "../shaders/colored2.frag",
|
||||
// &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 raw_window_handle::HasRawWindowHandle;
|
||||
use shipyard::{Unique, NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, AllStoragesView};
|
||||
use winit::{
|
||||
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}};
|
||||
use shipyard::{AllStoragesViewMut, IntoIter, IntoWorkload, SystemModificator, Unique, UniqueView, UniqueViewMut, View, Workload, WorkloadModificator};
|
||||
use winit::dpi::PhysicalSize;
|
||||
use glam::Vec3;
|
||||
use crate::{events::WindowResizedEvent, hui_integration::kubi_ui_draw, state::is_ingame};
|
||||
|
||||
pub mod primitives;
|
||||
mod renderer;
|
||||
mod primitives;
|
||||
mod selection_box;
|
||||
mod entities;
|
||||
pub use renderer::Renderer;
|
||||
|
||||
pub mod background;
|
||||
pub mod world;
|
||||
pub mod selection_box;
|
||||
pub mod entities;
|
||||
pub mod sumberge;
|
||||
pub mod camera_uniform;
|
||||
pub mod depth;
|
||||
pub mod smoverlay;
|
||||
|
||||
pub struct BufferPair {
|
||||
pub index: wgpu::Buffer,
|
||||
pub index_len: u32,
|
||||
pub vertex: wgpu::Buffer,
|
||||
pub vertex_len: u32,
|
||||
}
|
||||
|
||||
#[derive(Unique)]
|
||||
#[repr(transparent)]
|
||||
pub struct RenderTarget(pub glium::Frame);
|
||||
|
||||
#[derive(Unique)]
|
||||
#[repr(transparent)]
|
||||
pub struct BackgroundColor(pub Vec3);
|
||||
|
||||
#[derive(Unique, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct WindowSize(pub UVec2);
|
||||
|
||||
#[derive(Unique)]
|
||||
pub struct Renderer {
|
||||
pub window: Window,
|
||||
pub display: Display<WindowSurface>,
|
||||
pub struct RenderCtx<'a> {
|
||||
//pub renderer: &'a Renderer,
|
||||
pub encoder: &'a mut wgpu::CommandEncoder,
|
||||
pub surface_view: &'a wgpu::TextureView,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn init(event_loop: &EventLoopWindowTarget<()>, settings: &GameSettings) -> Self {
|
||||
log::info!("initializing display");
|
||||
//TODO run init_world_render_state, init_selection_box_state, etc. only once ingame?
|
||||
|
||||
let wb = WindowBuilder::new()
|
||||
.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
|
||||
}
|
||||
});
|
||||
pub fn init_rendering() -> Workload {
|
||||
(
|
||||
depth::init_depth_texture,
|
||||
camera_uniform::init_camera_uniform_buffer,
|
||||
primitives::init_primitives,
|
||||
world::init_world_render_state, //req: depth, camera
|
||||
entities::init_entities_render_state, //req: depth, camera
|
||||
selection_box::init_selection_box_render_state, //req: depth, camera, primitives
|
||||
smoverlay::init_smoverlay_render_state, //req: primitives
|
||||
).into_sequential_workload()
|
||||
}
|
||||
|
||||
let display_builder = DisplayBuilder::new()
|
||||
.with_window_builder(Some(wb));
|
||||
pub fn update_rendering_early() -> Workload {
|
||||
(
|
||||
resize_renderer,
|
||||
depth::resize_depth_texture,
|
||||
).into_sequential_workload()
|
||||
}
|
||||
|
||||
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());
|
||||
pub fn update_rendering_late() -> Workload {
|
||||
(
|
||||
camera_uniform::update_camera_uniform_buffer,
|
||||
(
|
||||
selection_box::update_selection_box_render_state,
|
||||
entities::update_entities_render_state,
|
||||
smoverlay::update_smoverlay_render_state,
|
||||
).into_workload().run_if(is_ingame),
|
||||
).into_workload()
|
||||
}
|
||||
|
||||
let (window, gl_config) = display_builder
|
||||
.build(event_loop, config_template_builder, |mut configs| {
|
||||
configs.next().unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
pub fn render_master(storages: AllStoragesViewMut) {
|
||||
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||
|
||||
let window = window.expect("no window");
|
||||
let mut encoder = renderer.device().create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("main_encoder"),
|
||||
});
|
||||
let surface_texture = renderer.surface().get_current_texture().unwrap();
|
||||
let surface_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
// 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(),
|
||||
);
|
||||
let mut data = RenderCtx {
|
||||
encoder: &mut encoder,
|
||||
surface_view: &surface_view,
|
||||
};
|
||||
|
||||
// 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()
|
||||
};
|
||||
storages.run_with_data(background::clear_bg, &mut data);
|
||||
if storages.run(is_ingame) {
|
||||
storages.run_with_data(world::draw_world, &mut data);
|
||||
storages.run_with_data(selection_box::draw_selection_box, &mut data);
|
||||
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 context_attributes = ContextAttributesBuilder::new()
|
||||
.with_debug(cfg!(debug_assertions))
|
||||
.with_context_api(glutin::context::ContextApi::Gles(None))
|
||||
.with_profile(GlProfile::Core)
|
||||
.build(Some(window.raw_window_handle()));
|
||||
renderer.queue().submit([encoder.finish()]);
|
||||
surface_texture.present();
|
||||
}
|
||||
|
||||
let current_context = unsafe {
|
||||
gl_config.display()
|
||||
.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();
|
||||
|
||||
//TODO MIGRATION
|
||||
// let cb = ContextBuilder::new()
|
||||
// //.with_srgb(false)
|
||||
// .with_depth_buffer(24)
|
||||
// .with_multisampling(settings.msaa.unwrap_or_default())
|
||||
// .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 }
|
||||
/// Resize the renderer when the window is resized
|
||||
pub fn resize_renderer(
|
||||
mut renderer: UniqueViewMut<Renderer>,
|
||||
resize: View<WindowResizedEvent>,
|
||||
) {
|
||||
if let Some(size) = resize.iter().last() {
|
||||
renderer.resize(PhysicalSize::new(size.0.x, size.0.y));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_background(
|
||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
||||
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
|
||||
}
|
||||
// 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 glium::{DepthTest, Depth, PolygonMode, BackfaceCullingMode, DrawParameters, Surface, uniform};
|
||||
use kubi_shared::{entity::Entity, transform::Transform};
|
||||
use crate::{
|
||||
prefabs::ColoredShaderPrefab,
|
||||
camera::Camera,
|
||||
settings::GameSettings
|
||||
};
|
||||
use super::{
|
||||
RenderTarget,
|
||||
primitives::cube::CenteredCubePrimitive
|
||||
};
|
||||
|
||||
// TODO: entity models
|
||||
pub fn render_entities(
|
||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
||||
buffers: NonSendSync<UniqueView<CenteredCubePrimitive>>,
|
||||
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
|
||||
camera: View<Camera>,
|
||||
settings: UniqueView<GameSettings>,
|
||||
entities: View<Entity>,
|
||||
transform: View<Transform>,
|
||||
) {
|
||||
let (camera_id, camera) = camera.iter().with_id().next().expect("No cameras in the scene");
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
use shipyard::{AllStoragesView, IntoIter, IntoWithId, Unique, UniqueView, View};
|
||||
use kubi_shared::{entity::Entity, transform::Transform};
|
||||
use crate::{
|
||||
camera::Camera, prefabs::GpuPrefabs, settings::GameSettings
|
||||
};
|
||||
|
||||
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
|
||||
pub fn render_entities(
|
||||
ctx: &mut RenderCtx,
|
||||
state: UniqueView<EntitiesRenderState>,
|
||||
depth: UniqueView<DepthTexture>,
|
||||
prefabs: UniqueView<GpuPrefabs>,
|
||||
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||
) {
|
||||
if state.instance_buffer.count == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
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 glium::implement_vertex;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use shipyard::{IntoWorkload, Workload};
|
||||
|
||||
pub mod cube;
|
||||
pub mod rect;
|
||||
pub mod stri;
|
||||
|
||||
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);
|
||||
mod cube;
|
||||
mod fstri;
|
||||
pub use cube::CubePrimitive;
|
||||
pub use fstri::FstriPrimitive;
|
||||
|
||||
pub fn init_primitives() -> Workload {
|
||||
(
|
||||
init_cube_primitive,
|
||||
init_rect_primitive,
|
||||
init_stri_primitive,
|
||||
cube::init_cube_primitive,
|
||||
fstri::init_fstri_primitive,
|
||||
).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 glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
||||
use crate::rendering::Renderer;
|
||||
use super::PositionOnlyVertex;
|
||||
|
||||
#[derive(Unique)]
|
||||
pub struct CubePrimitive(pub VertexBuffer<PositionOnlyVertex>, pub IndexBuffer<u16>);
|
||||
|
||||
#[derive(Unique)]
|
||||
pub struct CenteredCubePrimitive(pub VertexBuffer<PositionOnlyVertex>, pub IndexBuffer<u16>);
|
||||
|
||||
const CENTERED_CUBE_VERTICES: &[PositionOnlyVertex] = &[
|
||||
// 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] = &[
|
||||
// front
|
||||
0, 1, 2,
|
||||
2, 3, 0,
|
||||
// right
|
||||
1, 5, 6,
|
||||
6, 2, 1,
|
||||
// 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(
|
||||
storages: AllStoragesView,
|
||||
display: NonSendSync<UniqueView<Renderer>>
|
||||
) {
|
||||
{
|
||||
let vert = VertexBuffer::immutable(
|
||||
&display.display,
|
||||
CUBE_VERTICES
|
||||
).unwrap();
|
||||
let index = IndexBuffer::immutable(
|
||||
&display.display,
|
||||
PrimitiveType::TrianglesList,
|
||||
CUBE_INDICES
|
||||
).unwrap();
|
||||
storages.add_unique_non_send_sync(CubePrimitive(vert, index));
|
||||
}
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
use shipyard::{AllStoragesView, Unique, UniqueView};
|
||||
use wgpu::util::DeviceExt;
|
||||
use crate::rendering::{BufferPair, Renderer};
|
||||
use super::PrimitiveVertex;
|
||||
|
||||
#[derive(Unique)]
|
||||
pub struct CubePrimitive(pub BufferPair);
|
||||
|
||||
/// Vertices for a centered cube with a side length of 1
|
||||
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] },
|
||||
];
|
||||
|
||||
/// Indices for a cube primitive
|
||||
const CUBE_INDICES: &[u16] = &[
|
||||
0, 1, 2, 2, 3, 0, // front
|
||||
1, 5, 6, 6, 2, 1, // right
|
||||
7, 6, 5, 5, 4, 7, // back
|
||||
4, 0, 3, 3, 7, 4, // left
|
||||
4, 5, 1, 1, 0, 4, // bottom
|
||||
3, 2, 6, 6, 7, 3, // top
|
||||
];
|
||||
|
||||
pub fn init_cube_primitive(storages: AllStoragesView) {
|
||||
log::info!("init_cube_primitive");
|
||||
let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
|
||||
storages.add_unique(CubePrimitive(BufferPair {
|
||||
index: renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("cube_index_buffer"),
|
||||
contents: bytemuck::cast_slice(CUBE_INDICES),
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
|
||||
}),
|
||||
index_len: CUBE_INDICES.len() as u32,
|
||||
vertex: renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("cube_vertex_buffer"),
|
||||
contents: bytemuck::cast_slice(CUBE_VERTICES),
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||
}),
|
||||
vertex_len: CUBE_VERTICES.len() as u32,
|
||||
}));
|
||||
}
|
||||
|
|
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::{View, IntoIter, NonSendSync, UniqueViewMut, UniqueView};
|
||||
use glium::{
|
||||
Surface,
|
||||
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;
|
||||
|
||||
pub fn render_selection_box(
|
||||
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
|
||||
target.0.draw(
|
||||
&buffers.0,
|
||||
&buffers.1,
|
||||
&program.0,
|
||||
&uniform! {
|
||||
color: [0., 0., 0., 0.5_f32],
|
||||
model: Mat4::from_scale_rotation_translation(
|
||||
Vec3::splat(1. + SMOL * 2.),
|
||||
Quat::default(),
|
||||
lookat.block_position.as_vec3() - Vec3::splat(SMOL)
|
||||
).to_cols_array_2d(),
|
||||
perspective: camera.perspective_matrix.to_cols_array_2d(),
|
||||
view: camera.view_matrix.to_cols_array_2d(),
|
||||
},
|
||||
&DrawParameters {
|
||||
backface_culling: BackfaceCullingMode::CullClockwise,
|
||||
blend: Blend::alpha_blending(),
|
||||
depth: Depth {
|
||||
//this may be unreliable... unless scale is applied! hacky...
|
||||
test: DepthTest::IfLessOrEqual,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
).unwrap();
|
||||
}
|
||||
use shipyard::{AllStoragesView, IntoIter, Unique, UniqueView, View};
|
||||
use wgpu::RenderPassDescriptor;
|
||||
use crate::{player::MainPlayer, world::raycast::LookingAtBlock};
|
||||
use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, primitives::CubePrimitive, RenderCtx};
|
||||
|
||||
mod pipeline;
|
||||
mod uniform;
|
||||
|
||||
use uniform::SelectionBoxUniform;
|
||||
|
||||
#[derive(Unique)]
|
||||
pub struct SboxRenderState {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
uniform: SelectionBoxUniform,
|
||||
}
|
||||
|
||||
pub fn init_selection_box_render_state(storages: AllStoragesView) {
|
||||
let uniform = storages.run(uniform::init_selection_box_uniform);
|
||||
let pipeline = storages.run_with_data(pipeline::init_selection_box_pipeline, &uniform);
|
||||
storages.add_unique(SboxRenderState { pipeline, uniform });
|
||||
}
|
||||
|
||||
pub use uniform::update_selection_box_uniform
|
||||
as update_selection_box_render_state;
|
||||
|
||||
pub fn draw_selection_box(
|
||||
ctx: &mut RenderCtx,
|
||||
state: UniqueView<SboxRenderState>,
|
||||
depth: UniqueView<DepthTexture>,
|
||||
cube: UniqueView<CubePrimitive>,
|
||||
camera_ubo: UniqueView<CameraUniformBuffer>,
|
||||
lookat: View<LookingAtBlock>,
|
||||
player: View<MainPlayer>,
|
||||
) {
|
||||
let Some((LookingAtBlock(Some(_)), _)) = (&lookat, &player).iter().next() else {
|
||||
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 shipyard::{track, AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||
use glium::{
|
||||
draw_parameters::{
|
||||
BackfaceCullingMode, Depth, DepthTest, PolygonMode
|
||||
}, implement_vertex, uniform, uniforms::{
|
||||
MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction
|
||||
}, Blend, DrawParameters, Smooth, Surface
|
||||
};
|
||||
use glam::Vec3;
|
||||
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||
use kubi_shared::chunk::CHUNK_SIZE;
|
||||
use crate::{
|
||||
camera::Camera,
|
||||
player::MainPlayer,
|
||||
transform::Transform,
|
||||
prefabs::{
|
||||
ChunkShaderPrefab,
|
||||
BlockTexturesPrefab,
|
||||
ColoredShaderPrefab,
|
||||
},
|
||||
world::{
|
||||
ChunkStorage,
|
||||
ChunkMeshStorage,
|
||||
chunk::CHUNK_SIZE,
|
||||
}, settings::GameSettings,
|
||||
prefabs::GpuPrefabs,
|
||||
world::{ChunkMeshStorage, ChunkStorage},
|
||||
};
|
||||
use super::{RenderTarget, primitives::cube::CubePrimitive};
|
||||
use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, RenderCtx, Renderer};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct 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);
|
||||
mod pipeline;
|
||||
mod vertex;
|
||||
pub use vertex::ChunkVertex;
|
||||
|
||||
#[derive(Unique)]
|
||||
pub struct TransChunkQueue(pub Vec<IVec3>);
|
||||
|
||||
pub fn init_trans_chunk_queue(storages: AllStoragesView) {
|
||||
storages.add_unique(TransChunkQueue(Vec::with_capacity(512)));
|
||||
pub struct WorldRenderState {
|
||||
pub pipeline: wgpu::RenderPipeline,
|
||||
pub pipeline_trans: wgpu::RenderPipeline,
|
||||
pub trans_bundle: Option<wgpu::RenderBundle>,
|
||||
}
|
||||
|
||||
fn draw_params(settings: &GameSettings) -> DrawParameters {
|
||||
DrawParameters {
|
||||
depth: Depth {
|
||||
test: DepthTest::IfLess,
|
||||
write: true,
|
||||
..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 init_world_render_state(storages: AllStoragesView) {
|
||||
let (pipeline, pipeline_trans) = storages.run(pipeline::init_world_pipeline);
|
||||
storages.add_unique(WorldRenderState {
|
||||
pipeline, pipeline_trans,
|
||||
trans_bundle: None,
|
||||
})
|
||||
}
|
||||
|
||||
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>,
|
||||
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
|
||||
program: NonSendSync<UniqueView<ChunkShaderPrefab>>,
|
||||
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
|
||||
transform: View<Transform>,
|
||||
camera: View<Camera>,
|
||||
settings: UniqueView<GameSettings>,
|
||||
mut trans_queue: UniqueViewMut<TransChunkQueue>,
|
||||
//settings: UniqueView<GameSettings>,
|
||||
) {
|
||||
// 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 view = camera.view_matrix.to_cols_array_2d();
|
||||
let perspective = camera.perspective_matrix.to_cols_array_2d();
|
||||
|
||||
let draw_parameters = draw_params(&settings);
|
||||
let texture_sampler = texture_sampler(&texture.0, &settings);
|
||||
let mut render_pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
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 {
|
||||
if let Some(key) = chunk.mesh_index {
|
||||
let mesh = meshes.get(key).expect("Mesh index pointing to nothing");
|
||||
let world_position = position.as_vec3() * CHUNK_SIZE as f32;
|
||||
|
||||
//Skip mesh if its empty
|
||||
if mesh.index_buffer.len() == 0 && mesh.trans_index_buffer.len() == 0 {
|
||||
//Skip if mesh is empty
|
||||
if mesh.main.index.size() == 0 && mesh.trans.index.size() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
//Frustum culling
|
||||
{
|
||||
let minp = world_position;
|
||||
let maxp = world_position + Vec3::splat(CHUNK_SIZE as f32);
|
||||
if !camera.frustum.is_box_visible(minp, maxp) {
|
||||
continue
|
||||
}
|
||||
let minp = world_position;
|
||||
let maxp = world_position + Vec3::splat(CHUNK_SIZE as f32);
|
||||
if !camera.frustum.is_box_visible(minp, maxp) {
|
||||
continue
|
||||
}
|
||||
|
||||
//Draw chunk mesh
|
||||
if mesh.index_buffer.len() > 0 {
|
||||
target.0.draw(
|
||||
&mesh.vertex_buffer,
|
||||
&mesh.index_buffer,
|
||||
&program.0,
|
||||
&uniform! {
|
||||
position_offset: world_position.to_array(),
|
||||
view: view,
|
||||
perspective: perspective,
|
||||
tex: texture_sampler,
|
||||
discard_alpha: true,
|
||||
},
|
||||
&draw_parameters
|
||||
).unwrap();
|
||||
if mesh.main.index_len > 0 {
|
||||
render_pass.set_index_buffer(mesh.main.index.slice(..), wgpu::IndexFormat::Uint32);
|
||||
render_pass.set_vertex_buffer(0, mesh.main.vertex.slice(..));
|
||||
render_pass.draw_indexed(0..mesh.main.index_len, 0, 0..1);
|
||||
}
|
||||
|
||||
if mesh.trans_index_buffer.len() > 0 {
|
||||
trans_queue.0.push(position);
|
||||
//Draw transparent chunk mesh
|
||||
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);
|
||||
// trans_queue.0.sort_by_cached_key(|&pos| -(
|
||||
// (pos + HALF_CHUNK_SIZE).distance_squared(camera_position.as_ivec3())
|
||||
// ));
|
||||
drop(render_pass);
|
||||
|
||||
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(
|
||||
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
|
||||
chunks: UniqueView<ChunkStorage>,
|
||||
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
|
||||
program: NonSendSync<UniqueView<ChunkShaderPrefab>>,
|
||||
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
|
||||
camera: View<Camera>,
|
||||
settings: UniqueView<GameSettings>,
|
||||
mut trans_queue: UniqueViewMut<TransChunkQueue>,
|
||||
pub fn rpass_submit_trans_bundle(
|
||||
ctx: &mut RenderCtx,
|
||||
state: UniqueView<WorldRenderState>,
|
||||
depth: UniqueView<DepthTexture>,
|
||||
) {
|
||||
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 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") {
|
||||
let Some(bundle) = state.trans_bundle.as_ref() else {
|
||||
return
|
||||
}
|
||||
if !settings.debug_draw_current_chunk_border {
|
||||
return
|
||||
}
|
||||
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 (_, &player_transform) = (&player, &transforms).iter().next().expect("No player");
|
||||
let (_, _, player_position) = player_transform.0.to_scale_rotation_translation();
|
||||
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 {
|
||||
test: DepthTest::IfLess,
|
||||
..Default::default()
|
||||
};
|
||||
let mut rpass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("rpass_submit_trans_bundle"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: ctx.surface_view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
blend: Blend::alpha_blending(),
|
||||
..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();
|
||||
})],
|
||||
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.execute_bundles(Some(bundle));
|
||||
}
|
||||
|
|
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)]
|
||||
pub struct GameSettings {
|
||||
pub vsync: bool,
|
||||
// pub vsync: bool,
|
||||
pub fullscreen: Option<FullscreenSettings>,
|
||||
pub msaa: Option<u8>,
|
||||
pub max_anisotropy: Option<u16>,
|
||||
// pub msaa: Option<u8>,
|
||||
// pub max_anisotropy: Option<u16>,
|
||||
/// there's a 1 chunk border of loaded but invisible around this
|
||||
pub render_distance: u8,
|
||||
pub mouse_sensitivity: f32,
|
||||
|
@ -24,10 +24,10 @@ pub struct GameSettings {
|
|||
impl Default for GameSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
vsync: false,
|
||||
// vsync: false,
|
||||
fullscreen: None,
|
||||
msaa: Some(4),
|
||||
max_anisotropy: Some(16),
|
||||
// msaa: Some(4),
|
||||
// max_anisotropy: Some(16),
|
||||
render_distance: match true {
|
||||
cfg!(debug_assertions) => 5,
|
||||
cfg!(target_os = "android") => 6,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use hui::{color, element::{container::Container, text::Text, UiElementExt}, layout::Alignment, size};
|
||||
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(
|
||||
mut hui: NonSendSync<UniqueViewMut<UiState>>,
|
||||
size: UniqueView<WindowSize>,
|
||||
ren: UniqueView<Renderer>,
|
||||
chat: UniqueView<ChatHistory>,
|
||||
) {
|
||||
let messages = chat.get_messages();
|
||||
|
@ -39,5 +39,5 @@ pub fn render_chat(
|
|||
.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,
|
||||
loading_screen::loading_screen_base,
|
||||
networking::{ConnectionRejectionReason, ServerAddress},
|
||||
rendering::WindowSize,
|
||||
rendering::Renderer,
|
||||
state::{GameState, NextState}
|
||||
};
|
||||
|
||||
|
@ -14,7 +14,7 @@ fn render_connecting_ui(
|
|||
rejection: Option<UniqueView<ConnectionRejectionReason>>,
|
||||
join_state: UniqueView<ClientJoinState>,
|
||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||
size: UniqueView<WindowSize>,
|
||||
ren: UniqueView<Renderer>,
|
||||
) {
|
||||
let text = match (rejection, *join_state) {
|
||||
(Some(err), _) => {
|
||||
|
@ -32,7 +32,7 @@ fn render_connecting_ui(
|
|||
Text::new(text)
|
||||
.with_text_size(16)
|
||||
.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(
|
||||
|
|
|
@ -6,7 +6,7 @@ use hui::{
|
|||
size
|
||||
};
|
||||
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: &[u8] = &[
|
||||
|
@ -45,7 +45,7 @@ pub fn init_crosshair_image(storages: AllStoragesViewMut) {
|
|||
pub fn draw_crosshair(
|
||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||
crosshair: UniqueView<CrosshairImage>,
|
||||
size: UniqueView<WindowSize>,
|
||||
ren: UniqueView<Renderer>,
|
||||
player: View<MainPlayer>,
|
||||
raycast: View<LookingAtBlock>,
|
||||
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()
|
||||
.with_size(size!(100%))
|
||||
.with_align(Alignment::Center)
|
||||
|
@ -66,5 +69,5 @@ pub fn draw_crosshair(
|
|||
.with_size(size!((CROSSHAIR_SIZE * 2)))
|
||||
.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,
|
||||
},
|
||||
layout::{Alignment, Direction},
|
||||
frame_rect, size,
|
||||
rect_frame, size,
|
||||
};
|
||||
use shipyard::{UniqueView, UniqueViewMut, Workload, NonSendSync, IntoWorkload};
|
||||
use winit::keyboard::KeyCode;
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
|||
hui_integration::UiState,
|
||||
input::RawKbmInputState,
|
||||
networking::ServerAddress,
|
||||
rendering::WindowSize,
|
||||
rendering::Renderer,
|
||||
state::{GameState, NextState},
|
||||
world::ChunkStorage,
|
||||
};
|
||||
|
@ -28,7 +28,7 @@ pub fn loading_screen_base(bg_alpha: f32, xui: impl FnOnce(&mut ElementList)) ->
|
|||
.with_children(|ui| {
|
||||
Container::default()
|
||||
.with_size(size!(400, auto))
|
||||
.with_background(frame_rect! {
|
||||
.with_background(rect_frame! {
|
||||
color: (0.2, 0.2, 0.2),
|
||||
corner_radius: 8.
|
||||
})
|
||||
|
@ -43,7 +43,7 @@ fn render_loading_ui(
|
|||
addr: Option<UniqueView<ServerAddress>>,
|
||||
world: UniqueView<ChunkStorage>,
|
||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||
size: UniqueView<WindowSize>
|
||||
ren: UniqueView<Renderer>,
|
||||
) {
|
||||
let loaded = world.chunks.iter().fold(0, |acc, (&_, chunk)| {
|
||||
acc + chunk.desired_state.matches_current(chunk.current_state) as usize
|
||||
|
@ -63,11 +63,11 @@ fn render_loading_ui(
|
|||
ProgressBar::default()
|
||||
.with_value(value)
|
||||
.with_size(size!(100%, 15))
|
||||
.with_background(frame_rect! {
|
||||
.with_background(rect_frame! {
|
||||
color: (0.1, 0.1, 0.1),
|
||||
corner_radius: 2.
|
||||
})
|
||||
.with_foreground(frame_rect! {
|
||||
.with_foreground(rect_frame! {
|
||||
color: (0.4, 0.4, 1.0),
|
||||
corner_radius: 2.
|
||||
})
|
||||
|
@ -83,7 +83,7 @@ fn render_loading_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(
|
||||
|
|
|
@ -2,11 +2,11 @@ use hui::{
|
|||
element::{br::Break, container::Container, slider::Slider, text::Text, UiElementExt},
|
||||
layout::{Alignment, Direction},
|
||||
signal::Signal,
|
||||
frame_rect, size,
|
||||
rect_frame, size,
|
||||
};
|
||||
use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
|
||||
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)]
|
||||
enum SettingsSignal {
|
||||
|
@ -18,7 +18,7 @@ enum SettingsSignal {
|
|||
|
||||
pub fn render_settings_ui(
|
||||
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||
size: UniqueView<WindowSize>,
|
||||
ren: UniqueView<Renderer>,
|
||||
mut settings: UniqueViewMut<GameSettings>,
|
||||
kbd: UniqueView<RawKbmInputState>,
|
||||
) {
|
||||
|
@ -34,7 +34,7 @@ pub fn render_settings_ui(
|
|||
.with_align(Alignment::Center)
|
||||
.with_children(|ui| {
|
||||
Container::default()
|
||||
.with_background(frame_rect! {
|
||||
.with_background(rect_frame! {
|
||||
color: (0.2, 0.2, 0.2),
|
||||
corner_radius: 8.
|
||||
})
|
||||
|
@ -99,7 +99,7 @@ pub fn render_settings_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 {
|
||||
SettingsSignal::SetRenderDistance(value) => settings.render_distance = value,
|
||||
|
|
|
@ -102,6 +102,7 @@ impl ChunkMeshStorage {
|
|||
pub fn init_game_world(
|
||||
storages: AllStoragesView,
|
||||
) {
|
||||
log::info!("init_game_world called");
|
||||
storages.add_unique_non_send_sync(ChunkMeshStorage::new());
|
||||
storages.add_unique(ChunkStorage::new());
|
||||
storages.add_unique(ChunkTaskManager::new());
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
use glam::IVec3;
|
||||
use atomic::Atomic;
|
||||
use glium::{VertexBuffer, IndexBuffer};
|
||||
use kubi_shared::worldgen::AbortState;
|
||||
use crate::rendering::world::ChunkVertex;
|
||||
use crate::rendering::{world::ChunkVertex, BufferPair};
|
||||
|
||||
pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData};
|
||||
|
||||
|
@ -18,10 +17,8 @@ impl ChunkData {
|
|||
}
|
||||
|
||||
pub struct ChunkMesh {
|
||||
pub vertex_buffer: VertexBuffer<ChunkVertex>,
|
||||
pub index_buffer: IndexBuffer<u32>,
|
||||
pub trans_vertex_buffer: VertexBuffer<ChunkVertex>,
|
||||
pub trans_index_buffer: IndexBuffer<u32>,
|
||||
pub main: BufferPair,
|
||||
pub trans: BufferPair,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use std::sync::Arc;
|
||||
use atomic::{Atomic, Ordering};
|
||||
use glam::{IVec3, ivec3};
|
||||
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
||||
use kubi_shared::{networking::{channels::Channel, messages::ClientToServerMessage}, worldgen::AbortState};
|
||||
use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync, track};
|
||||
use uflow::SendMode;
|
||||
use wgpu::util::DeviceExt;
|
||||
use crate::{
|
||||
player::MainPlayer,
|
||||
transform::Transform,
|
||||
settings::GameSettings,
|
||||
rendering::Renderer,
|
||||
state::GameState,
|
||||
networking::UdpClient,
|
||||
player::MainPlayer,
|
||||
rendering::{world::ChunkVertex, BufferPair, Renderer},
|
||||
settings::GameSettings,
|
||||
state::GameState,
|
||||
transform::Transform,
|
||||
};
|
||||
use super::{
|
||||
ChunkStorage, ChunkMeshStorage,
|
||||
|
@ -266,7 +266,7 @@ fn process_completed_tasks(
|
|||
task_manager: UniqueView<ChunkTaskManager>,
|
||||
mut world: UniqueViewMut<ChunkStorage>,
|
||||
mut meshes: NonSendSync<UniqueViewMut<ChunkMeshStorage>>,
|
||||
renderer: NonSendSync<UniqueView<Renderer>>,
|
||||
renderer: UniqueView<Renderer>,
|
||||
state: UniqueView<GameState>,
|
||||
mut queue: UniqueViewMut<BlockUpdateQueue>,
|
||||
) {
|
||||
|
@ -327,12 +327,51 @@ fn process_completed_tasks(
|
|||
|
||||
//apply the mesh
|
||||
//TODO: Skip if mesh is empty? (i.e. set to None)
|
||||
let mesh = ChunkMesh {
|
||||
vertex_buffer: VertexBuffer::immutable(&renderer.display, &vertices).unwrap(),
|
||||
index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &indices).unwrap(),
|
||||
trans_vertex_buffer: VertexBuffer::immutable(&renderer.display, &trans_vertices).unwrap(),
|
||||
trans_index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &trans_indices).unwrap(),
|
||||
//TODO
|
||||
|
||||
let vtx_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("chunk_vertex_buffer"),
|
||||
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 {
|
||||
meshes.update(index, mesh).expect("Mesh update failed");
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use glam::{IVec3, ivec3};
|
||||
use glam::{ivec3, IVec3, Vec3};
|
||||
use strum::IntoEnumIterator;
|
||||
use kubi_shared::block::{Block, RenderType, Transparency};
|
||||
use crate::world::chunk::CHUNK_SIZE;
|
||||
|
@ -10,7 +10,7 @@ mod builder;
|
|||
use data::MeshGenData;
|
||||
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>),
|
||||
) {
|
||||
|
@ -32,8 +32,8 @@ pub fn generate_mesh(data: MeshGenData) -> (
|
|||
}
|
||||
};
|
||||
|
||||
let mut builder = MeshBuilder::new();
|
||||
let mut trans_builder = MeshBuilder::new();
|
||||
let mut builder = MeshBuilder::new_with_offset((position * CHUNK_SIZE as i32).as_vec3());
|
||||
let mut trans_builder = MeshBuilder::new_with_offset((position * CHUNK_SIZE as i32).as_vec3());
|
||||
|
||||
for x in 0..CHUNK_SIZE as i32 {
|
||||
for y in 0..CHUNK_SIZE as i32 {
|
||||
|
|
|
@ -1,176 +1,182 @@
|
|||
use strum::EnumIter;
|
||||
use glam::{Vec3, vec3, IVec3, ivec3};
|
||||
use std::f32::consts::FRAC_1_SQRT_2;
|
||||
use crate::rendering::world::ChunkVertex;
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Clone, Copy, Debug, EnumIter)]
|
||||
pub enum CubeFace {
|
||||
Top = 0,
|
||||
Front = 4,
|
||||
Left = 2,
|
||||
Right = 3,
|
||||
Back = 1,
|
||||
Bottom = 5,
|
||||
}
|
||||
impl CubeFace {
|
||||
pub const fn normal(self) -> IVec3 {
|
||||
CUBE_FACE_NORMALS_IVEC3[self as usize]
|
||||
}
|
||||
}
|
||||
|
||||
const CUBE_FACE_VERTICES: [[Vec3; 4]; 6] = [
|
||||
[vec3(0., 1., 0.), vec3(0., 1., 1.), vec3(1., 1., 0.), vec3(1., 1., 1.)],
|
||||
[vec3(0., 0., 0.), vec3(0., 1., 0.), vec3(1., 0., 0.), vec3(1., 1., 0.)],
|
||||
[vec3(0., 0., 1.), vec3(0., 1., 1.), vec3(0., 0., 0.), vec3(0., 1., 0.)],
|
||||
[vec3(1., 0., 0.), vec3(1., 1., 0.), vec3(1., 0., 1.), vec3(1., 1., 1.)],
|
||||
[vec3(1., 0., 1.), vec3(1., 1., 1.), vec3(0., 0., 1.), vec3(0., 1., 1.)],
|
||||
[vec3(0., 0., 1.), vec3(0., 0., 0.), vec3(1., 0., 1.), vec3(1., 0., 0.)],
|
||||
];
|
||||
const CUBE_FACE_NORMALS_IVEC3: [IVec3; 6] = [
|
||||
ivec3( 0, 1, 0),
|
||||
ivec3( 0, 0, -1),
|
||||
ivec3(-1, 0, 0),
|
||||
ivec3( 1, 0, 0),
|
||||
ivec3( 0, 0, 1),
|
||||
ivec3( 0, -1, 0)
|
||||
];
|
||||
const CUBE_FACE_NORMALS: [Vec3; 6] = [
|
||||
vec3(0., 1., 0.),
|
||||
vec3(0., 0., -1.),
|
||||
vec3(-1.,0., 0.),
|
||||
vec3(1., 0., 0.),
|
||||
vec3(0., 0., 1.),
|
||||
vec3(0., -1.,0.)
|
||||
];
|
||||
const CUBE_FACE_INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3];
|
||||
|
||||
#[repr(usize)]
|
||||
pub enum DiagonalFace {
|
||||
RigthZ = 0,
|
||||
LeftZ = 1,
|
||||
}
|
||||
const CROSS_FACES: [[Vec3; 4]; 2] = [
|
||||
[
|
||||
vec3(0., 0., 0.),
|
||||
vec3(0., 1., 0.),
|
||||
vec3(1., 0., 1.),
|
||||
vec3(1., 1., 1.),
|
||||
],
|
||||
[
|
||||
vec3(0., 0., 1.),
|
||||
vec3(0., 1., 1.),
|
||||
vec3(1., 0., 0.),
|
||||
vec3(1., 1., 0.),
|
||||
]
|
||||
];
|
||||
const CROSS_FACE_NORMALS: [Vec3; 2] = [
|
||||
vec3(-FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2),
|
||||
vec3( FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2),
|
||||
];
|
||||
const CROSS_FACE_NORMALS_BACK: [Vec3; 2] = [
|
||||
vec3( FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2),
|
||||
vec3(-FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2),
|
||||
];
|
||||
const CROSS_FACE_INDICES: [u32; 12] = [
|
||||
0, 1, 2, 2, 1, 3, //Front side
|
||||
6, 5, 4, 7, 5, 6, //Back side
|
||||
];
|
||||
|
||||
|
||||
const UV_COORDS: [[f32; 2]; 4] = [
|
||||
[0., 0.],
|
||||
[0., 1.],
|
||||
[1., 0.],
|
||||
[1., 1.],
|
||||
];
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MeshBuilder {
|
||||
vertex_buffer: Vec<ChunkVertex>,
|
||||
index_buffer: Vec<u32>,
|
||||
idx_counter: u32,
|
||||
}
|
||||
impl MeshBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn add_face(&mut self, face: CubeFace, coord: IVec3, texture: u8) {
|
||||
let coord = coord.as_vec3();
|
||||
let face_index = face as usize;
|
||||
|
||||
//Push vertices
|
||||
let norm = CUBE_FACE_NORMALS[face_index];
|
||||
let vert = CUBE_FACE_VERTICES[face_index];
|
||||
self.vertex_buffer.reserve(4);
|
||||
for i in 0..4 {
|
||||
self.vertex_buffer.push(ChunkVertex {
|
||||
position: (coord + vert[i]).to_array(),
|
||||
normal: norm.to_array(),
|
||||
uv: UV_COORDS[i],
|
||||
tex_index: texture
|
||||
});
|
||||
}
|
||||
|
||||
//Push indices
|
||||
self.index_buffer.extend_from_slice(&CUBE_FACE_INDICES.map(|x| x + self.idx_counter));
|
||||
|
||||
//Increment idx counter
|
||||
self.idx_counter += 4;
|
||||
}
|
||||
|
||||
pub fn add_diagonal_face(&mut self, coord: IVec3, face_type: DiagonalFace, front_texture: u8, back_texture: u8) {
|
||||
//Push vertices
|
||||
let face_type = face_type as usize;
|
||||
let vertices = CROSS_FACES[face_type];
|
||||
let normal_front = CROSS_FACE_NORMALS[face_type].to_array();
|
||||
let normal_back = CROSS_FACE_NORMALS_BACK[face_type].to_array();
|
||||
self.vertex_buffer.reserve(8);
|
||||
for i in 0..4 { //push front vertices
|
||||
self.vertex_buffer.push(ChunkVertex {
|
||||
position: (coord.as_vec3() + vertices[i]).to_array(),
|
||||
normal: normal_front,
|
||||
uv: UV_COORDS[i],
|
||||
tex_index: front_texture
|
||||
})
|
||||
}
|
||||
for i in 0..4 { //push back vertices
|
||||
self.vertex_buffer.push(ChunkVertex {
|
||||
position: (coord.as_vec3() + vertices[i]).to_array(),
|
||||
normal: normal_back,
|
||||
uv: UV_COORDS[i],
|
||||
tex_index: back_texture
|
||||
})
|
||||
}
|
||||
|
||||
//Push indices
|
||||
self.index_buffer.extend_from_slice(&CROSS_FACE_INDICES.map(|x| x + self.idx_counter));
|
||||
|
||||
//Increment idx counter
|
||||
self.idx_counter += 8;
|
||||
}
|
||||
|
||||
pub fn add_model(&mut self, position: Vec3, vertices: &[ChunkVertex], indices: Option<&[u32]>) {
|
||||
//push vertices
|
||||
self.vertex_buffer.extend(vertices.iter().map(|vertex| {
|
||||
let mut vertex = *vertex;
|
||||
vertex.position[0] += position.x;
|
||||
vertex.position[0] += position.y;
|
||||
vertex.position[0] += position.z;
|
||||
vertex
|
||||
}));
|
||||
//push indices
|
||||
if let Some(indices) = indices {
|
||||
self.index_buffer.extend(indices.iter().map(|x| x + self.idx_counter));
|
||||
} else {
|
||||
self.index_buffer.extend(0..(self.vertex_buffer.len() as u32));
|
||||
}
|
||||
//increment idx counter
|
||||
self.idx_counter += vertices.len() as u32;
|
||||
}
|
||||
|
||||
pub fn finish(self) -> (Vec<ChunkVertex>, Vec<u32>) {
|
||||
(self.vertex_buffer, self.index_buffer)
|
||||
}
|
||||
}
|
||||
use strum::EnumIter;
|
||||
use glam::{ivec3, vec3, IVec3, Vec3};
|
||||
use std::f32::consts::FRAC_1_SQRT_2;
|
||||
use crate::rendering::world::ChunkVertex;
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Clone, Copy, Debug, EnumIter)]
|
||||
pub enum CubeFace {
|
||||
Top = 0,
|
||||
Front = 4,
|
||||
Left = 2,
|
||||
Right = 3,
|
||||
Back = 1,
|
||||
Bottom = 5,
|
||||
}
|
||||
impl CubeFace {
|
||||
pub const fn normal(self) -> IVec3 {
|
||||
CUBE_FACE_NORMALS_IVEC3[self as usize]
|
||||
}
|
||||
}
|
||||
|
||||
const CUBE_FACE_VERTICES: [[Vec3; 4]; 6] = [
|
||||
[vec3(0., 1., 0.), vec3(0., 1., 1.), vec3(1., 1., 0.), vec3(1., 1., 1.)],
|
||||
[vec3(0., 0., 0.), vec3(0., 1., 0.), vec3(1., 0., 0.), vec3(1., 1., 0.)],
|
||||
[vec3(0., 0., 1.), vec3(0., 1., 1.), vec3(0., 0., 0.), vec3(0., 1., 0.)],
|
||||
[vec3(1., 0., 0.), vec3(1., 1., 0.), vec3(1., 0., 1.), vec3(1., 1., 1.)],
|
||||
[vec3(1., 0., 1.), vec3(1., 1., 1.), vec3(0., 0., 1.), vec3(0., 1., 1.)],
|
||||
[vec3(0., 0., 1.), vec3(0., 0., 0.), vec3(1., 0., 1.), vec3(1., 0., 0.)],
|
||||
];
|
||||
const CUBE_FACE_NORMALS_IVEC3: [IVec3; 6] = [
|
||||
ivec3( 0, 1, 0),
|
||||
ivec3( 0, 0, -1),
|
||||
ivec3(-1, 0, 0),
|
||||
ivec3( 1, 0, 0),
|
||||
ivec3( 0, 0, 1),
|
||||
ivec3( 0, -1, 0)
|
||||
];
|
||||
const CUBE_FACE_NORMALS: [Vec3; 6] = [
|
||||
vec3(0., 1., 0.),
|
||||
vec3(0., 0., -1.),
|
||||
vec3(-1.,0., 0.),
|
||||
vec3(1., 0., 0.),
|
||||
vec3(0., 0., 1.),
|
||||
vec3(0., -1.,0.)
|
||||
];
|
||||
const CUBE_FACE_INDICES: [u32; 6] = [0, 1, 2, 2, 1, 3];
|
||||
|
||||
#[repr(usize)]
|
||||
pub enum DiagonalFace {
|
||||
RigthZ = 0,
|
||||
LeftZ = 1,
|
||||
}
|
||||
const CROSS_FACES: [[Vec3; 4]; 2] = [
|
||||
[
|
||||
vec3(0., 0., 0.),
|
||||
vec3(0., 1., 0.),
|
||||
vec3(1., 0., 1.),
|
||||
vec3(1., 1., 1.),
|
||||
],
|
||||
[
|
||||
vec3(0., 0., 1.),
|
||||
vec3(0., 1., 1.),
|
||||
vec3(1., 0., 0.),
|
||||
vec3(1., 1., 0.),
|
||||
]
|
||||
];
|
||||
const CROSS_FACE_NORMALS: [Vec3; 2] = [
|
||||
vec3(-FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2),
|
||||
vec3( FRAC_1_SQRT_2, 0., FRAC_1_SQRT_2),
|
||||
];
|
||||
const CROSS_FACE_NORMALS_BACK: [Vec3; 2] = [
|
||||
vec3( FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2),
|
||||
vec3(-FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2),
|
||||
];
|
||||
const CROSS_FACE_INDICES: [u32; 12] = [
|
||||
0, 1, 2, 2, 1, 3, //Front side
|
||||
6, 5, 4, 7, 5, 6, //Back side
|
||||
];
|
||||
|
||||
|
||||
const UV_COORDS: [[f32; 2]; 4] = [
|
||||
[0., 1.],
|
||||
[0., 0.],
|
||||
[1., 1.],
|
||||
[1., 0.],
|
||||
];
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MeshBuilder {
|
||||
offset: Vec3,
|
||||
vertex_buffer: Vec<ChunkVertex>,
|
||||
index_buffer: Vec<u32>,
|
||||
idx_counter: u32,
|
||||
}
|
||||
impl MeshBuilder {
|
||||
pub fn new() -> Self {
|
||||
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) {
|
||||
let coord = coord.as_vec3();
|
||||
let face_index = face as usize;
|
||||
|
||||
//Push vertices
|
||||
let norm = CUBE_FACE_NORMALS[face_index];
|
||||
let vert = CUBE_FACE_VERTICES[face_index];
|
||||
self.vertex_buffer.reserve(4);
|
||||
for i in 0..4 {
|
||||
self.vertex_buffer.push(ChunkVertex {
|
||||
position: (coord + vert[i] + self.offset).to_array(),
|
||||
normal: norm.to_array(),
|
||||
uv: UV_COORDS[i],
|
||||
tex_index: texture as u32
|
||||
});
|
||||
}
|
||||
|
||||
//Push indices
|
||||
self.index_buffer.extend_from_slice(&CUBE_FACE_INDICES.map(|x| x + self.idx_counter));
|
||||
|
||||
//Increment idx counter
|
||||
self.idx_counter += 4;
|
||||
}
|
||||
|
||||
pub fn add_diagonal_face(&mut self, coord: IVec3, face_type: DiagonalFace, front_texture: u8, back_texture: u8) {
|
||||
//Push vertices
|
||||
let face_type = face_type as usize;
|
||||
let vertices = CROSS_FACES[face_type];
|
||||
let normal_front = CROSS_FACE_NORMALS[face_type].to_array();
|
||||
let normal_back = CROSS_FACE_NORMALS_BACK[face_type].to_array();
|
||||
self.vertex_buffer.reserve(8);
|
||||
for i in 0..4 { //push front vertices
|
||||
self.vertex_buffer.push(ChunkVertex {
|
||||
position: (coord.as_vec3() + vertices[i] + self.offset).to_array(),
|
||||
normal: normal_front,
|
||||
uv: UV_COORDS[i],
|
||||
tex_index: front_texture as u32
|
||||
})
|
||||
}
|
||||
for i in 0..4 { //push back vertices
|
||||
self.vertex_buffer.push(ChunkVertex {
|
||||
position: (coord.as_vec3() + vertices[i] + self.offset).to_array(),
|
||||
normal: normal_back,
|
||||
uv: UV_COORDS[i],
|
||||
tex_index: back_texture as u32
|
||||
})
|
||||
}
|
||||
|
||||
//Push indices
|
||||
self.index_buffer.extend_from_slice(&CROSS_FACE_INDICES.map(|x| x + self.idx_counter));
|
||||
|
||||
//Increment idx counter
|
||||
self.idx_counter += 8;
|
||||
}
|
||||
|
||||
//XXX: needs offset supprt
|
||||
// pub fn add_model(&mut self, position: Vec3, vertices: &[ChunkVertex], indices: Option<&[u32]>) {
|
||||
// //push vertices
|
||||
// self.vertex_buffer.extend(vertices.iter().map(|vertex| {
|
||||
// let mut vertex = *vertex;
|
||||
// vertex.position[0] += position.x;
|
||||
// vertex.position[0] += position.y;
|
||||
// vertex.position[0] += position.z;
|
||||
// vertex
|
||||
// }));
|
||||
// //push indices
|
||||
// if let Some(indices) = indices {
|
||||
// self.index_buffer.extend(indices.iter().map(|x| x + self.idx_counter));
|
||||
// } else {
|
||||
// self.index_buffer.extend(0..(self.vertex_buffer.len() as u32));
|
||||
// }
|
||||
// //increment idx counter
|
||||
// self.idx_counter += vertices.len() as u32;
|
||||
// }
|
||||
|
||||
pub fn finish(self) -> (Vec<ChunkVertex>, Vec<u32>) {
|
||||
(self.vertex_buffer, self.index_buffer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ impl ChunkTaskManager {
|
|||
let (
|
||||
(vertices, indices),
|
||||
(trans_vertices, trans_indices),
|
||||
) = generate_mesh(data);
|
||||
) = generate_mesh(position, data);
|
||||
ChunkTaskResponse::GeneratedMesh {
|
||||
position,
|
||||
vertices, indices,
|
||||
|
|
Loading…
Reference in a new issue