Revert "Added skybox to renderer and reflections to shader. Builds and runs. Flickering with many objects in scene, assuming swap chain issue."

This reverts commit 3231ed9190.
This commit is contained in:
Christian Lincoln 2026-09-03 22:55:16 +01:00
parent 3231ed9190
commit 6685277e29
10 changed files with 1438 additions and 1818 deletions

12
Cargo.lock generated
View file

@ -349,15 +349,6 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "colored"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
dependencies = [
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.7"
@ -1105,9 +1096,6 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]] [[package]]
name = "mars" name = "mars"
version = "0.1.0" version = "0.1.0"
dependencies = [
"colored",
]
[[package]] [[package]]
name = "memchr" name = "memchr"

BIN
game.core

Binary file not shown.

View file

@ -1,345 +1,312 @@
use std::collections::HashMap; use std::collections::HashMap;
// Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu // Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu
use crate::render::Renderer;
use crate::world::{SimpleObject, World};
use glam::{Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4};
use std::sync::Arc; use std::sync::Arc;
use glam::{Vec2, Affine3A, Vec3, Vec4, Mat4, EulerRot, Quat};
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use winit::dpi::PhysicalPosition; use winit::dpi::PhysicalPosition;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use winit::platform::web::EventLoopExtWebSys; use winit::platform::web::EventLoopExtWebSys;
use winit::{ use winit::{
application::ApplicationHandler, application::ApplicationHandler,
event::*, event::*,
event_loop::{ActiveEventLoop, EventLoop}, event_loop::{ActiveEventLoop, EventLoop},
keyboard::{KeyCode, PhysicalKey}, keyboard::{KeyCode, PhysicalKey},
window::Window, window::Window,
}; };
use crate::render;
use crate::render::{Renderer, SimpleTexture};
use crate::world::{SimpleObject, World};
struct Controller { struct Controller {
buttons: HashMap<MouseButton, bool>, buttons: HashMap<MouseButton,bool>,
keys: HashMap<KeyCode, bool>, keys: HashMap<KeyCode,bool>,
mouse: Vec2, mouse: Vec2,
} }
impl Controller { impl Controller {
fn new() -> Controller { fn new() -> Controller {
Controller { Controller {
buttons: Default::default(), buttons: Default::default(),
keys: HashMap::new(), keys: HashMap::new(),
mouse: Vec2::new(0.0, 0.0), mouse: Vec2::new(0.0,0.0),
}
} }
}
} }
pub struct AppState { pub struct AppState {
world: World, world: World,
controller: Controller, controller: Controller,
window: Arc<Window>, window: Arc<Window>,
clients: Vec<SimpleObject>, clients: Vec<SimpleObject>,
} }
const BLOCKS: i32 = 1; const BLOCKS: i32 = 50;
impl AppState { impl AppState {
// We don't need this to be async right now, // We don't need this to be async right now,
// but we will in the next tutorial // but we will in the next tutorial
pub async fn new(window: Arc<Window>) -> Result<AppState, Box<dyn std::error::Error>> { pub async fn new(window: Arc<Window>) -> Result<AppState,Box<dyn std::error::Error>> {
let mut world = World::new(); let size = window.inner_size();
world.add_renderer(Renderer::new(&window).await?); let mut world = World::new();
let mut clients = Vec::new(); world.add_renderer(Renderer::new(&window).await?);
{ let mut clients = Vec::new();
let file = world {
.renderer let file = world.renderer.as_mut().unwrap().load_from_gltf( include_bytes!("assets/cube.glb"));
.as_mut() let block = file.first_object().unwrap();
.unwrap() //let color = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png"));
.load_from_gltf(include_bytes!("assets/cube.glb")); //let normal = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png"));
let block = file.first_object().unwrap(); //let roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"));
let skybox = world.renderer.as_mut().unwrap().load_texture_from_bytes( //let mat = renderer.new_material(color,normal,roughness);
include_bytes!("assets/skybox1.png"), for x in -BLOCKS..BLOCKS {
Some(image::ImageFormat::Png), for y in -BLOCKS..BLOCKS {
); let block = block.hard_clone();
world.renderer.as_mut().unwrap().set_skybox(skybox); world.add_object(block.clone());
//let color = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png")); {
//let normal = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png")); let mut model = block.0.borrow_mut();
//let roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png")); model.model.as_mut().unwrap().instance.transform = Mat4::from_translation(Vec3::new(-x as f32 * 3.0,0.0,-y as f32 * 3.0))
//let mat = renderer.new_material(color,normal,roughness); * Mat4::from_rotation_z((x * y) as f32 / 1.23);
for x in -BLOCKS..BLOCKS { model.model.as_mut().unwrap().instance.color = Vec4::new(0.3,(x + BLOCKS) as f32 / BLOCKS as f32,(y + BLOCKS) as f32 / BLOCKS as f32,1.0);
for y in -BLOCKS..BLOCKS { }
let block = block.hard_clone(); clients.push(block)
world.add_object(block.clone());
{
let mut model = block.0.borrow_mut();
model.model.as_mut().unwrap().instance.transform = Mat4::from_translation(
Vec3::new(-x as f32 * 3.0, -8.0, -y as f32 * 3.0),
)
* Mat4::from_rotation_z((x * y) as f32 / 1.23);
model.model.as_mut().unwrap().instance.color = Vec4::new(
0.3,
(x + BLOCKS) as f32 / BLOCKS as f32,
(y + BLOCKS) as f32 / BLOCKS as f32,
1.0,
);
}
clients.push(block)
}
}
} }
Ok(Self { }
world,
clients,
controller: Controller::new(),
window,
})
} }
Ok(Self {
world,
clients,
controller: Controller::new(),
window,
})
}
pub fn bounds(&self) -> (u32, u32) { pub fn bounds(&self) -> (u32,u32) {
let size = self.window.inner_size(); let size = self.window.inner_size();
(size.width, size.height) (size.width,size.height)
} }
pub fn resize(&mut self, width: u32, height: u32) { pub fn resize(&mut self, width: u32, height: u32) {
if width > 0 && height > 0 { if width > 0 && height > 0 {
self.world.renderer.as_mut().unwrap().resize(width, height); let max = 2048;
} self.world.renderer.as_mut().unwrap().resize(width,height);
} }
}
fn update(&mut self) { fn update(&mut self) {
// ... // ...
} }
fn handle_key(&mut self, event_loop: &ActiveEventLoop, code: KeyCode, is_pressed: bool) { fn handle_key(&mut self, event_loop: &ActiveEventLoop, code: KeyCode, is_pressed: bool) {
match (code, is_pressed) { match (code, is_pressed) {
(KeyCode::Escape, true) => event_loop.exit(), (KeyCode::Escape, true) => event_loop.exit(),
(KeyCode::Space, true) => {} (KeyCode::Space, true) => {},
(KeyCode::KeyR, true) => { (KeyCode::KeyR, true) => {
self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY
} }
_ => {} _ => {}
}
self.controller.keys.insert(code, is_pressed);
} }
self.controller.keys.insert(code, is_pressed);
}
fn handle_mouse_moved(&mut self, position: PhysicalPosition<f64>) { fn handle_mouse_moved(&mut self, position: PhysicalPosition<f64>) {
if let Some(true) = self.controller.buttons.get(&MouseButton::Right) { if let Some(true) = self.controller.buttons.get(&MouseButton::Right) {
self.world.renderer.as_mut().unwrap().eye.rotate( self.world.renderer.as_mut().unwrap().eye.rotate(position.x as f32 - self.controller.mouse.x, position.y as f32 - self.controller.mouse.y);
position.x as f32 - self.controller.mouse.x,
position.y as f32 - self.controller.mouse.y,
);
}
self.controller.mouse = Vec2::new(position.x as f32, position.y as f32);
} }
self.controller.mouse = Vec2::new(position.x as f32, position.y as f32);
}
fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState) { fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState ) {
self.controller.buttons.insert(button, state.is_pressed()); self.controller.buttons.insert(button,state.is_pressed());
} }
} }
pub struct App { pub struct App {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
proxy: Option<winit::event_loop::EventLoopProxy<AppState>>, proxy: Option<winit::event_loop::EventLoopProxy<AppState>>,
state: Option<AppState>, state: Option<AppState>,
} }
impl App { impl App {
pub fn new(#[cfg(target_arch = "wasm32")] event_loop: &EventLoop<AppState>) -> Self { pub fn new(#[cfg(target_arch = "wasm32")] event_loop: &EventLoop<AppState>) -> Self {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
let proxy = Some(event_loop.create_proxy()); let proxy = Some(event_loop.create_proxy());
Self { Self {
state: None, state: None,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
proxy, proxy,
}
} }
}
} }
impl ApplicationHandler<AppState> for App { impl ApplicationHandler<AppState> for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) { fn resumed(&mut self, event_loop: &ActiveEventLoop) {
#[allow(unused_mut)]
let mut window_attributes = Window::default_attributes();
#[cfg(target_arch = "wasm32")]
{
use wasm_bindgen::JsCast;
use winit::platform::web::WindowAttributesExtWebSys;
const CANVAS_ID: &str = "canvas";
let window = wgpu::web_sys::window().unwrap_throw();
let document = window.document().unwrap_throw();
let canvas = document.get_element_by_id(CANVAS_ID).unwrap_throw();
let html_canvas_element = canvas.unchecked_into();
window_attributes = window_attributes.with_canvas(Some(html_canvas_element));
}
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
#[cfg(not(target_arch = "wasm32"))]
{
// If we are not on web we can use pollster to
// await the window creation
self.state = Some(pollster::block_on(AppState::new(window)).unwrap());
}
#[cfg(target_arch = "wasm32")]
{
// Run the future asynchronously and use the
// proxy to send the results to the event loop
if let Some(proxy) = self.proxy.take() {
wasm_bindgen_futures::spawn_local(async move {
assert!(
proxy
.send_event(
AppState::new(window)
.await
.expect("Unable to create canvas!!!")
)
.is_ok()
)
});
}
}
}
#[allow(unused_mut)] #[allow(unused_mut)]
fn user_event(&mut self, _event_loop: &ActiveEventLoop, mut event: AppState) { let mut window_attributes = Window::default_attributes();
// This is where proxy.send_event() ends up
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
event.window.request_redraw(); use wasm_bindgen::JsCast;
event.resize( use winit::platform::web::WindowAttributesExtWebSys;
event.window.inner_size().width,
event.window.inner_size().height, const CANVAS_ID: &str = "canvas";
);
} let window = wgpu::web_sys::window().unwrap_throw();
self.state = Some(event); let document = window.document().unwrap_throw();
let canvas = document.get_element_by_id(CANVAS_ID).unwrap_throw();
let html_canvas_element = canvas.unchecked_into();
window_attributes = window_attributes.with_canvas(Some(html_canvas_element));
} }
fn window_event( let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
&mut self,
event_loop: &ActiveEventLoop, #[cfg(not(target_arch = "wasm32"))]
_window_id: winit::window::WindowId, {
event: WindowEvent, // If we are not on web we can use pollster to
) { // await the window creation
let state = match &mut self.state { self.state = Some(pollster::block_on(AppState::new(window)).unwrap());
Some(canvas) => canvas, }
None => return,
#[cfg(target_arch = "wasm32")]
{
// Run the future asynchronously and use the
// proxy to send the results to the event loop
if let Some(proxy) = self.proxy.take() {
wasm_bindgen_futures::spawn_local(async move {
assert!(
proxy
.send_event(
AppState::new(window)
.await
.expect("Unable to create canvas!!!")
)
.is_ok()
)
});
}
}
}
#[allow(unused_mut)]
fn user_event(&mut self, _event_loop: &ActiveEventLoop, mut event: AppState) {
// This is where proxy.send_event() ends up
#[cfg(target_arch = "wasm32")]
{
event.window.request_redraw();
event.resize(
event.window.inner_size().width,
event.window.inner_size().height,
);
}
self.state = Some(event);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: winit::window::WindowId,
event: WindowEvent,
) {
let state = match &mut self.state {
Some(canvas) => canvas,
None => return,
};
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Resized(size) => state.resize(size.width, size.height),
WindowEvent::RedrawRequested => {
state.update();
let mut movement = Vec3::new(0.0,0.0,0.0);
let pressed = |keycode: KeyCode| {
if let Some(true) = state.controller.keys.get(&keycode) {
true
} else {
false
}
}; };
match event { if pressed(KeyCode::KeyA) {
WindowEvent::CloseRequested => event_loop.exit(), movement.x -= 1.0;
WindowEvent::Resized(size) => state.resize(size.width, size.height),
WindowEvent::RedrawRequested => {
state.update();
let mut movement = Vec3::new(0.0, 0.0, 0.0);
let pressed = |keycode: KeyCode| {
if let Some(true) = state.controller.keys.get(&keycode) {
true
} else {
false
}
};
if pressed(KeyCode::KeyA) {
movement.x -= 1.0;
}
if pressed(KeyCode::KeyD) {
movement.x += 1.0;
}
if pressed(KeyCode::KeyW) {
movement.z += 1.0;
}
if pressed(KeyCode::KeyS) {
movement.z -= 1.0;
}
if pressed(KeyCode::KeyE) {
movement.y += 1.0;
}
if pressed(KeyCode::KeyQ) {
movement.y -= 1.0;
}
state
.world
.renderer
.as_mut()
.unwrap()
.eye
.control(movement * 0.1);
match state.world.renderer.as_mut().unwrap().render(&state.window) {
Ok(_) => {}
Err(e) => {
// Log the error and exit gracefully
log::error!("{e}");
event_loop.exit();
}
}
for object in state.clients.iter() {
object
.0
.borrow_mut()
.model
.as_mut()
.unwrap()
.instance
.transform *= Mat4::from_rotation_translation(
Quat::from_euler(EulerRot::XYZ, 0.001, -0.001, 0.001),
Vec3::new(0.0, 0.0, 0.0),
);
}
}
WindowEvent::MouseInput {
button,
state: element,
..
} => state.handle_mouse_button(button, element),
WindowEvent::CursorMoved { position: pos, .. } => state.handle_mouse_moved(pos),
WindowEvent::KeyboardInput {
event:
KeyEvent {
physical_key: PhysicalKey::Code(code),
state: key_state,
..
},
..
} => state.handle_key(event_loop, code, key_state.is_pressed()),
_ => {}
} }
if pressed(KeyCode::KeyD) {
movement.x += 1.0;
}
if pressed(KeyCode::KeyW) {
movement.z += 1.0;
}
if pressed(KeyCode::KeyS) {
movement.z -= 1.0;
}
if pressed(KeyCode::KeyE) {
movement.y += 1.0;
}
if pressed(KeyCode::KeyQ) {
movement.y -= 1.0;
}
state.world.renderer.as_mut().unwrap().eye.control(movement * 0.1);
match state.world.renderer.as_mut().unwrap().render(&state.window) {
Ok(_) => {}
Err(e) => {
// Log the error and exit gracefully
log::error!("{e}");
event_loop.exit();
}
}
for object in state.clients.iter() {
object.0.borrow_mut().model.as_mut().unwrap().instance.transform *= Mat4::from_rotation_translation(
Quat::from_euler(EulerRot::XYZ,0.001,-0.001,0.001),
Vec3::new(0.0,0.0,0.0)
);
}
}
WindowEvent::MouseInput { button, state: element, .. } => state.handle_mouse_button(button,element),
WindowEvent::CursorMoved { position: pos, .. } => state.handle_mouse_moved(pos),
WindowEvent::KeyboardInput {
event:
KeyEvent {
physical_key: PhysicalKey::Code(code),
state: key_state,
..
},
..
} => state.handle_key(event_loop, code, key_state.is_pressed()),
_ => {}
} }
}
} }
pub fn run() -> anyhow::Result<()> { pub fn run() -> anyhow::Result<()> {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
env_logger::init(); env_logger::init();
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
console_log::init_with_level(log::Level::Info).unwrap_throw(); console_log::init_with_level(log::Level::Info).unwrap_throw();
} }
let event_loop = EventLoop::with_user_event().build()?; let event_loop = EventLoop::with_user_event().build()?;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
let mut app = App::new(); let mut app = App::new();
event_loop.run_app(&mut app)?; event_loop.run_app(&mut app)?;
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
let app = App::new(&event_loop); let app = App::new(&event_loop);
event_loop.spawn_app(app); event_loop.spawn_app(app);
} }
Ok(()) Ok(())
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen(start)] #[wasm_bindgen(start)]
pub fn run_web() -> Result<(), wasm_bindgen::JsValue> { pub fn run_web() -> Result<(), wasm_bindgen::JsValue> {
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
run().unwrap_throw(); run().unwrap_throw();
Ok(()) Ok(())
} }

