Merge pull request #21 from griffi-gh/wgpu2

wgpu rewrite (v2)
This commit is contained in:
griffi-gh 2024-05-13 23:57:28 +02:00 committed by GitHub
commit 0802e4806c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
67 changed files with 3077 additions and 1580 deletions

7
.gitignore vendored
View file

@ -27,3 +27,10 @@ _visualizer.json
*.so *.so
*.dylib *.dylib
*.rlib *.rlib
#but keep the dxcompiler.dll/dxil.dll
!dxcompiler.dll
!dxil.dll
# blender backup files
*.blend1

872
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -21,10 +21,13 @@ opt-level = 1
[profile.dev.package.uflow] [profile.dev.package.uflow]
opt-level = 3 opt-level = 3
[profile.dev.package.glium] [profile.dev.package.wgpu]
opt-level = 3 opt-level = 3
[profile.dev.package.bracket-noise] [profile.dev.package.wgpu-core]
opt-level = 3
[profile.dev.package.wgpu-hal]
opt-level = 3 opt-level = 3
[profile.dev.package.fastnoise-lite] [profile.dev.package.fastnoise-lite]
@ -38,5 +41,7 @@ opt-level = 3
[profile.dev.package.android-activity] [profile.dev.package.android-activity]
debug-assertions = false debug-assertions = false
[patch.crates-io] # [patch.'https://github.com/griffi-gh/hui']
glium = { git = "https://github.com/glium/glium", rev = "a352c667" } # hui = { path = "X:/Projects/hui/hui" }
# hui-winit = { path = "X:/Projects/hui/hui-winit" }
# hui-wgpu = { path = "X:/Projects/hui/hui-wgpu" }

Binary file not shown.

64
assets/playermodel1.obj Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View file

@ -9,7 +9,7 @@ pub fn init() {
use env_logger::{fmt::Color, Builder, Env}; use env_logger::{fmt::Color, Builder, Env};
let env = Env::default() let env = Env::default()
.filter_or("RUST_LOG", "trace,gilrs=warn,rusty_xinput=warn"); .filter_or("RUST_LOG", "trace,gilrs=warn,rusty_xinput=warn,wgpu=warn,wgpu_core=warn,wgpu_hal=warn,hui=info,hui-winit=info,hui-glium=info,hui-wgpu=info,naga=warn");
Builder::from_env(env) Builder::from_env(env)
.format(|buf, record| { .format(|buf, record| {
let mut level_style = buf.style(); let mut level_style = buf.style();

View file

@ -15,7 +15,7 @@ glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
hashbrown = "0.14" hashbrown = "0.14"
nohash-hasher = "0.2" nohash-hasher = "0.2"
anyhow = "1.0" anyhow = "1.0"
rayon = "1.7" rayon = "1.10"
flume = "0.11" flume = "0.11"
rand = "0.8" rand = "0.8"
uflow = "0.7" uflow = "0.7"

View file

@ -1,4 +1,3 @@
use fastnoise_lite::FastNoiseLite;
use glam::ivec3; use glam::ivec3;
use crate::{block::Block, chunk::CHUNK_SIZE, worldgen::SeedThingy}; use crate::{block::Block, chunk::CHUNK_SIZE, worldgen::SeedThingy};
use super::{ use super::{

View file

@ -11,21 +11,21 @@ crate-type = ["lib", "cdylib"]
[dependencies] [dependencies]
kubi-shared = { path = "../kubi-shared" } kubi-shared = { path = "../kubi-shared" }
kubi-logging = { path = "../kubi-logging" } kubi-logging = { path = "../kubi-logging" }
hui = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" } hui = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4" }
hui-glium = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" } hui-wgpu = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4" }
hui-winit = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" } hui-winit = { git = "https://github.com/griffi-gh/hui", rev = "6eb3d98ad4", features = ["winit_30"] }
log = "0.4" log = "0.4"
glium = { git = "https://github.com/glium/glium", rev = "a352c667" } wgpu = { version = "0.20", features = ["webgl"] }
glutin = "0.31" pollster = "0.3"
winit = { version = "0.29", features = ["android-native-activity"] } bytemuck = { version = "1.15", features = ["derive"] }
glutin-winit = "0.4" winit = { version = "0.30", features = ["android-native-activity"] }
raw-window-handle = "=0.5.*" raw-window-handle = "0.6"
glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] } glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
image = { version = "0.25", default_features = false, features = ["png"] } image = { version = "0.25", default_features = false, features = ["png"] }
strum = { version = "0.26", features = ["derive"] } strum = { version = "0.26", features = ["derive"] }
hashbrown = "0.14" hashbrown = "0.14"
nohash-hasher = "0.2" nohash-hasher = "0.2"
rayon = "1.7" rayon = "1.10"
shipyard = { git = "https://github.com/leudz/shipyard", rev = "aacf3b1df5", default-features = false, features = ["std", "proc", "thread_local"] } shipyard = { git = "https://github.com/leudz/shipyard", rev = "aacf3b1df5", default-features = false, features = ["std", "proc", "thread_local"] }
anyhow = "1.0" anyhow = "1.0"
flume = "0.11" flume = "0.11"
@ -38,10 +38,11 @@ tinyset = "0.4"
serde_json = { version = "1.0", optional = true } #only used for `generate_visualizer_data` serde_json = { version = "1.0", optional = true } #only used for `generate_visualizer_data`
rand = { version = "0.8", features = ["alloc", "small_rng"]} rand = { version = "0.8", features = ["alloc", "small_rng"]}
atomic = "0.6" atomic = "0.6"
tobj = "4.0"
[target.'cfg(target_os = "android")'.dependencies] [target.'cfg(target_os = "android")'.dependencies]
android-activity = "^0.5.2" android-activity = "0.6"
ndk = "0.8" ndk = "0.9"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["wincon"] } winapi = { version = "0.3", features = ["wincon"] }

14
kubi/shaders/c2d.wgsl Normal file
View 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;
}

View file

@ -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;
}

View file

@ -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.);
}

View file

@ -1,10 +0,0 @@
#version 300 es
precision highp float;
out vec4 out_color;
uniform vec4 color;
void main() {
out_color = color;
}

View file

@ -1,9 +0,0 @@
#version 300 es
precision highp float;
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}

View 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);
}

View 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);
}

View file

@ -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.;
}

View file

@ -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
View 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);
}

View file

