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"
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]]
name = "combine"
version = "4.6.7"
@ -1105,9 +1096,6 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "mars"
version = "0.1.0"
dependencies = [
"colored",
]
[[package]]
name = "memchr"

BIN
game.core

Binary file not shown.

View file

@ -1,345 +1,312 @@
use std::collections::HashMap;
// 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 glam::{Vec2, Affine3A, Vec3, Vec4, Mat4, EulerRot, Quat};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
use winit::dpi::PhysicalPosition;
#[cfg(target_arch = "wasm32")]
use winit::platform::web::EventLoopExtWebSys;
use winit::{
application::ApplicationHandler,
event::*,
event_loop::{ActiveEventLoop, EventLoop},
keyboard::{KeyCode, PhysicalKey},
window::Window,
application::ApplicationHandler,
event::*,
event_loop::{ActiveEventLoop, EventLoop},
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
use crate::render;
use crate::render::{Renderer, SimpleTexture};
use crate::world::{SimpleObject, World};
struct Controller {
buttons: HashMap<MouseButton, bool>,
keys: HashMap<KeyCode, bool>,
mouse: Vec2,
buttons: HashMap<MouseButton,bool>,
keys: HashMap<KeyCode,bool>,
mouse: Vec2,
}
impl Controller {
fn new() -> Controller {
Controller {
buttons: Default::default(),
keys: HashMap::new(),
mouse: Vec2::new(0.0, 0.0),
}
fn new() -> Controller {
Controller {
buttons: Default::default(),
keys: HashMap::new(),
mouse: Vec2::new(0.0,0.0),
}
}
}
pub struct AppState {
world: World,
controller: Controller,
window: Arc<Window>,
clients: Vec<SimpleObject>,
world: World,
controller: Controller,
window: Arc<Window>,
clients: Vec<SimpleObject>,
}
const BLOCKS: i32 = 1;
const BLOCKS: i32 = 50;
impl AppState {
// We don't need this to be async right now,
// but we will in the next tutorial
pub async fn new(window: Arc<Window>) -> Result<AppState, Box<dyn std::error::Error>> {
let mut world = World::new();
world.add_renderer(Renderer::new(&window).await?);
let mut clients = Vec::new();
{
let file = world
.renderer
.as_mut()
.unwrap()
.load_from_gltf(include_bytes!("assets/cube.glb"));
let block = file.first_object().unwrap();
let skybox = world.renderer.as_mut().unwrap().load_texture_from_bytes(
include_bytes!("assets/skybox1.png"),
Some(image::ImageFormat::Png),
);
world.renderer.as_mut().unwrap().set_skybox(skybox);
//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 roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"));
//let mat = renderer.new_material(color,normal,roughness);
for x in -BLOCKS..BLOCKS {
for y in -BLOCKS..BLOCKS {
let block = block.hard_clone();
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)
}
}
// We don't need this to be async right now,
// but we will in the next tutorial
pub async fn new(window: Arc<Window>) -> Result<AppState,Box<dyn std::error::Error>> {
let size = window.inner_size();
let mut world = World::new();
world.add_renderer(Renderer::new(&window).await?);
let mut clients = Vec::new();
{
let file = world.renderer.as_mut().unwrap().load_from_gltf( include_bytes!("assets/cube.glb"));
let block = file.first_object().unwrap();
//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 roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"));
//let mat = renderer.new_material(color,normal,roughness);
for x in -BLOCKS..BLOCKS {
for y in -BLOCKS..BLOCKS {
let block = block.hard_clone();
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,0.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) {
let size = self.window.inner_size();
(size.width, size.height)
}
pub fn bounds(&self) -> (u32,u32) {
let size = self.window.inner_size();
(size.width,size.height)
}
pub fn resize(&mut self, width: u32, height: u32) {
if width > 0 && height > 0 {
self.world.renderer.as_mut().unwrap().resize(width, height);
}
pub fn resize(&mut self, width: u32, height: u32) {
if width > 0 && height > 0 {
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) {
match (code, is_pressed) {
(KeyCode::Escape, true) => event_loop.exit(),
(KeyCode::Space, true) => {}
(KeyCode::KeyR, true) => {
self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY
}
_ => {}
}
self.controller.keys.insert(code, is_pressed);
fn handle_key(&mut self, event_loop: &ActiveEventLoop, code: KeyCode, is_pressed: bool) {
match (code, is_pressed) {
(KeyCode::Escape, true) => event_loop.exit(),
(KeyCode::Space, true) => {},
(KeyCode::KeyR, true) => {
self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY
}
_ => {}
}
self.controller.keys.insert(code, is_pressed);
}
fn handle_mouse_moved(&mut self, position: PhysicalPosition<f64>) {
if let Some(true) = self.controller.buttons.get(&MouseButton::Right) {
self.world.renderer.as_mut().unwrap().eye.rotate(
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);
fn handle_mouse_moved(&mut self, position: PhysicalPosition<f64>) {
if let Some(true) = self.controller.buttons.get(&MouseButton::Right) {
self.world.renderer.as_mut().unwrap().eye.rotate(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);
}
fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState) {
self.controller.buttons.insert(button, state.is_pressed());
}
fn handle_mouse_button(&mut self, button: MouseButton, state: ElementState ) {
self.controller.buttons.insert(button,state.is_pressed());
}
}
pub struct App {
#[cfg(target_arch = "wasm32")]
proxy: Option<winit::event_loop::EventLoopProxy<AppState>>,
state: Option<AppState>,
#[cfg(target_arch = "wasm32")]
proxy: Option<winit::event_loop::EventLoopProxy<AppState>>,
state: Option<AppState>,
}
impl App {
pub fn new(#[cfg(target_arch = "wasm32")] event_loop: &EventLoop<AppState>) -> Self {
#[cfg(target_arch = "wasm32")]
let proxy = Some(event_loop.create_proxy());
Self {
state: None,
#[cfg(target_arch = "wasm32")]
proxy,
}
pub fn new(#[cfg(target_arch = "wasm32")] event_loop: &EventLoop<AppState>) -> Self {
#[cfg(target_arch = "wasm32")]
let proxy = Some(event_loop.create_proxy());
Self {
state: None,
#[cfg(target_arch = "wasm32")]
proxy,
}
}
}
impl ApplicationHandler<AppState> for App {
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()
)
});
}
}
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
#[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);
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));
}
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,
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)]
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 {
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
}
};
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::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()),
_ => {}
}
}
}
pub fn run() -> anyhow::Result<()> {
#[cfg(not(target_arch = "wasm32"))]
{
env_logger::init();
}
#[cfg(target_arch = "wasm32")]
{
console_log::init_with_level(log::Level::Info).unwrap_throw();
}
#[cfg(not(target_arch = "wasm32"))]
{
env_logger::init();
}
#[cfg(target_arch = "wasm32")]
{
console_log::init_with_level(log::Level::Info).unwrap_throw();
}
let event_loop = EventLoop::with_user_event().build()?;
#[cfg(not(target_arch = "wasm32"))]
{
let mut app = App::new();
event_loop.run_app(&mut app)?;
}
#[cfg(target_arch = "wasm32")]
{
let app = App::new(&event_loop);
event_loop.spawn_app(app);
}
let event_loop = EventLoop::with_user_event().build()?;
#[cfg(not(target_arch = "wasm32"))]
{
let mut app = App::new();
event_loop.run_app(&mut app)?;
}
#[cfg(target_arch = "wasm32")]
{
let app = App::new(&event_loop);
event_loop.spawn_app(app);
}
Ok(())
Ok(())
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(start)]
pub fn run_web() -> Result<(), wasm_bindgen::JsValue> {
console_error_panic_hook::set_once();
run().unwrap_throw();
console_error_panic_hook::set_once();
run().unwrap_throw();
Ok(())
Ok(())
}

