summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock172
-rw-r--r--Cargo.toml2
-rw-r--r--assets/font/bold-italic.otfbin0 -> 11772 bytes
-rw-r--r--assets/font/bold.otfbin0 -> 11164 bytes
-rw-r--r--assets/font/info.txt2
-rw-r--r--assets/font/italic.otfbin0 -> 12100 bytes
-rw-r--r--assets/font/regular.otfbin0 -> 11016 bytes
-rw-r--r--src/gfx.rs89
-rw-r--r--src/gfx/camera.rs67
-rw-r--r--src/gfx/debug_menu.rs57
-rw-r--r--src/gfx/font.rs44
-rw-r--r--src/gfx/gpu.rs168
-rw-r--r--src/gfx/map.rs89
-rw-r--r--src/gfx/map/mesh.rs2
-rw-r--r--src/gfx/state.rs243
15 files changed, 617 insertions, 318 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a3511eb..132816f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -87,6 +87,15 @@ dependencies = [
]
[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -286,7 +295,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "283944cdecc44bf0b8dd010ec9af888d3b4f142844fdbe026c20ef68148d6fe7"
dependencies = [
- "approx",
+ "approx 0.3.2",
"num-traits",
"rand 0.6.5",
"serde",
@@ -317,7 +326,7 @@ version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6107f6be76c2269a9c8d89e707a66122bd3086f987fa508133a5f774e8ac4ced"
dependencies = [
- "approx",
+ "approx 0.3.2",
"bit-set",
"cgmath",
"num",
@@ -416,6 +425,30 @@ dependencies = [
]
[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
+dependencies = [
+ "autocfg 1.1.0",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset 0.8.0",
+ "scopeguard",
+]
+
+[[package]]
name = "crossbeam-utils"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -529,6 +562,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
name = "enumset"
version = "1.0.12"
source = "git+https://github.com/Lymia/enumset#d82528d19c052f3783529a178a960d61527aef71"
@@ -600,6 +639,12 @@ dependencies = [
]
[[package]]
+name = "fps_counter"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3aaba7ff514ee9d802b562927f80b1e94e93d8e74c31b134c9c3762dabf1a36b"
+
+[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -732,9 +777,9 @@ checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "glow"
-version = "0.12.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8edf6019dff2d92ad27c1e3ff82ad50a0aea5b01370353cc928bfdc33e95925c"
+checksum = "4e007a07a24de5ecae94160f141029e9a347282cfe25d1d58d85d845cf3130f1"
dependencies = [
"js-sys",
"slotmap",
@@ -743,6 +788,44 @@ dependencies = [
]
[[package]]
+name = "glyph_brush"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4edefd123f28a0b1d41ec4a489c2b43020b369180800977801611084f342978d"
+dependencies = [
+ "glyph_brush_draw_cache",
+ "glyph_brush_layout",
+ "ordered-float",
+ "rustc-hash",
+ "twox-hash",
+]
+
+[[package]]
+name = "glyph_brush_draw_cache"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6010675390f6889e09a21e2c8b575b3ee25667ea8237a8d59423f73cb8c28610"
+dependencies = [
+ "ab_glyph",
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "linked-hash-map",
+ "rayon",
+ "rustc-hash",
+]
+
+[[package]]
+name = "glyph_brush_layout"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc32c2334f00ca5ac3695c5009ae35da21da8c62d255b5b96d56e2597a637a38"
+dependencies = [
+ "ab_glyph",
+ "approx 0.5.1",
+ "xi-unicode",
+]
+
+[[package]]
name = "gpu-alloc"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -976,6 +1059,12 @@ dependencies = [
]
[[package]]
+name = "linked-hash-map"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+
+[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1034,6 +1123,15 @@ dependencies = [
]
[[package]]
+name = "memoffset"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+dependencies = [
+ "autocfg 1.1.0",
+]
+
+[[package]]
name = "metal"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1083,6 +1181,7 @@ dependencies = [
"collision",
"crossbeam-channel",
"fps-camera",
+ "fps_counter",
"futures",
"guillotiere",
"image",
@@ -1097,6 +1196,7 @@ dependencies = [
"threadpool",
"tokio",
"wgpu",
+ "wgpu_glyph",
"winit",
]
@@ -1214,7 +1314,7 @@ dependencies = [
"bitflags",
"cfg-if",
"libc",
- "memoffset",
+ "memoffset 0.6.5",
]
[[package]]
@@ -1227,7 +1327,7 @@ dependencies = [
"bitflags",
"cfg-if",
"libc",
- "memoffset",
+ "memoffset 0.6.5",
]
[[package]]
@@ -1455,6 +1555,15 @@ dependencies = [
]
[[package]]
+name = "ordered-float"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fc2dbde8f8a79f2102cc474ceb0ad68e3b80b85289ea62389b60e66777e4213"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "owned_ttf_parser"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1745,6 +1854,28 @@ dependencies = [
]
[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2167,6 +2298,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633"
[[package]]
+name = "twox-hash"
+version = "1.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
+dependencies = [
+ "cfg-if",
+ "rand 0.8.5",
+ "static_assertions",
+]
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2496,6 +2638,18 @@ dependencies = [
]
[[package]]
+name = "wgpu_glyph"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e25440d5f32ec39de49c57c15c2d3f9133a7939b069b5ad07e5afd8b78fb8adc"
+dependencies = [
+ "bytemuck",
+ "glyph_brush",
+ "log",
+ "wgpu",
+]
+
+[[package]]
name = "widestring"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2678,6 +2832,12 @@ dependencies = [
]
[[package]]
+name = "xi-unicode"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
+
+[[package]]
name = "xml-rs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index e93a015..e4d5af9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ cgmath = "0.17.0"
collision = "0.20.1"
crossbeam-channel = "0.5.8"
fps-camera = "0.1.2"
+fps_counter = "2.0.0"
futures = { version = "0.3.26" }
guillotiere = "0.6.2"
image = { version = "0.24.5", features = ["jpeg", "png", "bmp", "tga"], default-features = false }
@@ -29,4 +30,5 @@ srp = { git = "https://github.com/minetest-rust/PAKEs.git" }
threadpool = "1.8.1"
tokio = { version = "1.25.0", features = ["rt", "rt-multi-thread", "signal"] }
wgpu = "0.15.1"
+wgpu_glyph = "0.19.0"
winit = "0.28.1"
diff --git a/assets/font/bold-italic.otf b/assets/font/bold-italic.otf
new file mode 100644
index 0000000..1f74f38
--- /dev/null
+++ b/assets/font/bold-italic.otf
Binary files differ
diff --git a/assets/font/bold.otf b/assets/font/bold.otf
new file mode 100644
index 0000000..87b124c
--- /dev/null
+++ b/assets/font/bold.otf
Binary files differ
diff --git a/assets/font/info.txt b/assets/font/info.txt
new file mode 100644
index 0000000..5238883
--- /dev/null
+++ b/assets/font/info.txt
@@ -0,0 +1,2 @@
+license: Public Domain
+link: https://www.fontspace.com/minecraft-font-f28180 \ No newline at end of file
diff --git a/assets/font/italic.otf b/assets/font/italic.otf
new file mode 100644
index 0000000..6801bd8
--- /dev/null
+++ b/assets/font/italic.otf
Binary files differ
diff --git a/assets/font/regular.otf b/assets/font/regular.otf
new file mode 100644
index 0000000..54f08ad
--- /dev/null
+++ b/assets/font/regular.otf
Binary files differ
diff --git a/src/gfx.rs b/src/gfx.rs
index ce7ae2e..c465baa 100644
--- a/src/gfx.rs
+++ b/src/gfx.rs
@@ -8,9 +8,12 @@ use winit::{
platform::run_return::EventLoopExtRunReturn,
};
+mod camera;
+mod debug_menu;
+mod font;
+mod gpu;
mod map;
mod media;
-mod state;
mod util;
pub async fn run(
@@ -23,51 +26,80 @@ pub async fn run(
window.set_cursor_visible(false);
- let mut state = state::State::new(&window).await;
+ let mut gpu = gpu::Gpu::new(&window).await;
let mut map: Option<map::MapRender> = None;
+ let mut font = font::Font::new(&gpu);
+ let mut debug_menu = debug_menu::DebugMenu::new();
let mut media = media::MediaMgr::new();
+ let mut camera = camera::Camera::new(&gpu);
let mut nodedefs = None;
-
let mut last_frame = Instant::now();
-
+ let mut fps_counter = fps_counter::FPSCounter::new();
let mut game_paused = false;
- event_loop.run_return(move |event, _, flow| match event {
+ event_loop.run_return(|event, _, flow| match event {
MainEventsCleared => window.request_redraw(),
RedrawRequested(id) if id == window.id() => {
let now = Instant::now();
let dt = now - last_frame;
last_frame = now;
- state.update(dt);
+ debug_menu.fps = fps_counter.tick();
+ camera.update(&gpu, dt);
if let Some(map) = &mut map {
- map.update(&mut state);
+ map.update(&gpu);
}
net_events
.send(NetEvent::PlayerPos(
- state.camera.position.into(),
- Rad(state.camera.yaw).into(),
- Rad(state.camera.pitch).into(),
+ camera.first_person.position.into(),
+ Rad(camera.first_person.yaw).into(),
+ Rad(camera.first_person.pitch).into(),
))
.ok();
+ let mut render = || {
+ let size = (gpu.config.width as f32, gpu.config.height as f32);
+ let mut frame = gpu::Frame::new(&mut gpu)?;
+
+ {
+ let mut pass = frame.pass();
+ if let Some(map) = &mut map {
+ map.render(&camera, &mut debug_menu, &mut pass);
+ }
+ }
+
+ debug_menu.render(size, &camera, &mut font);
+ font.submit(&mut frame);
+
+ frame.finish();
+ font.cleanup();
+
+ Ok(())
+ };
+
use wgpu::SurfaceError::*;
- match state.render(&map) {
+ match render() {
Ok(_) => {}
- Err(Lost) => state.configure_surface(),
+ Err(Lost) => gpu.configure_surface(),
Err(OutOfMemory) => *flow = ExitWithCode(0),
Err(err) => eprintln!("gfx error: {err:?}"),
}
}
WindowEvent {
- ref event,
+ event,
window_id: id,
} if id == window.id() => match event {
CloseRequested => *flow = ExitWithCode(0),
- Resized(size) => state.resize(*size),
- ScaleFactorChanged { new_inner_size, .. } => state.resize(**new_inner_size),
+ Resized(size)
+ | ScaleFactorChanged {
+ new_inner_size: &mut size,
+ ..
+ } => {
+ gpu.resize(size);
+ camera.resize(size);
+ }
KeyboardInput {
input:
winit::event::KeyboardInput {
@@ -80,11 +112,15 @@ pub async fn run(
use fps_camera::Actions;
use winit::event::{ElementState, VirtualKeyCode as Key};
- if key == &Key::Escape && key_state == &ElementState::Pressed {
+ if key == Key::Escape && key_state == ElementState::Pressed {
game_paused = !game_paused;
window.set_cursor_visible(game_paused);
}
+ if key == Key::F3 && key_state == ElementState::Pressed {
+ debug_menu.enabled = !debug_menu.enabled;
+ }
+
if !game_paused {
let actions = match key {
Key::W => Actions::MOVE_FORWARD,
@@ -97,8 +133,8 @@ pub async fn run(
};
match key_state {
- ElementState::Pressed => state.camera.enable_actions(actions),
- ElementState::Released => state.camera.disable_action(actions),
+ ElementState::Pressed => camera.first_person.enable_actions(actions),
+ ElementState::Released => camera.first_person.disable_action(actions),
}
}
}
@@ -109,11 +145,13 @@ pub async fn run(
..
} => {
if !game_paused {
- state.camera.update_mouse(-delta.0 as f32, delta.1 as f32);
+ camera
+ .first_person
+ .update_mouse(-delta.0 as f32, delta.1 as f32);
window
.set_cursor_position(winit::dpi::PhysicalPosition::new(
- state.config.width / 2,
- state.config.height / 2,
+ gpu.config.width / 2,
+ gpu.config.height / 2,
))
.ok();
}
@@ -131,7 +169,8 @@ pub async fn run(
if finished {
map = Some(map::MapRender::new(
- &mut state,
+ &mut gpu,
+ &camera,
&media,
nodedefs.take().unwrap_or_default(),
));
@@ -140,9 +179,9 @@ pub async fn run(
}
}
PlayerPos(pos, pitch, yaw) => {
- state.camera.position = pos.into();
- state.camera.pitch = Rad::<f32>::from(pitch).0;
- state.camera.yaw = Rad::<f32>::from(yaw).0;
+ camera.first_person.position = pos.into();
+ camera.first_person.pitch = Rad::<f32>::from(pitch).0;
+ camera.first_person.yaw = Rad::<f32>::from(yaw).0;
}
},
_ => {}
diff --git a/src/gfx/camera.rs b/src/gfx/camera.rs
new file mode 100644
index 0000000..874c9fc
--- /dev/null
+++ b/src/gfx/camera.rs
@@ -0,0 +1,67 @@
+use super::{gpu::Gpu, util::MatrixUniform};
+use cgmath::{prelude::*, Deg, Matrix4, Rad};
+use collision::Frustum;
+use fps_camera::{FirstPerson, FirstPersonSettings};
+use std::time::Duration;
+
+pub struct Camera {
+ pub fov: Rad<f32>,
+ pub view: Matrix4<f32>,
+ pub proj: Matrix4<f32>,
+ pub frustum: Frustum<f32>,
+ pub first_person: FirstPerson,
+ pub uniform: MatrixUniform,
+ pub layout: wgpu::BindGroupLayout,
+}
+
+impl Camera {
+ pub fn new(gpu: &Gpu) -> Self {
+ let first_person = FirstPerson::new(
+ [0.0, 0.0, 0.0],
+ FirstPersonSettings {
+ speed_horizontal: 10.0,
+ speed_vertical: 10.0,
+ mouse_sensitivity_horizontal: 1.0,
+ mouse_sensitivity_vertical: 1.0,
+ },
+ );
+
+ let layout = MatrixUniform::layout(&gpu.device, "camera");
+ let uniform = MatrixUniform::new(&gpu.device, &layout, Matrix4::identity(), "camera", true);
+
+ Self {
+ fov: Deg(90.0).into(),
+ proj: Matrix4::identity(),
+ view: Matrix4::identity(),
+ frustum: Frustum::from_matrix4(Matrix4::identity()).unwrap(),
+ first_person,
+ uniform,
+ layout,
+ }
+ }
+
+ pub fn update(&mut self, gpu: &Gpu, dt: Duration) {
+ self.first_person.yaw += Rad::from(Deg(180.0)).0;
+ self.first_person.yaw *= -1.0;
+
+ let cam = self.first_person.camera(dt.as_secs_f32());
+
+ self.first_person.yaw *= -1.0;
+ self.first_person.yaw -= Rad::from(Deg(180.0)).0;
+
+ self.first_person.position = cam.position;
+
+ self.view = Matrix4::from(cam.orthogonal());
+ self.uniform.set(&gpu.queue, self.proj * self.view);
+ }
+
+ pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
+ self.proj = cgmath::perspective(
+ self.fov,
+ size.width as f32 / size.height as f32,
+ 0.1,
+ 100000.0,
+ );
+ self.frustum = Frustum::from_matrix4(self.proj).unwrap();
+ }
+}
diff --git a/src/gfx/debug_menu.rs b/src/gfx/debug_menu.rs
new file mode 100644
index 0000000..daca08d
--- /dev/null
+++ b/src/gfx/debug_menu.rs
@@ -0,0 +1,57 @@
+use super::{camera::Camera, font::Font};
+use cgmath::{Deg, Rad};
+use wgpu_glyph::{Section, Text};
+
+pub struct DebugMenu {
+ pub enabled: bool,
+ pub fps: usize,
+}
+
+impl DebugMenu {
+ pub fn new() -> Self {
+ Self {
+ enabled: false,
+ fps: 0,
+ }
+ }
+
+ pub fn render(&self, bounds: (f32, f32), camera: &Camera, font: &mut Font) {
+ if !self.enabled {
+ return;
+ }
+
+ let mut offset = 0.0;
+
+ let mut add_text = |txt: &str| {
+ offset += 2.0;
+
+ font.add(Section {
+ screen_position: (2.0, offset),
+ bounds,
+ text: vec![Text::new(txt)
+ .with_color([1.0, 1.0, 1.0, 1.0])
+ .with_scale(20.0)],
+ ..Section::default()
+ });
+
+ offset += 20.0;
+ };
+
+ let angle = |x| Deg::from(Rad(x)).0;
+
+ let pos = camera.first_person.position;
+
+ add_text(&format!(
+ "{} {}",
+ env!("CARGO_PKG_NAME"),
+ env!("CARGO_PKG_VERSION")
+ ));
+ add_text(&format!("{} FPS", self.fps));
+ add_text(&format!("({:.1}, {:.1}, {:.1})", pos[0], pos[1], pos[2]));
+ add_text(&format!(
+ "yaw: {:.1}°",
+ (angle(camera.first_person.yaw) + 360.0) % 360.0
+ ));
+ add_text(&format!("pitch: {:.1}°", angle(camera.first_person.pitch)));
+ }
+}
diff --git a/src/gfx/font.rs b/src/gfx/font.rs
new file mode 100644
index 0000000..6875a3b
--- /dev/null
+++ b/src/gfx/font.rs
@@ -0,0 +1,44 @@
+use super::gpu::{Frame, Gpu};
+
+pub struct Font {
+ glyph_brush: wgpu_glyph::GlyphBrush<()>,
+ staging_belt: wgpu::util::StagingBelt,
+}
+
+impl Font {
+ pub fn new(gpu: &Gpu) -> Self {
+ Self {
+ glyph_brush: wgpu_glyph::GlyphBrushBuilder::using_font(
+ wgpu_glyph::ab_glyph::FontArc::try_from_slice(include_bytes!(
+ "../../assets/font/regular.otf"
+ ))
+ .unwrap(),
+ )
+ .build(&gpu.device, gpu.config.format),
+ staging_belt: wgpu::util::StagingBelt::new(1024),
+ }
+ }
+
+ pub fn add(&mut self, section: wgpu_glyph::Section) {
+ self.glyph_brush.queue(section);
+ }
+
+ pub fn submit(&mut self, frame: &mut Frame) {
+ self.glyph_brush
+ .draw_queued(
+ &frame.gpu.device,
+ &mut self.staging_belt,
+ &mut frame.encoder,
+ &frame.view,
+ frame.gpu.config.width,
+ frame.gpu.config.height,
+ )
+ .unwrap();
+
+ self.staging_belt.finish();
+ }
+
+ pub fn cleanup(&mut self) {
+ self.staging_belt.recall();
+ }
+}
diff --git a/src/gfx/gpu.rs b/src/gfx/gpu.rs
new file mode 100644
index 0000000..f187fee
--- /dev/null
+++ b/src/gfx/gpu.rs
@@ -0,0 +1,168 @@
+pub struct Frame<'a> {
+ pub gpu: &'a mut Gpu,
+ pub encoder: wgpu::CommandEncoder,
+ pub output: wgpu::SurfaceTexture,
+ pub view: wgpu::TextureView,
+}
+
+impl<'a> Frame<'a> {
+ pub fn new(gpu: &'a mut Gpu) -> Result<Self, wgpu::SurfaceError> {
+ let output = gpu.surface.get_current_texture()?;
+ let view = output
+ .texture
+ .create_view(&wgpu::TextureViewDescriptor::default());
+
+ let encoder = gpu
+ .device
+ .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+
+ Ok(Self {
+ gpu,
+ encoder,
+ output,
+ view,
+ })
+ }
+
+ pub fn pass(&mut self) -> wgpu::RenderPass {
+ self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+ label: None,
+ color_attachments: &[Some(wgpu::RenderPassColorAttachment {
+ view: &self.view,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Clear(wgpu::Color {
+ r: 0x87 as f64 / 255.0,
+ g: 0xCE as f64 / 255.0,
+ b: 0xEB as f64 / 255.0,
+ a: 1.0,
+ }),
+ store: true,
+ },
+ })],
+ depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
+ view: &self.gpu.depth_texture,
+ depth_ops: Some(wgpu::Operations {
+ load: wgpu::LoadOp::Clear(1.0),
+ store: true,
+ }),
+ stencil_ops: None,
+ }),
+ })
+ }
+
+ pub fn finish(self) {
+ self.gpu.queue.submit(Some(self.encoder.finish()));
+ self.output.present();
+ }
+}
+
+pub struct Gpu {
+ pub surface: wgpu::Surface,
+ pub device: wgpu::Device,
+ pub queue: wgpu::Queue,
+ pub config: wgpu::SurfaceConfiguration,
+ pub depth_texture: wgpu::TextureView,
+}
+
+impl Gpu {
+ pub async fn new(window: &winit::window::Window) -> Self {
+ let size = window.inner_size();
+
+ let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
+ backends: wgpu::Backends::all(),
+ dx12_shader_compiler: Default::default(),
+ });
+
+ let surface = unsafe { instance.create_surface(window) }.unwrap();
+
+ let adapter = instance
+ .request_adapter(&wgpu::RequestAdapterOptions {
+ power_preference: wgpu::PowerPreference::default(),
+ compatible_surface: Some(&surface),
+ force_fallback_adapter: false,
+ })
+ .await
+ .unwrap();
+
+ let (device, queue) = adapter
+ .request_device(
+ &wgpu::DeviceDescriptor {
+ features: wgpu::Features::empty(),
+ limits: Default::default(),
+ label: None,
+ },
+ None,
+ )
+ .await
+ .unwrap();
+
+ let surface_caps = surface.get_capabilities(&adapter);
+ let surface_format = surface_caps
+ .formats
+ .iter()
+ .copied()
+ .find(|f| f.describe().srgb)
+ .unwrap_or(surface_caps.formats[0]);
+
+ let config = wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+ format: surface_format,
+ width: size.width,
+ height: size.height,
+ present_mode: surface_caps.present_modes[0],
+ alpha_mode: surface_caps.alpha_modes[0],
+ view_formats: vec![],
+ };
+
+ let depth_texture = Self::create_depth_texture(&config, &device);
+
+ let mut state = Self {
+ surface,
+ device,
+ queue,
+ config,
+ depth_texture,
+ };
+
+ state.resize(size);
+
+ state
+ }
+
+ pub fn create_depth_texture(
+ config: &wgpu::SurfaceConfiguration,
+ device: &wgpu::Device,
+ ) -> wgpu::TextureView {
+ device
+ .create_texture(&wgpu::TextureDescriptor {
+ label: Some("depth texture"),
+ size: wgpu::Extent3d {
+ width: config.width,
+ height: config.height,
+ 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: &[],
+ })
+ .create_view(&wgpu::TextureViewDescriptor::default())
+ }
+
+ pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
+ if size.width > 0 && size.height > 0 {
+ self.config.width = size.width;
+ self.config.height = size.height;
+ self.configure_surface();
+ self.depth_texture = Self::create_depth_texture(&self.config, &self.device);
+ }
+ }
+
+ pub fn configure_surface(&mut self) {
+ self.surface.configure(&self.device, &self.config);
+ }
+}
diff --git a/src/gfx/map.rs b/src/gfx/map.rs
index 7bd5dd3..04865a6 100644
--- a/src/gfx/map.rs
+++ b/src/gfx/map.rs
@@ -1,7 +1,9 @@
mod atlas;
mod mesh;
-use super::{media::MediaMgr, state::State, util::MatrixUniform};
+use super::{
+ camera::Camera, debug_menu::DebugMenu, gpu::Gpu, media::MediaMgr, util::MatrixUniform,
+};
use atlas::create_atlas;
use cgmath::{prelude::*, Matrix4, Point3, Vector3};
use collision::{prelude::*, Aabb3, Relation};
@@ -101,13 +103,13 @@ struct BlockMesh {
}
impl BlockMesh {
- fn new(state: &State, vertices: &[Vertex]) -> Option<Self> {
+ fn new(gpu: &Gpu, vertices: &[Vertex]) -> Option<Self> {
if vertices.is_empty() {
return None;
}
Some(BlockMesh {
- vertex_buffer: state
+ vertex_buffer: gpu
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("mapblock.vertex_buffer"),
@@ -136,10 +138,15 @@ fn block_float_pos(pos: Point3<i16>) -> Point3<f32> {
}
impl MapRender {
- pub fn render<'a>(&'a self, state: &'a State, pass: &mut wgpu::RenderPass<'a>) {
+ pub fn render<'a>(
+ &'a self,
+ camera: &'a Camera,
+ debug_menu: &mut DebugMenu,
+ pass: &mut wgpu::RenderPass<'a>,
+ ) {
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &self.atlas, &[]);
- pass.set_bind_group(1, &state.camera_uniform.bind_group, &[]);
+ pass.set_bind_group(1, &camera.uniform.bind_group, &[]);
struct BlendEntry<'a> {
dist: f32,
@@ -157,9 +164,9 @@ impl MapRender {
let fpos = block_float_pos(pos);
let one = Vector3::new(1.0, 1.0, 1.0);
- let aabb = Aabb3::new(fpos - one * 0.5, fpos + one * 15.5).transform(&state.view);
+ let aabb = Aabb3::new(fpos - one * 0.5, fpos + one * 15.5).transform(&camera.view);
- if state.frustum.contains(&aabb) == Relation::Out {
+ if camera.frustum.contains(&aabb) == Relation::Out {
continue;
}
@@ -170,7 +177,7 @@ impl MapRender {
if let Some(mesh) = &model.mesh_blend {
blend.push(BlendEntry {
index,
- dist: (state.view * (fpos + one * 8.5).to_homogeneous())
+ dist: (camera.view * (fpos + one * 8.5).to_homogeneous())
.truncate()
.magnitude(),
mesh,
@@ -191,7 +198,7 @@ impl MapRender {
}
}
- pub fn update(&mut self, state: &mut State) {
+ pub fn update(&mut self, gpu: &Gpu) {
for (pos, _) in self
.blocks_defer
.drain_filter(|_, v| v.time.elapsed().as_millis() > 100)
@@ -208,10 +215,10 @@ impl MapRender {
self.block_models.insert(
pos,
BlockModel {
- mesh: BlockMesh::new(state, &data.vertices),
- mesh_blend: BlockMesh::new(state, &data.vertices_blend),
+ mesh: BlockMesh::new(gpu, &data.vertices),
+ mesh_blend: BlockMesh::new(gpu, &data.vertices_blend),
transform: MatrixUniform::new(
- &state.device,
+ &gpu.device,
&self.model,
Matrix4::from_translation(block_float_pos(pos).to_vec()),
"mapblock",
@@ -277,7 +284,12 @@ impl MapRender {
}
}
- pub fn new(state: &mut State, media: &MediaMgr, mut nodes: HashMap<u16, NodeDef>) -> Self {
+ pub fn new(
+ gpu: &mut Gpu,
+ camera: &Camera,
+ media: &MediaMgr,
+ mut nodes: HashMap<u16, NodeDef>,
+ ) -> Self {
let (atlas_img, atlas_slices) = create_atlas(&mut nodes, media);
let atlas_size = wgpu::Extent3d {
@@ -286,7 +298,7 @@ impl MapRender {
depth_or_array_layers: 1,
};
- let atlas_texture = state.device.create_texture(&wgpu::TextureDescriptor {
+ let atlas_texture = gpu.device.create_texture(&wgpu::TextureDescriptor {
size: atlas_size,
mip_level_count: 1,
sample_count: 1,
@@ -297,7 +309,7 @@ impl MapRender {
view_formats: &[],
});
- state.queue.write_texture(
+ gpu.queue.write_texture(
wgpu::ImageCopyTexture {
texture: &atlas_texture,
mip_level: 0,
@@ -315,7 +327,7 @@ impl MapRender {
let atlas_view = atlas_texture.create_view(&wgpu::TextureViewDescriptor::default());
- let atlas_sampler = state.device.create_sampler(&wgpu::SamplerDescriptor {
+ let atlas_sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
@@ -328,8 +340,7 @@ impl MapRender {
});
let atlas_bind_group_layout =
- state
- .device
+ gpu.device
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
@@ -352,7 +363,7 @@ impl MapRender {
label: Some("atlas.bind_group_layout"),
});
- let atlas_bind_group = state.device.create_bind_group(&wgpu::BindGroupDescriptor {
+ let atlas_bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &atlas_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
@@ -367,26 +378,25 @@ impl MapRender {
label: Some("atlas.bind_group"),
});
- let model_bind_group_layout = MatrixUniform::layout(&state.device, "mapblock");
+ let model_bind_group_layout = MatrixUniform::layout(&gpu.device, "mapblock");
- let shader = state
+ let shader = gpu
.device
.create_shader_module(wgpu::include_wgsl!("../../assets/shaders/map.wgsl"));
- let pipeline_layout =
- state
- .device
- .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
- label: None,
- bind_group_layouts: &[
- &atlas_bind_group_layout,
- &model_bind_group_layout,
- &state.camera_bind_group_layout,
- ],
- push_constant_ranges: &[],
- });
+ let pipeline_layout = gpu
+ .device
+ .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+ label: None,
+ bind_group_layouts: &[
+ &atlas_bind_group_layout,
+ &model_bind_group_layout,
+ &camera.layout,
+ ],
+ push_constant_ranges: &[],
+ });
- let pipeline = state
+ let pipeline = gpu
.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
@@ -400,15 +410,8 @@ impl MapRender {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
- format: state.config.format,
- blend: Some(wgpu::BlendState {
- color: wgpu::BlendComponent {
- src_factor: wgpu::BlendFactor::SrcAlpha,
- dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
- operation: wgpu::BlendOperation::Add,
- },
- alpha: wgpu::BlendComponent::OVER,
- }),
+ format: gpu.config.format,
+ blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
diff --git a/src/gfx/map/mesh.rs b/src/gfx/map/mesh.rs
index 139b6c7..3d56ad8 100644
--- a/src/gfx/map/mesh.rs
+++ b/src/gfx/map/mesh.rs
@@ -93,7 +93,7 @@ pub(super) fn create_mesh(
if let Some(ndef) = &mkinfo.nodes[ncontent as usize] {
if match draw_type {
DrawType::Cube => ndef.draw_type == DrawType::Cube,
- DrawType::Liquid => ncontent == content,
+ DrawType::Liquid => ndef.draw_type == DrawType::Cube || ncontent == content,
_ => false,
} {
continue;
diff --git a/src/gfx/state.rs b/src/gfx/state.rs
deleted file mode 100644
index b307d85..0000000
--- a/src/gfx/state.rs
+++ /dev/null
@@ -1,243 +0,0 @@
-use super::util::MatrixUniform;
-use cgmath::{prelude::*, Deg, Matrix4, Rad};
-use collision::Frustum;
-use fps_camera::{FirstPerson, FirstPersonSettings};
-use std::time::Duration;
-
-pub struct State {
- pub surface: wgpu::Surface,
- pub device: wgpu::Device,
- pub queue: wgpu::Queue,
- pub config: wgpu::SurfaceConfiguration,
- pub fov: Rad<f32>,
- pub view: Matrix4<f32>,
- pub proj: Matrix4<f32>,
- pub frustum: Frustum<f32>,
- pub camera: FirstPerson,
- pub camera_uniform: MatrixUniform,
- pub camera_bind_group_layout: wgpu::BindGroupLayout,
- pub depth_texture: wgpu::Texture,
- pub depth_view: wgpu::TextureView,
- pub depth_sampler: wgpu::Sampler,
-}
-
-impl State {
- pub async fn new(window: &winit::window::Window) -> Self {
- let size = window.inner_size();
-
- let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
- backends: wgpu::Backends::all(),
- dx12_shader_compiler: Default::default(),
- });
-
- let surface = unsafe { instance.create_surface(window) }.unwrap();
-
- let adapter = instance
- .request_adapter(&wgpu::RequestAdapterOptions {
- power_preference: wgpu::PowerPreference::default(),
- compatible_surface: Some(&surface),
- force_fallback_adapter: false,
- })
- .await
- .unwrap();
-
- let (device, queue) = adapter
- .request_device(
- &wgpu::DeviceDescriptor {
- features: wgpu::Features::empty(),
- limits: Default::default(),
- label: None,
- },
- None,
- )
- .await
- .unwrap();
-
- let surface_caps = surface.get_capabilities(&adapter);
- let surface_format = surface_caps
- .formats
- .iter()
- .copied()
- .find(|f| f.describe().srgb)
- .unwrap_or(surface_caps.formats[0]);
-
- let config = wgpu::SurfaceConfiguration {
- usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
- format: surface_format,
- width: size.width,
- height: size.height,
- present_mode: surface_caps.present_modes[0],
- alpha_mode: surface_caps.alpha_modes[0],
- view_formats: vec![],
- };
-
- let (depth_texture, depth_view, depth_sampler) =
- Self::create_depth_texture(&config, &device);
-
- let camera = FirstPerson::new(
- [0.0, 0.0, 0.0],
- FirstPersonSettings {
- speed_horizontal: 10.0,
- speed_vertical: 10.0,
- mouse_sensitivity_horizontal: 1.0,
- mouse_sensitivity_vertical: 1.0,
- },
- );
-
- let camera_bind_group_layout = MatrixUniform::layout(&device, "camera");
-
- let camera_uniform = MatrixUniform::new(
- &device,
- &camera_bind_group_layout,
- Matrix4::identity(),
- "camera",
- true,
- );
-
- let mut state = Self {
- surface,
- device,
- queue,
- config,
- fov: Deg(90.0).into(),
- proj: Matrix4::identity(),
- view: Matrix4::identity(),
- frustum: Frustum::from_matrix4(Matrix4::identity()).unwrap(),
- camera,
- camera_uniform,
- camera_bind_group_layout,
- depth_texture,
- depth_view,
- depth_sampler,
- };
-
- state.resize(size);
-
- state
- }
-
- pub fn create_depth_texture(
- config: &wgpu::SurfaceConfiguration,
- device: &wgpu::Device,
- ) -> (wgpu::Texture, wgpu::TextureView, wgpu::Sampler) {
- let depth_size = wgpu::Extent3d {
- width: config.width,
- height: config.height,
- depth_or_array_layers: 1,
- };
- let depth_descriptor = wgpu::TextureDescriptor {
- label: Some("depth texture"),
- size: depth_size,
- 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: &[],
- };
- let depth_texture = device.create_texture(&depth_descriptor);
-
- let depth_view = depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
- let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
- address_mode_u: wgpu::AddressMode::ClampToEdge,
- address_mode_v: wgpu::AddressMode::ClampToEdge,
- address_mode_w: wgpu::AddressMode::ClampToEdge,
- mag_filter: wgpu::FilterMode::Linear,
- min_filter: wgpu::FilterMode::Linear,
- mipmap_filter: wgpu::FilterMode::Nearest,
- compare: Some(wgpu::CompareFunction::LessEqual),
- lod_min_clamp: 0.0,
- lod_max_clamp: 100.0,
- ..Default::default()
- });
-
- (depth_texture, depth_view, depth_sampler)
- }
-
- pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
- if size.width > 0 && size.height > 0 {
- self.config.width = size.width;
- self.config.height = size.height;
- self.configure_surface();
- self.update_projection();
- (self.depth_texture, self.depth_view, self.depth_sampler) =
- Self::create_depth_texture(&self.config, &self.device);
- }
- }
-
- pub fn configure_surface(&mut self) {
- self.surface.configure(&self.device, &self.config);
- }
-
- pub fn update_projection(&mut self) {
- self.proj = cgmath::perspective(
- self.fov,
- self.config.width as f32 / self.config.height as f32,
- 0.1,
- 100000.0,
- );
- self.frustum = Frustum::from_matrix4(self.proj).unwrap();
- }
-
- pub fn update(&mut self, dt: Duration) {
- self.camera.yaw += Rad::from(Deg(180.0)).0;
- self.camera.yaw *= -1.0;
-
- let cam = self.camera.camera(dt.as_secs_f32());
-
- self.camera.yaw *= -1.0;
- self.camera.yaw -= Rad::from(Deg(180.0)).0;
-
- self.camera.position = cam.position;
-
- self.view = Matrix4::from(cam.orthogonal());
- self.camera_uniform.set(&self.queue, self.proj * self.view);
- }
-
- pub fn render(&self, map: &Option<super::map::MapRender>) -> Result<(), wgpu::SurfaceError> {
- let output = self.surface.get_current_texture()?;
- let view = output
- .texture
- .create_view(&wgpu::TextureViewDescriptor::default());
-
- let mut encoder = self
- .device
- .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
-
- {
- let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
- label: None,
- color_attachments: &[Some(wgpu::RenderPassColorAttachment {
- view: &view,
- resolve_target: None,
- ops: wgpu::Operations {
- load: wgpu::LoadOp::Clear(wgpu::Color {
- r: 0x87 as f64 / 255.0,
- g: 0xCE as f64 / 255.0,
- b: 0xEB as f64 / 255.0,
- a: 1.0,
- }),
- store: true,
- },
- })],
- depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
- view: &self.depth_view,
- depth_ops: Some(wgpu::Operations {
- load: wgpu::LoadOp::Clear(1.0),
- store: true,
- }),
- stencil_ops: None,
- }),
- });
-
- if let Some(map) = map.as_ref() {
- map.render(self, &mut render_pass);
- }
- }
-
- self.queue.submit(std::iter::once(encoder.finish()));
- output.present();
-
- Ok(())
- }
-}