@ -1,6 +1,6 @@
use glam::{Vec3, Mat4}; use glam::{Vec3, Mat4};
use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator}; use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator};
use crate::{transform::Transform, rendering::WindowSize, events::WindowResizedEvent}; use crate::{transform::Transform, rendering::Renderer, events::WindowResizedEvent};
use super::Camera; use super::Camera;
//maybe parallelize these two? //maybe parallelize these two?
@ -18,12 +18,13 @@ fn update_view_matrix(
fn update_perspective_matrix( fn update_perspective_matrix(
mut vm_camera: ViewMut<Camera>, mut vm_camera: ViewMut<Camera>,
size: UniqueView<WindowSize>, ren: UniqueView<Renderer>,
) { ) {
let sz = ren.size_vec2();
for mut camera in (&mut vm_camera).iter() { for mut camera in (&mut vm_camera).iter() {
camera.perspective_matrix = Mat4::perspective_rh_gl( camera.perspective_matrix = Mat4::perspective_rh(
camera.fov, camera.fov,
size.0.x as f32 / size.0.y as f32, sz.x / sz.y,
camera.z_near, camera.z_near,
camera.z_far, camera.z_far,
) )

View file

@ -1,5 +1,5 @@
use kubi_shared::networking::client::ClientId; use kubi_shared::networking::client::ClientId;
use shipyard::{AllStoragesView, Unique, UniqueViewMut}; use shipyard::{AllStoragesView, Unique};
pub enum ChatMessage { pub enum ChatMessage {
PlayerMessage { PlayerMessage {

View file

@ -1,7 +1,7 @@
//TODO client-side physics //TODO client-side physics
//TODO move this to shared //TODO move this to shared
use glam::{vec3, IVec3, Mat4, Vec3, Vec3Swizzles}; use glam::{vec3, Mat4, Vec3, Vec3Swizzles};
use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, View, ViewMut}; use shipyard::{track, AllStoragesView, Component, IntoIter, Unique, UniqueView, ViewMut};
use kubi_shared::{block::{Block, CollisionType}, transform::Transform}; use kubi_shared::{block::{Block, CollisionType}, transform::Transform};
use crate::{delta_time::DeltaTime, world::ChunkStorage}; use crate::{delta_time::DeltaTime, world::ChunkStorage};

View file

@ -1,5 +1,5 @@
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View}; use shipyard::{AllStoragesView, IntoIter, Unique, UniqueView, UniqueViewMut, View};
use crate::{events::InputDeviceEvent, rendering::{Renderer, WindowSize}}; use crate::{events::InputDeviceEvent, rendering::Renderer};
use winit::{ use winit::{
dpi::PhysicalPosition, event::{DeviceEvent, ElementState, RawKeyEvent}, keyboard::{KeyCode, PhysicalKey}, window::CursorGrabMode dpi::PhysicalPosition, event::{DeviceEvent, ElementState, RawKeyEvent}, keyboard::{KeyCode, PhysicalKey}, window::CursorGrabMode
}; };
@ -9,14 +9,14 @@ pub struct CursorLock(pub bool);
pub fn update_cursor_lock_state( pub fn update_cursor_lock_state(
lock: UniqueView<CursorLock>, lock: UniqueView<CursorLock>,
display: NonSendSync<UniqueView<Renderer>> display: UniqueView<Renderer>
) { ) {
if cfg!(target_os = "android") { if cfg!(target_os = "android") {
return return
} }
if lock.is_inserted_or_modified() { if lock.is_inserted_or_modified() {
//TODO MIGRATION //TODO MIGRATION
let window = &display.window; let window = display.window();
window.set_cursor_grab(match lock.0 { window.set_cursor_grab(match lock.0 {
true => CursorGrabMode::Confined, true => CursorGrabMode::Confined,
false => CursorGrabMode::None, false => CursorGrabMode::None,
@ -41,8 +41,7 @@ pub fn lock_cursor_now(
pub fn debug_toggle_lock( pub fn debug_toggle_lock(
mut lock: UniqueViewMut<CursorLock>, mut lock: UniqueViewMut<CursorLock>,
device_events: View<InputDeviceEvent>, device_events: View<InputDeviceEvent>,
ren: NonSendSync<UniqueView<Renderer>>, ren: UniqueView<Renderer>,
size: UniqueView<WindowSize>,
) { ) {
for evt in device_events.iter() { for evt in device_events.iter() {
if let DeviceEvent::Key(RawKeyEvent { if let DeviceEvent::Key(RawKeyEvent {
@ -51,8 +50,8 @@ pub fn debug_toggle_lock(
}) = evt.event { }) = evt.event {
lock.0 = !lock.0; lock.0 = !lock.0;
if !lock.0 { if !lock.0 {
let center = PhysicalPosition::new(size.0.x as f64 / 2., size.0.y as f64 / 2.); let center = PhysicalPosition::new(ren.size().width as f64 / 2., ren.size().height as f64 / 2.);
let _ = ren.window.set_cursor_position(center); let _ = ren.window().set_cursor_position(center);
} }
} }
} }

View file

@ -41,7 +41,7 @@ pub fn process_winit_events(world: &mut World, event: &Event<()>) {
EventComponent, EventComponent,
InputDeviceEvent { InputDeviceEvent {
device_id: *device_id, device_id: *device_id,
event: DeviceEvent::Key(RawKeyEvent { event: DeviceEvent::Key(winit::event::RawKeyEvent {
physical_key: event.physical_key, physical_key: event.physical_key,
state: event.state, state: event.state,
}) })
@ -86,18 +86,18 @@ pub fn process_winit_events(world: &mut World, event: &Event<()>) {
} }
} }
pub fn initial_resize_event( // pub fn initial_resize_event(
mut storages: AllStoragesViewMut, // mut storages: AllStoragesViewMut,
) { // ) {
let (w, h) = { // let (w, h) = {
let renderer = storages.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap(); // let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
renderer.display.get_framebuffer_dimensions() // (renderer.size().width, renderer.size().height)
}; // };
storages.add_entity(( // storages.add_entity((
EventComponent, // EventComponent,
WindowResizedEvent(UVec2::new(w, h)) // WindowResizedEvent(UVec2::new(w, h))
)); // ));
} // }
pub fn clear_events( pub fn clear_events(
mut all_storages: AllStoragesViewMut, mut all_storages: AllStoragesViewMut,

View file

@ -1,21 +1,22 @@
use hui::UiInstance; use hui::UiInstance;
use hui_glium::GliumUiRenderer; use hui_wgpu::WgpuUiRenderer;
//use hui_glium::GliumUiRenderer;
use shipyard::{AllStoragesView, Unique, UniqueView, NonSendSync, UniqueViewMut}; use shipyard::{AllStoragesView, Unique, UniqueView, NonSendSync, UniqueViewMut};
use crate::rendering::{RenderTarget, Renderer, WindowSize}; use crate::rendering::{RenderCtx, Renderer};
#[derive(Unique)] #[derive(Unique)]
pub struct UiState { pub struct UiState {
pub hui: UiInstance, pub hui: UiInstance,
pub renderer: GliumUiRenderer pub renderer: WgpuUiRenderer,
} }
pub fn kubi_ui_init( pub fn kubi_ui_init(
storages: AllStoragesView storages: AllStoragesView
) { ) {
let renderer = storages.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap(); let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
storages.add_unique_non_send_sync(UiState { storages.add_unique_non_send_sync(UiState {
hui: UiInstance::new(), hui: UiInstance::new(),
renderer: GliumUiRenderer::new(&renderer.display), renderer: WgpuUiRenderer::new(renderer.device(), renderer.surface_config().format),
}); });
} }
@ -26,20 +27,20 @@ pub fn kubi_ui_begin(
} }
pub fn kubi_ui_end( pub fn kubi_ui_end(
mut ui: NonSendSync<UniqueViewMut<UiState>> mut ui: NonSendSync<UniqueViewMut<UiState>>,
renderer: UniqueView<Renderer>,
) { ) {
let ui: &mut UiState = &mut ui; let ui: &mut UiState = &mut ui;
let UiState { hui, renderer, .. } = ui; let UiState { hui, renderer: ui_renderer } = ui;
hui.end(); hui.end();
renderer.update(hui); ui_renderer.update(hui, renderer.queue(), renderer.device(), renderer.size_vec2());
} }
pub fn kubi_ui_draw( pub fn kubi_ui_draw(
ctx: &mut RenderCtx,
ui: NonSendSync<UniqueView<UiState>>, ui: NonSendSync<UniqueView<UiState>>,
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
size: UniqueView<WindowSize>
) { ) {
ui.renderer.draw(&mut target.0, size.0.as_vec2()); ui.renderer.draw(ctx.encoder, ctx.surface_view);
} }
pub fn hui_process_winit_events( pub fn hui_process_winit_events(

View file

@ -10,7 +10,7 @@ use nohash_hasher::BuildNoHashHasher;
use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync}; use shipyard::{AllStoragesView, Unique, View, IntoIter, UniqueViewMut, Workload, IntoWorkload, UniqueView, NonSendSync};
use crate::{ use crate::{
events::{InputDeviceEvent, TouchEvent}, events::{InputDeviceEvent, TouchEvent},
rendering::WindowSize rendering::Renderer,
}; };
#[derive(Unique, Clone, Copy, Default, Debug)] #[derive(Unique, Clone, Copy, Default, Debug)]
@ -211,10 +211,10 @@ fn update_input_state_gamepad (
fn update_input_state_touch ( fn update_input_state_touch (
touch_state: UniqueView<RawTouchState>, touch_state: UniqueView<RawTouchState>,
win_size: UniqueView<WindowSize>, renderer: UniqueView<Renderer>,
mut inputs: UniqueViewMut<Inputs>, mut inputs: UniqueViewMut<Inputs>,
) { ) {
let w = win_size.0.as_dvec2(); let w = renderer.size_uvec2().as_dvec2();
//Movement //Movement
if let Some(finger) = touch_state.query_area( if let Some(finger) = touch_state.query_area(

View file

@ -13,7 +13,7 @@
use shipyard::{ use shipyard::{
World, Workload, IntoWorkload, World, Workload, IntoWorkload,
UniqueView, UniqueViewMut, UniqueView, UniqueViewMut,
NonSendSync, WorkloadModificator, WorkloadModificator,
SystemModificator SystemModificator
}; };
use winit::{ use winit::{
@ -67,27 +67,10 @@ use player::{spawn_player, MainPlayer};
use prefabs::load_prefabs; use prefabs::load_prefabs;
use settings::{load_settings, GameSettings}; use settings::{load_settings, GameSettings};
use camera::compute_cameras; use camera::compute_cameras;
use events::{ use events::{clear_events, process_winit_events, player_actions::generate_move_events};
clear_events,
process_winit_events,
initial_resize_event,
player_actions::generate_move_events,
};
use input::{init_input, process_inputs}; use input::{init_input, process_inputs};
use player_controller::{debug_switch_ctl_type, update_player_controllers}; use player_controller::{debug_switch_ctl_type, update_player_controllers};
use rendering::{ use rendering::{BackgroundColor, Renderer, init_rendering, render_master, update_rendering_early, update_rendering_late};
Renderer,
RenderTarget,
BackgroundColor,
clear_background,
init_window_size,
update_window_size,
primitives::init_primitives,
world::{init_trans_chunk_queue, draw_world, draw_world_trans, draw_current_chunk_border},
selection_box::render_selection_box,
entities::render_entities,
sumberge::render_submerged_view,
};
use block_placement::update_block_placement; use block_placement::update_block_placement;
use delta_time::{DeltaTime, init_delta_time}; use delta_time::{DeltaTime, init_delta_time};
use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state}; use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state};
@ -95,7 +78,7 @@ use control_flow::{exit_on_esc, insert_control_flow_unique, RequestExit};
use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting}; use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting};
use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer}; use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
use init::initialize_from_args; use init::initialize_from_args;
use hui_integration::{kubi_ui_begin, kubi_ui_draw, kubi_ui_end, kubi_ui_init}; use hui_integration::{kubi_ui_begin, /*kubi_ui_draw,*/ kubi_ui_end, kubi_ui_init};
use loading_screen::update_loading_screen; use loading_screen::update_loading_screen;
use connecting_screen::update_connecting_screen; use connecting_screen::update_connecting_screen;
use fixed_timestamp::init_fixed_timestamp_storage; use fixed_timestamp::init_fixed_timestamp_storage;
@ -105,8 +88,7 @@ use chat_ui::render_chat;
use chat::init_chat_manager; use chat::init_chat_manager;
use crosshair_ui::{init_crosshair_image, draw_crosshair}; use crosshair_ui::{init_crosshair_image, draw_crosshair};
use settings_ui::render_settings_ui; use settings_ui::render_settings_ui;
use hui_integration::hui_process_winit_events;
use crate::hui_integration::hui_process_winit_events;
/// stuff required to init the renderer and other basic systems /// stuff required to init the renderer and other basic systems
fn pre_startup() -> Workload { fn pre_startup() -> Workload {
@ -118,11 +100,9 @@ fn pre_startup() -> Workload {
fn startup() -> Workload { fn startup() -> Workload {
( (
init_fixed_timestamp_storage, init_fixed_timestamp_storage,
initial_resize_event,
init_window_size,
kubi_ui_init, kubi_ui_init,
load_prefabs, load_prefabs,
init_primitives, init_rendering,
insert_lock_state, insert_lock_state,
init_state, init_state,
initialize_from_args, initialize_from_args,
@ -133,14 +113,13 @@ fn startup() -> Workload {
init_client_physics, init_client_physics,
init_chat_manager, init_chat_manager,
init_crosshair_image, init_crosshair_image,
init_trans_chunk_queue,
).into_sequential_workload() ).into_sequential_workload()
} }
fn update() -> Workload { fn update() -> Workload {
( (
update_rendering_early,
debug_toggle_lock, debug_toggle_lock,
update_window_size,
update_cursor_lock_state, update_cursor_lock_state,
process_inputs, process_inputs,
kubi_ui_begin, kubi_ui_begin,
@ -179,25 +158,26 @@ fn update() -> Workload {
update_state, update_state,
exit_on_esc, exit_on_esc,
disconnect_on_exit.run_if(is_multiplayer), disconnect_on_exit.run_if(is_multiplayer),
update_rendering_late,
).into_sequential_workload() ).into_sequential_workload()
} }
fn render() -> Workload { // fn render() -> Workload {
( // (
clear_background, // clear_background,
( // (
draw_world, // draw_world,
draw_current_chunk_border, // draw_current_chunk_border,
render_selection_box, // render_selection_box,
render_entities, // render_entities,
draw_world_trans, // draw_world_trans,
render_submerged_view, // render_submerged_view,
).into_sequential_workload().run_if(is_ingame), // ).into_sequential_workload().run_if(is_ingame),
kubi_ui_draw, // kubi_ui_draw,
).into_sequential_workload() // ).into_sequential_workload()
} // }
fn after_frame_end() -> Workload { fn after_render() -> Workload {
( (
clear_events, clear_events,
).into_sequential_workload() ).into_sequential_workload()
@ -245,8 +225,8 @@ pub fn kubi_main(
world.add_workload(pre_startup); world.add_workload(pre_startup);
world.add_workload(startup); world.add_workload(startup);
world.add_workload(update); world.add_workload(update);
world.add_workload(render); //world.add_workload(render);
world.add_workload(after_frame_end); world.add_workload(after_render);
//Save _visualizer.json //Save _visualizer.json
#[cfg(feature = "generate_visualizer_data")] #[cfg(feature = "generate_visualizer_data")]
@ -286,7 +266,7 @@ pub fn kubi_main(
let settings = world.borrow::<UniqueView<GameSettings>>().unwrap(); let settings = world.borrow::<UniqueView<GameSettings>>().unwrap();
world.add_unique_non_send_sync(Renderer::init(window_target, &settings)); world.add_unique_non_send_sync(Renderer::init(window_target, &settings));
} }
world.add_unique(BackgroundColor(vec3(0.5, 0.5, 1.))); world.add_unique(BackgroundColor(vec3(0.21, 0.21, 1.)));
//Run startup systems //Run startup systems
world.run_workload(startup).unwrap(); world.run_workload(startup).unwrap();
@ -326,22 +306,24 @@ pub fn kubi_main(
//Run update workflows //Run update workflows
world.run_workload(update).unwrap(); world.run_workload(update).unwrap();
world.run(render_master);
//Start rendering (maybe use custom views for this?) //Start rendering (maybe use custom views for this?)
let target = { // let target = {
let renderer = world.borrow::<NonSendSync<UniqueView<Renderer>>>().unwrap(); // let renderer = world.borrow::<UniqueView<Renderer>>().unwrap();
renderer.display.draw() // renderer.display.draw()
}; // };
world.add_unique_non_send_sync(RenderTarget(target)); // world.add_unique_non_send_sync(RenderTarget(target));
//Run render workflow //Run render workflow
world.run_workload(render).unwrap(); //world.run_workload(render).unwrap();
//Finish rendering //Finish rendering
let target = world.remove_unique::<RenderTarget>().unwrap(); // let target = world.remove_unique::<RenderTarget>().unwrap();
target.0.finish().unwrap(); // target.0.finish().unwrap();
//After frame end //After frame end
world.run_workload(after_frame_end).unwrap(); world.run_workload(after_render).unwrap();
//Process control flow changes //Process control flow changes
if world.borrow::<UniqueView<RequestExit>>().unwrap().0 { if world.borrow::<UniqueView<RequestExit>>().unwrap().0 {

View file

@ -1,15 +1,35 @@
use std::{io::{BufReader, Read}, path::Path}; use std::{io::{BufReader, Read}, path::{Path, PathBuf}};
use bytemuck::{Pod, Zeroable};
use hui::text::FontHandle; use hui::text::FontHandle;
use shipyard::{AllStoragesView, NonSendSync, Unique, UniqueView, UniqueViewMut}; use shipyard::{AllStoragesView, NonSendSync, Unique, UniqueView, UniqueViewMut};
use glium::{texture::{SrgbTexture2dArray, MipmapsOption}, Program};
use kubi_shared::block::BlockTexture; use kubi_shared::block::BlockTexture;
use crate::{filesystem::AssetManager, hui_integration::UiState, rendering::Renderer}; use crate::{filesystem::AssetManager, hui_integration::UiState, rendering::{BufferPair, Renderer}};
mod texture; //TODO move to rendering module
mod shaders;
use texture::load_texture2darray_prefab; mod loader;
use shaders::include_shader_prefab; use loader::{load_texture2darray_prefab, load_texture2d_prefab, load_obj_prefab};
#[derive(Clone, Copy, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct ModelVertex {
pub tex_coords: [f32; 2],
pub position: [f32; 3],
pub _padding: u32,
pub normal: [f32; 3],
}
impl ModelVertex {
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<ModelVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![
0 => Float32x2,
1 => Float32x3,
2 => Float32x3,
],
};
}
pub trait AssetPaths { pub trait AssetPaths {
fn file_name(self) -> &'static str; fn file_name(self) -> &'static str;
@ -40,20 +60,15 @@ impl AssetPaths for BlockTexture {
} }
#[derive(Unique)] #[derive(Unique)]
#[repr(transparent)] pub struct GpuPrefabs {
pub struct BlockTexturesPrefab(pub SrgbTexture2dArray); pub block_diffuse_texture: wgpu::Texture,
pub block_diffuse_bind_group_layout: wgpu::BindGroupLayout,
#[derive(Unique)] pub block_diffuse_bind_group: wgpu::BindGroup,
#[repr(transparent)] pub player_model_diffuse_texture: wgpu::Texture,
pub struct ChunkShaderPrefab(pub Program); pub player_model_diffuse_bind_group_layout: wgpu::BindGroupLayout,
pub player_model_diffuse_bind_group: wgpu::BindGroup,
#[derive(Unique)] pub player_model: BufferPair,
#[repr(transparent)] }
pub struct ColoredShaderPrefab(pub Program);
#[derive(Unique)]
#[repr(transparent)]
pub struct Colored2ShaderPrefab(pub Program);
#[derive(Unique)] #[derive(Unique)]
#[repr(transparent)] #[repr(transparent)]
@ -61,19 +76,132 @@ pub struct UiFontPrefab(pub FontHandle);
pub fn load_prefabs( pub fn load_prefabs(
storages: AllStoragesView, storages: AllStoragesView,
renderer: NonSendSync<UniqueView<Renderer>>, renderer: UniqueView<Renderer>,
mut ui: NonSendSync<UniqueViewMut<UiState>>, mut ui: NonSendSync<UniqueViewMut<UiState>>,
assman: UniqueView<AssetManager> assman: UniqueView<AssetManager>
) { ) {
log::info!("Loading textures..."); log::info!("Loading textures...");
storages.add_unique_non_send_sync(BlockTexturesPrefab( let block_diffuse_texture = load_texture2darray_prefab::<BlockTexture>(
load_texture2darray_prefab::<BlockTexture, _>( &renderer,
&assman, &assman,
"blocks".into(), "blocks".into(),
&renderer.display, );
MipmapsOption::AutoGeneratedMipmaps
) log::info!("Creating bing groups");
)); let block_diffuse_view = block_diffuse_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("block_texture_view"),
..Default::default()
});
let block_diffuse_sampler = renderer.device().create_sampler(&wgpu::SamplerDescriptor {
label: Some("block_diffuse_sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
let block_diffuse_bind_group_layout = renderer.device()
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("block_diffuse_bind_group_layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2Array,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
}
]
});
let block_diffuse_bind_group = renderer.device().create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("block_diffuse_bind_group"),
layout: &block_diffuse_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&block_diffuse_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&block_diffuse_sampler),
}
]
});
let player_model_diffuse_texture = load_texture2d_prefab(&renderer, &assman, &PathBuf::from("playermodel1.png"));
let player_model_diffuse_view = player_model_diffuse_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("player_model_texture_view"),
..Default::default()
});
let player_model_diffuse_sampler = renderer.device().create_sampler(&wgpu::SamplerDescriptor {
label: Some("player_model_sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
let player_model_diffuse_bind_group_layout = renderer.device()
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("player_model_bind_group_layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
}
]
});
let player_model_diffuse_bind_group = renderer.device().create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("player_model_bind_group"),
layout: &player_model_diffuse_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&player_model_diffuse_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&player_model_diffuse_sampler),
}
]
});
let player_model = load_obj_prefab(&renderer, &assman, &PathBuf::from("playermodel1.obj"));
storages.add_unique_non_send_sync(GpuPrefabs {
block_diffuse_texture,
block_diffuse_bind_group_layout,
block_diffuse_bind_group,
player_model_diffuse_texture,
player_model_diffuse_bind_group_layout,
player_model_diffuse_bind_group,
player_model,
});
log::info!("Loading the UI stuff..."); log::info!("Loading the UI stuff...");
{ {
@ -85,33 +213,33 @@ pub fn load_prefabs(
storages.add_unique(UiFontPrefab(font_handle)); storages.add_unique(UiFontPrefab(font_handle));
} }
log::info!("Compiling shaders..."); //log::info!("Compiling shaders...");
storages.add_unique_non_send_sync(ChunkShaderPrefab( // storages.add_unique_non_send_sync(ChunkShaderPrefab(
include_shader_prefab!( // include_shader_prefab!(
"world", // "world",
"../shaders/world.vert", // "../shaders/world.vert",
"../shaders/world.frag", // "../shaders/world.frag",
&renderer.display // &renderer.display
) // )
)); // ));
storages.add_unique_non_send_sync(ColoredShaderPrefab( // storages.add_unique_non_send_sync(ColoredShaderPrefab(
include_shader_prefab!( // include_shader_prefab!(
"colored", // "colored",
"../shaders/colored.vert", // "../shaders/colored.vert",
"../shaders/colored.frag", // "../shaders/colored.frag",
&renderer.display // &renderer.display
) // )
)); // ));
storages.add_unique_non_send_sync(Colored2ShaderPrefab( // storages.add_unique_non_send_sync(Colored2ShaderPrefab(
include_shader_prefab!( // include_shader_prefab!(
"colored", // "colored",
"../shaders/colored2.vert", // "../shaders/colored2.vert",
"../shaders/colored2.frag", // "../shaders/colored2.frag",
&renderer.display // &renderer.display
) // )
)); // ));
log::info!("releasing shader compiler"); //log::info!("releasing shader compiler");
renderer.display.release_shader_compiler(); //renderer.display.release_shader_compiler();
} }

168
kubi/src/prefabs/loader.rs Normal file
View 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,
}
}

View file

@ -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;

View file

@ -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")
}

View file

@ -1,191 +1,106 @@
use std::num::NonZeroU32; use shipyard::{AllStoragesViewMut, IntoIter, IntoWorkload, SystemModificator, Unique, UniqueView, UniqueViewMut, View, Workload, WorkloadModificator};
use raw_window_handle::HasRawWindowHandle; use winit::dpi::PhysicalSize;
use shipyard::{Unique, NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, AllStoragesView}; use glam::Vec3;
use winit::{ use crate::{events::WindowResizedEvent, hui_integration::kubi_ui_draw, state::is_ingame};
event_loop::EventLoopWindowTarget,
window::{WindowBuilder, Fullscreen, Window},
dpi::PhysicalSize
};
use glium::{Display, Surface, Version, Api as GlApiTy};
use glutin::{
config::{Api, ConfigTemplateBuilder}, context::{ContextAttributesBuilder, GlProfile}, display::GetGlDisplay, prelude::*, surface::{SurfaceAttributesBuilder, WindowSurface}
};
use glutin_winit::DisplayBuilder;
use glam::{Vec3, UVec2};
use crate::{events::WindowResizedEvent, settings::{GameSettings, FullscreenMode}};
pub mod primitives; mod renderer;
mod primitives;
mod selection_box;
mod entities;
pub use renderer::Renderer;
pub mod background;
pub mod world; pub mod world;
pub mod selection_box; pub mod camera_uniform;
pub mod entities; pub mod depth;
pub mod sumberge; pub mod smoverlay;
pub struct BufferPair {
pub index: wgpu::Buffer,
pub index_len: u32,
pub vertex: wgpu::Buffer,
pub vertex_len: u32,
}
#[derive(Unique)] #[derive(Unique)]
#[repr(transparent)]
pub struct RenderTarget(pub glium::Frame);
#[derive(Unique)]
#[repr(transparent)]
pub struct BackgroundColor(pub Vec3); pub struct BackgroundColor(pub Vec3);
#[derive(Unique, Clone, Copy)] pub struct RenderCtx<'a> {
#[repr(transparent)] //pub renderer: &'a Renderer,
pub struct WindowSize(pub UVec2); pub encoder: &'a mut wgpu::CommandEncoder,
pub surface_view: &'a wgpu::TextureView,
#[derive(Unique)]
pub struct Renderer {
pub window: Window,
pub display: Display<WindowSurface>,
} }
impl Renderer { //TODO run init_world_render_state, init_selection_box_state, etc. only once ingame?
pub fn init(event_loop: &EventLoopWindowTarget<()>, settings: &GameSettings) -> Self {
log::info!("initializing display");
let wb = WindowBuilder::new() pub fn init_rendering() -> Workload {
.with_title("kubi") (
.with_maximized(true) depth::init_depth_texture,
.with_min_inner_size(PhysicalSize::new(640, 480)) camera_uniform::init_camera_uniform_buffer,
.with_fullscreen({ primitives::init_primitives,
//this has no effect on android, so skip this pointless stuff world::init_world_render_state, //req: depth, camera
#[cfg(target_os = "android")] { entities::init_entities_render_state, //req: depth, camera
None selection_box::init_selection_box_render_state, //req: depth, camera, primitives
} smoverlay::init_smoverlay_render_state, //req: primitives
#[cfg(not(target_os = "android"))] ).into_sequential_workload()
if let Some(fs_settings) = &settings.fullscreen { }
let monitor = event_loop.primary_monitor().or_else(|| {
event_loop.available_monitors().next() pub fn update_rendering_early() -> Workload {
}); (
if let Some(monitor) = monitor { resize_renderer,
log::info!("monitor: {}", monitor.name().unwrap_or_else(|| "generic".into())); depth::resize_depth_texture,
match fs_settings.mode { ).into_sequential_workload()
FullscreenMode::Borderless => { }
log::info!("starting in borderless fullscreen mode");
Some(Fullscreen::Borderless(Some(monitor))) pub fn update_rendering_late() -> Workload {
}, (
FullscreenMode::Exclusive => { camera_uniform::update_camera_uniform_buffer,
log::warn!("exclusive fullscreen mode is experimental"); (
log::info!("starting in exclusive fullscreen mode"); selection_box::update_selection_box_render_state,
//TODO: grabbing the first video mode is probably not the best idea... entities::update_entities_render_state,
monitor.video_modes().next() smoverlay::update_smoverlay_render_state,
.map(|vmode| { ).into_workload().run_if(is_ingame),
log::info!("video mode: {}", vmode.to_string()); ).into_workload()
Some(Fullscreen::Exclusive(vmode)) }
})
.unwrap_or_else(|| { pub fn render_master(storages: AllStoragesViewMut) {
log::warn!("no valid video modes found, falling back to windowed mode instead"); let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
None
}) let mut encoder = renderer.device().create_command_encoder(&wgpu::CommandEncoderDescriptor {
} label: Some("main_encoder"),
}
} else {
log::warn!("no monitors found, falling back to windowed mode");
None
}
} else {
log::info!("starting in windowed mode");
None
}
}); });
let surface_texture = renderer.surface().get_current_texture().unwrap();
let surface_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
let display_builder = DisplayBuilder::new() let mut data = RenderCtx {
.with_window_builder(Some(wb)); encoder: &mut encoder,
surface_view: &surface_view,
let config_template_builder = ConfigTemplateBuilder::new()
.with_api(Api::GLES3)
.prefer_hardware_accelerated(Some(true))
.with_depth_size(24)
.with_multisampling(settings.msaa.unwrap_or_default());
let (window, gl_config) = display_builder
.build(event_loop, config_template_builder, |mut configs| {
configs.next().unwrap()
})
.unwrap();
let window = window.expect("no window");
// Now we get the window size to use as the initial size of the Surface
let (width, height): (u32, u32) = window.inner_size().into();
let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
window.raw_window_handle(),
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
);
// Finally we can create a Surface, use it to make a PossiblyCurrentContext and create the glium Display
let surface = unsafe {
gl_config.display().create_window_surface(&gl_config, &attrs).unwrap()
}; };
let context_attributes = ContextAttributesBuilder::new() storages.run_with_data(background::clear_bg, &mut data);
.with_debug(cfg!(debug_assertions)) if storages.run(is_ingame) {
.with_context_api(glutin::context::ContextApi::Gles(None)) storages.run_with_data(world::draw_world, &mut data);
.with_profile(GlProfile::Core) storages.run_with_data(selection_box::draw_selection_box, &mut data);
.build(Some(window.raw_window_handle())); storages.run_with_data(entities::render_entities, &mut data);
storages.run_with_data(world::rpass_submit_trans_bundle, &mut data);
storages.run_with_data(smoverlay::render_submerged_view, &mut data);
}
storages.run_with_data(kubi_ui_draw, &mut data);
let current_context = unsafe { renderer.queue().submit([encoder.finish()]);
gl_config.display() surface_texture.present();
.create_context(&gl_config, &context_attributes) }
.expect("failed to create context")
}.make_current(&surface).unwrap();
let display = Display::from_context_surface(current_context, surface).unwrap(); /// Resize the renderer when the window is resized
pub fn resize_renderer(
//TODO MIGRATION mut renderer: UniqueViewMut<Renderer>,
// let cb = ContextBuilder::new() resize: View<WindowResizedEvent>,
// //.with_srgb(false) ) {
// .with_depth_buffer(24) if let Some(size) = resize.iter().last() {
// .with_multisampling(settings.msaa.unwrap_or_default()) renderer.resize(PhysicalSize::new(size.0.x, size.0.y));
// .with_vsync(settings.vsync)
// .with_gl_profile(GlProfile::Core)
// .with_gl(GlRequest::Latest);
// let display = Display::new(wb, cb)
// .expect("Failed to create a glium Display");
log::info!("Vendor: {}", display.get_opengl_vendor_string());
log::info!("Renderer: {}", display.get_opengl_renderer_string());
log::info!("OpenGL: {}", display.get_opengl_version_string());
log::info!("Supports GLSL: {:?}", display.get_supported_glsl_version());
log::info!("Framebuffer dimensions: {:?}", display.get_framebuffer_dimensions());
if display.is_context_loss_possible() { log::warn!("OpenGL context loss possible") }
if display.is_robust() { log::warn!("OpenGL implementation is not robust") }
if display.is_debug() { log::info!("OpenGL context is in debug mode") }
assert!(display.is_glsl_version_supported(&Version(GlApiTy::GlEs, 3, 0)), "GLSL ES 3.0 is not supported");
Self { window, display }
} }
} }
pub fn clear_background( // pub fn if_resized (resize: View<WindowResizedEvent>,) -> bool {
mut target: NonSendSync<UniqueViewMut<RenderTarget>>, // resize.len() > 0
color: UniqueView<BackgroundColor>, // }
) {
target.0.clear_color_srgb_and_depth((color.0.x, color.0.y, color.0.z, 1.), 1.);
}
//not sure if this belongs here
pub fn init_window_size(
storages: AllStoragesView,
) {
let size = storages.borrow::<View<WindowResizedEvent>>().unwrap().iter().next().unwrap().0;
storages.add_unique(WindowSize(size))
}
pub fn update_window_size(
mut win_size: UniqueViewMut<WindowSize>,
resize: View<WindowResizedEvent>,
) {
if let Some(resize) = resize.iter().next() {
win_size.0 = resize.0;
}
}
pub fn if_resized (
resize: View<WindowResizedEvent>,
) -> bool {
resize.len() > 0
}

View 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()
});
}

View 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() });
}

View 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);
}

View file

@ -1,58 +1,67 @@
use shipyard::{NonSendSync, UniqueViewMut, UniqueView, View, IntoIter, IntoWithId}; use shipyard::{AllStoragesView, IntoIter, IntoWithId, Unique, UniqueView, View};
use glium::{DepthTest, Depth, PolygonMode, BackfaceCullingMode, DrawParameters, Surface, uniform};
use kubi_shared::{entity::Entity, transform::Transform}; use kubi_shared::{entity::Entity, transform::Transform};
use crate::{ use crate::{
prefabs::ColoredShaderPrefab, camera::Camera, prefabs::GpuPrefabs, settings::GameSettings
camera::Camera,
settings::GameSettings
};
use super::{
RenderTarget,
primitives::cube::CenteredCubePrimitive
}; };
use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, RenderCtx};
mod instance;
mod pipeline;
#[derive(Unique)]
pub struct EntitiesRenderState {
pub pipeline: wgpu::RenderPipeline,
pub instance_buffer: instance::InstanceBuffer,
}
pub fn init_entities_render_state(storages: AllStoragesView) {
storages.add_unique(EntitiesRenderState {
pipeline: storages.run(pipeline::init_entities_pipeline),
instance_buffer: storages.run(instance::create_instance_buffer),
});
}
pub use instance::update_instance_buffer as update_entities_render_state;
// TODO: entity models // TODO: entity models
pub fn render_entities( pub fn render_entities(
mut target: NonSendSync<UniqueViewMut<RenderTarget>>, ctx: &mut RenderCtx,
buffers: NonSendSync<UniqueView<CenteredCubePrimitive>>, state: UniqueView<EntitiesRenderState>,
program: NonSendSync<UniqueView<ColoredShaderPrefab>>, depth: UniqueView<DepthTexture>,
camera: View<Camera>, prefabs: UniqueView<GpuPrefabs>,
settings: UniqueView<GameSettings>, camera_ubo: UniqueView<CameraUniformBuffer>,
entities: View<Entity>,
transform: View<Transform>,
) { ) {
let (camera_id, camera) = camera.iter().with_id().next().expect("No cameras in the scene"); if state.instance_buffer.count == 0 {
return
let draw_parameters = DrawParameters {
depth: Depth {
test: DepthTest::IfLess,
write: true,
..Default::default()
},
multisampling: settings.msaa.is_some(),
polygon_mode: PolygonMode::Fill,
backface_culling: BackfaceCullingMode::CullClockwise,
..Default::default()
};
let view = camera.view_matrix.to_cols_array_2d();
let perspective = camera.perspective_matrix.to_cols_array_2d();
for (entity_id, (_, trans)) in (&entities, &transform).iter().with_id() {
//skip rendering camera holder (as the entity would block the view)
if entity_id == camera_id { continue }
//render entity
target.0.draw(
&buffers.0,
&buffers.1,
&program.0,
&uniform! {
color: [1.0, 1.0, 1.0, 1.0_f32],
model: trans.0.to_cols_array_2d(),
view: view,
perspective: perspective,
},
&draw_parameters
).unwrap();
} }
let mut rpass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("rpass_draw_entities"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: ctx.surface_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth.depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
rpass.set_pipeline(&state.pipeline);
rpass.set_bind_group(0, &prefabs.player_model_diffuse_bind_group, &[]);
rpass.set_bind_group(1, &camera_ubo.camera_bind_group, &[]);
rpass.set_vertex_buffer(0, prefabs.player_model.vertex.slice(..));
rpass.set_vertex_buffer(1, state.instance_buffer.buffer.slice(..));
rpass.set_index_buffer(prefabs.player_model.index.slice(..), wgpu::IndexFormat::Uint32);
rpass.draw_indexed(0..prefabs.player_model.index_len, 0, 0..state.instance_buffer.count);
} }

View 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)
);
}
}

View 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,
})
}

