mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-11-25 08:18:43 -06:00
Compare commits
14 commits
772a8ea7db
...
8e39fc24fd
Author | SHA1 | Date | |
---|---|---|---|
griffi-gh | 8e39fc24fd | ||
griffi-gh | a63deb5173 | ||
griffi-gh | 8c5b0aa47e | ||
griffi-gh | 8e907a9fbc | ||
griffi-gh | ce5dd6f011 | ||
griffi-gh | a5fae8ad2b | ||
griffi-gh | bb9107e912 | ||
griffi-gh | dd6f52edb5 | ||
griffi-gh | 6cde878a50 | ||
griffi-gh | 62c3c2105e | ||
griffi-gh | e1f1ba706c | ||
griffi-gh | 204bb882a6 | ||
griffi-gh | 610d309ead | ||
griffi-gh | 66d3ea656b |
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -960,6 +960,17 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hui-winit"
|
||||||
|
version = "0.1.0-alpha.4"
|
||||||
|
source = "git+https://github.com/griffi-gh/hui?rev=dd5af8b9e2#dd5af8b9e2dc4cb2beb0b130d82167258ea2bd4e"
|
||||||
|
dependencies = [
|
||||||
|
"glam",
|
||||||
|
"hui",
|
||||||
|
"log",
|
||||||
|
"winit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -1148,6 +1159,7 @@ dependencies = [
|
||||||
"hashbrown 0.14.3",
|
"hashbrown 0.14.3",
|
||||||
"hui",
|
"hui",
|
||||||
"hui-glium",
|
"hui-glium",
|
||||||
|
"hui-winit",
|
||||||
"image",
|
"image",
|
||||||
"kubi-logging",
|
"kubi-logging",
|
||||||
"kubi-shared",
|
"kubi-shared",
|
||||||
|
|
BIN
assets/blocks/water.png
Normal file
BIN
assets/blocks/water.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 341 B |
|
@ -22,6 +22,7 @@ pub enum BlockTexture {
|
||||||
Cobblestone,
|
Cobblestone,
|
||||||
Planks,
|
Planks,
|
||||||
WaterSolid,
|
WaterSolid,
|
||||||
|
Water,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, EnumIter, TryFromPrimitive)]
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, EnumIter, TryFromPrimitive)]
|
||||||
|
@ -62,81 +63,108 @@ impl Block {
|
||||||
},
|
},
|
||||||
Self::Stone => BlockDescriptor {
|
Self::Stone => BlockDescriptor {
|
||||||
name: "stone",
|
name: "stone",
|
||||||
render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Stone)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Solid,
|
||||||
|
CubeTexture::all(BlockTexture::Stone)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Dirt => BlockDescriptor {
|
Self::Dirt => BlockDescriptor {
|
||||||
name: "dirt",
|
name: "dirt",
|
||||||
render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Dirt)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Solid,
|
||||||
|
CubeTexture::all(BlockTexture::Dirt)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Grass => BlockDescriptor {
|
Self::Grass => BlockDescriptor {
|
||||||
name: "grass",
|
name: "grass",
|
||||||
render: RenderType::SolidBlock(CubeTexture::top_sides_bottom(
|
render: RenderType::Cube(
|
||||||
BlockTexture::GrassTop,
|
Transparency::Solid,
|
||||||
BlockTexture::GrassSide,
|
CubeTexture::top_sides_bottom(
|
||||||
BlockTexture::Dirt
|
BlockTexture::GrassTop,
|
||||||
)),
|
BlockTexture::GrassSide,
|
||||||
|
BlockTexture::Dirt
|
||||||
|
)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Sand => BlockDescriptor {
|
Self::Sand => BlockDescriptor {
|
||||||
name: "sand",
|
name: "sand",
|
||||||
render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Sand)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Solid,
|
||||||
|
CubeTexture::all(BlockTexture::Sand)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Cobblestone => BlockDescriptor {
|
Self::Cobblestone => BlockDescriptor {
|
||||||
name: "cobblestone",
|
name: "cobblestone",
|
||||||
render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Cobblestone)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Solid,
|
||||||
|
CubeTexture::all(BlockTexture::Cobblestone)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::TallGrass => BlockDescriptor {
|
Self::TallGrass => BlockDescriptor {
|
||||||
name: "tall grass",
|
name: "tall grass",
|
||||||
render: RenderType::CrossShape(CrossTexture::all(BlockTexture::TallGrass)),
|
render: RenderType::Cross(CrossTexture::all(BlockTexture::TallGrass)),
|
||||||
collision: CollisionType::None,
|
collision: CollisionType::None,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Planks => BlockDescriptor {
|
Self::Planks => BlockDescriptor {
|
||||||
name: "planks",
|
name: "planks",
|
||||||
render: RenderType::SolidBlock(CubeTexture::all(BlockTexture::Planks)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Solid,
|
||||||
|
CubeTexture::all(BlockTexture::Planks)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Torch => BlockDescriptor {
|
Self::Torch => BlockDescriptor {
|
||||||
name: "torch",
|
name: "torch",
|
||||||
render: RenderType::CrossShape(CrossTexture::all(BlockTexture::Torch)),
|
render: RenderType::Cross(CrossTexture::all(BlockTexture::Torch)),
|
||||||
collision: CollisionType::None,
|
collision: CollisionType::None,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Wood => BlockDescriptor {
|
Self::Wood => BlockDescriptor {
|
||||||
name: "leaf",
|
name: "leaf",
|
||||||
render: RenderType::SolidBlock(CubeTexture::horizontal_vertical(BlockTexture::Wood, BlockTexture::WoodTop)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Solid,
|
||||||
|
CubeTexture::horizontal_vertical(BlockTexture::Wood, BlockTexture::WoodTop)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Leaf => BlockDescriptor {
|
Self::Leaf => BlockDescriptor {
|
||||||
name: "leaf",
|
name: "leaf",
|
||||||
render: RenderType::BinaryTransparency(CubeTexture::all(BlockTexture::Leaf)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Binary,
|
||||||
|
CubeTexture::all(BlockTexture::Leaf)
|
||||||
|
),
|
||||||
collision: CollisionType::Solid,
|
collision: CollisionType::Solid,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
},
|
},
|
||||||
Self::Water => BlockDescriptor {
|
Self::Water => BlockDescriptor {
|
||||||
name: "water",
|
name: "water",
|
||||||
render: RenderType::BinaryTransparency(CubeTexture::all(BlockTexture::WaterSolid)),
|
render: RenderType::Cube(
|
||||||
|
Transparency::Trans,
|
||||||
|
CubeTexture::all(BlockTexture::Water)
|
||||||
|
),
|
||||||
collision: CollisionType::None,
|
collision: CollisionType::None,
|
||||||
raycast_collision: true,
|
raycast_collision: true,
|
||||||
drops: None,
|
drops: None,
|
||||||
|
@ -213,10 +241,16 @@ pub enum CollisionType {
|
||||||
Solid,
|
Solid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Transparency {
|
||||||
|
Solid,
|
||||||
|
Binary,
|
||||||
|
Trans,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum RenderType {
|
pub enum RenderType {
|
||||||
None,
|
None,
|
||||||
SolidBlock(CubeTexture),
|
Cube(Transparency, CubeTexture),
|
||||||
BinaryTransparency(CubeTexture),
|
Cross(CrossTexture),
|
||||||
CrossShape(CrossTexture),
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ 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 = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
||||||
hui-glium = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
hui-glium = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
||||||
|
hui-winit = { version = "0.1.0-alpha.4", git = "https://github.com/griffi-gh/hui", rev = "dd5af8b9e2" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
glium = { git = "https://github.com/glium/glium", rev = "a352c667" }
|
glium = { git = "https://github.com/glium/glium", rev = "a352c667" }
|
||||||
glutin = "0.31"
|
glutin = "0.31"
|
||||||
|
|
|
@ -1,29 +1,26 @@
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
precision lowp sampler2DArray;
|
precision lowp sampler2DArray;
|
||||||
|
|
||||||
in vec3 v_normal;
|
in vec3 v_normal;
|
||||||
in vec2 v_uv;
|
in vec2 v_uv;
|
||||||
flat in uint v_tex_index;
|
flat in uint v_tex_index;
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
uniform sampler2DArray tex;
|
uniform sampler2DArray tex;
|
||||||
|
uniform bool discard_alpha;
|
||||||
// vec4 alpha_drop(vec4 b, vec4 a) {
|
|
||||||
// if ((a.w < 1.) || (b.w < 1.)) {
|
void main() {
|
||||||
// return vec4(b.xyz, 0.);
|
// base color from texture
|
||||||
// }
|
color = texture(tex, vec3(v_uv, v_tex_index));
|
||||||
// return a;
|
|
||||||
// }
|
// discard fully transparent pixels
|
||||||
|
if (discard_alpha ? (color.w < 0.5) : (color.w == 0.)) discard;
|
||||||
void main() {
|
|
||||||
// base color from texture
|
//basic "lighting"
|
||||||
color = texture(tex, vec3(v_uv, v_tex_index));
|
float light = abs(v_normal.x) + .8 * abs(v_normal.y) + .6 * abs(v_normal.z);
|
||||||
// discard transparent pixels
|
color *= vec4(vec3(light), 1.);
|
||||||
if (color.w < 0.5) discard;
|
|
||||||
//basic "lighting"
|
//discard alpha
|
||||||
float light = abs(v_normal.x) + .8 * abs(v_normal.y) + .6 * abs(v_normal.z);
|
if (discard_alpha) color.w = 1.;
|
||||||
color *= vec4(vec3(light), 1.);
|
}
|
||||||
//discard alpha
|
|
||||||
color.w = 1.;
|
|
||||||
}
|
|
||||||
|
|
|
@ -60,5 +60,6 @@ pub fn init_chat_manager(
|
||||||
) {
|
) {
|
||||||
let mut chat_manager = ChatHistory::default();
|
let mut chat_manager = ChatHistory::default();
|
||||||
chat_manager.add_system_message("Welcome to Kubi! Chat messages will appear here".to_string());
|
chat_manager.add_system_message("Welcome to Kubi! Chat messages will appear here".to_string());
|
||||||
|
chat_manager.add_system_message("F1 (Hold): Settings; F3: Release cursor; F4/F5: Gamemode".to_string());
|
||||||
storages.add_unique(chat_manager);
|
storages.add_unique(chat_manager);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||||
use crate::{events::InputDeviceEvent, rendering::Renderer};
|
use crate::{events::InputDeviceEvent, rendering::{Renderer, WindowSize}};
|
||||||
use winit::{
|
use winit::{
|
||||||
event::{DeviceEvent, ElementState, RawKeyEvent},
|
dpi::PhysicalPosition, event::{DeviceEvent, ElementState, RawKeyEvent}, keyboard::{KeyCode, PhysicalKey}, window::CursorGrabMode
|
||||||
keyboard::{KeyCode, PhysicalKey},
|
|
||||||
window::CursorGrabMode
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
|
@ -43,6 +41,8 @@ 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>>,
|
||||||
|
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 {
|
||||||
|
@ -50,6 +50,10 @@ pub fn debug_toggle_lock(
|
||||||
state: ElementState::Pressed,
|
state: ElementState::Pressed,
|
||||||
}) = evt.event {
|
}) = evt.event {
|
||||||
lock.0 = !lock.0;
|
lock.0 = !lock.0;
|
||||||
|
if !lock.0 {
|
||||||
|
let center = PhysicalPosition::new(size.0.x as f64 / 2., size.0.y as f64 / 2.);
|
||||||
|
let _ = ren.window.set_cursor_position(center);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,3 +41,10 @@ pub fn kubi_ui_draw(
|
||||||
) {
|
) {
|
||||||
ui.renderer.draw(&mut target.0, size.0.as_vec2());
|
ui.renderer.draw(&mut target.0, size.0.as_vec2());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hui_process_winit_events(
|
||||||
|
event: &winit::event::Event<()>,
|
||||||
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
|
) {
|
||||||
|
hui_winit::handle_winit_event(&mut ui.hui, event);
|
||||||
|
}
|
||||||
|
|
|
@ -15,13 +15,14 @@ use std::time::Instant;
|
||||||
|
|
||||||
pub(crate) use kubi_shared::transform;
|
pub(crate) use kubi_shared::transform;
|
||||||
|
|
||||||
mod ui {
|
mod ui;
|
||||||
pub(crate) mod loading_screen;
|
pub(crate) use ui::{
|
||||||
pub(crate) mod connecting_screen;
|
loading_screen,
|
||||||
pub(crate) mod chat_ui;
|
connecting_screen,
|
||||||
}
|
chat_ui,
|
||||||
pub(crate) use ui::{loading_screen, connecting_screen, chat_ui};
|
crosshair_ui,
|
||||||
|
settings_ui,
|
||||||
|
};
|
||||||
pub(crate) mod rendering;
|
pub(crate) mod rendering;
|
||||||
pub(crate) mod world;
|
pub(crate) mod world;
|
||||||
pub(crate) mod player;
|
pub(crate) mod player;
|
||||||
|
@ -91,6 +92,10 @@ use filesystem::AssetManager;
|
||||||
use client_physics::{init_client_physics, update_client_physics_late};
|
use client_physics::{init_client_physics, update_client_physics_late};
|
||||||
use chat_ui::render_chat;
|
use chat_ui::render_chat;
|
||||||
use chat::init_chat_manager;
|
use chat::init_chat_manager;
|
||||||
|
use crosshair_ui::{init_crosshair_image, draw_crosshair};
|
||||||
|
use settings_ui::render_settings_ui;
|
||||||
|
|
||||||
|
use crate::hui_integration::hui_process_winit_events;
|
||||||
|
|
||||||
/// 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 {
|
||||||
|
@ -116,6 +121,7 @@ fn startup() -> Workload {
|
||||||
init_delta_time,
|
init_delta_time,
|
||||||
init_client_physics,
|
init_client_physics,
|
||||||
init_chat_manager,
|
init_chat_manager,
|
||||||
|
init_crosshair_image,
|
||||||
).into_sequential_workload()
|
).into_sequential_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +156,10 @@ fn update() -> Workload {
|
||||||
update_raycasts,
|
update_raycasts,
|
||||||
update_block_placement,
|
update_block_placement,
|
||||||
apply_queued_blocks,
|
apply_queued_blocks,
|
||||||
|
//UI:
|
||||||
render_chat,
|
render_chat,
|
||||||
|
draw_crosshair,
|
||||||
|
render_settings_ui,
|
||||||
).into_sequential_workload().run_if(is_ingame),
|
).into_sequential_workload().run_if(is_ingame),
|
||||||
update_networking_late.run_if(is_multiplayer),
|
update_networking_late.run_if(is_multiplayer),
|
||||||
compute_cameras,
|
compute_cameras,
|
||||||
|
@ -273,6 +282,7 @@ pub fn kubi_main(
|
||||||
|
|
||||||
window_target.set_control_flow(ControlFlow::Poll);
|
window_target.set_control_flow(ControlFlow::Poll);
|
||||||
|
|
||||||
|
world.run_with_data(hui_process_winit_events, &event);
|
||||||
process_winit_events(&mut world, &event);
|
process_winit_events(&mut world, &event);
|
||||||
|
|
||||||
#[allow(clippy::collapsible_match, clippy::single_match)]
|
#[allow(clippy::collapsible_match, clippy::single_match)]
|
||||||
|
|
|
@ -2,7 +2,7 @@ use glam::{vec3, EulerRot, Mat4, Quat, Vec2, Vec2Swizzles, Vec3, Vec3Swizzles};
|
||||||
use shipyard::{track, Component, Get, IntoIter, IntoWithId, IntoWorkload, Unique, UniqueView, View, ViewMut, Workload};
|
use shipyard::{track, Component, Get, IntoIter, IntoWithId, IntoWorkload, Unique, UniqueView, View, ViewMut, Workload};
|
||||||
use winit::keyboard::KeyCode;
|
use winit::keyboard::KeyCode;
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use crate::{client_physics::ClPhysicsActor, delta_time::DeltaTime, input::{Inputs, PrevInputs, RawKbmInputState}, settings::GameSettings, transform::Transform};
|
use crate::{client_physics::ClPhysicsActor, cursor_lock::CursorLock, delta_time::DeltaTime, input::{Inputs, PrevInputs, RawKbmInputState}, settings::GameSettings, transform::Transform};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum PlayerControllerType {
|
pub enum PlayerControllerType {
|
||||||
|
@ -43,7 +43,10 @@ fn update_look(
|
||||||
inputs: UniqueView<Inputs>,
|
inputs: UniqueView<Inputs>,
|
||||||
settings: UniqueView<GameSettings>,
|
settings: UniqueView<GameSettings>,
|
||||||
dt: UniqueView<DeltaTime>,
|
dt: UniqueView<DeltaTime>,
|
||||||
|
lock: UniqueView<CursorLock>,
|
||||||
) {
|
) {
|
||||||
|
//Only update if the cursor is locked
|
||||||
|
if !lock.0 { return }
|
||||||
let look = inputs.look * settings.mouse_sensitivity * dt.0.as_secs_f32();
|
let look = inputs.look * settings.mouse_sensitivity * dt.0.as_secs_f32();
|
||||||
if look == Vec2::ZERO { return }
|
if look == Vec2::ZERO { return }
|
||||||
for (_, mut transform) in (&controllers, &mut transforms).iter() {
|
for (_, mut transform) in (&controllers, &mut transforms).iter() {
|
||||||
|
|
|
@ -34,6 +34,7 @@ impl AssetPaths for BlockTexture {
|
||||||
Self::Cobblestone => "cobblestone.png",
|
Self::Cobblestone => "cobblestone.png",
|
||||||
Self::Planks => "planks.png",
|
Self::Planks => "planks.png",
|
||||||
Self::WaterSolid => "solid_water.png",
|
Self::WaterSolid => "solid_water.png",
|
||||||
|
Self::Water => "water.png",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,11 @@
|
||||||
use glam::{Vec3, Mat4, Quat, ivec3};
|
use glam::{ivec3, IVec3, Mat4, Quat, Vec3};
|
||||||
use shipyard::{NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, track};
|
use shipyard::{NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, track};
|
||||||
use glium::{
|
use glium::{
|
||||||
implement_vertex, uniform,
|
|
||||||
Surface, DrawParameters,
|
|
||||||
uniforms::{
|
|
||||||
Sampler,
|
|
||||||
SamplerBehavior,
|
|
||||||
MinifySamplerFilter,
|
|
||||||
MagnifySamplerFilter,
|
|
||||||
SamplerWrapFunction
|
|
||||||
},
|
|
||||||
draw_parameters::{
|
draw_parameters::{
|
||||||
Depth,
|
BackfaceCullingMode, Depth, DepthTest, PolygonMode
|
||||||
DepthTest,
|
}, implement_vertex, uniform, uniforms::{
|
||||||
PolygonMode,
|
MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction
|
||||||
BackfaceCullingMode,
|
}, Blend, DrawParameters, Smooth, Surface
|
||||||
}, Blend
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
|
@ -50,11 +40,14 @@ pub fn draw_world(
|
||||||
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
|
meshes: NonSendSync<UniqueView<ChunkMeshStorage>>,
|
||||||
program: NonSendSync<UniqueView<ChunkShaderPrefab>>,
|
program: NonSendSync<UniqueView<ChunkShaderPrefab>>,
|
||||||
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
|
texture: NonSendSync<UniqueView<BlockTexturesPrefab>>,
|
||||||
|
transform: View<Transform>,
|
||||||
camera: View<Camera>,
|
camera: View<Camera>,
|
||||||
settings: UniqueView<GameSettings>
|
settings: UniqueView<GameSettings>
|
||||||
) {
|
) {
|
||||||
let camera = camera.iter().next().expect("No cameras in the scene");
|
let (camera, transform) = (&camera, &transform).iter().next().expect("No cameras in the scene");
|
||||||
let draw_parameters = DrawParameters {
|
let camera_position = transform.0.to_scale_rotation_translation().2;
|
||||||
|
|
||||||
|
let mut draw_parameters = DrawParameters {
|
||||||
depth: Depth {
|
depth: Depth {
|
||||||
test: DepthTest::IfLess,
|
test: DepthTest::IfLess,
|
||||||
write: true,
|
write: true,
|
||||||
|
@ -75,6 +68,8 @@ pub fn draw_world(
|
||||||
let view = camera.view_matrix.to_cols_array_2d();
|
let view = camera.view_matrix.to_cols_array_2d();
|
||||||
let perspective = camera.perspective_matrix.to_cols_array_2d();
|
let perspective = camera.perspective_matrix.to_cols_array_2d();
|
||||||
|
|
||||||
|
let mut enqueue_trans = Vec::new();
|
||||||
|
|
||||||
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");
|
||||||
|
@ -104,11 +99,41 @@ pub fn draw_world(
|
||||||
view: view,
|
view: view,
|
||||||
perspective: perspective,
|
perspective: perspective,
|
||||||
tex: texture_sampler,
|
tex: texture_sampler,
|
||||||
|
discard_alpha: true,
|
||||||
},
|
},
|
||||||
&draw_parameters
|
&draw_parameters
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
if mesh.trans_index_buffer.len() > 0 {
|
||||||
|
enqueue_trans.push((chunk, mesh));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
draw_parameters.blend = Blend::alpha_blending();
|
||||||
|
draw_parameters.backface_culling = BackfaceCullingMode::CullingDisabled;
|
||||||
|
draw_parameters.smooth = Some(Smooth::Fastest);
|
||||||
|
|
||||||
|
// enqueue_trans.sort_by_key(|k| -(
|
||||||
|
// (k.0.position + IVec3::splat((CHUNK_SIZE >> 1) as i32)).distance_squared(camera_position.as_ivec3())
|
||||||
|
// ));
|
||||||
|
|
||||||
|
for (chunk, mesh) in enqueue_trans.drain(..).rev() {
|
||||||
|
let world_position = chunk.position.as_vec3() * CHUNK_SIZE as f32;
|
||||||
|
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(
|
pub fn draw_current_chunk_border(
|
||||||
|
|
|
@ -1,47 +1,49 @@
|
||||||
use shipyard::{Unique, AllStoragesView};
|
use shipyard::{Unique, AllStoragesView};
|
||||||
|
|
||||||
pub enum FullscreenMode {
|
pub enum FullscreenMode {
|
||||||
Borderless,
|
Borderless,
|
||||||
Exclusive,
|
Exclusive,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FullscreenSettings {
|
pub struct FullscreenSettings {
|
||||||
pub mode: FullscreenMode,
|
pub mode: FullscreenMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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,
|
||||||
pub debug_draw_current_chunk_border: bool,
|
pub debug_draw_current_chunk_border: bool,
|
||||||
}
|
pub dynamic_crosshair: bool,
|
||||||
impl Default for GameSettings {
|
}
|
||||||
fn default() -> Self {
|
impl Default for GameSettings {
|
||||||
Self {
|
fn default() -> Self {
|
||||||
vsync: false,
|
Self {
|
||||||
fullscreen: None,
|
vsync: false,
|
||||||
msaa: Some(4),
|
fullscreen: None,
|
||||||
max_anisotropy: Some(16),
|
msaa: Some(4),
|
||||||
render_distance: match true {
|
max_anisotropy: Some(16),
|
||||||
cfg!(debug_assertions) => 5,
|
render_distance: match true {
|
||||||
cfg!(target_os = "android") => 6,
|
cfg!(debug_assertions) => 5,
|
||||||
#[allow(unreachable_patterns)] _ => 7,
|
cfg!(target_os = "android") => 6,
|
||||||
},
|
#[allow(unreachable_patterns)] _ => 7,
|
||||||
mouse_sensitivity: 1.,
|
},
|
||||||
debug_draw_current_chunk_border: false, //cfg!(not(target_os = "android")) && cfg!(debug_assertions),
|
mouse_sensitivity: 1.,
|
||||||
}
|
debug_draw_current_chunk_border: false, //cfg!(not(target_os = "android")) && cfg!(debug_assertions),
|
||||||
}
|
dynamic_crosshair: true,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn load_settings(
|
}
|
||||||
storages: AllStoragesView
|
|
||||||
) {
|
pub fn load_settings(
|
||||||
log::info!("loading game settings");
|
storages: AllStoragesView
|
||||||
//todo
|
) {
|
||||||
storages.add_unique(GameSettings::default());
|
log::info!("loading game settings");
|
||||||
}
|
//todo
|
||||||
|
storages.add_unique(GameSettings::default());
|
||||||
|
}
|
||||||
|
|
5
kubi/src/ui.rs
Normal file
5
kubi/src/ui.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pub(crate) mod loading_screen;
|
||||||
|
pub(crate) mod connecting_screen;
|
||||||
|
pub(crate) mod chat_ui;
|
||||||
|
pub(crate) mod crosshair_ui;
|
||||||
|
pub(crate) mod settings_ui;
|
72
kubi/src/ui/crosshair_ui.rs
Normal file
72
kubi/src/ui/crosshair_ui.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
use glam::{uvec2, Vec2};
|
||||||
|
use hui::{
|
||||||
|
draw::{ImageHandle, TextureFormat},
|
||||||
|
element::{container::Container, image::Image, transformer::ElementTransformExt, UiElementExt},
|
||||||
|
layout::Alignment,
|
||||||
|
size
|
||||||
|
};
|
||||||
|
use shipyard::{AllStoragesViewMut, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||||
|
use crate::{hui_integration::UiState, player::MainPlayer, rendering::WindowSize, settings::GameSettings, world::raycast::LookingAtBlock};
|
||||||
|
|
||||||
|
const CROSSHAIR_SIZE: usize = 9;
|
||||||
|
const CROSSHAIR: &[u8] = &[
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
const CROSSHAIR_ALT: &[u8] = &[
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||||
|
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
|
||||||
|
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
|
||||||
|
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
|
||||||
|
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
|
||||||
|
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
|
||||||
|
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct CrosshairImage(ImageHandle, ImageHandle);
|
||||||
|
|
||||||
|
pub fn init_crosshair_image(storages: AllStoragesViewMut) {
|
||||||
|
let mut ui = storages.borrow::<NonSendSync<UniqueViewMut<UiState>>>().unwrap();
|
||||||
|
let image = ui.hui.add_image(TextureFormat::Grayscale, CROSSHAIR, CROSSHAIR_SIZE);
|
||||||
|
let image_alt = ui.hui.add_image(TextureFormat::Grayscale, CROSSHAIR_ALT, CROSSHAIR_SIZE);
|
||||||
|
storages.add_unique(CrosshairImage(image, image_alt));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_crosshair(
|
||||||
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
|
crosshair: UniqueView<CrosshairImage>,
|
||||||
|
size: UniqueView<WindowSize>,
|
||||||
|
player: View<MainPlayer>,
|
||||||
|
raycast: View<LookingAtBlock>,
|
||||||
|
settings: UniqueView<GameSettings>,
|
||||||
|
) {
|
||||||
|
let mut active = !settings.dynamic_crosshair;
|
||||||
|
if settings.dynamic_crosshair {
|
||||||
|
if let Some((_, raycast)) = (&player, &raycast).iter().next() {
|
||||||
|
active = raycast.0.is_some();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Image::new(if active { crosshair.0 } else { crosshair.1 })
|
||||||
|
.with_color((1., 1., 1., 0.5))
|
||||||
|
.with_size(size!((CROSSHAIR_SIZE * 2)))
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(&mut ui.hui, uvec2(size.0.x & !1, size.0.y & !1).as_vec2());
|
||||||
|
}
|
110
kubi/src/ui/settings_ui.rs
Normal file
110
kubi/src/ui/settings_ui.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use hui::{
|
||||||
|
element::{br::Break, container::Container, slider::Slider, text::Text, UiElementExt},
|
||||||
|
layout::{Alignment, Direction},
|
||||||
|
signal::Signal,
|
||||||
|
frame_rect, size,
|
||||||
|
};
|
||||||
|
use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
|
||||||
|
use winit::keyboard::KeyCode;
|
||||||
|
use crate::{hui_integration::UiState, input::RawKbmInputState, rendering::WindowSize, settings::GameSettings};
|
||||||
|
|
||||||
|
#[derive(Signal)]
|
||||||
|
enum SettingsSignal {
|
||||||
|
SetRenderDistance(u8),
|
||||||
|
SetEnableDynamicCrosshair(bool),
|
||||||
|
SetEnableDebugChunkBorder(bool),
|
||||||
|
SetMouseSensitivity(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_settings_ui(
|
||||||
|
mut ui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
|
size: UniqueView<WindowSize>,
|
||||||
|
mut settings: UniqueViewMut<GameSettings>,
|
||||||
|
kbd: UniqueView<RawKbmInputState>,
|
||||||
|
) {
|
||||||
|
//f1 must be held down to open settings
|
||||||
|
//TODO implement ModalManager instead of this
|
||||||
|
if !kbd.keyboard_state.contains(KeyCode::F1 as u32) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_background((0., 0., 0., 0.5))
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Container::default()
|
||||||
|
.with_background(frame_rect! {
|
||||||
|
color: (0.2, 0.2, 0.2),
|
||||||
|
corner_radius: 8.
|
||||||
|
})
|
||||||
|
.with_size(size!(600, 300))
|
||||||
|
.with_direction(Direction::Horizontal)
|
||||||
|
.with_gap(10.)
|
||||||
|
.with_padding(10.)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%, auto))
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Text::new("Settings")
|
||||||
|
.with_text_size(32)
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_child(ui);
|
||||||
|
Break.add_child(ui);
|
||||||
|
|
||||||
|
Text::new("Render Distance")
|
||||||
|
.add_child(ui);
|
||||||
|
Slider::new(settings.render_distance as f32 / 16.)
|
||||||
|
.with_size(size!(300, auto))
|
||||||
|
.on_change(|f| SettingsSignal::SetRenderDistance((f * 16.).round() as u8))
|
||||||
|
.add_child(ui);
|
||||||
|
Text::new(format!("{} Chunks", settings.render_distance))
|
||||||
|
.add_child(ui);
|
||||||
|
Break.add_child(ui);
|
||||||
|
|
||||||
|
Text::new("Dynamic Crosshair")
|
||||||
|
.add_child(ui);
|
||||||
|
Slider::new(settings.dynamic_crosshair as u32 as f32)
|
||||||
|
.with_size(size!(50, auto))
|
||||||
|
.with_track_height(1.)
|
||||||
|
.with_handle_size((25., 1.))
|
||||||
|
.on_change(|f| SettingsSignal::SetEnableDynamicCrosshair(f >= 0.5))
|
||||||
|
.add_child(ui);
|
||||||
|
Text::new(if settings.dynamic_crosshair { "On" } else { "Off" })
|
||||||
|
.add_child(ui);
|
||||||
|
Break.add_child(ui);
|
||||||
|
|
||||||
|
Text::new("Enable debug chunk border")
|
||||||
|
.add_child(ui);
|
||||||
|
Slider::new(settings.debug_draw_current_chunk_border as u32 as f32)
|
||||||
|
.with_size(size!(50, (Slider::DEFAULT_HEIGHT)))
|
||||||
|
.with_track_height(1.)
|
||||||
|
.with_handle_size((25., 1.))
|
||||||
|
.on_change(|f| SettingsSignal::SetEnableDebugChunkBorder(f >= 0.5))
|
||||||
|
.add_child(ui);
|
||||||
|
Text::new(if settings.debug_draw_current_chunk_border { "On" } else { "Off" })
|
||||||
|
.add_child(ui);
|
||||||
|
Break.add_child(ui);
|
||||||
|
|
||||||
|
Text::new("Mouse Sensitivity")
|
||||||
|
.add_child(ui);
|
||||||
|
Slider::new(settings.mouse_sensitivity / 5.)
|
||||||
|
.with_size(size!(300, (Slider::DEFAULT_HEIGHT)))
|
||||||
|
.on_change(|f| SettingsSignal::SetMouseSensitivity(5. * f))
|
||||||
|
.add_child(ui);
|
||||||
|
Text::new(format!("{:.2}", settings.mouse_sensitivity))
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(&mut ui.hui, size.0.as_vec2());
|
||||||
|
|
||||||
|
ui.hui.process_signals(|signal: SettingsSignal| match signal {
|
||||||
|
SettingsSignal::SetRenderDistance(value) => settings.render_distance = value,
|
||||||
|
SettingsSignal::SetEnableDynamicCrosshair(value) => settings.dynamic_crosshair = value,
|
||||||
|
SettingsSignal::SetEnableDebugChunkBorder(value) => settings.debug_draw_current_chunk_border = value && cfg!(not(target_os = "android")),
|
||||||
|
SettingsSignal::SetMouseSensitivity(value) => settings.mouse_sensitivity = value,
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,69 +1,71 @@
|
||||||
use glam::IVec3;
|
use glam::IVec3;
|
||||||
use glium::{VertexBuffer, IndexBuffer};
|
use glium::{VertexBuffer, IndexBuffer};
|
||||||
use crate::rendering::world::ChunkVertex;
|
use crate::rendering::world::ChunkVertex;
|
||||||
|
|
||||||
pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData};
|
pub use kubi_shared::chunk::{CHUNK_SIZE, BlockData};
|
||||||
|
|
||||||
pub struct ChunkData {
|
pub struct ChunkData {
|
||||||
pub blocks: BlockData,
|
pub blocks: BlockData,
|
||||||
//pub has_renderable_blocks: bool,
|
//pub has_renderable_blocks: bool,
|
||||||
}
|
}
|
||||||
impl ChunkData {
|
impl ChunkData {
|
||||||
// pub fn update_metadata(&mut self) {
|
// pub fn update_metadata(&mut self) {
|
||||||
// todo!()
|
// todo!()
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChunkMesh {
|
pub struct ChunkMesh {
|
||||||
pub vertex_buffer: VertexBuffer<ChunkVertex>,
|
pub vertex_buffer: VertexBuffer<ChunkVertex>,
|
||||||
pub index_buffer: IndexBuffer<u32>,
|
pub index_buffer: IndexBuffer<u32>,
|
||||||
}
|
pub trans_vertex_buffer: VertexBuffer<ChunkVertex>,
|
||||||
|
pub trans_index_buffer: IndexBuffer<u32>,
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
}
|
||||||
pub enum CurrentChunkState {
|
|
||||||
#[default]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||||
Nothing,
|
pub enum CurrentChunkState {
|
||||||
Loading,
|
#[default]
|
||||||
Loaded,
|
Nothing,
|
||||||
CalculatingMesh,
|
Loading,
|
||||||
Rendered,
|
Loaded,
|
||||||
RecalculatingMesh,
|
CalculatingMesh,
|
||||||
Unloading,
|
Rendered,
|
||||||
}
|
RecalculatingMesh,
|
||||||
|
Unloading,
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
}
|
||||||
pub enum DesiredChunkState {
|
|
||||||
#[default]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
|
||||||
Nothing,
|
pub enum DesiredChunkState {
|
||||||
Loaded,
|
#[default]
|
||||||
Rendered,
|
Nothing,
|
||||||
ToUnload,
|
Loaded,
|
||||||
}
|
Rendered,
|
||||||
impl DesiredChunkState {
|
ToUnload,
|
||||||
pub fn matches_current(self, current: CurrentChunkState) -> bool {
|
}
|
||||||
(matches!(self, DesiredChunkState::Nothing) && matches!(current, CurrentChunkState::Nothing)) ||
|
impl DesiredChunkState {
|
||||||
(matches!(self, DesiredChunkState::Loaded) && matches!(current, CurrentChunkState::Loaded)) ||
|
pub fn matches_current(self, current: CurrentChunkState) -> bool {
|
||||||
(matches!(self, DesiredChunkState::Rendered) && matches!(current, CurrentChunkState::Rendered))
|
(matches!(self, DesiredChunkState::Nothing) && matches!(current, CurrentChunkState::Nothing)) ||
|
||||||
}
|
(matches!(self, DesiredChunkState::Loaded) && matches!(current, CurrentChunkState::Loaded)) ||
|
||||||
}
|
(matches!(self, DesiredChunkState::Rendered) && matches!(current, CurrentChunkState::Rendered))
|
||||||
|
}
|
||||||
pub struct Chunk {
|
}
|
||||||
pub position: IVec3,
|
|
||||||
pub block_data: Option<ChunkData>,
|
pub struct Chunk {
|
||||||
pub mesh_index: Option<usize>,
|
pub position: IVec3,
|
||||||
pub current_state: CurrentChunkState,
|
pub block_data: Option<ChunkData>,
|
||||||
pub desired_state: DesiredChunkState,
|
pub mesh_index: Option<usize>,
|
||||||
pub mesh_dirty: bool,
|
pub current_state: CurrentChunkState,
|
||||||
}
|
pub desired_state: DesiredChunkState,
|
||||||
impl Chunk {
|
pub mesh_dirty: bool,
|
||||||
pub fn new(position: IVec3) -> Self {
|
}
|
||||||
Self {
|
impl Chunk {
|
||||||
position,
|
pub fn new(position: IVec3) -> Self {
|
||||||
block_data: None,
|
Self {
|
||||||
mesh_index: None,
|
position,
|
||||||
current_state: Default::default(),
|
block_data: None,
|
||||||
desired_state: Default::default(),
|
mesh_index: None,
|
||||||
mesh_dirty: false,
|
current_state: Default::default(),
|
||||||
}
|
desired_state: Default::default(),
|
||||||
}
|
mesh_dirty: false,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,262 +1,267 @@
|
||||||
use glam::{IVec3, ivec3};
|
use glam::{IVec3, ivec3};
|
||||||
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
use glium::{VertexBuffer, IndexBuffer, index::PrimitiveType};
|
||||||
use kubi_shared::networking::messages::ClientToServerMessage;
|
use kubi_shared::networking::messages::ClientToServerMessage;
|
||||||
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 crate::{
|
use crate::{
|
||||||
player::MainPlayer,
|
player::MainPlayer,
|
||||||
transform::Transform,
|
transform::Transform,
|
||||||
settings::GameSettings,
|
settings::GameSettings,
|
||||||
rendering::Renderer,
|
rendering::Renderer,
|
||||||
state::GameState,
|
state::GameState,
|
||||||
networking::UdpClient,
|
networking::UdpClient,
|
||||||
};
|
};
|
||||||
use super::{
|
use super::{
|
||||||
ChunkStorage, ChunkMeshStorage,
|
ChunkStorage, ChunkMeshStorage,
|
||||||
chunk::{Chunk, DesiredChunkState, CHUNK_SIZE, ChunkMesh, CurrentChunkState, ChunkData},
|
chunk::{Chunk, DesiredChunkState, CHUNK_SIZE, ChunkMesh, CurrentChunkState, ChunkData},
|
||||||
tasks::{ChunkTaskManager, ChunkTaskResponse, ChunkTask},
|
tasks::{ChunkTaskManager, ChunkTaskResponse, ChunkTask},
|
||||||
queue::BlockUpdateQueue
|
queue::BlockUpdateQueue
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_CHUNK_OPS_INGAME: usize = 6;
|
const MAX_CHUNK_OPS_INGAME: usize = 6;
|
||||||
const MAX_CHUNK_OPS: usize = 32;
|
const MAX_CHUNK_OPS: usize = 32;
|
||||||
|
|
||||||
pub fn update_loaded_world_around_player() -> Workload {
|
pub fn update_loaded_world_around_player() -> Workload {
|
||||||
(
|
(
|
||||||
update_chunks_if_player_moved,
|
update_chunks_if_player_moved,
|
||||||
unload_downgrade_chunks,
|
unload_downgrade_chunks,
|
||||||
start_required_tasks,
|
start_required_tasks,
|
||||||
process_completed_tasks,
|
process_completed_tasks,
|
||||||
).into_sequential_workload()
|
).into_sequential_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_chunks_if_player_moved(
|
pub fn update_chunks_if_player_moved(
|
||||||
v_settings: UniqueView<GameSettings>,
|
v_settings: UniqueView<GameSettings>,
|
||||||
v_local_player: View<MainPlayer>,
|
v_local_player: View<MainPlayer>,
|
||||||
v_transform: View<Transform, track::All>,
|
v_transform: View<Transform, track::All>,
|
||||||
mut vm_world: UniqueViewMut<ChunkStorage>,
|
mut vm_world: UniqueViewMut<ChunkStorage>,
|
||||||
) {
|
) {
|
||||||
//Check if the player actually moved
|
//Check if the player actually moved
|
||||||
//TODO fix this also triggers on rotation, only activate when the player crosses the chunk border
|
//TODO fix this also triggers on rotation, only activate when the player crosses the chunk border
|
||||||
let Some((_, transform)) = (&v_local_player, v_transform.inserted_or_modified()).iter().next() else {
|
let Some((_, transform)) = (&v_local_player, v_transform.inserted_or_modified()).iter().next() else {
|
||||||
return
|
return
|
||||||
};
|
};
|
||||||
|
|
||||||
//Read game settings
|
//Read game settings
|
||||||
let load_distance = (v_settings.render_distance + 1) as i32;
|
let load_distance = (v_settings.render_distance + 1) as i32;
|
||||||
|
|
||||||
//If it did, get it's position and current chunk
|
//If it did, get it's position and current chunk
|
||||||
let player_position = transform.0.to_scale_rotation_translation().2;
|
let player_position = transform.0.to_scale_rotation_translation().2;
|
||||||
let player_position_ivec3 = player_position.as_ivec3();
|
let player_position_ivec3 = player_position.as_ivec3();
|
||||||
let player_at_chunk = ivec3(
|
let player_at_chunk = ivec3(
|
||||||
player_position_ivec3.x.div_euclid(CHUNK_SIZE as i32),
|
player_position_ivec3.x.div_euclid(CHUNK_SIZE as i32),
|
||||||
player_position_ivec3.y.div_euclid(CHUNK_SIZE as i32),
|
player_position_ivec3.y.div_euclid(CHUNK_SIZE as i32),
|
||||||
player_position_ivec3.z.div_euclid(CHUNK_SIZE as i32),
|
player_position_ivec3.z.div_euclid(CHUNK_SIZE as i32),
|
||||||
);
|
);
|
||||||
|
|
||||||
//Then, mark *ALL* chunks with ToUnload
|
//Then, mark *ALL* chunks with ToUnload
|
||||||
for (_, chunk) in &mut vm_world.chunks {
|
for (_, chunk) in &mut vm_world.chunks {
|
||||||
chunk.desired_state = DesiredChunkState::ToUnload;
|
chunk.desired_state = DesiredChunkState::ToUnload;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Then mark chunks that are near to the player
|
//Then mark chunks that are near to the player
|
||||||
for x in -load_distance..=load_distance {
|
for x in -load_distance..=load_distance {
|
||||||
for y in -load_distance..=load_distance {
|
for y in -load_distance..=load_distance {
|
||||||
for z in -load_distance..=load_distance {
|
for z in -load_distance..=load_distance {
|
||||||
let chunk_pos_offset = ivec3(x, y, z);
|
let chunk_pos_offset = ivec3(x, y, z);
|
||||||
let chunk_pos = player_at_chunk + chunk_pos_offset;
|
let chunk_pos = player_at_chunk + chunk_pos_offset;
|
||||||
let is_border = {
|
let is_border = {
|
||||||
chunk_pos_offset.x.abs() == load_distance ||
|
chunk_pos_offset.x.abs() == load_distance ||
|
||||||
chunk_pos_offset.y.abs() == load_distance ||
|
chunk_pos_offset.y.abs() == load_distance ||
|
||||||
chunk_pos_offset.z.abs() == load_distance
|
chunk_pos_offset.z.abs() == load_distance
|
||||||
};
|
};
|
||||||
//If chunk doesn't exist create it
|
//If chunk doesn't exist create it
|
||||||
let chunk = match vm_world.chunks.get_mut(&chunk_pos) {
|
let chunk = match vm_world.chunks.get_mut(&chunk_pos) {
|
||||||
Some(chunk) => chunk,
|
Some(chunk) => chunk,
|
||||||
None => {
|
None => {
|
||||||
let chunk = Chunk::new(chunk_pos);
|
let chunk = Chunk::new(chunk_pos);
|
||||||
vm_world.chunks.insert_unique_unchecked(chunk_pos, chunk);
|
vm_world.chunks.insert_unique_unchecked(chunk_pos, chunk);
|
||||||
vm_world.chunks.get_mut(&chunk_pos).unwrap()
|
vm_world.chunks.get_mut(&chunk_pos).unwrap()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let desired = match is_border {
|
let desired = match is_border {
|
||||||
true => DesiredChunkState::Loaded,
|
true => DesiredChunkState::Loaded,
|
||||||
false => DesiredChunkState::Rendered,
|
false => DesiredChunkState::Rendered,
|
||||||
};
|
};
|
||||||
chunk.desired_state = desired;
|
chunk.desired_state = desired;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unload_downgrade_chunks(
|
fn unload_downgrade_chunks(
|
||||||
mut vm_world: UniqueViewMut<ChunkStorage>,
|
mut vm_world: UniqueViewMut<ChunkStorage>,
|
||||||
mut vm_meshes: NonSendSync<UniqueViewMut<ChunkMeshStorage>>
|
mut vm_meshes: NonSendSync<UniqueViewMut<ChunkMeshStorage>>
|
||||||
) {
|
) {
|
||||||
if !vm_world.is_modified() {
|
if !vm_world.is_modified() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//TODO refactor this
|
//TODO refactor this
|
||||||
//TODO unsubscibe if in multiplayer
|
//TODO unsubscibe if in multiplayer
|
||||||
vm_world.chunks.retain(|_, chunk| {
|
vm_world.chunks.retain(|_, chunk| {
|
||||||
if chunk.desired_state == DesiredChunkState::ToUnload {
|
if chunk.desired_state == DesiredChunkState::ToUnload {
|
||||||
if let Some(mesh_index) = chunk.mesh_index {
|
if let Some(mesh_index) = chunk.mesh_index {
|
||||||
vm_meshes.remove(mesh_index).unwrap();
|
vm_meshes.remove(mesh_index).unwrap();
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
match chunk.desired_state {
|
match chunk.desired_state {
|
||||||
DesiredChunkState::Loaded if matches!(chunk.current_state, CurrentChunkState::Rendered | CurrentChunkState::CalculatingMesh | CurrentChunkState::RecalculatingMesh) => {
|
DesiredChunkState::Loaded if matches!(chunk.current_state, CurrentChunkState::Rendered | CurrentChunkState::CalculatingMesh | CurrentChunkState::RecalculatingMesh) => {
|
||||||
if let Some(mesh_index) = chunk.mesh_index {
|
if let Some(mesh_index) = chunk.mesh_index {
|
||||||
vm_meshes.remove(mesh_index).unwrap();
|
vm_meshes.remove(mesh_index).unwrap();
|
||||||
}
|
}
|
||||||
chunk.mesh_index = None;
|
chunk.mesh_index = None;
|
||||||
chunk.current_state = CurrentChunkState::Loaded;
|
chunk.current_state = CurrentChunkState::Loaded;
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_required_tasks(
|
fn start_required_tasks(
|
||||||
task_manager: UniqueView<ChunkTaskManager>,
|
task_manager: UniqueView<ChunkTaskManager>,
|
||||||
mut udp_client: Option<UniqueViewMut<UdpClient>>,
|
mut udp_client: Option<UniqueViewMut<UdpClient>>,
|
||||||
mut world: UniqueViewMut<ChunkStorage>,
|
mut world: UniqueViewMut<ChunkStorage>,
|
||||||
) {
|
) {
|
||||||
if !world.is_modified() {
|
if !world.is_modified() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//HACK: cant iterate over chunks.keys() or chunk directly!
|
//HACK: cant iterate over chunks.keys() or chunk directly!
|
||||||
let hashmap_keys: Vec<IVec3> = world.chunks.keys().copied().collect();
|
let hashmap_keys: Vec<IVec3> = world.chunks.keys().copied().collect();
|
||||||
for position in hashmap_keys {
|
for position in hashmap_keys {
|
||||||
let chunk = world.chunks.get(&position).unwrap();
|
let chunk = world.chunks.get(&position).unwrap();
|
||||||
match chunk.desired_state {
|
match chunk.desired_state {
|
||||||
DesiredChunkState::Loaded | DesiredChunkState::Rendered if chunk.current_state == CurrentChunkState::Nothing => {
|
DesiredChunkState::Loaded | DesiredChunkState::Rendered if chunk.current_state == CurrentChunkState::Nothing => {
|
||||||
//start load task
|
//start load task
|
||||||
if let Some(client) = &mut udp_client {
|
if let Some(client) = &mut udp_client {
|
||||||
client.0.send(
|
client.0.send(
|
||||||
postcard::to_allocvec(&ClientToServerMessage::ChunkSubRequest {
|
postcard::to_allocvec(&ClientToServerMessage::ChunkSubRequest {
|
||||||
chunk: position,
|
chunk: position,
|
||||||
}).unwrap().into_boxed_slice(),
|
}).unwrap().into_boxed_slice(),
|
||||||
0,
|
0,
|
||||||
SendMode::Reliable
|
SendMode::Reliable
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
task_manager.spawn_task(ChunkTask::LoadChunk {
|
task_manager.spawn_task(ChunkTask::LoadChunk {
|
||||||
seed: 0xbeef_face_dead_cafe,
|
seed: 0xbeef_face_dead_cafe,
|
||||||
position
|
position
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//Update chunk state
|
//Update chunk state
|
||||||
let chunk = world.chunks.get_mut(&position).unwrap();
|
let chunk = world.chunks.get_mut(&position).unwrap();
|
||||||
chunk.current_state = CurrentChunkState::Loading;
|
chunk.current_state = CurrentChunkState::Loading;
|
||||||
// ===========
|
// ===========
|
||||||
//log::trace!("Started loading chunk {position}");
|
//log::trace!("Started loading chunk {position}");
|
||||||
},
|
},
|
||||||
DesiredChunkState::Rendered if (chunk.current_state == CurrentChunkState::Loaded || chunk.mesh_dirty) => {
|
DesiredChunkState::Rendered if (chunk.current_state == CurrentChunkState::Loaded || chunk.mesh_dirty) => {
|
||||||
//get needed data
|
//get needed data
|
||||||
let Some(neighbors) = world.neighbors_all(position) else {
|
let Some(neighbors) = world.neighbors_all(position) else {
|
||||||
continue
|
continue
|
||||||
};
|
};
|
||||||
let Some(data) = neighbors.mesh_data() else {
|
let Some(data) = neighbors.mesh_data() else {
|
||||||
continue
|
continue
|
||||||
};
|
};
|
||||||
//spawn task
|
//spawn task
|
||||||
task_manager.spawn_task(ChunkTask::GenerateMesh { data, position });
|
task_manager.spawn_task(ChunkTask::GenerateMesh { data, position });
|
||||||
//Update chunk state
|
//Update chunk state
|
||||||
let chunk = world.chunks.get_mut(&position).unwrap();
|
let chunk = world.chunks.get_mut(&position).unwrap();
|
||||||
if chunk.mesh_dirty {
|
if chunk.mesh_dirty {
|
||||||
chunk.current_state = CurrentChunkState::RecalculatingMesh;
|
chunk.current_state = CurrentChunkState::RecalculatingMesh;
|
||||||
} else {
|
} else {
|
||||||
chunk.current_state = CurrentChunkState::CalculatingMesh;
|
chunk.current_state = CurrentChunkState::CalculatingMesh;
|
||||||
}
|
}
|
||||||
chunk.mesh_dirty = false;
|
chunk.mesh_dirty = false;
|
||||||
// ===========
|
// ===========
|
||||||
//log::trace!("Started generating mesh for chunk {position}");
|
//log::trace!("Started generating mesh for chunk {position}");
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_completed_tasks(
|
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: NonSendSync<UniqueView<Renderer>>,
|
||||||
state: UniqueView<GameState>,
|
state: UniqueView<GameState>,
|
||||||
mut queue: UniqueViewMut<BlockUpdateQueue>,
|
mut queue: UniqueViewMut<BlockUpdateQueue>,
|
||||||
) {
|
) {
|
||||||
let mut ops: usize = 0;
|
let mut ops: usize = 0;
|
||||||
while let Some(res) = task_manager.receive() {
|
while let Some(res) = task_manager.receive() {
|
||||||
match res {
|
match res {
|
||||||
ChunkTaskResponse::LoadedChunk { position, chunk_data, mut queued } => {
|
ChunkTaskResponse::LoadedChunk { position, chunk_data, mut queued } => {
|
||||||
//check if chunk exists
|
//check if chunk exists
|
||||||
let Some(chunk) = world.chunks.get_mut(&position) else {
|
let Some(chunk) = world.chunks.get_mut(&position) else {
|
||||||
log::warn!("blocks data discarded: chunk doesn't exist");
|
log::warn!("blocks data discarded: chunk doesn't exist");
|
||||||
return
|
return
|
||||||
};
|
};
|
||||||
|
|
||||||
//check if chunk still wants it
|
//check if chunk still wants it
|
||||||
if !matches!(chunk.desired_state, DesiredChunkState::Loaded | DesiredChunkState::Rendered) {
|
if !matches!(chunk.desired_state, DesiredChunkState::Loaded | DesiredChunkState::Rendered) {
|
||||||
log::warn!("block data discarded: state undesirable: {:?}", chunk.desired_state);
|
log::warn!("block data discarded: state undesirable: {:?}", chunk.desired_state);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//set the block data
|
//set the block data
|
||||||
chunk.block_data = Some(ChunkData {
|
chunk.block_data = Some(ChunkData {
|
||||||
blocks: chunk_data
|
blocks: chunk_data
|
||||||
});
|
});
|
||||||
|
|
||||||
//update chunk state
|
//update chunk state
|
||||||
chunk.current_state = CurrentChunkState::Loaded;
|
chunk.current_state = CurrentChunkState::Loaded;
|
||||||
|
|
||||||
//push queued blocks
|
//push queued blocks
|
||||||
queue.0.append(&mut queued);
|
queue.0.append(&mut queued);
|
||||||
drop(queued); //`queued` is empty after `append`
|
drop(queued); //`queued` is empty after `append`
|
||||||
|
|
||||||
//increase ops counter
|
//increase ops counter
|
||||||
ops += 1;
|
ops += 1;
|
||||||
},
|
},
|
||||||
ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } => {
|
ChunkTaskResponse::GeneratedMesh {
|
||||||
//check if chunk exists
|
position,
|
||||||
let Some(chunk) = world.chunks.get_mut(&position) else {
|
vertices, indices,
|
||||||
log::warn!("mesh discarded: chunk doesn't exist");
|
trans_vertices, trans_indices,
|
||||||
return
|
} => {
|
||||||
};
|
//check if chunk exists
|
||||||
|
let Some(chunk) = world.chunks.get_mut(&position) else {
|
||||||
//check if chunk still wants it
|
log::warn!("mesh discarded: chunk doesn't exist");
|
||||||
if chunk.desired_state != DesiredChunkState::Rendered {
|
return
|
||||||
log::warn!("mesh discarded: state undesirable: {:?}", chunk.desired_state);
|
};
|
||||||
return
|
|
||||||
}
|
//check if chunk still wants it
|
||||||
|
if chunk.desired_state != DesiredChunkState::Rendered {
|
||||||
//apply the mesh
|
log::warn!("mesh discarded: state undesirable: {:?}", chunk.desired_state);
|
||||||
let vertex_buffer = VertexBuffer::immutable(&renderer.display, &vertices).unwrap();
|
return
|
||||||
let index_buffer = IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &indexes).unwrap();
|
}
|
||||||
let mesh = ChunkMesh {
|
|
||||||
vertex_buffer,
|
//apply the mesh
|
||||||
index_buffer,
|
//TODO: Skip if mesh is empty? (i.e. set to None)
|
||||||
};
|
let mesh = ChunkMesh {
|
||||||
if let Some(index) = chunk.mesh_index {
|
vertex_buffer: VertexBuffer::immutable(&renderer.display, &vertices).unwrap(),
|
||||||
meshes.update(index, mesh).expect("Mesh update failed");
|
index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &indices).unwrap(),
|
||||||
} else {
|
trans_vertex_buffer: VertexBuffer::immutable(&renderer.display, &trans_vertices).unwrap(),
|
||||||
let mesh_index = meshes.insert(mesh);
|
trans_index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &trans_indices).unwrap(),
|
||||||
chunk.mesh_index = Some(mesh_index);
|
};
|
||||||
}
|
if let Some(index) = chunk.mesh_index {
|
||||||
|
meshes.update(index, mesh).expect("Mesh update failed");
|
||||||
//update chunk state
|
} else {
|
||||||
chunk.current_state = CurrentChunkState::Rendered;
|
let mesh_index = meshes.insert(mesh);
|
||||||
|
chunk.mesh_index = Some(mesh_index);
|
||||||
//increase ops counter
|
}
|
||||||
ops += 1;
|
|
||||||
}
|
//update chunk state
|
||||||
}
|
chunk.current_state = CurrentChunkState::Rendered;
|
||||||
if ops >= match *state {
|
|
||||||
GameState::InGame => MAX_CHUNK_OPS_INGAME,
|
//increase ops counter
|
||||||
_ => MAX_CHUNK_OPS,
|
ops += 1;
|
||||||
} { break }
|
}
|
||||||
}
|
}
|
||||||
}
|
if ops >= match *state {
|
||||||
|
GameState::InGame => MAX_CHUNK_OPS_INGAME,
|
||||||
|
_ => MAX_CHUNK_OPS,
|
||||||
|
} { break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use glam::{IVec3, ivec3};
|
use glam::{IVec3, ivec3};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use kubi_shared::block::{Block, RenderType};
|
use kubi_shared::block::{Block, BlockTexture, RenderType, Transparency};
|
||||||
use crate::world::chunk::CHUNK_SIZE;
|
use crate::world::chunk::CHUNK_SIZE;
|
||||||
use crate::rendering::world::ChunkVertex;
|
use crate::rendering::world::ChunkVertex;
|
||||||
|
|
||||||
|
@ -10,7 +10,10 @@ mod builder;
|
||||||
use data::MeshGenData;
|
use data::MeshGenData;
|
||||||
use builder::{MeshBuilder, CubeFace, DiagonalFace};
|
use builder::{MeshBuilder, CubeFace, DiagonalFace};
|
||||||
|
|
||||||
pub fn generate_mesh(data: MeshGenData) -> (Vec<ChunkVertex>, Vec<u32>) {
|
pub fn generate_mesh(data: MeshGenData) -> (
|
||||||
|
(Vec<ChunkVertex>, Vec<u32>),
|
||||||
|
(Vec<ChunkVertex>, Vec<u32>),
|
||||||
|
) {
|
||||||
let get_block = |pos: IVec3| -> Block {
|
let get_block = |pos: IVec3| -> Block {
|
||||||
if pos.x < 0 {
|
if pos.x < 0 {
|
||||||
data.block_data_neg_x[(CHUNK_SIZE as i32 + pos.x) as usize][pos.y as usize][pos.z as usize]
|
data.block_data_neg_x[(CHUNK_SIZE as i32 + pos.x) as usize][pos.y as usize][pos.z as usize]
|
||||||
|
@ -30,6 +33,7 @@ pub fn generate_mesh(data: MeshGenData) -> (Vec<ChunkVertex>, Vec<u32>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut builder = MeshBuilder::new();
|
let mut builder = MeshBuilder::new();
|
||||||
|
let mut trans_builder = MeshBuilder::new();
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -39,22 +43,23 @@ pub fn generate_mesh(data: MeshGenData) -> (Vec<ChunkVertex>, Vec<u32>) {
|
||||||
let descriptor = block.descriptor();
|
let descriptor = block.descriptor();
|
||||||
match descriptor.render {
|
match descriptor.render {
|
||||||
RenderType::None => continue,
|
RenderType::None => continue,
|
||||||
RenderType::SolidBlock(textures) | RenderType::BinaryTransparency(textures) => {
|
RenderType::Cube(trans_type, textures) => {
|
||||||
for face in CubeFace::iter() {
|
for face in CubeFace::iter() {
|
||||||
let facing_direction = face.normal();
|
let facing_direction = face.normal();
|
||||||
let facing_coord = coord + facing_direction;
|
let facing_coord = coord + facing_direction;
|
||||||
let facing_block = get_block(facing_coord);
|
let facing_block = get_block(facing_coord);
|
||||||
let facing_descriptor = facing_block.descriptor();
|
let facing_descriptor = facing_block.descriptor();
|
||||||
let face_obstructed = match descriptor.render {
|
let face_obstructed = match trans_type {
|
||||||
RenderType::SolidBlock(_) => matches!(facing_descriptor.render, RenderType::SolidBlock(_)),
|
Transparency::Solid => matches!(facing_descriptor.render, RenderType::Cube(Transparency::Solid, _)),
|
||||||
RenderType::BinaryTransparency(_) => {
|
Transparency::Binary | Transparency::Trans => {
|
||||||
match facing_descriptor.render {
|
match facing_descriptor.render {
|
||||||
RenderType::SolidBlock(_) => true,
|
RenderType::Cube(trans_type, _) => match trans_type {
|
||||||
RenderType::BinaryTransparency(_) => block == facing_block,
|
Transparency::Solid => true,
|
||||||
|
Transparency::Binary | Transparency::Trans => block == facing_block,
|
||||||
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
};
|
||||||
if !face_obstructed {
|
if !face_obstructed {
|
||||||
let face_texture = match face {
|
let face_texture = match face {
|
||||||
|
@ -65,11 +70,15 @@ pub fn generate_mesh(data: MeshGenData) -> (Vec<ChunkVertex>, Vec<u32>) {
|
||||||
CubeFace::Back => textures.back,
|
CubeFace::Back => textures.back,
|
||||||
CubeFace::Bottom => textures.bottom,
|
CubeFace::Bottom => textures.bottom,
|
||||||
};
|
};
|
||||||
builder.add_face(face, coord, face_texture as u8);
|
let target_builder = match trans_type {
|
||||||
|
Transparency::Trans => &mut trans_builder,
|
||||||
|
_ => &mut builder,
|
||||||
|
};
|
||||||
|
target_builder.add_face(face, coord, face_texture as u8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RenderType::CrossShape(textures) => {
|
RenderType::Cross(textures) => {
|
||||||
builder.add_diagonal_face(
|
builder.add_diagonal_face(
|
||||||
coord,
|
coord,
|
||||||
DiagonalFace::LeftZ,
|
DiagonalFace::LeftZ,
|
||||||
|
@ -88,5 +97,5 @@ pub fn generate_mesh(data: MeshGenData) -> (Vec<ChunkVertex>, Vec<u32>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.finish()
|
(builder.finish(), trans_builder.finish())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +1,80 @@
|
||||||
use flume::{Sender, Receiver};
|
use flume::{Sender, Receiver};
|
||||||
use glam::IVec3;
|
use glam::IVec3;
|
||||||
use kubi_shared::queue::QueuedBlock;
|
use kubi_shared::queue::QueuedBlock;
|
||||||
use shipyard::Unique;
|
use shipyard::Unique;
|
||||||
use rayon::{ThreadPool, ThreadPoolBuilder};
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
||||||
use super::{
|
use super::{
|
||||||
chunk::BlockData,
|
chunk::BlockData,
|
||||||
mesh::{generate_mesh, data::MeshGenData},
|
mesh::{generate_mesh, data::MeshGenData},
|
||||||
worldgen::generate_world,
|
worldgen::generate_world,
|
||||||
};
|
};
|
||||||
use crate::rendering::world::ChunkVertex;
|
use crate::rendering::world::ChunkVertex;
|
||||||
|
|
||||||
pub enum ChunkTask {
|
pub enum ChunkTask {
|
||||||
LoadChunk {
|
LoadChunk {
|
||||||
seed: u64,
|
seed: u64,
|
||||||
position: IVec3
|
position: IVec3
|
||||||
},
|
},
|
||||||
GenerateMesh {
|
GenerateMesh {
|
||||||
position: IVec3,
|
position: IVec3,
|
||||||
data: MeshGenData
|
data: MeshGenData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub enum ChunkTaskResponse {
|
pub enum ChunkTaskResponse {
|
||||||
LoadedChunk {
|
LoadedChunk {
|
||||||
position: IVec3,
|
position: IVec3,
|
||||||
chunk_data: BlockData,
|
chunk_data: BlockData,
|
||||||
queued: Vec<QueuedBlock>
|
queued: Vec<QueuedBlock>
|
||||||
},
|
},
|
||||||
GeneratedMesh {
|
GeneratedMesh {
|
||||||
position: IVec3,
|
position: IVec3,
|
||||||
vertices: Vec<ChunkVertex>,
|
vertices: Vec<ChunkVertex>,
|
||||||
indexes: Vec<u32>
|
indices: Vec<u32>,
|
||||||
},
|
trans_vertices: Vec<ChunkVertex>,
|
||||||
}
|
trans_indices: Vec<u32>,
|
||||||
|
},
|
||||||
#[derive(Unique)]
|
}
|
||||||
pub struct ChunkTaskManager {
|
|
||||||
channel: (Sender<ChunkTaskResponse>, Receiver<ChunkTaskResponse>),
|
#[derive(Unique)]
|
||||||
pool: ThreadPool,
|
pub struct ChunkTaskManager {
|
||||||
}
|
channel: (Sender<ChunkTaskResponse>, Receiver<ChunkTaskResponse>),
|
||||||
impl ChunkTaskManager {
|
pool: ThreadPool,
|
||||||
pub fn new() -> Self {
|
}
|
||||||
Self {
|
impl ChunkTaskManager {
|
||||||
channel: flume::unbounded::<ChunkTaskResponse>(), //maybe put a bound or even bound(0)?
|
pub fn new() -> Self {
|
||||||
pool: ThreadPoolBuilder::new().num_threads(4).build().unwrap()
|
Self {
|
||||||
}
|
channel: flume::unbounded::<ChunkTaskResponse>(), //maybe put a bound or even bound(0)?
|
||||||
}
|
pool: ThreadPoolBuilder::new().num_threads(4).build().unwrap()
|
||||||
pub fn add_sussy_response(&self, response: ChunkTaskResponse) {
|
}
|
||||||
// this WILL get stuck if the channel is bounded
|
}
|
||||||
// don't make the channel bounded ever
|
pub fn add_sussy_response(&self, response: ChunkTaskResponse) {
|
||||||
self.channel.0.send(response).unwrap()
|
// this WILL get stuck if the channel is bounded
|
||||||
}
|
// don't make the channel bounded ever
|
||||||
pub fn spawn_task(&self, task: ChunkTask) {
|
self.channel.0.send(response).unwrap()
|
||||||
let sender = self.channel.0.clone();
|
}
|
||||||
self.pool.spawn(move || {
|
pub fn spawn_task(&self, task: ChunkTask) {
|
||||||
let _ = sender.send(match task {
|
let sender = self.channel.0.clone();
|
||||||
ChunkTask::GenerateMesh { position, data } => {
|
self.pool.spawn(move || {
|
||||||
let (vertices, indexes) = generate_mesh(data);
|
let _ = sender.send(match task {
|
||||||
ChunkTaskResponse::GeneratedMesh { position, vertices, indexes }
|
ChunkTask::GenerateMesh { position, data } => {
|
||||||
},
|
let (
|
||||||
ChunkTask::LoadChunk { position, seed } => {
|
(vertices, indices),
|
||||||
let (chunk_data, queued) = generate_world(position, seed);
|
(trans_vertices, trans_indices),
|
||||||
ChunkTaskResponse::LoadedChunk { position, chunk_data, queued }
|
) = generate_mesh(data);
|
||||||
}
|
ChunkTaskResponse::GeneratedMesh {
|
||||||
});
|
position,
|
||||||
});
|
vertices, indices,
|
||||||
}
|
trans_vertices, trans_indices,
|
||||||
pub fn receive(&self) -> Option<ChunkTaskResponse> {
|
}
|
||||||
self.channel.1.try_recv().ok()
|
},
|
||||||
}
|
ChunkTask::LoadChunk { position, seed } => {
|
||||||
}
|
let (chunk_data, queued) = generate_world(position, seed);
|
||||||
|
ChunkTaskResponse::LoadedChunk { position, chunk_data, queued }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn receive(&self) -> Option<ChunkTaskResponse> {
|
||||||
|
self.channel.1.try_recv().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue