Compare commits

...

14 commits

Author SHA1 Message Date
griffi-gh 8e39fc24fd more settings 2024-05-02 02:43:58 +02:00
griffi-gh a63deb5173 add tip to chat 2024-05-02 02:22:55 +02:00
griffi-gh 8c5b0aa47e integrate hui-winit 2024-05-02 02:20:54 +02:00
griffi-gh 8e907a9fbc if not locked dont move camera 2024-05-02 02:07:49 +02:00
griffi-gh ce5dd6f011 move cursor to center on unlock 2024-05-02 02:07:36 +02:00
griffi-gh a5fae8ad2b wip settings 2024-05-02 02:06:23 +02:00
griffi-gh bb9107e912 ~~scuffed sorting?~~ 2024-05-02 01:42:07 +02:00
griffi-gh dd6f52edb5 scuffed sorting 2024-05-02 01:38:58 +02:00
griffi-gh 6cde878a50 fix shader 2024-05-02 01:24:27 +02:00
griffi-gh 62c3c2105e refactor transparency descriptor 2024-05-02 01:21:17 +02:00
griffi-gh e1f1ba706c set discard_alpha 2024-05-02 01:13:21 +02:00
griffi-gh 204bb882a6 alt shape 2024-05-02 01:05:34 +02:00
griffi-gh 610d309ead dynamic crosshair stuff 2024-05-02 00:43:24 +02:00
griffi-gh 66d3ea656b add trans rendering and crosshair 2024-05-02 00:32:43 +02:00
20 changed files with 846 additions and 537 deletions

12
Cargo.lock generated
View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

View file

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

View file

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

View file

@ -8,22 +8,19 @@ 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.)) {
// return vec4(b.xyz, 0.);
// }
// return a;
// }
void main() { void main() {
// base color from texture // base color from texture
color = texture(tex, vec3(v_uv, v_tex_index)); color = texture(tex, vec3(v_uv, v_tex_index));
// discard transparent pixels
if (color.w < 0.5) discard; // discard fully transparent pixels
if (discard_alpha ? (color.w < 0.5) : (color.w == 0.)) discard;
//basic "lighting" //basic "lighting"
float light = abs(v_normal.x) + .8 * abs(v_normal.y) + .6 * abs(v_normal.z); float light = abs(v_normal.x) + .8 * abs(v_normal.y) + .6 * abs(v_normal.z);
color *= vec4(vec3(light), 1.); color *= vec4(vec3(light), 1.);
//discard alpha //discard alpha
color.w = 1.; if (discard_alpha) color.w = 1.;
} }

View file

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

View file

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

View file

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

View file

@ -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)]

View file

@ -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() {

View file

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

View file

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

View file

@ -19,6 +19,7 @@ pub struct GameSettings {
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 { impl Default for GameSettings {
fn default() -> Self { fn default() -> Self {
@ -34,6 +35,7 @@ impl Default for GameSettings {
}, },
mouse_sensitivity: 1., mouse_sensitivity: 1.,
debug_draw_current_chunk_border: false, //cfg!(not(target_os = "android")) && cfg!(debug_assertions), debug_draw_current_chunk_border: false, //cfg!(not(target_os = "android")) && cfg!(debug_assertions),
dynamic_crosshair: true,
} }
} }
} }

5
kubi/src/ui.rs Normal file
View 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;

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

View file

@ -17,6 +17,8 @@ impl ChunkData {
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)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]

View file

@ -220,7 +220,11 @@ fn process_completed_tasks(
//increase ops counter //increase ops counter
ops += 1; ops += 1;
}, },
ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } => { ChunkTaskResponse::GeneratedMesh {
position,
vertices, indices,
trans_vertices, trans_indices,
} => {
//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!("mesh discarded: chunk doesn't exist"); log::warn!("mesh discarded: chunk doesn't exist");
@ -234,11 +238,12 @@ fn process_completed_tasks(
} }
//apply the mesh //apply the mesh
let vertex_buffer = VertexBuffer::immutable(&renderer.display, &vertices).unwrap(); //TODO: Skip if mesh is empty? (i.e. set to None)
let index_buffer = IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &indexes).unwrap();
let mesh = ChunkMesh { let mesh = ChunkMesh {
vertex_buffer, vertex_buffer: VertexBuffer::immutable(&renderer.display, &vertices).unwrap(),
index_buffer, index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &indices).unwrap(),
trans_vertex_buffer: VertexBuffer::immutable(&renderer.display, &trans_vertices).unwrap(),
trans_index_buffer: IndexBuffer::immutable(&renderer.display, PrimitiveType::TrianglesList, &trans_indices).unwrap(),
}; };
if let Some(index) = chunk.mesh_index { if let Some(index) = chunk.mesh_index {
meshes.update(index, mesh).expect("Mesh update failed"); meshes.update(index, mesh).expect("Mesh update failed");

View file

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

View file

@ -29,7 +29,9 @@ pub enum ChunkTaskResponse {
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>,
}, },
} }
@ -55,8 +57,15 @@ impl ChunkTaskManager {
self.pool.spawn(move || { self.pool.spawn(move || {
let _ = sender.send(match task { let _ = sender.send(match task {
ChunkTask::GenerateMesh { position, data } => { ChunkTask::GenerateMesh { position, data } => {
let (vertices, indexes) = generate_mesh(data); let (
ChunkTaskResponse::GeneratedMesh { position, vertices, indexes } (vertices, indices),
(trans_vertices, trans_indices),
) = generate_mesh(data);
ChunkTaskResponse::GeneratedMesh {
position,
vertices, indices,
trans_vertices, trans_indices,
}
}, },
ChunkTask::LoadChunk { position, seed } => { ChunkTask::LoadChunk { position, seed } => {
let (chunk_data, queued) = generate_world(position, seed); let (chunk_data, queued) = generate_world(position, seed);