View file

@ -1,30 +1,42 @@
use shipyard::{Workload, IntoWorkload}; use bytemuck::{Pod, Zeroable};
use glium::implement_vertex; use shipyard::{IntoWorkload, Workload};
pub mod cube; mod cube;
pub mod rect; mod fstri;
pub mod stri; pub use cube::CubePrimitive;
pub use fstri::FstriPrimitive;
use cube::init_cube_primitive;
use rect::init_rect_primitive;
use stri::init_stri_primitive;
#[derive(Clone, Copy, Default)]
pub struct PositionOnlyVertex {
pub position: [f32; 3],
}
implement_vertex!(PositionOnlyVertex, position);
#[derive(Clone, Copy, Default)]
pub struct PositionOnlyVertex2d {
pub position: [f32; 2],
}
implement_vertex!(PositionOnlyVertex2d, position);
pub fn init_primitives() -> Workload { pub fn init_primitives() -> Workload {
( (
init_cube_primitive, cube::init_cube_primitive,
init_rect_primitive, fstri::init_fstri_primitive,
init_stri_primitive,
).into_workload() ).into_workload()
} }
#[derive(Clone, Copy, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct PrimitiveVertex {
pub position: [f32; 3],
}
impl PrimitiveVertex {
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<PrimitiveVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![0 => Float32x3],
};
}
#[derive(Clone, Copy, Default, Pod, Zeroable)]
#[repr(C, packed)]
pub struct PrimitiveVertex2 {
pub position: [f32; 2],
}
impl PrimitiveVertex2 {
pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<PrimitiveVertex2>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![0 => Float32x2],
};
}