View file

@ -4,26 +4,16 @@ struct Environment {
dir: vec4<f32>,
}
struct Eye {
// from camera to screen
proj: mat4x4<f32>,
// from screen to camera
inv: mat4x4<f32>,
// world to camera
struct View {
view: mat4x4<f32>,
// camera transform
frame: mat4x4<f32>,
}
// Vertex shader
@group(0) @binding(0)
var<uniform> eye: Eye;
var<uniform> view: View;
@group(0) @binding(1)
var<uniform> environment: Environment;
@group(0) @binding(2)
var sky_sampler: sampler;
@group(0) @binding(3)
var sky_texture: texture_2d<f32>;
struct VertexInput {
@location(0) position: vec3<f32>,
@ -70,7 +60,7 @@ fn vs_main(
out.world_normal = model_rot_matrix * model.normal;
var world_position: vec4<f32> = model_matrix * vec4<f32>(model.position, 1.0);
out.world_position = world_position.xyz;
out.clip_position = eye.proj * eye.view * world_position;
out.clip_position = view.view * world_position;
return out;
}
@ -85,79 +75,23 @@ var t_normal: texture_2d<f32>;
@group(1) @binding(3)
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
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
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 diffuse_color = object_color.xyz;
let specular_color = vec3<f32>(0.0,0.0,0.0);
let reflection = sky_aspect(reflect(normalize(in.world_position-translation(eye.frame).xyz),normalize(in.world_normal)));
let tangent_normal = object_normal.xyz * 2.0 - 1.0;
let light_dir = normalize(environment.dir.xyz);
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 view_dir = normalize(eye.frame[3].xyz - in.world_position);
//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 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;
return vec4<f32>(reflection.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);
}
return vec4<f32>(result.xyz,object_color.a);
}

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 glam::camera::lh::proj::directx::perspective;
use env_logger::Env;
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::util::DeviceExt;
pub(crate) struct Eye {
pub(crate) frame: Affine3A,
pub(crate) environment: Environment,
aspect_ratio: f32,
z_near: f32,
z_far: f32,
fov_y: f32,
pub(crate) environment_buffer: wgpu::Buffer,
pub(crate) camera_buffer: wgpu::Buffer,
pub(crate) layout: wgpu::BindGroupLayout,
pub(crate) group: wgpu::BindGroup,
pub(crate) frame: Affine3A,
pub(crate) environment: Environment,
aspect_ratio: f32,
z_near: f32,
z_far: f32,
fov_y: f32,
pub(crate) environment_buffer: wgpu::Buffer,
pub(crate) buffer: wgpu::Buffer,
pub(crate) layout: wgpu::BindGroupLayout,
pub(crate) group: wgpu::BindGroup,
}
#[repr(C)]
#[derive(Pod, Copy, Clone, Zeroable)]
pub struct Environment {
ambient: Vec4,
light: Vec4,
dir: Vec4,
struct Environment {
ambient: Vec4,
light: Vec4,
dir: Vec4,
}
impl Eye {
pub(crate) fn write(&mut self, queue: &Queue) {
let camera = Mat4::from_mat3_translation(
self.frame.matrix3.into(),
Vec3::from(self.frame.translation),
);
let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far);
queue.write_buffer(
&self.camera_buffer,
0,
bytemuck::cast_slice(&[
projection,
camera * projection.inverse(),
camera.inverse(),
camera,
]),
);
queue.write_buffer(
&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,
});
pub(crate) fn view(&self) -> Mat4 {
let projection = perspective(self.fov_y, self.aspect_ratio, self.z_near, self.z_far);
projection * self.frame.inverse()
}
pub(crate) fn write(&mut self, queue: &Queue) {
queue.write_buffer(&self.buffer,0,bytemuck::cast_slice(&[
self.view(),
Mat4::from_mat3_translation(self.frame.matrix3.into(), Vec3::from(self.frame.translation))
]));
queue.write_buffer(&self.environment_buffer,0,bytemuck::cast_slice(&[self.environment]));
}
pub(crate) fn new(device: &Device, width: u32, height: u32) -> Eye {
let 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]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let dir = Vec3::new(1.0, 0.5, 1.0).normalize();
let environment = Environment {
ambient: Vec4::new(0.15, 0.15, 0.15, 0.0),
light: Vec4::new(1.0, 1.0, 1.0, 0.0),
dir: Vec4::new(dir.x, dir.y, dir.z, 0.0),
};
let dir = Vec3::new(1.0,0.5,1.0).normalize();
let environment = Environment {
ambient: Vec4::new(0.15,0.15,0.15, 0.0),
light: Vec4::new(1.0,1.0,1.0, 0.0),
dir: Vec4::new(dir.x,dir.y,dir.z,0.0),
};
let environment_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(""),
contents: bytemuck::cast_slice(&[environment]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let environment_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some(""),
contents: bytemuck::cast_slice(&[environment]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: 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,
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
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,
device: &wgpu::Device,
camera: &wgpu::Buffer,
environment: &wgpu::Buffer,
skybox: SimpleTexture,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
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);
}
}
}
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::hash::{BuildHasherDefault, Hash, Hasher};
use std::ops::{Div, Mul};
use std::rc::Rc;
use glam::{DVec3, IVec3, Mat4, UVec3, Vec3, Vec4Swizzles};
use wgpu::naga::{FastHashMap, FastHashSet};
use crate::render::{SimpleModelData, SimpleLightData, Renderer};
#[derive(Clone)]
pub enum Shape {
Block(Vec3),
Sphere(f32),
None,
Block(Vec3),
Sphere(f32),
None,
}
#[derive(Clone)]
pub struct SimpleColliderData {
pub transform: Mat4,
pub shape: Shape,
pub radius: f32,
pub transform: Mat4,
pub shape: Shape,
pub radius: f32,
}
#[derive(Clone)]
pub struct SimpleObjectData {
pub model: Option<SimpleModelData>,
pub collider: SimpleColliderData,
pub model: Option<SimpleModelData>,
pub collider: SimpleColliderData,
}
#[derive(Clone)]
pub struct SimpleLight(pub Rc<RefCell<SimpleLightData>>);
impl PartialEq for SimpleLight {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for SimpleLight {}
impl Hash for SimpleLight {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleLight>).hash(state)
}
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleLight>).hash(state)
}
}
#[derive(Clone)]
pub struct SimpleObject(pub Rc<RefCell<SimpleObjectData>>);
impl SimpleObject {
pub(crate) fn hard_clone(&self) -> SimpleObject {
SimpleObject {
0: Rc::new(RefCell::new(SimpleObjectData {
model: self.0.borrow().model.clone(),
collider: self.0.borrow().collider.clone(),
})),
}
pub(crate) fn hard_clone(&self) -> SimpleObject {
SimpleObject {
0: Rc::new(RefCell::new(SimpleObjectData {
model: self.0.borrow().model.clone(),
collider: self.0.borrow().collider.clone(),
})),
}
}
}
impl PartialEq for SimpleObject {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0,&other.0)
}
}
impl Eq for SimpleObject {}
impl Hash for SimpleObject {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state);
}
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state);
}
}
#[derive(Eq, Hash, PartialEq, Clone)]
enum Interest {
Light(SimpleLight),
Object(SimpleObject),
Light(SimpleLight),
Object(SimpleObject),
}
struct Block {
debug: Option<SimpleObject>,
volume: u8,
velocity: Vec3,
material: u8,
interests: FastHashSet<Interest>,
blocks: [Option<Box<Block>>; 64],
debug: Option<SimpleObject>,
volume: u8,
velocity: Vec3,
material: u8,
interests: FastHashSet<Interest>,
blocks: [Option<Box<Block>>;64],
}
const TREE_ATTACK: usize = 4;
const TREE_FLOOR: usize = 4;
impl Block {
fn new() -> Block {
Block {
debug: None,
volume: 0,
velocity: Vec3::ZERO,
material: 0,
interests: FastHashSet::with_hasher(BuildHasherDefault::default()),
blocks: [const { None }; const { TREE_ATTACK * TREE_ATTACK * TREE_ATTACK }],
}
fn new() -> Block {
Block {
debug: None,
volume: 0,
velocity: Vec3::ZERO,
material: 0,
interests: FastHashSet::with_hasher(BuildHasherDefault::default()),
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 };
if rad < cs || block_size >= TREE_FLOOR as f32 {
let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3();
let new_pos = pos - (rel.as_vec3() + cs / 2.0);
let index = (rel.x + rel.y * 4 + rel.z * 16) as usize;
match self.blocks[index] {
Some(ref mut block) => {
block.place_pos(interest, new_pos, rad, cs);
}
None => {
let mut block = Box::new(Block::new());
block.place_pos(interest, new_pos, rad, cs);
self.blocks[index] = Some(block);
}
}
}
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32, block_size: f32) {
let cs = block_size / const { TREE_ATTACK as f32 };
if rad < cs || block_size >= TREE_FLOOR as f32 {
let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3();
let new_pos = pos - (rel.as_vec3() + cs / 2.0);
let index = (rel.x + rel.y * 4 + rel.z * 16) as usize;
match self.blocks[index] {
Some(ref mut block) => {
block.place_pos(interest,new_pos,rad,cs);
},
None => {
let mut block = Box::new(Block::new());
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 {
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 {
chunk_size: u32,
map: FastHashMap<IVec3, Block>,
pub renderer: Option<Renderer>,
debug_world_map: bool,
debug_world_map_object: Option<SimpleObject>,
chunk_size: u32,
map: FastHashMap<IVec3,Block>,
pub renderer: Option<Renderer>,
debug_world_map: bool,
debug_world_map_object: Option<SimpleObject>,
}
impl World {
pub fn new() -> World {
World {
chunk_size: 512,
map: FastHashMap::with_hasher(BuildHasherDefault::default()),
renderer: None,
debug_world_map: false,
debug_world_map_object: None,
pub fn new() -> World {
World {
chunk_size: 512,
map: FastHashMap::with_hasher(BuildHasherDefault::default()),
renderer: None,
debug_world_map: false,
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);
self.debug_world_map_object = self
.renderer
.as_mut()
.unwrap()
.load_from_gltf(include_bytes!("../assets/cube.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
});
}
}
}
}
}
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);
}
}
}
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);
}
}