View file

@ -4,26 +4,16 @@ struct Environment {
dir: vec4<f32>, dir: vec4<f32>,
} }
struct Eye { struct View {
// from camera to screen
proj: mat4x4<f32>,
// from screen to camera
inv: mat4x4<f32>,
// world to camera
view: mat4x4<f32>, view: mat4x4<f32>,
// camera transform
frame: mat4x4<f32>, frame: mat4x4<f32>,
} }
// Vertex shader // Vertex shader
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> eye: Eye; var<uniform> view: View;
@group(0) @binding(1) @group(0) @binding(1)
var<uniform> environment: Environment; var<uniform> environment: Environment;
@group(0) @binding(2)
var sky_sampler: sampler;
@group(0) @binding(3)
var sky_texture: texture_2d<f32>;
struct VertexInput { struct VertexInput {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
@ -70,7 +60,7 @@ fn vs_main(
out.world_normal = model_rot_matrix * model.normal; out.world_normal = model_rot_matrix * model.normal;
var world_position: vec4<f32> = model_matrix * vec4<f32>(model.position, 1.0); var world_position: vec4<f32> = model_matrix * vec4<f32>(model.position, 1.0);
out.world_position = world_position.xyz; out.world_position = world_position.xyz;
out.clip_position = eye.proj * eye.view * world_position; out.clip_position = view.view * world_position;
return out; return out;
} }
@ -85,79 +75,23 @@ var t_normal: texture_2d<f32>;
@group(1) @binding(3) @group(1) @binding(3)
var t_rough: texture_2d<f32>; var t_rough: texture_2d<f32>;
fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
var pi = 3.14159;
let u_angle = atan2(look.x,look.z);
let u = (u_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let v_angle = atan2(-look.y,sqrt(look.x * look.x + look.z * look.z));
let v = (v_angle/pi) + 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
let uv = vec2<f32>(u,v); // Get UV on skybox
return textureSample(sky_texture, sky_sampler, uv);
}
fn rotation(mat: mat4x4<f32>) -> mat3x3<f32> {
return mat3x3<f32>(
mat[0].xyz,
mat[1].xyz,
mat[2].xyz,
);
}
fn translation(mat: mat4x4<f32>) -> vec4<f32> {
//return vec4<f32>(mat[0][3],mat[1][3],mat[2][3],mat[3][3]);
return mat[3];
}
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords); let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
let object_normal: vec4<f32> = textureSample(t_normal, s_diffuse, in.tex_coords); let object_normal: vec4<f32> = textureSample(t_normal, s_diffuse, in.tex_coords);
let diffuse_color = object_color.xyz; let tangent_normal = object_normal.xyz * 2.0 - 1.0;
let specular_color = vec3<f32>(0.0,0.0,0.0); let light_dir = normalize(environment.dir.xyz);
let reflection = sky_aspect(reflect(normalize(in.world_position-translation(eye.frame).xyz),normalize(in.world_normal))); let view_dir = normalize(view.frame[3].xyz - in.world_position);
let half_dir = normalize(view_dir + light_dir);
//let result = (environment.ambient.xyz + diffuse_color + specular_color) * object_color.xyz; let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0);
let diffuse_color = environment.light.xyz * diffuse_strength;
//let light_dir = normalize(environment.dir.xyz); let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0);
//let view_dir = normalize(eye.frame[3].xyz - in.world_position); let specular_color = specular_strength * environment.light.xyz;
//let half_dir = normalize(view_dir + light_dir);
//let diffuse_strength = max(dot(tangent_normal, light_dir), 0.0);
//let diffuse_color = environment.light.xyz * diffuse_strength;
//let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0);
//let specular_color = specular_strength * environment.light.xyz;
let result = (environment.ambient.xyz + diffuse_color.xyz + specular_color.xyz) * object_color.xyz; let result = (environment.ambient.xyz + diffuse_color.xyz + specular_color.xyz) * object_color.xyz;
return vec4<f32>(reflection.xyz,object_color.a); return vec4<f32>(result.xyz,object_color.a);
} }
struct SkyOutput {
@builtin(position) position: vec4<f32>,
@location(0) pos: vec4<f32> // unadulterated by WGSL
}
const TRI_VERTICES = array(
vec4(-1.0, -1.0, 1.0, 1.0),
vec4(-1.0, 1.0, 1.0, 1.0),
vec4( 1.0, -1.0, 1.0, 1.0),
vec4( 1.0, 1.0, 1.0, 1.0),
vec4(-1.0, 1.0, 1.0, 1.0),
vec4( 1.0, -1.0, 1.0, 1.0),
);
@vertex
fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput {
var out: SkyOutput;
out.position = TRI_VERTICES[index];
out.pos = out.position;
return out;
}
@fragment
fn fs_sky(in: SkyOutput) -> @location(0) vec4<f32> {
let look = rotation(eye.frame) * in.pos.xyz;
return sky_aspect(look);
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

BIN
src/assets/test.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

View file

@ -1,187 +1,131 @@
use crate::render::{MaterialProperties, SimpleTexture};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use glam::camera::lh::proj::directx::perspective; use env_logger::Env;
use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4}; use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4};
use wgpu::util::DeviceExt; use glam::camera::lh::proj::directx::perspective;
use wgpu::{Device, Queue}; use wgpu::{Device, Queue};
use wgpu::util::DeviceExt;
pub(crate) struct Eye { pub(crate) struct Eye {
pub(crate) frame: Affine3A, pub(crate) frame: Affine3A,
pub(crate) environment: Environment, pub(crate) environment: Environment,
aspect_ratio: f32, aspect_ratio: f32,
z_near: f32, z_near: f32,
z_far: f32, z_far: f32,
fov_y: f32, fov_y: f32,
pub(crate) environment_buffer: wgpu::Buffer, pub(crate) environment_buffer: wgpu::Buffer,
pub(crate) camera_buffer: wgpu::Buffer, pub(crate) buffer: wgpu::Buffer,
pub(crate) layout: wgpu::BindGroupLayout, pub(crate) layout: wgpu::BindGroupLayout,
pub(crate) group: wgpu::BindGroup, pub(crate) group: wgpu::BindGroup,
} }
#[repr(C)] #[repr(C)]
#[derive(Pod, Copy, Clone, Zeroable)] #[derive(Pod, Copy, Clone, Zeroable)]
pub struct Environment { struct Environment {
ambient: Vec4, ambient: Vec4,
light: Vec4, light: Vec4,
dir: Vec4, dir: Vec4,
} }
impl Eye { impl Eye {
pub(crate) fn write(&mut self, queue: &Queue) { pub(crate) fn view(&self) -> Mat4 {
let camera = Mat4::from_mat3_translation( let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far);
self.frame.matrix3.into(), projection * self.frame.inverse()
Vec3::from(self.frame.translation), }
); pub(crate) fn write(&mut self, queue: &Queue) {
let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far); queue.write_buffer(&self.buffer,0,bytemuck::cast_slice(&[
queue.write_buffer( self.view(),
&self.camera_buffer, Mat4::from_mat3_translation(self.frame.matrix3.into(), Vec3::from(self.frame.translation))
0, ]));
bytemuck::cast_slice(&[ queue.write_buffer(&self.environment_buffer,0,bytemuck::cast_slice(&[self.environment]));
projection, }
camera * projection.inverse(), pub(crate) fn new(device: &Device, width: u32, height: u32) -> Eye {
camera.inverse(), let buffer = device.create_buffer_init(
camera, &wgpu::util::BufferInitDescriptor {
]), label: Some("Camera Buffer"),
); contents: bytemuck::cast_slice(&[Mat4::from_translation(Vec3::new(0.0,2.0,-8.0)),Mat4::IDENTITY]),
queue.write_buffer( usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
&self.environment_buffer, }
0, );
bytemuck::cast_slice(&[self.environment]),
);
}
pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: SimpleTexture) -> Eye {
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[
Mat4::from_translation(Vec3::new(0.0, 2.0, -8.0)),
Mat4::IDENTITY,
Mat4::IDENTITY,
Mat4::IDENTITY,
]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let dir = Vec3::new(1.0, 0.5, 1.0).normalize(); let dir = Vec3::new(1.0,0.5,1.0).normalize();
let environment = Environment { let environment = Environment {
ambient: Vec4::new(0.15, 0.15, 0.15, 0.0), ambient: Vec4::new(0.15,0.15,0.15, 0.0),
light: Vec4::new(1.0, 1.0, 1.0, 0.0), light: Vec4::new(1.0,1.0,1.0, 0.0),
dir: Vec4::new(dir.x, dir.y, dir.z, 0.0), dir: Vec4::new(dir.x,dir.y,dir.z,0.0),
}; };
let environment_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let environment_buffer = device.create_buffer_init(
label: Some(""), &wgpu::util::BufferInitDescriptor {
contents: bytemuck::cast_slice(&[environment]), label: Some(""),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, contents: bytemuck::cast_slice(&[environment]),
}); usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: false,
min_binding_size: None, min_binding_size: None,
}, },
count: None, count: None,
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT, visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: false,
min_binding_size: None, min_binding_size: None,
}, },
count: None, count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
],
label: Some("eye_bind_group_layout"),
});
let group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox);
Eye {
aspect_ratio: width as f32 / height as f32,
frame: Affine3A::IDENTITY,
fov_y: 90.0,
z_near: 0.1,
z_far: 1000.0,
camera_buffer,
group,
layout,
environment,
environment_buffer,
} }
],
label: Some("eye_bind_group_layout"),
});
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: environment_buffer.as_entire_binding(),
}
],
label: Some("eye_bind_group"),
});
Eye {
aspect_ratio: width as f32 / height as f32,
frame: Affine3A::IDENTITY,
fov_y: 90.0,
z_near: 0.1,
z_far: 1000.0,
buffer,
group,
layout,
environment,
environment_buffer,
} }
fn bind_group( }
layout: &wgpu::BindGroupLayout, pub(crate) fn resize(&mut self, width: u32, height: u32) {
device: &wgpu::Device, self.aspect_ratio = width as f32 / height as f32
camera: &wgpu::Buffer, }
environment: &wgpu::Buffer, pub(crate) fn control(&mut self, delta: Vec3) {
skybox: SimpleTexture, self.frame *= Affine3A::from_translation(delta);
) -> wgpu::BindGroup { }
device.create_bind_group(&wgpu::BindGroupDescriptor { pub(crate) fn rotate(&mut self, yaw: f32, pitch: f32) {
layout: &layout, let (mut y,mut x,z) = self.frame.matrix3.to_euler(EulerRot::YXZ);
entries: &[ x = (x + pitch * 0.005).clamp(-1.4,1.4);
wgpu::BindGroupEntry { y += yaw * 0.005;
binding: 0, self.frame.matrix3 = Mat3A::from_euler(EulerRot::YXZ,y,x,z);
resource: camera.as_entire_binding(), }
}, }
wgpu::BindGroupEntry {
binding: 1,
resource: environment.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(
&MaterialProperties::default().sampler(device),
),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::TextureView(&skybox.view),
},
],
label: Some("eye_bind_group"),
})
}
pub fn skybox(&mut self, device: &wgpu::Device, texture: SimpleTexture) {
self.group = Eye::bind_group(
&self.layout,
device,
&self.camera_buffer,
&self.environment_buffer,
texture,
)
}
pub(crate) fn resize(&mut self, width: u32, height: u32) {
self.aspect_ratio = width as f32 / height as f32
}
pub(crate) fn control(&mut self, delta: Vec3) {
self.frame *= Affine3A::from_translation(delta);
}
pub(crate) fn rotate(&mut self, yaw: f32, pitch: f32) {
let (mut y, mut x, z) = self.frame.matrix3.to_euler(EulerRot::YXZ);
x = (x + pitch * 0.005).clamp(-1.4, 1.4);
y += yaw * 0.005;
self.frame.matrix3 = Mat3A::from_euler(EulerRot::YXZ, y, x, z);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,228 +1,223 @@
use crate::render::{Renderer, SimpleLightData, SimpleModelData};
use glam::{DVec3, IVec3, Mat4, UVec3, Vec3, Vec4Swizzles};
use std::cell::RefCell; use std::cell::RefCell;
use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::ops::{Div, Mul}; use std::ops::{Div, Mul};
use std::rc::Rc; use std::rc::Rc;
use glam::{DVec3, IVec3, Mat4, UVec3, Vec3, Vec4Swizzles};
use wgpu::naga::{FastHashMap, FastHashSet}; use wgpu::naga::{FastHashMap, FastHashSet};
use crate::render::{SimpleModelData, SimpleLightData, Renderer};
#[derive(Clone)] #[derive(Clone)]
pub enum Shape { pub enum Shape {
Block(Vec3), Block(Vec3),
Sphere(f32), Sphere(f32),
None, None,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleColliderData { pub struct SimpleColliderData {
pub transform: Mat4, pub transform: Mat4,
pub shape: Shape, pub shape: Shape,
pub radius: f32, pub radius: f32,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleObjectData { pub struct SimpleObjectData {
pub model: Option<SimpleModelData>, pub model: Option<SimpleModelData>,
pub collider: SimpleColliderData, pub collider: SimpleColliderData,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleLight(pub Rc<RefCell<SimpleLightData>>); pub struct SimpleLight(pub Rc<RefCell<SimpleLightData>>);
impl PartialEq for SimpleLight { impl PartialEq for SimpleLight {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0) Rc::ptr_eq(&self.0, &other.0)
} }
} }
impl Eq for SimpleLight {} impl Eq for SimpleLight {}
impl Hash for SimpleLight { impl Hash for SimpleLight {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleLight>).hash(state) (self.0.as_ptr() as *const RefCell<SimpleLight>).hash(state)
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleObject(pub Rc<RefCell<SimpleObjectData>>); pub struct SimpleObject(pub Rc<RefCell<SimpleObjectData>>);
impl SimpleObject { impl SimpleObject {
pub(crate) fn hard_clone(&self) -> SimpleObject { pub(crate) fn hard_clone(&self) -> SimpleObject {
SimpleObject { SimpleObject {
0: Rc::new(RefCell::new(SimpleObjectData { 0: Rc::new(RefCell::new(SimpleObjectData {
model: self.0.borrow().model.clone(), model: self.0.borrow().model.clone(),
collider: self.0.borrow().collider.clone(), collider: self.0.borrow().collider.clone(),
})), })),
}
} }
}
} }
impl PartialEq for SimpleObject { impl PartialEq for SimpleObject {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0) Rc::ptr_eq(&self.0,&other.0)
} }
} }
impl Eq for SimpleObject {} impl Eq for SimpleObject {}
impl Hash for SimpleObject { impl Hash for SimpleObject {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state); (self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state);
} }
} }
#[derive(Eq, Hash, PartialEq, Clone)] #[derive(Eq, Hash, PartialEq, Clone)]
enum Interest { enum Interest {
Light(SimpleLight), Light(SimpleLight),
Object(SimpleObject), Object(SimpleObject),
} }
struct Block { struct Block {
debug: Option<SimpleObject>, debug: Option<SimpleObject>,
volume: u8, volume: u8,
velocity: Vec3, velocity: Vec3,
material: u8, material: u8,
interests: FastHashSet<Interest>, interests: FastHashSet<Interest>,
blocks: [Option<Box<Block>>; 64], blocks: [Option<Box<Block>>;64],
} }
const TREE_ATTACK: usize = 4; const TREE_ATTACK: usize = 4;
const TREE_FLOOR: usize = 4; const TREE_FLOOR: usize = 4;
impl Block { impl Block {
fn new() -> Block { fn new() -> Block {
Block { Block {
debug: None, debug: None,
volume: 0, volume: 0,
velocity: Vec3::ZERO, velocity: Vec3::ZERO,
material: 0, material: 0,
interests: FastHashSet::with_hasher(BuildHasherDefault::default()), interests: FastHashSet::with_hasher(BuildHasherDefault::default()),
blocks: [const { None }; const { TREE_ATTACK * TREE_ATTACK * TREE_ATTACK }], blocks: [const { None }; const { TREE_ATTACK * TREE_ATTACK * TREE_ATTACK }],
}
} }
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32, block_size: f32) { }
let cs = block_size / const { TREE_ATTACK as f32 }; fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32, block_size: f32) {
if rad < cs || block_size >= TREE_FLOOR as f32 { let cs = block_size / const { TREE_ATTACK as f32 };
let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3(); if rad < cs || block_size >= TREE_FLOOR as f32 {
let new_pos = pos - (rel.as_vec3() + cs / 2.0); let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3();
let index = (rel.x + rel.y * 4 + rel.z * 16) as usize; let new_pos = pos - (rel.as_vec3() + cs / 2.0);
match self.blocks[index] { let index = (rel.x + rel.y * 4 + rel.z * 16) as usize;
Some(ref mut block) => { match self.blocks[index] {
block.place_pos(interest, new_pos, rad, cs); Some(ref mut block) => {
} block.place_pos(interest,new_pos,rad,cs);
None => { },
let mut block = Box::new(Block::new()); None => {
block.place_pos(interest, new_pos, rad, cs); let mut block = Box::new(Block::new());
self.blocks[index] = Some(block); block.place_pos(interest,new_pos,rad,cs);
} self.blocks[index] = Some(block);
} }
}
} else {
self.interests.insert(interest);
}
}
fn debug_step(&mut self, value: bool) {
match self.debug {
Some(ref mut debug) => {
if value {
} else { } else {
self.interests.insert(interest);
}
}
fn debug_step(&mut self, value: bool) {
match self.debug {
Some(ref mut debug) => {
if value {
} else {
}
}
None => {
if value {
} else {
}
}
}
for maybe_block in self.blocks.iter_mut() {
if let Some(block) = maybe_block {
block.debug_step(value)
}
} }
},
None => {
if value {
} else {
}
}
} }
}
} }
pub struct World { pub struct World {
chunk_size: u32, chunk_size: u32,
map: FastHashMap<IVec3, Block>, map: FastHashMap<IVec3,Block>,
pub renderer: Option<Renderer>, pub renderer: Option<Renderer>,
debug_world_map: bool, debug_world_map: bool,
debug_world_map_object: Option<SimpleObject>, debug_world_map_object: Option<SimpleObject>,
} }
impl World { impl World {
pub fn new() -> World { pub fn new() -> World {
World { World {
chunk_size: 512, chunk_size: 512,
map: FastHashMap::with_hasher(BuildHasherDefault::default()), map: FastHashMap::with_hasher(BuildHasherDefault::default()),
renderer: None, renderer: None,
debug_world_map: false, debug_world_map: false,
debug_world_map_object: None, debug_world_map_object: None,
}
}
pub fn add_renderer(&mut self, renderer: Renderer) {
self.renderer = Some(renderer);
self.debug_world_map_object = self.renderer.as_mut().unwrap().load_from_gltf(include_bytes!("../assets/debug.glb")).first_object()
}
pub fn add_object(&mut self, object: SimpleObject) {
self.place(Interest::Object(object.clone()));
if let Some(ref mut renderer) = self.renderer {
renderer.instances.add_object(object)
}
}
pub fn set_debug(&mut self, value: bool) {
self.debug_world_map = value;
}
pub fn light_step(&mut self) {
}
pub fn object_step(&mut self) {
}
pub fn debug_step(&mut self) {
self.light_step();
self.object_step();
for (index, item) in self.map.iter_mut() {
item.debug_step(self.debug_world_map);
}
}
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32) {
let cs = self.chunk_size as f32;
let c_rad = ((cs / 2.0) * (cs / 2.0)) * 3.0;
for x in (pos.x - rad).div(cs) as i32..=(pos.x + rad).div(cs).ceil() as i32 {
for y in (pos.y - rad).div(cs) as i32..=(pos.y + rad).div(cs).ceil() as i32 {
for z in (pos.z - rad).div(cs) as i32..=(pos.z + rad).div(cs).ceil() as i32 {
let block_pos = IVec3::new(x,y,z);
let block_pos_f32 = block_pos.as_vec3();
if block_pos_f32.distance_squared(pos) < rad + c_rad {
self.map.entry(block_pos).or_insert_with(|| {
let mut block = Block::new();
block.place_pos(
interest.clone(),
pos - block_pos_f32,
rad,
self.chunk_size as f32,
);
block
});
}
} }
}
} }
pub fn add_renderer(&mut self, renderer: Renderer) { }
self.renderer = Some(renderer); fn place(&mut self, interest: Interest) {
self.debug_world_map_object = self let (pos,rad) = match &interest {
.renderer Interest::Light(light) => {
.as_mut() let light = light.0.borrow();
.unwrap() (light.instance.location.xyz(),light.instance.color.length())
.load_from_gltf(include_bytes!("../assets/cube.glb")) },
.first_object() Interest::Object(object) => {
} let collider = &object.0.borrow().collider;
pub fn add_object(&mut self, object: SimpleObject) { (collider.transform.to_scale_rotation_translation().2,collider.radius)
self.place(Interest::Object(object.clone())); },
if let Some(ref mut renderer) = self.renderer { };
renderer.instances.add_object(object) self.place_pos(interest,pos,rad);
} }
} }
pub fn set_debug(&mut self, value: bool) {
self.debug_world_map = value;
}
pub fn light_step(&mut self) {}
pub fn object_step(&mut self) {}
pub fn debug_step(&mut self) {
self.light_step();
self.object_step();
for (index, item) in self.map.iter_mut() {
item.debug_step(self.debug_world_map);
}
}
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32) {
let cs = self.chunk_size as f32;
let c_rad = ((cs / 2.0) * (cs / 2.0)) * 3.0;
for x in (pos.x - rad).div(cs) as i32..=(pos.x + rad).div(cs).ceil() as i32 {
for y in (pos.y - rad).div(cs) as i32..=(pos.y + rad).div(cs).ceil() as i32 {
for z in (pos.z - rad).div(cs) as i32..=(pos.z + rad).div(cs).ceil() as i32 {
let block_pos = IVec3::new(x, y, z);
let block_pos_f32 = block_pos.as_vec3();
if block_pos_f32.distance_squared(pos) < rad + c_rad {
self.map.entry(block_pos).or_insert_with(|| {
let mut block = Block::new();
block.place_pos(
interest.clone(),
pos - block_pos_f32,
rad,
self.chunk_size as f32,
);
block
});
}
}
}
}
}
fn place(&mut self, interest: Interest) {
let (pos, rad) = match &interest {
Interest::Light(light) => {
let light = light.0.borrow();
(light.instance.location.xyz(), light.instance.color.length())
}
Interest::Object(object) => {
let collider = &object.0.borrow().collider;
(
collider.transform.to_scale_rotation_translation().2,
collider.radius,
)
}
};
self.place_pos(interest, pos, rad);
}
}