View file

@ -1,85 +1,50 @@
use shipyard::{AllStoragesView, NonSendSync, UniqueView, Unique}; use shipyard::{AllStoragesView, Unique, UniqueView};
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType}; use wgpu::util::DeviceExt;
use crate::rendering::Renderer; use crate::rendering::{BufferPair, Renderer};
use super::PositionOnlyVertex; use super::PrimitiveVertex;
#[derive(Unique)] #[derive(Unique)]
pub struct CubePrimitive(pub VertexBuffer<PositionOnlyVertex>, pub IndexBuffer<u16>); pub struct CubePrimitive(pub BufferPair);
#[derive(Unique)] /// Vertices for a centered cube with a side length of 1
pub struct CenteredCubePrimitive(pub VertexBuffer<PositionOnlyVertex>, pub IndexBuffer<u16>); const CUBE_VERTICES: &[PrimitiveVertex] = &[
// front
PrimitiveVertex { position: [-0.5, -0.5, 0.5] },
PrimitiveVertex { position: [ 0.5, -0.5, 0.5] },
PrimitiveVertex { position: [ 0.5, 0.5, 0.5] },
PrimitiveVertex { position: [-0.5, 0.5, 0.5] },
// back
PrimitiveVertex { position: [-0.5, -0.5, -0.5] },
PrimitiveVertex { position: [ 0.5, -0.5, -0.5] },
PrimitiveVertex { position: [ 0.5, 0.5, -0.5] },
PrimitiveVertex { position: [-0.5, 0.5, -0.5] },
];
const CENTERED_CUBE_VERTICES: &[PositionOnlyVertex] = &[ /// Indices for a cube primitive
// front
PositionOnlyVertex { position: [-0.5, -0.5, 0.5] },
PositionOnlyVertex { position: [ 0.5, -0.5, 0.5] },
PositionOnlyVertex { position: [ 0.5, 0.5, 0.5] },
PositionOnlyVertex { position: [-0.5, 0.5, 0.5] },
// back
PositionOnlyVertex { position: [-0.5, -0.5, -0.5] },
PositionOnlyVertex { position: [ 0.5, -0.5, -0.5] },
PositionOnlyVertex { position: [ 0.5, 0.5, -0.5] },
PositionOnlyVertex { position: [-0.5, 0.5, -0.5] },
];
const CUBE_VERTICES: &[PositionOnlyVertex] = &[
// front
PositionOnlyVertex { position: [0.0, 0.0, 1.0] },
PositionOnlyVertex { position: [1.0, 0.0, 1.0] },
PositionOnlyVertex { position: [1.0, 1.0, 1.0] },
PositionOnlyVertex { position: [0.0, 1.0, 1.0] },
// back
PositionOnlyVertex { position: [0.0, 0.0, 0.0] },
PositionOnlyVertex { position: [1.0, 0.0, 0.0] },
PositionOnlyVertex { position: [1.0, 1.0, 0.0] },
PositionOnlyVertex { position: [0.0, 1.0, 0.0] },
];
const CUBE_INDICES: &[u16] = &[ const CUBE_INDICES: &[u16] = &[
// front 0, 1, 2, 2, 3, 0, // front
0, 1, 2, 1, 5, 6, 6, 2, 1, // right
2, 3, 0, 7, 6, 5, 5, 4, 7, // back
// right 4, 0, 3, 3, 7, 4, // left
1, 5, 6, 4, 5, 1, 1, 0, 4, // bottom
6, 2, 1, 3, 2, 6, 6, 7, 3, // top
// back
7, 6, 5,
5, 4, 7,
// left
4, 0, 3,
3, 7, 4,
// bottom
4, 5, 1,
1, 0, 4,
// top
3, 2, 6,
6, 7, 3
]; ];
pub(super) fn init_cube_primitive( pub fn init_cube_primitive(storages: AllStoragesView) {
storages: AllStoragesView, log::info!("init_cube_primitive");
display: NonSendSync<UniqueView<Renderer>> let renderer = storages.borrow::<UniqueView<Renderer>>().unwrap();
) { storages.add_unique(CubePrimitive(BufferPair {
{ index: renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
let vert = VertexBuffer::immutable( label: Some("cube_index_buffer"),
&display.display, contents: bytemuck::cast_slice(CUBE_INDICES),
CUBE_VERTICES usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
).unwrap(); }),
let index = IndexBuffer::immutable( index_len: CUBE_INDICES.len() as u32,
&display.display, vertex: renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
PrimitiveType::TrianglesList, label: Some("cube_vertex_buffer"),
CUBE_INDICES contents: bytemuck::cast_slice(CUBE_VERTICES),
).unwrap(); usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
storages.add_unique_non_send_sync(CubePrimitive(vert, index)); }),
} vertex_len: CUBE_VERTICES.len() as u32,
{ }));
let vert = VertexBuffer::immutable(
&display.display,
CENTERED_CUBE_VERTICES
).unwrap();
let index = IndexBuffer::immutable(
&display.display,
PrimitiveType::TrianglesList,
CUBE_INDICES
).unwrap();
storages.add_unique_non_send_sync(CenteredCubePrimitive(vert, index));
}
} }

View 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));
}

View file

@ -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));
}

View file

@ -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));
}

View 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
}
}

View file

@ -1,59 +1,65 @@
use glam::{Mat4, Vec3, Quat}; use shipyard::{AllStoragesView, IntoIter, Unique, UniqueView, View};
use shipyard::{View, IntoIter, NonSendSync, UniqueViewMut, UniqueView}; use wgpu::RenderPassDescriptor;
use glium::{ use crate::{player::MainPlayer, world::raycast::LookingAtBlock};
Surface, use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, primitives::CubePrimitive, RenderCtx};
DrawParameters,
BackfaceCullingMode,
Blend, Depth, DepthTest,
uniform,
};
use crate::{
world::raycast::LookingAtBlock,
camera::Camera,
prefabs::ColoredShaderPrefab
};
use super::{
RenderTarget,
primitives::cube::CubePrimitive,
};
const SMOL: f32 = 0.0025; mod pipeline;
mod uniform;
pub fn render_selection_box( use uniform::SelectionBoxUniform;
lookat: View<LookingAtBlock>,
camera: View<Camera>,
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
buffers: NonSendSync<UniqueView<CubePrimitive>>,
) {
let camera = camera.iter().next().unwrap();
let Some(lookat) = lookat.iter().next() else { return };
let Some(lookat) = lookat.0 else { return };
//Darken block #[derive(Unique)]
target.0.draw( pub struct SboxRenderState {
&buffers.0, pipeline: wgpu::RenderPipeline,
&buffers.1, uniform: SelectionBoxUniform,
&program.0, }
&uniform! {
color: [0., 0., 0., 0.5_f32], pub fn init_selection_box_render_state(storages: AllStoragesView) {
model: Mat4::from_scale_rotation_translation( let uniform = storages.run(uniform::init_selection_box_uniform);
Vec3::splat(1. + SMOL * 2.), let pipeline = storages.run_with_data(pipeline::init_selection_box_pipeline, &uniform);
Quat::default(), storages.add_unique(SboxRenderState { pipeline, uniform });
lookat.block_position.as_vec3() - Vec3::splat(SMOL) }
).to_cols_array_2d(),
perspective: camera.perspective_matrix.to_cols_array_2d(), pub use uniform::update_selection_box_uniform
view: camera.view_matrix.to_cols_array_2d(), as update_selection_box_render_state;
},
&DrawParameters { pub fn draw_selection_box(
backface_culling: BackfaceCullingMode::CullClockwise, ctx: &mut RenderCtx,
blend: Blend::alpha_blending(), state: UniqueView<SboxRenderState>,
depth: Depth { depth: UniqueView<DepthTexture>,
//this may be unreliable... unless scale is applied! hacky... cube: UniqueView<CubePrimitive>,
test: DepthTest::IfLessOrEqual, camera_ubo: UniqueView<CameraUniformBuffer>,
..Default::default() lookat: View<LookingAtBlock>,
}, player: View<MainPlayer>,
..Default::default() ) {
} let Some((LookingAtBlock(Some(_)), _)) = (&lookat, &player).iter().next() else {
).unwrap(); return
};
let mut rpass = ctx.encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("rpass_selection_box"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: ctx.surface_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth.depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
rpass.set_pipeline(&state.pipeline);
rpass.set_bind_group(0, &camera_ubo.camera_bind_group, &[]);
rpass.set_bind_group(1, &state.uniform.bind_group, &[]);
rpass.set_index_buffer(cube.0.index.slice(..), wgpu::IndexFormat::Uint16);
rpass.set_vertex_buffer(0, cube.0.vertex.slice(..));
rpass.draw_indexed(0..cube.0.index_len, 0, 0..1);
} }

View 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,
})
}

View 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,
}]),
);
};
}

View 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);
}

View 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,
})
}

View 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]),
);
}

View file

@ -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();
}

View file

@ -1,248 +1,161 @@
use glam::{ivec3, IVec3, Mat4, Quat, Vec3}; use glam::Vec3;
use shipyard::{track, AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View}; use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
use glium::{ use kubi_shared::chunk::CHUNK_SIZE;
draw_parameters::{
BackfaceCullingMode, Depth, DepthTest, PolygonMode
}, implement_vertex, uniform, uniforms::{
MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction
}, Blend, DrawParameters, Smooth, Surface
};
use crate::{ use crate::{
camera::Camera, camera::Camera,
player::MainPlayer, prefabs::GpuPrefabs,
transform::Transform, world::{ChunkMeshStorage, ChunkStorage},
prefabs::{
ChunkShaderPrefab,
BlockTexturesPrefab,
ColoredShaderPrefab,
},
world::{
ChunkStorage,
ChunkMeshStorage,
chunk::CHUNK_SIZE,
}, settings::GameSettings,
}; };
use super::{RenderTarget, primitives::cube::CubePrimitive}; use super::{camera_uniform::CameraUniformBuffer, depth::DepthTexture, RenderCtx, Renderer};
#[derive(Clone, Copy)] mod pipeline;
#[repr(C)] mod vertex;
pub struct ChunkVertex { pub use vertex::ChunkVertex;
pub position: [f32; 3],
pub normal: [f32; 3],
pub uv: [f32; 2],
pub tex_index: u8,
}
implement_vertex!(ChunkVertex, position, normal, uv, tex_index);
#[derive(Unique)] #[derive(Unique)]
pub struct TransChunkQueue(pub Vec<IVec3>); pub struct WorldRenderState {
pub pipeline: wgpu::RenderPipeline,
pub fn init_trans_chunk_queue(storages: AllStoragesView) { pub pipeline_trans: wgpu::RenderPipeline,
storages.add_unique(TransChunkQueue(Vec::with_capacity(512))); pub trans_bundle: Option<wgpu::RenderBundle>,
} }
fn draw_params(settings: &GameSettings) -> DrawParameters { pub fn init_world_render_state(storages: AllStoragesView) {
DrawParameters { let (pipeline, pipeline_trans) = storages.run(pipeline::init_world_pipeline);
depth: Depth { storages.add_unique(WorldRenderState {
test: DepthTest::IfLess, pipeline, pipeline_trans,
write: true, trans_bundle: None,
..Default::default()
},
multisampling: settings.msaa.is_some(),
polygon_mode: PolygonMode::Fill, //Change to Line for wireframe
backface_culling: BackfaceCullingMode::CullClockwise,
..Default::default()
}
}
fn texture_sampler<'a, T>(texture: &'a T, settings: &GameSettings) -> Sampler<'a, T> {
Sampler(texture, SamplerBehavior {
minify_filter: MinifySamplerFilter::LinearMipmapLinear,
magnify_filter: MagnifySamplerFilter::Nearest,
max_anisotropy: settings.max_anisotropy.unwrap_or_default(),
wrap_function: (SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp),
depth_texture_comparison: None,
}) })
} }
pub fn draw_world( pub fn draw_world(
mut target: NonSendSync<UniqueViewMut<RenderTarget>>, ctx: &mut RenderCtx,
mut state: UniqueViewMut<WorldRenderState>,
renderer: UniqueView<Renderer>,
camera_ubo: UniqueView<CameraUniformBuffer>,
depth: UniqueView<DepthTexture>,
textures: UniqueView<GpuPrefabs>,
camera: View<Camera>,
chunks: UniqueView<ChunkStorage>, chunks: UniqueView<ChunkStorage>,
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>, meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
program: NonSendSync<UniqueView<ChunkShaderPrefab>>, //settings: UniqueView<GameSettings>,
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
transform: View<Transform>,
camera: View<Camera>,
settings: UniqueView<GameSettings>,
mut trans_queue: UniqueViewMut<TransChunkQueue>,
) { ) {
// let (camera, transform) = (&camera, &transform).iter().next().expect("No cameras in the scene");
// let camera_position = transform.0.to_scale_rotation_translation().2;
let camera = camera.iter().next().expect("No cameras in the scene"); let camera = camera.iter().next().expect("No cameras in the scene");
let view = camera.view_matrix.to_cols_array_2d();
let perspective = camera.perspective_matrix.to_cols_array_2d();
let draw_parameters = draw_params(&settings); let mut render_pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
let texture_sampler = texture_sampler(&texture.0, &settings); label: Some("rpass_draw_world"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: ctx.surface_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth.depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
render_pass.set_pipeline(&state.pipeline);
render_pass.set_bind_group(0, &textures.block_diffuse_bind_group, &[]);
render_pass.set_bind_group(1, &camera_ubo.camera_bind_group, &[]);
let mut trans_bundle_used = false;
let mut trans_bundle = renderer.device().create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor {
label: Some("trans_bundle_encoder"),
color_formats: &[Some(renderer.surface_config().format)],
depth_stencil: Some(wgpu::RenderBundleDepthStencil {
format: depth.depth_texture.format(),
depth_read_only: true,
stencil_read_only: true,
}),
sample_count: 1,
multiview: None,
});
trans_bundle.set_pipeline(&state.pipeline_trans);
trans_bundle.set_bind_group(0, &textures.block_diffuse_bind_group, &[]);
trans_bundle.set_bind_group(1, &camera_ubo.camera_bind_group, &[]);
for (&position, chunk) in &chunks.chunks { for (&position, chunk) in &chunks.chunks {
if let Some(key) = chunk.mesh_index { if let Some(key) = chunk.mesh_index {
let mesh = meshes.get(key).expect("Mesh index pointing to nothing"); let mesh = meshes.get(key).expect("Mesh index pointing to nothing");
let world_position = position.as_vec3() * CHUNK_SIZE as f32; let world_position = position.as_vec3() * CHUNK_SIZE as f32;
//Skip mesh if its empty //Skip if mesh is empty
if mesh.index_buffer.len() == 0 && mesh.trans_index_buffer.len() == 0 { if mesh.main.index.size() == 0 && mesh.trans.index.size() == 0 {
continue continue
} }
//Frustum culling //Frustum culling
{
let minp = world_position; let minp = world_position;
let maxp = world_position + Vec3::splat(CHUNK_SIZE as f32); let maxp = world_position + Vec3::splat(CHUNK_SIZE as f32);
if !camera.frustum.is_box_visible(minp, maxp) { if !camera.frustum.is_box_visible(minp, maxp) {
continue continue
} }
}
//Draw chunk mesh //Draw chunk mesh
if mesh.index_buffer.len() > 0 { if mesh.main.index_len > 0 {
target.0.draw( render_pass.set_index_buffer(mesh.main.index.slice(..), wgpu::IndexFormat::Uint32);
&mesh.vertex_buffer, render_pass.set_vertex_buffer(0, mesh.main.vertex.slice(..));
&mesh.index_buffer, render_pass.draw_indexed(0..mesh.main.index_len, 0, 0..1);
&program.0,
&uniform! {
position_offset: world_position.to_array(),
view: view,
perspective: perspective,
tex: texture_sampler,
discard_alpha: true,
},
&draw_parameters
).unwrap();
} }
if mesh.trans_index_buffer.len() > 0 { //Draw transparent chunk mesh
trans_queue.0.push(position); if mesh.trans.index_len > 0 {
trans_bundle_used = true;
trans_bundle.set_index_buffer(mesh.trans.index.slice(..), wgpu::IndexFormat::Uint32);
trans_bundle.set_vertex_buffer(0, mesh.trans.vertex.slice(..));
trans_bundle.draw_indexed(0..mesh.trans.index_len, 0, 0..1);
} }
} }
} }
// const HALF_CHUNK_SIZE: IVec3 = IVec3::splat((CHUNK_SIZE >> 1) as i32); drop(render_pass);
// trans_queue.0.sort_by_cached_key(|&pos| -(
// (pos + HALF_CHUNK_SIZE).distance_squared(camera_position.as_ivec3()) if trans_bundle_used {
// )); let bundle = trans_bundle.finish(&wgpu::RenderBundleDescriptor {
label: Some("trans_bundle"),
});
state.trans_bundle = Some(bundle);
} else {
state.trans_bundle = None;
}
} }
pub fn draw_world_trans( pub fn rpass_submit_trans_bundle(
mut target: NonSendSync<UniqueViewMut<RenderTarget>>, ctx: &mut RenderCtx,
chunks: UniqueView<ChunkStorage>, state: UniqueView<WorldRenderState>,
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>, depth: UniqueView<DepthTexture>,
program: NonSendSync<UniqueView<ChunkShaderPrefab>>,
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
camera: View<Camera>,
settings: UniqueView<GameSettings>,
mut trans_queue: UniqueViewMut<TransChunkQueue>,
) { ) {
let camera = camera.iter().next().expect("No cameras in the scene"); let Some(bundle) = state.trans_bundle.as_ref() else {
let view = camera.view_matrix.to_cols_array_2d();
let perspective = camera.perspective_matrix.to_cols_array_2d();
let mut draw_parameters = draw_params(&settings);
draw_parameters.blend = Blend::alpha_blending();
draw_parameters.backface_culling = BackfaceCullingMode::CullingDisabled;
draw_parameters.smooth = Some(Smooth::Fastest);
let texture_sampler = texture_sampler(&texture.0, &settings);
for position in trans_queue.0.drain(..).rev() {
let world_position = position.as_vec3() * CHUNK_SIZE as f32;
let mesh_idx = chunks.chunks[&position].mesh_index.expect("No mesh index");
let mesh = meshes.get(mesh_idx).expect("Mesh index pointing to nothing");
target.0.draw(
&mesh.trans_vertex_buffer,
&mesh.trans_index_buffer,
&program.0,
&uniform! {
position_offset: world_position.to_array(),
view: view,
perspective: perspective,
tex: texture_sampler,
discard_alpha: false,
},
&draw_parameters
).unwrap();
}
}
pub fn draw_current_chunk_border(
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
player: View<MainPlayer>,
transforms: View<Transform, track::All>,
buffers: NonSendSync<UniqueView<CubePrimitive>>,
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
camera: View<Camera>,
settings: UniqueView<GameSettings>,
) {
if cfg!(target_os = "android") {
return return
} };
if !settings.debug_draw_current_chunk_border { let mut rpass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
return label: Some("rpass_submit_trans_bundle"),
} color_attachments: &[Some(wgpu::RenderPassColorAttachment {
let camera = camera.iter().next().expect("No cameras in the scene"); view: ctx.surface_view,
let view = camera.view_matrix.to_cols_array_2d(); resolve_target: None,
let perspective = camera.perspective_matrix.to_cols_array_2d(); ops: wgpu::Operations {
let (_, &player_transform) = (&player, &transforms).iter().next().expect("No player"); load: wgpu::LoadOp::Load,
let (_, _, player_position) = player_transform.0.to_scale_rotation_translation(); store: wgpu::StoreOp::Store,
let player_in_chunk = ivec3(
(player_position.x as i32).div_euclid(CHUNK_SIZE as i32),
(player_position.y as i32).div_euclid(CHUNK_SIZE as i32),
(player_position.z as i32).div_euclid(CHUNK_SIZE as i32),
);
let world_position = player_in_chunk.as_vec3() * CHUNK_SIZE as f32;
target.0.draw(
&buffers.0,
&buffers.1,
&program.0,
&uniform! {
model: Mat4::from_scale_rotation_translation(
Vec3::splat(CHUNK_SIZE as f32),
Quat::default(),
world_position
).to_cols_array_2d(),
color: [0.25f32; 4],
view: view,
perspective: perspective,
}, },
&DrawParameters { })],
depth: Depth { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
test: DepthTest::IfLess, view: &depth.depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default() ..Default::default()
}, });
blend: Blend::alpha_blending(), rpass.execute_bundles(Some(bundle));
..Default::default()
}
).unwrap();
target.0.draw(
&buffers.0,
&buffers.1,
&program.0,
&uniform! {
model: Mat4::from_scale_rotation_translation(
Vec3::splat(CHUNK_SIZE as f32),
Quat::default(),
world_position
).to_cols_array_2d(),
color: [0.0f32; 4],
view: view,
perspective: perspective,
},
&DrawParameters {
polygon_mode: PolygonMode::Point,
line_width: Some(2.),
point_size: Some(5.),
..Default::default()
}
).unwrap();
} }

View 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)
}

View 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,
],
};
}

View file

@ -11,10 +11,10 @@ pub struct FullscreenSettings {
#[derive(Unique)] #[derive(Unique)]
pub struct GameSettings { pub struct GameSettings {
pub vsync: bool, // pub vsync: bool,
pub fullscreen: Option<FullscreenSettings>, pub fullscreen: Option<FullscreenSettings>,
pub msaa: Option<u8>, // pub msaa: Option<u8>,
pub max_anisotropy: Option<u16>, // pub max_anisotropy: Option<u16>,
/// there's a 1 chunk border of loaded but invisible around this /// there's a 1 chunk border of loaded but invisible around this
pub render_distance: u8, pub render_distance: u8,
pub mouse_sensitivity: f32, pub mouse_sensitivity: f32,
@ -24,10 +24,10 @@ pub struct GameSettings {
impl Default for GameSettings { impl Default for GameSettings {
fn default() -> Self { fn default() -> Self {
Self { Self {
vsync: false, // vsync: false,
fullscreen: None, fullscreen: None,
msaa: Some(4), // msaa: Some(4),
max_anisotropy: Some(16), // max_anisotropy: Some(16),
render_distance: match true { render_distance: match true {
cfg!(debug_assertions) => 5, cfg!(debug_assertions) => 5,
cfg!(target_os = "android") => 6, cfg!(target_os = "android") => 6,

View file

@ -1,10 +1,10 @@
use hui::{color, element::{container::Container, text::Text, UiElementExt}, layout::Alignment, size}; use hui::{color, element::{container::Container, text::Text, UiElementExt}, layout::Alignment, size};
use shipyard::{NonSendSync, UniqueView, UniqueViewMut}; use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
use crate::{chat::{ChatHistory, ChatMessage}, hui_integration::UiState, rendering::WindowSize}; use crate::{chat::{ChatHistory, ChatMessage}, hui_integration::UiState, rendering::Renderer};
pub fn render_chat( pub fn render_chat(
mut hui: NonSendSync<UniqueViewMut<UiState>>, mut hui: NonSendSync<UniqueViewMut<UiState>>,
size: UniqueView<WindowSize>, ren: UniqueView<Renderer>,
chat: UniqueView<ChatHistory>, chat: UniqueView<ChatHistory>,
) { ) {
let messages = chat.get_messages(); let messages = chat.get_messages();
@ -39,5 +39,5 @@ pub fn render_chat(
.add_child(ui); .add_child(ui);
} }
}) })
.add_root(&mut hui.hui, size.0.as_vec2()); .add_root(&mut hui.hui, ren.size_vec2());
} }

View file

@ -5,7 +5,7 @@ use crate::{
hui_integration::UiState, hui_integration::UiState,
loading_screen::loading_screen_base, loading_screen::loading_screen_base,
networking::{ConnectionRejectionReason, ServerAddress}, networking::{ConnectionRejectionReason, ServerAddress},
rendering::WindowSize, rendering::Renderer,
state::{GameState, NextState} state::{GameState, NextState}
}; };
@ -14,7 +14,7 @@ fn render_connecting_ui(
rejection: Option<UniqueView<ConnectionRejectionReason>>, rejection: Option<UniqueView<ConnectionRejectionReason>>,
join_state: UniqueView<ClientJoinState>, join_state: UniqueView<ClientJoinState>,
mut ui: NonSendSync<UniqueViewMut<UiState>>, mut ui: NonSendSync<UniqueViewMut<UiState>>,
size: UniqueView<WindowSize>, ren: UniqueView<Renderer>,
) { ) {
let text = match (rejection, *join_state) { let text = match (rejection, *join_state) {
(Some(err), _) => { (Some(err), _) => {
@ -32,7 +32,7 @@ fn render_connecting_ui(
Text::new(text) Text::new(text)
.with_text_size(16) .with_text_size(16)
.add_child(ui); .add_child(ui);
}).add_root(&mut ui.hui, size.0.as_vec2()) }).add_root(&mut ui.hui, ren.size_vec2())
} }
fn switch_to_loading_if_connected( fn switch_to_loading_if_connected(

View file

@ -6,7 +6,7 @@ use hui::{
size size
}; };
use shipyard::{AllStoragesViewMut, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View}; use shipyard::{AllStoragesViewMut, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
use crate::{hui_integration::UiState, player::MainPlayer, rendering::WindowSize, settings::GameSettings, world::raycast::LookingAtBlock}; use crate::{hui_integration::UiState, player::MainPlayer, rendering::Renderer, settings::GameSettings, world::raycast::LookingAtBlock};
const CROSSHAIR_SIZE: usize = 9; const CROSSHAIR_SIZE: usize = 9;
const CROSSHAIR: &[u8] = &[ const CROSSHAIR: &[u8] = &[
@ -45,7 +45,7 @@ pub fn init_crosshair_image(storages: AllStoragesViewMut) {
pub fn draw_crosshair( pub fn draw_crosshair(
mut ui: NonSendSync<UniqueViewMut<UiState>>, mut ui: NonSendSync<UniqueViewMut<UiState>>,
crosshair: UniqueView<CrosshairImage>, crosshair: UniqueView<CrosshairImage>,
size: UniqueView<WindowSize>, ren: UniqueView<Renderer>,
player: View<MainPlayer>, player: View<MainPlayer>,
raycast: View<LookingAtBlock>, raycast: View<LookingAtBlock>,
settings: UniqueView<GameSettings>, settings: UniqueView<GameSettings>,
@ -57,6 +57,9 @@ pub fn draw_crosshair(
} }
} }
let size = ren.size_uvec2();
let size_rounded = uvec2(size.x & !1, size.y & !1).as_vec2();
Container::default() Container::default()
.with_size(size!(100%)) .with_size(size!(100%))
.with_align(Alignment::Center) .with_align(Alignment::Center)
@ -66,5 +69,5 @@ pub fn draw_crosshair(
.with_size(size!((CROSSHAIR_SIZE * 2))) .with_size(size!((CROSSHAIR_SIZE * 2)))
.add_child(ui); .add_child(ui);
}) })
.add_root(&mut ui.hui, uvec2(size.0.x & !1, size.0.y & !1).as_vec2()); .add_root(&mut ui.hui, size_rounded);
} }

View file

@ -7,7 +7,7 @@ use hui::{
UiElementExt, UiElementExt,
}, },
layout::{Alignment, Direction}, layout::{Alignment, Direction},
frame_rect, size, rect_frame, size,
}; };
use shipyard::{UniqueView, UniqueViewMut, Workload, NonSendSync, IntoWorkload}; use shipyard::{UniqueView, UniqueViewMut, Workload, NonSendSync, IntoWorkload};
use winit::keyboard::KeyCode; use winit::keyboard::KeyCode;
@ -15,7 +15,7 @@ use crate::{
hui_integration::UiState, hui_integration::UiState,
input::RawKbmInputState, input::RawKbmInputState,
networking::ServerAddress, networking::ServerAddress,
rendering::WindowSize, rendering::Renderer,
state::{GameState, NextState}, state::{GameState, NextState},
world::ChunkStorage, world::ChunkStorage,
}; };
@ -28,7 +28,7 @@ pub fn loading_screen_base(bg_alpha: f32, xui: impl FnOnce(&mut ElementList)) ->
.with_children(|ui| { .with_children(|ui| {
Container::default() Container::default()
.with_size(size!(400, auto)) .with_size(size!(400, auto))
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0.2, 0.2, 0.2), color: (0.2, 0.2, 0.2),
corner_radius: 8. corner_radius: 8.
}) })
@ -43,7 +43,7 @@ fn render_loading_ui(
addr: Option<UniqueView<ServerAddress>>, addr: Option<UniqueView<ServerAddress>>,
world: UniqueView<ChunkStorage>, world: UniqueView<ChunkStorage>,
mut ui: NonSendSync<UniqueViewMut<UiState>>, mut ui: NonSendSync<UniqueViewMut<UiState>>,
size: UniqueView<WindowSize> ren: UniqueView<Renderer>,
) { ) {
let loaded = world.chunks.iter().fold(0, |acc, (&_, chunk)| { let loaded = world.chunks.iter().fold(0, |acc, (&_, chunk)| {
acc + chunk.desired_state.matches_current(chunk.current_state) as usize acc + chunk.desired_state.matches_current(chunk.current_state) as usize
@ -63,11 +63,11 @@ fn render_loading_ui(
ProgressBar::default() ProgressBar::default()
.with_value(value) .with_value(value)
.with_size(size!(100%, 15)) .with_size(size!(100%, 15))
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0.1, 0.1, 0.1), color: (0.1, 0.1, 0.1),
corner_radius: 2. corner_radius: 2.
}) })
.with_foreground(frame_rect! { .with_foreground(rect_frame! {
color: (0.4, 0.4, 1.0), color: (0.4, 0.4, 1.0),
corner_radius: 2. corner_radius: 2.
}) })
@ -83,7 +83,7 @@ fn render_loading_ui(
.add_child(ui) .add_child(ui)
}) })
.add_child(ui); .add_child(ui);
}).add_root(&mut ui.hui, size.0.as_vec2()); }).add_root(&mut ui.hui, ren.size_vec2());
} }
fn switch_to_ingame_if_loaded( fn switch_to_ingame_if_loaded(

View file

@ -2,11 +2,11 @@ use hui::{
element::{br::Break, container::Container, slider::Slider, text::Text, UiElementExt}, element::{br::Break, container::Container, slider::Slider, text::Text, UiElementExt},
layout::{Alignment, Direction}, layout::{Alignment, Direction},
signal::Signal, signal::Signal,
frame_rect, size, rect_frame, size,
}; };
use shipyard::{NonSendSync, UniqueView, UniqueViewMut}; use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
use winit::keyboard::KeyCode; use winit::keyboard::KeyCode;
use crate::{hui_integration::UiState, input::RawKbmInputState, rendering::WindowSize, settings::GameSettings}; use crate::{hui_integration::UiState, input::RawKbmInputState, rendering::Renderer, settings::GameSettings};
#[derive(Signal)] #[derive(Signal)]
enum SettingsSignal { enum SettingsSignal {
@ -18,7 +18,7 @@ enum SettingsSignal {
pub fn render_settings_ui( pub fn render_settings_ui(
mut ui: NonSendSync<UniqueViewMut<UiState>>, mut ui: NonSendSync<UniqueViewMut<UiState>>,
size: UniqueView<WindowSize>, ren: UniqueView<Renderer>,
mut settings: UniqueViewMut<GameSettings>, mut settings: UniqueViewMut<GameSettings>,
kbd: UniqueView<RawKbmInputState>, kbd: UniqueView<RawKbmInputState>,
) { ) {
@ -34,7 +34,7 @@ pub fn render_settings_ui(
.with_align(Alignment::Center) .with_align(Alignment::Center)
.with_children(|ui| { .with_children(|ui| {
Container::default() Container::default()
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0.2, 0.2, 0.2), color: (0.2, 0.2, 0.2),
corner_radius: 8. corner_radius: 8.
}) })
@ -99,7 +99,7 @@ pub fn render_settings_ui(
}) })
.add_child(ui); .add_child(ui);
}) })
.add_root(&mut ui.hui, size.0.as_vec2()); .add_root(&mut ui.hui, ren.size_vec2());
ui.hui.process_signals(|signal: SettingsSignal| match signal { ui.hui.process_signals(|signal: SettingsSignal| match signal {
SettingsSignal::SetRenderDistance(value) => settings.render_distance = value, SettingsSignal::SetRenderDistance(value) => settings.render_distance = value,

View file

@ -102,6 +102,7 @@ impl ChunkMeshStorage {
pub fn init_game_world( pub fn init_game_world(
storages: AllStoragesView, storages: AllStoragesView,
) { ) {
log::info!("init_game_world called");
storages.add_unique_non_send_sync(ChunkMeshStorage::new()); storages.add_unique_non_send_sync(ChunkMeshStorage::new());
storages.add_unique(ChunkStorage::new()); storages.add_unique(ChunkStorage::new());
storages.add_unique(ChunkTaskManager::new()); storages.add_unique(ChunkTaskManager::new());

View file

@ -1,9 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use glam::IVec3; use glam::IVec3;
use atomic::Atomic; use atomic::Atomic;
use glium::{VertexBuffer, IndexBuffer};
use kubi_shared::worldgen::AbortState; use kubi_shared::worldgen::AbortState;
use crate::rendering::world::ChunkVertex; use crate::rendering::{world::ChunkVertex, BufferPair};
pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData}; pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData};
@ -18,10 +17,8 @@ impl ChunkData {
} }
pub struct ChunkMesh { pub struct ChunkMesh {
pub vertex_buffer: VertexBuffer<ChunkVertex>, pub main: BufferPair,
pub index_buffer: IndexBuffer<u32>, pub trans: BufferPair,
pub trans_vertex_buffer: VertexBuffer<ChunkVertex>,
pub trans_index_buffer: IndexBuffer<u32>,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]

View file

@ -1,17 +1,17 @@
use std::sync::Arc; use std::sync::Arc;
use atomic::{Atomic, Ordering}; use atomic::{Atomic, Ordering};
use glam::{IVec3, ivec3}; use glam::{IVec3, ivec3};
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
use kubi_shared::{networking::{channels::Channel, messages::ClientToServerMessage}, worldgen::AbortState}; use kubi_shared::{networking::{channels::Channel, messages::ClientToServerMessage}, worldgen::AbortState};
use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync, track}; use shipyard::{View, UniqueView, UniqueViewMut, IntoIter, Workload, IntoWorkload, NonSendSync, track};
use uflow::SendMode; use uflow::SendMode;
use wgpu::util::DeviceExt;
use crate::{ use crate::{
player::MainPlayer,
transform::Transform,
settings::GameSettings,
rendering::Renderer,
state::GameState,
networking::UdpClient, networking::UdpClient,
player::MainPlayer,
rendering::{world::ChunkVertex, BufferPair, Renderer},
settings::GameSettings,
state::GameState,
transform::Transform,
}; };
use super::{ use super::{
ChunkStorage, ChunkMeshStorage, ChunkStorage, ChunkMeshStorage,
@ -266,7 +266,7 @@ fn process_completed_tasks(
task_manager: UniqueView<ChunkTaskManager>, task_manager: UniqueView<ChunkTaskManager>,
mut world: UniqueViewMut<ChunkStorage>, mut world: UniqueViewMut<ChunkStorage>,
mut meshes: NonSendSync<UniqueViewMut<ChunkMeshStorage>>, mut meshes: NonSendSync<UniqueViewMut<ChunkMeshStorage>>,
renderer: NonSendSync<UniqueView<Renderer>>, renderer: UniqueView<Renderer>,
state: UniqueView<GameState>, state: UniqueView<GameState>,
mut queue: UniqueViewMut<BlockUpdateQueue>, mut queue: UniqueViewMut<BlockUpdateQueue>,
) { ) {
@ -327,12 +327,51 @@ fn process_completed_tasks(
//apply the mesh //apply the mesh
//TODO: Skip if mesh is empty? (i.e. set to None) //TODO: Skip if mesh is empty? (i.e. set to None)
let mesh = ChunkMesh { //TODO
vertex_buffer: VertexBuffer::immutable(&renderer.display, &vertices).unwrap(),
index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &indices).unwrap(), let vtx_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
trans_vertex_buffer: VertexBuffer::immutable(&renderer.display, &trans_vertices).unwrap(), label: Some("chunk_vertex_buffer"),
trans_index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &trans_indices).unwrap(), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
});
let idx_buffer = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("chunk_vertex_buffer"),
contents: bytemuck::cast_slice(&indices),
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
});
let vtx_buffer_trans = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("chunk_trans_vertex_buffer"),
contents: bytemuck::cast_slice(&trans_vertices),
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
});
let idx_buffer_trans = renderer.device().create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("chunk_trans_index_buffer"),
contents: bytemuck::cast_slice(&trans_indices),
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::INDEX,
});
let main_buffer_pair = BufferPair {
vertex: vtx_buffer,
vertex_len: vertices.len() as u32,
index: idx_buffer,
index_len: indices.len() as u32,
}; };
let trans_buffer_pair = BufferPair {
vertex: vtx_buffer_trans,
vertex_len: trans_vertices.len() as u32,
index: idx_buffer_trans,
index_len: trans_indices.len() as u32,
};
let mesh = ChunkMesh {
main: main_buffer_pair,
trans: trans_buffer_pair,
};
if let Some(index) = chunk.mesh_index { if let Some(index) = chunk.mesh_index {
meshes.update(index, mesh).expect("Mesh update failed"); meshes.update(index, mesh).expect("Mesh update failed");
} else { } else {

View file

@ -1,4 +1,4 @@
use glam::{IVec3, ivec3}; use glam::{ivec3, IVec3, Vec3};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use kubi_shared::block::{Block, RenderType, Transparency}; use kubi_shared::block::{Block, RenderType, Transparency};
use crate::world::chunk::CHUNK_SIZE; use crate::world::chunk::CHUNK_SIZE;
@ -10,7 +10,7 @@ mod builder;
use data::MeshGenData; use data::MeshGenData;
use builder::{MeshBuilder, CubeFace, DiagonalFace}; use builder::{MeshBuilder, CubeFace, DiagonalFace};
pub fn generate_mesh(data: MeshGenData) -> ( pub fn generate_mesh(position: IVec3, data: MeshGenData) -> (
(Vec<ChunkVertex>, Vec<u32>), (Vec<ChunkVertex>, Vec<u32>),
(Vec<ChunkVertex>, Vec<u32>), (Vec<ChunkVertex>, Vec<u32>),
) { ) {
@ -32,8 +32,8 @@ pub fn generate_mesh(data: MeshGenData) -> (
} }
}; };
let mut builder = MeshBuilder::new(); let mut builder = MeshBuilder::new_with_offset((position * CHUNK_SIZE as i32).as_vec3());
let mut trans_builder = MeshBuilder::new(); let mut trans_builder = MeshBuilder::new_with_offset((position * CHUNK_SIZE as i32).as_vec3());
for x in 0..CHUNK_SIZE as i32 { for x in 0..CHUNK_SIZE as i32 {
for y in 0..CHUNK_SIZE as i32 { for y in 0..CHUNK_SIZE as i32 {

View file

@ -1,5 +1,5 @@
use strum::EnumIter; use strum::EnumIter;
use glam::{Vec3, vec3, IVec3, ivec3}; use glam::{ivec3, vec3, IVec3, Vec3};
use std::f32::consts::FRAC_1_SQRT_2; use std::f32::consts::FRAC_1_SQRT_2;
use crate::rendering::world::ChunkVertex; use crate::rendering::world::ChunkVertex;
@ -79,14 +79,15 @@ const CROSS_FACE_INDICES: [u32; 12] = [
const UV_COORDS: [[f32; 2]; 4] = [ const UV_COORDS: [[f32; 2]; 4] = [
[0., 0.],
[0., 1.], [0., 1.],
[1., 0.], [0., 0.],
[1., 1.], [1., 1.],
[1., 0.],
]; ];
#[derive(Default)] #[derive(Default)]
pub struct MeshBuilder { pub struct MeshBuilder {
offset: Vec3,
vertex_buffer: Vec<ChunkVertex>, vertex_buffer: Vec<ChunkVertex>,
index_buffer: Vec<u32>, index_buffer: Vec<u32>,
idx_counter: u32, idx_counter: u32,
@ -96,6 +97,10 @@ impl MeshBuilder {
Self::default() Self::default()
} }
pub fn new_with_offset(offset: Vec3) -> Self {
Self { offset, ..Self::new() }
}
pub fn add_face(&mut self, face: CubeFace, coord: IVec3, texture: u8) { pub fn add_face(&mut self, face: CubeFace, coord: IVec3, texture: u8) {
let coord = coord.as_vec3(); let coord = coord.as_vec3();
let face_index = face as usize; let face_index = face as usize;
@ -106,10 +111,10 @@ impl MeshBuilder {
self.vertex_buffer.reserve(4); self.vertex_buffer.reserve(4);
for i in 0..4 { for i in 0..4 {
self.vertex_buffer.push(ChunkVertex { self.vertex_buffer.push(ChunkVertex {
position: (coord + vert[i]).to_array(), position: (coord + vert[i] + self.offset).to_array(),
normal: norm.to_array(), normal: norm.to_array(),
uv: UV_COORDS[i], uv: UV_COORDS[i],
tex_index: texture tex_index: texture as u32
}); });
} }
@ -129,18 +134,18 @@ impl MeshBuilder {
self.vertex_buffer.reserve(8); self.vertex_buffer.reserve(8);
for i in 0..4 { //push front vertices for i in 0..4 { //push front vertices
self.vertex_buffer.push(ChunkVertex { self.vertex_buffer.push(ChunkVertex {
position: (coord.as_vec3() + vertices[i]).to_array(), position: (coord.as_vec3() + vertices[i] + self.offset).to_array(),
normal: normal_front, normal: normal_front,
uv: UV_COORDS[i], uv: UV_COORDS[i],
tex_index: front_texture tex_index: front_texture as u32
}) })
} }
for i in 0..4 { //push back vertices for i in 0..4 { //push back vertices
self.vertex_buffer.push(ChunkVertex { self.vertex_buffer.push(ChunkVertex {
position: (coord.as_vec3() + vertices[i]).to_array(), position: (coord.as_vec3() + vertices[i] + self.offset).to_array(),
normal: normal_back, normal: normal_back,
uv: UV_COORDS[i], uv: UV_COORDS[i],
tex_index: back_texture tex_index: back_texture as u32
}) })
} }
@ -151,24 +156,25 @@ impl MeshBuilder {
self.idx_counter += 8; self.idx_counter += 8;
} }
pub fn add_model(&mut self, position: Vec3, vertices: &[ChunkVertex], indices: Option<&[u32]>) { //XXX: needs offset supprt
//push vertices // pub fn add_model(&mut self, position: Vec3, vertices: &[ChunkVertex], indices: Option<&[u32]>) {
self.vertex_buffer.extend(vertices.iter().map(|vertex| { // //push vertices
let mut vertex = *vertex; // self.vertex_buffer.extend(vertices.iter().map(|vertex| {
vertex.position[0] += position.x; // let mut vertex = *vertex;
vertex.position[0] += position.y; // vertex.position[0] += position.x;
vertex.position[0] += position.z; // vertex.position[0] += position.y;
vertex // vertex.position[0] += position.z;
})); // vertex
//push indices // }));
if let Some(indices) = indices { // //push indices
self.index_buffer.extend(indices.iter().map(|x| x + self.idx_counter)); // if let Some(indices) = indices {
} else { // self.index_buffer.extend(indices.iter().map(|x| x + self.idx_counter));
self.index_buffer.extend(0..(self.vertex_buffer.len() as u32)); // } else {
} // self.index_buffer.extend(0..(self.vertex_buffer.len() as u32));
//increment idx counter // }
self.idx_counter += vertices.len() as u32; // //increment idx counter
} // self.idx_counter += vertices.len() as u32;
// }
pub fn finish(self) -> (Vec<ChunkVertex>, Vec<u32>) { pub fn finish(self) -> (Vec<ChunkVertex>, Vec<u32>) {
(self.vertex_buffer, self.index_buffer) (self.vertex_buffer, self.index_buffer)

View file

@ -63,7 +63,7 @@ impl ChunkTaskManager {
let ( let (
(vertices, indices), (vertices, indices),
(trans_vertices, trans_indices), (trans_vertices, trans_indices),
) = generate_mesh(data); ) = generate_mesh(position, data);
ChunkTaskResponse::GeneratedMesh { ChunkTaskResponse::GeneratedMesh {
position, position,
vertices, indices, vertices, indices,