load arbitrary model data

This commit is contained in:
Chance 2025-03-29 01:31:16 -04:00 committed by BitSyndicate
parent 87289bd8b3
commit 901f7a63cf
Signed by untrusted user: bitsyndicate
GPG key ID: 443E4198D6BBA6DE
11 changed files with 959 additions and 314 deletions

View file

@ -1 +1,4 @@
[alias] [alias]
[build]
rustflags = ["-Ctarget-cpu=native"]

20
Cargo.lock generated
View file

@ -2188,6 +2188,16 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "tobj"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04aca6092e5978e708ee784e8ab9b5cf3cdb598b28f99a2f257446e7081a7025"
dependencies = [
"ahash",
"tokio",
]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.44.1" version = "1.44.1"
@ -2195,6 +2205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes",
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"tokio-macros", "tokio-macros",
@ -2291,6 +2302,12 @@ version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"
@ -3181,6 +3198,7 @@ dependencies = [
name = "zenyx" name = "zenyx"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ahash",
"anyhow", "anyhow",
"backtrace", "backtrace",
"bytemuck", "bytemuck",
@ -3196,9 +3214,11 @@ dependencies = [
"regex", "regex",
"rustyline", "rustyline",
"thiserror 2.0.12", "thiserror 2.0.12",
"tobj",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"typenum",
"wgpu", "wgpu",
"winit", "winit",
] ]

View file

@ -27,12 +27,7 @@ codegen-units = 512
strip = "symbols" strip = "symbols"
debug-assertions = true debug-assertions = true
[profile.dev.build-override]
opt-level = 0
debug = false
overflow-checks = false
incremental = true
codegen-units = 512
[workspace.dependencies] [workspace.dependencies]
lazy_static = "1.5.0" lazy_static = "1.5.0"
@ -40,3 +35,6 @@ parking_lot = "0.12.3"
[profile.release] [profile.release]
debug-assertions = false debug-assertions = false
lto = true
codegen-units = 1
panic = "abort"

View file

@ -17,7 +17,7 @@ parking_lot.workspace = true
regex = "1.11.1" regex = "1.11.1"
rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] } rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] }
thiserror = "2.0.11" thiserror = "2.0.11"
tokio = { version = "1.44.1", features = ["macros", "parking_lot","rt-multi-thread"] } tokio = { version = "1.44.1", features = ["fs", "macros", "parking_lot", "rt-multi-thread"] }
wgpu = "24.0.3" wgpu = "24.0.3"
winit = "0.30.9" winit = "0.30.9"
bytemuck = "1.21.0" bytemuck = "1.21.0"
@ -25,6 +25,9 @@ futures = "0.3.31"
cgmath = "0.18.0" cgmath = "0.18.0"
tracing = "0.1.41" tracing = "0.1.41"
tracing-subscriber = "0.3.19" tracing-subscriber = "0.3.19"
typenum = { version = "1.18.0", features = ["const-generics"] }
tobj = { version = "4.0.3", features = ["tokio"] }
ahash = "0.8.11"
[profile.dev] [profile.dev]

334
engine/src/bint.rs Normal file
View file

@ -0,0 +1,334 @@
use std::{
cmp::Ordering,
convert::From,
fmt,
hash::{Hash, Hasher},
ops::{
Add, AddAssign, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, DivAssign, Mul, MulAssign,
Rem, RemAssign, Shl, Shr, Sub, SubAssign,
},
str::FromStr,
};
#[derive(Debug, Clone, Copy)]
pub struct BigInt<const N: usize> {
bytes: [u8; N],
len: usize,
}
impl<const N: usize> BigInt<N> {
pub fn from_bytes(bytes: &[u8]) -> Self {
debug_assert!(bytes.len() != 0);
let mut len = bytes.len();
while len > 1 && bytes[len - 1] == 0 {
len -= 1;
}
let mut arr = [0u8; N];
arr[..len].copy_from_slice(&bytes[..len]);
BigInt { bytes: arr, len }
}
pub fn from_u64(value: u64) -> Self {
Self::from_bytes(&value.to_le_bytes())
}
fn trim_zeros(mut self) -> Self {
while self.len > 1 && self.bytes[self.len - 1] == 0 {
self.len -= 1;
}
self
}
pub fn max_value() -> Self {
let mut arr = [0u8; N];
arr.fill(255);
BigInt { bytes: arr, len: N }
}
pub fn div_rem(&self, other: &Self) -> (Self, Self) {
let mut quotient = BigInt::from(0);
let mut remainder = self.clone();
let divisor = other.clone();
while remainder >= divisor {
let mut multiple = divisor.clone();
let mut temp_quotient = BigInt::from(1);
while multiple + multiple <= remainder {
multiple = multiple + multiple;
temp_quotient = temp_quotient + temp_quotient;
}
remainder = remainder - multiple;
quotient = quotient + temp_quotient;
}
(quotient.trim_zeros(), remainder.trim_zeros())
}
}
impl<const N: usize> Add for BigInt<N> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
let mut result = [0u8; N];
let mut carry = 0u16;
let max_len = self.len.max(rhs.len);
let mut res_len = 0;
for i in 0..max_len {
let a = self.bytes.get(i).copied().unwrap_or(0) as u16;
let b = rhs.bytes.get(i).copied().unwrap_or(0) as u16;
let sum = a + b + carry;
result[i] = (sum % 256) as u8;
carry = sum / 256;
res_len = i + 1;
}
if carry > 0 {
result[res_len] = carry as u8;
res_len += 1;
}
BigInt {
bytes: result,
len: res_len,
}
.trim_zeros()
}
}
impl<const N: usize> AddAssign for BigInt<N> {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<const N: usize> Sub for BigInt<N> {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
let mut result = [0u8; N];
let mut borrow = 0i16;
let mut res_len = 0;
for i in 0..self.len {
let a = self.bytes[i] as i16;
let b = rhs.bytes.get(i).copied().unwrap_or(0) as i16;
let mut diff = a - b - borrow;
borrow = 0;
if diff < 0 {
diff += 256;
borrow = 1;
}
result[i] = diff as u8;
res_len = i + 1;
}
BigInt {
bytes: result,
len: res_len,
}
.trim_zeros()
}
}
impl<const N: usize> SubAssign for BigInt<N> {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<const N: usize> Mul for BigInt<N> {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let mut result = BigInt {
bytes: [0; N],
len: 0,
};
for i in 0..self.len {
let mut carry = 0u16;
for j in 0..rhs.len {
let idx = i + j;
if idx >= N {
panic!("Multiplication overflow");
}
let product =
self.bytes[i] as u16 * rhs.bytes[j] as u16 + carry + result.bytes[idx] as u16;
result.bytes[idx] = (product % 256) as u8;
carry = product / 256;
}
let mut k = i + rhs.len;
while carry > 0 && k < N {
let sum = result.bytes[k] as u16 + carry;
result.bytes[k] = (sum % 256) as u8;
carry = sum / 256;
k += 1;
}
result.len = result.len.max(k);
}
result.trim_zeros()
}
}
impl<const N: usize> MulAssign for BigInt<N> {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<const N: usize> Div for BigInt<N> {
type Output = Self;
fn div(self, rhs: Self) -> Self {
self.div_rem(&rhs).0
}
}
impl<const N: usize> DivAssign for BigInt<N> {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl<const N: usize> Rem for BigInt<N> {
type Output = Self;
fn rem(self, rhs: Self) -> Self {
self.div_rem(&rhs).1
}
}
impl<const N: usize> RemAssign for BigInt<N> {
fn rem_assign(&mut self, rhs: Self) {
*self = *self % rhs;
}
}
impl<const N: usize> PartialEq for BigInt<N> {
fn eq(&self, other: &Self) -> bool {
self.bytes[..self.len] == other.bytes[..other.len]
}
}
impl<const N: usize> Eq for BigInt<N> {}
impl<const N: usize> Hash for BigInt<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.bytes[..self.len].hash(state);
}
}
impl<const N: usize> PartialOrd for BigInt<N> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize> Ord for BigInt<N> {
fn cmp(&self, other: &Self) -> Ordering {
if self.len != other.len {
return self.len.cmp(&other.len);
}
for i in (0..self.len).rev() {
match self.bytes[i].cmp(&other.bytes[i]) {
Ordering::Equal => continue,
ord => return ord,
}
}
Ordering::Equal
}
}
impl<const N: usize> Deref for BigInt<N> {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.bytes[..self.len]
}
}
impl<const N: usize> DerefMut for BigInt<N> {
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.bytes[..self.len]
}
}
macro_rules! impl_from_int {
($($t:ty),*) => {$(
impl<const N: usize> From<$t> for BigInt<N> {
fn from(value: $t) -> Self {
let bytes = value.to_le_bytes();
Self::from_bytes(&bytes)
}
}
)*};
}
impl_from_int!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
impl<const N: usize> FromStr for BigInt<N> {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut result = BigInt::from(0u8);
for c in s.chars() {
if !c.is_digit(10) {
return Err("Invalid digit");
}
let digit = c.to_digit(10).unwrap() as u8;
result = result * BigInt::from(10u8) + BigInt::from(digit);
}
Ok(result)
}
}
impl<const N: usize> fmt::Display for BigInt<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.len == 0 {
return write!(f, "0");
}
let mut digits = Vec::new();
let mut bytes = self.bytes[..self.len].to_vec();
while !bytes.is_empty() {
let mut remainder = 0u16;
for byte in bytes.iter_mut().rev() {
let value = *byte as u16 + remainder * 256;
*byte = (value / 10) as u8;
remainder = value % 10;
}
digits.push(remainder as u8);
while !bytes.is_empty() && bytes[bytes.len() - 1] == 0 {
bytes.pop();
}
}
digits.reverse();
let s = digits
.iter()
.map(|d| char::from_digit(*d as u32, 10).unwrap())
.collect::<String>();
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
struct Dint {
bytes: Vec<u8>,
}
impl Deref for Dint {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.bytes
}
}
impl DerefMut for Dint {
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.bytes
}
}
pub fn main() {
type TheInt = BigInt<2>;
let mut test: TheInt = TheInt::max_value();
let thing = BigInt::<9>::max_value();
test /= 2.into();
println!("{test}")
}

View file

@ -1,3 +1,4 @@
use std::error::Error;
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use std::mem; use std::mem;
@ -8,50 +9,43 @@ use regex::Regex;
static INIT: parking_lot::Once = Once::new(); static INIT: parking_lot::Once = Once::new();
pub fn set_panic_hook() { pub fn set_panic_hook() {
use std::io::Write;
use colored::Colorize;
use crate::workspace;
INIT.call_once(|| { INIT.call_once(|| {
let default_hook = std::panic::take_hook(); let default_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| { std::panic::set_hook(Box::new(move |info| {
let log_path = workspace::get_working_dir().unwrap_or_else(|_| { if let Err(e) = process_panic(info) {
eprintln!("Error in panic hook: {}", e);
default_hook(info); default_hook(info);
std::process::exit(0);
});
if !log_path.exists() {
std::fs::create_dir_all(&log_path).unwrap_or_else(|_| {
default_hook(info);
std::process::exit(0);
});
} }
let log_path = log_path.join("panic.log"); std::process::exit(0);
}));
});
}
let mut file = std::fs::File::create(&log_path).unwrap_or_else(|_| { fn process_panic(info: &std::panic::PanicHookInfo<'_>) -> Result<(), Box<dyn Error>> {
default_hook(info); use crate::workspace;
std::process::exit(0); use colored::Colorize;
}); use std::io::Write;
let payload = info.payload();
let payload_str = if let Some(s) = payload.downcast_ref::<&str>() {
*s
} else if let Some(s) = payload.downcast_ref::<String>() {
s
} else {
"<non-string panic payload>"
};
writeln!(file, "{}", payload_str).unwrap_or_else(|_| { let log_dir = workspace::get_working_dir()?;
default_hook(info); if !log_dir.exists() {
std::process::exit(0); std::fs::create_dir_all(&log_dir)?;
}); }
writeln!(file, "{}", render_backtrace().sanitize_path()).unwrap_or_else(|_| { let log_path = log_dir.join("panic.log");
default_hook(info);
std::process::exit(0);
});
let panic_msg = format!( let mut file = std::fs::File::create(&log_path)?;
let payload = info.payload();
let payload_str = if let Some(s) = payload.downcast_ref::<&str>() {
*s
} else if let Some(s) = payload.downcast_ref::<String>() {
s
} else {
"<non-string panic payload>"
};
writeln!(file, "{}", payload_str)?;
writeln!(file, "{}", render_backtrace().sanitize_path())?;
let panic_msg = format!(
"Zenyx had a problem and crashed. To help us diagnose the problem you can send us a crash report. "Zenyx had a problem and crashed. To help us diagnose the problem you can send us a crash report.
We have generated a report file at \"{}\". Submit an issue or email with the subject of \"Zenyx Crash Report\" and include the report as an attachment. We have generated a report file at \"{}\". Submit an issue or email with the subject of \"Zenyx Crash Report\" and include the report as an attachment.
@ -63,11 +57,12 @@ https://github.com/Zenyx-Engine/Zenyx/issues
We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports. We take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports.
Thank you kindly!", log_path.display()); Thank you kindly!", log_path.display());
println!("{}", panic_msg.red().bold()); println!("{}", panic_msg.red().bold());
println!("\nFor future reference, the error summary is as follows:\n{}", payload_str.red().bold()); println!(
std::process::exit(0); "\nFor future reference, the error summary is as follows:\n{}",
})); payload_str.red().bold()
}); );
Ok(())
} }
fn render_backtrace() -> String { fn render_backtrace() -> String {
@ -130,7 +125,7 @@ trait Sanitize {
impl Sanitize for str { impl Sanitize for str {
fn sanitize_path(&self) -> String { fn sanitize_path(&self) -> String {
let username_pattern = r"(?i)(/home/|/Users/|\\Users\\)([^/\\]+)"; let username_pattern = r"(?i)(/home/|/Users/|\\Users\\)([^/\\]+)";
let re = Regex::new(username_pattern).expect("Failed to sanitize path, aborting operation"); let re = Regex::new(username_pattern).expect("Failed to compile regex for sanitization");
re.replace_all(self, "${1}<USER>").to_string() re.replace_all(self, "${1}<USER>").to_string()
} }
} }

View file

@ -1,14 +1,16 @@
use std::borrow::Cow;
use std::mem::offset_of;
use std::sync::Arc; use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
use std::{backtrace::Backtrace, borrow::Cow};
use cgmath::{Matrix4, Point3, Rad, Vector3, perspective}; use cgmath::{Deg, Matrix4, Point3, Rad, SquareMatrix, Vector3, perspective};
use futures::executor::block_on; use futures::executor::block_on;
use thiserror::Error; use thiserror::Error;
use tracing::{error, trace}; use tracing::{error, trace};
use wgpu::TextureUsages; use wgpu::TextureUsages;
use wgpu::{Backends, InstanceDescriptor, util::DeviceExt}; use wgpu::{Backends, InstanceDescriptor, util::DeviceExt};
use winit::window::Window; use winit::window::Window;
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[error(transparent)] #[error(transparent)]
pub enum ContextErrorKind { pub enum ContextErrorKind {
@ -127,13 +129,21 @@ impl From<wgpu::RequestDeviceError> for RenderContextError {
} }
} }
const CUBE_SHADER: &str = r" const SHADER_SRC: &str = r#"
struct Uniforms { struct CameraUniform {
mvp: mat4x4<f32>, view: mat4x4<f32>,
proj: mat4x4<f32>,
};
struct ModelUniform {
model: mat4x4<f32>,
}; };
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> u: Uniforms; var<uniform> camera: CameraUniform;
@group(1) @binding(0)
var<uniform> model: ModelUniform;
struct VertexInput { struct VertexInput {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
@ -148,27 +158,27 @@ struct VertexOutput {
@vertex @vertex
fn vs_main(input: VertexInput) -> VertexOutput { fn vs_main(input: VertexInput) -> VertexOutput {
var output: VertexOutput; var output: VertexOutput;
output.clip_position = u.mvp * vec4<f32>(input.position, 1.0); let model_pos = model.model * vec4<f32>(input.position, 1.0);
output.clip_position = camera.proj * camera.view * model_pos;
output.normal = input.normal; output.normal = input.normal;
return output; return output;
} }
@fragment @fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> { fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
let ambient: f32 = 0.2; let ambient: f32 = 0.2;
let light_dir = normalize(vec3<f32>(0.5, 1.0, 0.5)); let light_dir = normalize(vec3<f32>(0.5, 1.0, 0.5));
let diffuse = clamp(dot(normalize(input.normal), light_dir), 0.0, 1.0); let diffuse = clamp(dot(normalize(input.normal), light_dir), 0.0, 1.0);
// Mix ambient light to ensure no face is completely dark.
let brightness = ambient + (1.0 - ambient) * diffuse; let brightness = ambient + (1.0 - ambient) * diffuse;
return vec4<f32>(0.7 * brightness, 0.7 * brightness, 0.9 * brightness, 1.0); return vec4<f32>(0.7 * brightness, 0.7 * brightness, 0.9 * brightness, 1.0);
} }
"; "#;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct Vertex { pub struct Vertex {
position: [f32; 3], pub position: [f32; 3],
normal: [f32; 3], pub normal: [f32; 3],
} }
impl Vertex { impl Vertex {
@ -179,7 +189,7 @@ impl Vertex {
format: wgpu::VertexFormat::Float32x3, format: wgpu::VertexFormat::Float32x3,
}, },
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, offset: offset_of!(Vertex, normal) as u64,
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float32x3, format: wgpu::VertexFormat::Float32x3,
}, },
@ -194,170 +204,165 @@ impl Vertex {
} }
} }
static CUBE_VERTICES: &[Vertex] = &[ #[repr(C)]
Vertex { #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
position: [-0.5, -0.5, 0.5], struct CameraUniform {
normal: [0.0, 0.0, 1.0], view: [[f32; 4]; 4],
}, proj: [[f32; 4]; 4],
Vertex { }
position: [0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
];
pub struct WgpuCtx<'window> { #[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct ModelUniform {
model: [[f32; 4]; 4],
}
struct Camera {
uniform_buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup,
view: Matrix4<f32>,
proj: Matrix4<f32>,
}
impl Camera {
fn new(
device: &wgpu::Device,
bind_group_layout: &wgpu::BindGroupLayout,
width: u32,
height: u32,
) -> Self {
let view = Matrix4::look_at_rh(
Point3::new(0.0, 0.0, 3.0),
Point3::new(0.0, 0.0, 0.0),
Vector3::unit_y(),
);
let aspect = width as f32 / height as f32;
let proj = perspective(Rad::from(Deg(45.0)), aspect, 0.1, 100.0);
let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Camera Uniform Buffer"),
size: std::mem::size_of::<CameraUniform>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Camera Bind Group"),
layout: bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}],
});
Self {
uniform_buffer,
bind_group,
view,
proj,
}
}
fn resize(&mut self, width: u32, height: u32) {
let aspect = width as f32 / height as f32;
self.proj = perspective(Rad::from(Deg(45.0)), aspect, 0.1, 100.0);
}
fn update(&self, queue: &wgpu::Queue) {
let view_array: [[f32; 4]; 4] = self.view.into();
let proj_array: [[f32; 4]; 4] = self.proj.into();
let uniform = CameraUniform {
view: view_array,
proj: proj_array,
};
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&uniform));
}
}
struct Model {
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
uniform_buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup,
index_count: u32, // Changed from vertex_count to index_count
transform: Matrix4<f32>,
}
impl Model {
fn new(
device: &wgpu::Device,
vertices: &[Vertex],
indices: &[u32],
bind_group_layout: &wgpu::BindGroupLayout,
) -> Self {
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(indices), // Use proper indices
usage: wgpu::BufferUsages::INDEX,
});
let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Model Uniform Buffer"),
size: std::mem::size_of::<ModelUniform>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Model Bind Group"),
layout: bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}],
});
Self {
vertex_buffer,
index_buffer,
uniform_buffer,
bind_group,
index_count: indices.len() as u32,
transform: Matrix4::identity(),
}
}
fn update(&self, queue: &wgpu::Queue) {
let model_array: [[f32; 4]; 4] = self.transform.into();
let uniform = ModelUniform { model: model_array };
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&uniform));
}
fn set_transform(&mut self, transform: Matrix4<f32>) {
self.transform = transform;
}
}
pub struct Renderer<'window> {
device: wgpu::Device, device: wgpu::Device,
queue: wgpu::Queue, queue: wgpu::Queue,
surface: wgpu::Surface<'window>, surface: wgpu::Surface<'window>,
surface_config: wgpu::SurfaceConfiguration, surface_config: wgpu::SurfaceConfiguration,
adapter: wgpu::Adapter, camera: Camera,
models: Vec<Model>,
render_pipeline: wgpu::RenderPipeline, render_pipeline: wgpu::RenderPipeline,
uniform_buffer: wgpu::Buffer, depth_texture: wgpu::Texture,
vertex_buffer: wgpu::Buffer, depth_texture_view: wgpu::TextureView,
start_time: Instant, camera_bind_group_layout: wgpu::BindGroupLayout,
model_bind_group_layout: wgpu::BindGroupLayout,
bg_color: wgpu::Color, bg_color: wgpu::Color,
start_time: Instant,
last_frame_instant: Instant, last_frame_instant: Instant,
frame_count: u32, frame_count: u32,
} }
impl<'window> WgpuCtx<'window> { impl<'window> Renderer<'window> {
pub async fn new(window: Arc<Window>) -> Result<WgpuCtx<'window>, RenderContextError> { pub async fn new(window: Arc<Window>) -> Result<Self, RenderContextError> {
let instance = wgpu::Instance::new(&InstanceDescriptor { let instance = wgpu::Instance::new(&InstanceDescriptor {
backends: Backends::from_comma_list("dx12,metal,opengl,webgpu"), backends: Backends::from_comma_list("dx12,metal,opengl,webgpu"),
..Default::default() ..Default::default()
@ -381,86 +386,97 @@ impl<'window> WgpuCtx<'window> {
None, None,
) )
})?; })?;
let (device, queue) = adapter let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor::default(), None) .request_device(&wgpu::DeviceDescriptor::default(), None)
.await .await
.ctx_err(ContextErrorKind::DeviceRequest, "Device configuration")?; .ctx_err(ContextErrorKind::DeviceRequest, "Device configuration")?;
let size = window.inner_size(); let size = window.inner_size();
let width = size.width.max(1); let width = size.width.max(1);
let height = size.height.max(1); let height = size.height.max(1);
let surface_config = wgpu::SurfaceConfiguration {
width: width.max(1), let camera_bind_group_layout =
height: height.max(1), device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
format: wgpu::TextureFormat::Rgba8UnormSrgb, label: Some("Camera Bind Group Layout"),
present_mode: wgpu::PresentMode::AutoNoVsync, entries: &[wgpu::BindGroupLayoutEntry {
alpha_mode: wgpu::CompositeAlphaMode::Auto, binding: 0,
view_formats: Vec::new(), visibility: wgpu::ShaderStages::VERTEX,
usage: TextureUsages::RENDER_ATTACHMENT, ty: wgpu::BindingType::Buffer {
desired_maximum_frame_latency: 3, ty: wgpu::BufferBindingType::Uniform,
}; has_dynamic_offset: false,
surface.configure(&device, &surface_config); min_binding_size: wgpu::BufferSize::new(
let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor { std::mem::size_of::<CameraUniform>() as u64,
label: Some("Uniform Buffer"), ),
size: 64, },
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, count: None,
mapped_at_creation: false, }],
}); });
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Cube Vertex Buffer"), let model_bind_group_layout =
contents: bytemuck::cast_slice(CUBE_VERTICES), device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
usage: wgpu::BufferUsages::VERTEX, label: Some("Model Bind Group Layout"),
}); entries: &[wgpu::BindGroupLayoutEntry {
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { binding: 0,
label: Some("Cube Shader"), visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(CUBE_SHADER)), ty: wgpu::BindingType::Buffer {
}); ty: wgpu::BufferBindingType::Uniform,
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { has_dynamic_offset: false,
label: Some("Uniform Bind Group Layout"), min_binding_size: wgpu::BufferSize::new(
entries: &[wgpu::BindGroupLayoutEntry { std::mem::size_of::<ModelUniform>() as u64,
binding: 0, ),
visibility: wgpu::ShaderStages::VERTEX, },
ty: wgpu::BindingType::Buffer { count: None,
ty: wgpu::BufferBindingType::Uniform, }],
has_dynamic_offset: false, });
min_binding_size: wgpu::BufferSize::new(64),
},
count: None,
}],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Cube Pipeline Layout"), label: Some("Pipeline Layout"),
bind_group_layouts: &[&bind_group_layout], bind_group_layouts: &[&camera_bind_group_layout, &model_bind_group_layout],
push_constant_ranges: &[], push_constant_ranges: &[],
}); });
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Main Shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(SHADER_SRC)),
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Cube Render Pipeline"), label: Some("Main Pipeline"),
layout: Some(&pipeline_layout), layout: Some(&pipeline_layout),
vertex: wgpu::VertexState { vertex: wgpu::VertexState {
module: &shader, module: &shader,
entry_point: Some("vs_main"), entry_point: Some("vs_main"),
buffers: &[Vertex::desc()], buffers: &[Vertex::desc()],
compilation_options: wgpu::PipelineCompilationOptions::default(), compilation_options: Default::default(),
}, },
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &shader, module: &shader,
entry_point: Some("fs_main"), entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format: surface_config.format, format: surface.get_capabilities(&adapter).formats[0],
blend: Some(wgpu::BlendState::ALPHA_BLENDING), blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
})], })],
compilation_options: wgpu::PipelineCompilationOptions::default(), compilation_options: Default::default(),
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None, strip_index_format: None,
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back), cull_mode: Some(wgpu::Face::Back),
// cull_mode: ,
polygon_mode: wgpu::PolygonMode::Fill, polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false, unclipped_depth: false,
conservative: false, conservative: false,
}, },
depth_stencil: None, depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multisample: wgpu::MultisampleState { multisample: wgpu::MultisampleState {
count: 1, count: 1,
mask: !0, mask: !0,
@ -469,15 +485,40 @@ impl<'window> WgpuCtx<'window> {
multiview: None, multiview: None,
cache: None, cache: None,
}); });
Ok(WgpuCtx {
let camera = Camera::new(&device, &camera_bind_group_layout, width, height);
let surface_caps = surface.get_capabilities(&adapter);
let surface_config = wgpu::SurfaceConfiguration {
width,
height,
format: surface_caps.formats[0],
present_mode: wgpu::PresentMode::AutoNoVsync,
alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: vec![],
usage: TextureUsages::RENDER_ATTACHMENT,
desired_maximum_frame_latency: 3,
};
surface.configure(&device, &surface_config);
let (depth_texture, depth_texture_view) = create_depth_texture(
&device,
surface_config.width,
surface_config.height,
// surface_config.format,
);
Ok(Self {
device, device,
queue, queue,
surface, surface,
surface_config, surface_config,
adapter, camera,
models: Vec::new(),
render_pipeline, render_pipeline,
uniform_buffer, camera_bind_group_layout,
vertex_buffer, model_bind_group_layout,
bg_color: wgpu::Color { bg_color: wgpu::Color {
r: 0.1, r: 0.1,
g: 0.1, g: 0.1,
@ -487,79 +528,101 @@ impl<'window> WgpuCtx<'window> {
start_time: Instant::now(), start_time: Instant::now(),
last_frame_instant: Instant::now(), last_frame_instant: Instant::now(),
frame_count: 0, frame_count: 0,
depth_texture,
depth_texture_view,
}) })
} }
pub fn new_blocking(window: Arc<Window>) -> Result<WgpuCtx<'window>, RenderContextError> { pub fn new_blocking(window: Arc<Window>) -> Result<Self, RenderContextError> {
block_on(Self::new(window)) block_on(Self::new(window))
} }
pub fn add_model(&mut self, vertices: &[Vertex], indicies: &[u32]) {
let model = Model::new(
&self.device,
vertices,
indicies,
&self.model_bind_group_layout,
);
self.models.push(model);
}
pub fn resize(&mut self, new_size: (u32, u32)) { pub fn resize(&mut self, new_size: (u32, u32)) {
let (width, height) = new_size; let (width, height) = new_size;
let (depth_texture,depth_view) = create_depth_texture(&self.device, width, height);
self.surface_config.width = width.max(1); self.surface_config.width = width.max(1);
self.surface_config.height = height.max(1); self.surface_config.height = height.max(1);
self.surface.configure(&self.device, &self.surface_config); self.surface.configure(&self.device, &self.surface_config);
self.depth_texture = depth_texture;
self.depth_texture_view = depth_view;
self.camera.resize(width, height);
} }
pub fn draw(&mut self) { pub fn draw(&mut self) {
let elapsed = self.start_time.elapsed().as_secs_f32() * 0.80f32; let elapsed = self.start_time.elapsed().as_secs_f32();
let model = Matrix4::from_angle_x(Rad(elapsed)) * Matrix4::from_angle_y(Rad(elapsed));
let view = Matrix4::look_at_rh( self.camera.update(&self.queue);
Point3::new(0.0, 0.0, 3.0),
Point3::new(0.0, 0.0, 0.0), for (i, model) in self.models.iter_mut().enumerate() {
Vector3::unit_y(), let angle = Rad(elapsed * 0.8 + i as f32 * 0.3);
); model.set_transform(Matrix4::from_angle_x(angle) * Matrix4::from_angle_y(angle));
let aspect = self.surface_config.width as f32 / self.surface_config.height as f32; model.update(&self.queue);
let proj = perspective(Rad(std::f32::consts::FRAC_PI_4), aspect, 0.1, 100.0); }
let mvp = proj * view * model;
let mvp_array: [[f32; 4]; 4] = [
[mvp.x.x, mvp.x.y, mvp.x.z, mvp.x.w],
[mvp.y.x, mvp.y.y, mvp.y.z, mvp.y.w],
[mvp.z.x, mvp.z.y, mvp.z.z, mvp.z.w],
[mvp.w.x, mvp.w.y, mvp.w.z, mvp.w.w],
];
self.queue
.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&mvp_array));
let surface_texture = self let surface_texture = self
.surface .surface
.get_current_texture() .get_current_texture()
.expect("Failed to get surface texture"); .ctx_err(
let view_texture = surface_texture ContextErrorKind::SurfaceTexture,
"Surface texture acquisition",
)
.unwrap();
let view = surface_texture
.texture .texture
.create_view(&wgpu::TextureViewDescriptor::default()); .create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self let mut encoder = self
.device .device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { .create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Cube Command Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Cube Render Pass"), label: Some("Main Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment { color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view_texture, view: &view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(self.bg_color), load: wgpu::LoadOp::Clear(self.bg_color),
store: wgpu::StoreOp::Store, store: wgpu::StoreOp::Store,
}, },
})], })],
depth_stencil_attachment: None, depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
timestamp_writes: None, view: &self.depth_texture_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
occlusion_query_set: None, occlusion_query_set: None,
timestamp_writes: None,
}); });
render_pass.set_pipeline(&self.render_pipeline); render_pass.set_pipeline(&self.render_pipeline);
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { render_pass.set_bind_group(0, &self.camera.bind_group, &[]);
label: Some("Uniform Bind Group"),
layout: &self.render_pipeline.get_bind_group_layout(0), for model in &self.models {
entries: &[wgpu::BindGroupEntry { render_pass.set_bind_group(1, &model.bind_group, &[]);
binding: 0, render_pass.set_vertex_buffer(0, model.vertex_buffer.slice(..));
resource: self.uniform_buffer.as_entire_binding(), render_pass
}], .set_index_buffer(model.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
}); render_pass.draw_indexed(0..model.index_count, 0, 0..1);
render_pass.set_bind_group(0, &bind_group, &[]); }
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.draw(0..36, 0..1);
} }
self.queue.submit(Some(encoder.finish())); self.queue.submit(Some(encoder.finish()));
surface_texture.present(); surface_texture.present();
@ -573,10 +636,38 @@ impl<'window> WgpuCtx<'window> {
} }
} }
pub fn change_bg_color(&mut self, color: wgpu::Color) { pub fn set_bg_color(&mut self, color: wgpu::Color) {
self.bg_color = color; self.bg_color = color;
} }
pub fn bg_color(&self) -> wgpu::Color { pub fn bg_color(&self) -> wgpu::Color {
self.bg_color.clone() self.bg_color
} }
} }
fn create_depth_texture(
device: &wgpu::Device,
width: u32,
height: u32,
// format: wgpu::TextureFormat,
) -> (wgpu::Texture, wgpu::TextureView) {
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {
label: Some("Depth Texture"),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
};
let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}

View file

@ -2,7 +2,8 @@ use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
use ctx::WgpuCtx; use ctx::{Renderer, Vertex};
use tobj::LoadOptions;
use tracing::{debug, error, info, trace, warn}; use tracing::{debug, error, info, trace, warn};
use wgpu::rwh::HasWindowHandle; use wgpu::rwh::HasWindowHandle;
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
@ -15,7 +16,7 @@ pub mod ctx;
struct WindowContext<'window> { struct WindowContext<'window> {
window: Arc<Window>, window: Arc<Window>,
ctx: WgpuCtx<'window>, ctx: Renderer<'window>,
main_window: bool, main_window: bool,
} }
impl Deref for WindowContext<'_> { impl Deref for WindowContext<'_> {
@ -35,6 +36,161 @@ impl WindowContext<'_> {
pub struct App<'window> { pub struct App<'window> {
windows: HashMap<WindowId, WindowContext<'window>>, windows: HashMap<WindowId, WindowContext<'window>>,
} }
static CUBE_VERTICES: &[Vertex] = &[
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, 0.0, 1.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, 0.0, -1.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [-1.0, 0.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, -0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, 0.5, 0.5],
normal: [0.0, 1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, 0.5],
normal: [0.0, -1.0, 0.0],
},
Vertex {
position: [-0.5, -0.5, -0.5],
normal: [0.0, -1.0, 0.0],
},
];
const CUBE_INDICES: &[u32] = &[
0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20,
];
impl App<'_> { impl App<'_> {
fn create_main_window(&mut self, event_loop: &ActiveEventLoop) { fn create_main_window(&mut self, event_loop: &ActiveEventLoop) {
@ -43,8 +199,45 @@ impl App<'_> {
Ok(window) => { Ok(window) => {
let window = Arc::new(window); let window = Arc::new(window);
let window_id = window.id(); let window_id = window.id();
match WgpuCtx::new_blocking(window.clone()) { match Renderer::new_blocking(window.clone()) {
Ok(wgpu_ctx) => { Ok(mut wgpu_ctx) => {
let obj = match tobj::load_obj(
"test.obj",
&LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
},
) {
Ok(obj) => obj,
Err(e) => {
error!("{e}");
panic!()
}
};
let mesh = obj.0.get(0).unwrap().mesh.clone();
let vertices: Vec<Vertex> = (0..mesh.positions.len() / 3)
.map(|i| Vertex {
position: [
mesh.positions[i * 3],
mesh.positions[i * 3 + 1],
mesh.positions[i * 3 + 2],
],
normal: if !mesh.normals.is_empty() {
[
mesh.normals[i * 3],
mesh.normals[i * 3 + 1],
mesh.normals[i * 3 + 2],
]
} else {
[0.0; 3]
},
})
.collect();
wgpu_ctx.add_model(&vertices, &mesh.indices);
// wgpu_ctx.add_model(CUBE_VERTICES,CUBE_INDICES);
self.windows.insert( self.windows.insert(
window_id, window_id,
WindowContext { WindowContext {
@ -101,7 +294,7 @@ impl App<'_> {
wgpu::Color::BLACK => wgpu::Color::WHITE, wgpu::Color::BLACK => wgpu::Color::WHITE,
_ => wgpu::Color::WHITE, _ => wgpu::Color::WHITE,
}; };
window_context.ctx.change_bg_color(new_color); window_context.ctx.set_bg_color(new_color);
debug!("Toggled background color for window {:?}", window_id); debug!("Toggled background color for window {:?}", window_id);
} else { } else {
warn!("No window context for toggling background: {:?}", window_id); warn!("No window context for toggling background: {:?}", window_id);
@ -111,7 +304,7 @@ impl App<'_> {
fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) { fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) {
if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) { if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) {
let title = format!("Zenyx - New Window {}", self.windows.len()); let title = format!("Zenyx - New Window {}", self.windows.len());
//TODO: Verify that this is safe instead of matching on it // TODO: Verify that this is safe instead of matching on it
let win_attr = unsafe { let win_attr = unsafe {
let base = Window::default_attributes().with_title(title); let base = Window::default_attributes().with_title(title);
match main_ctx.window_handle() { match main_ctx.window_handle() {
@ -123,8 +316,9 @@ impl App<'_> {
Ok(window) => { Ok(window) => {
let window = Arc::new(window); let window = Arc::new(window);
let window_id = window.id(); let window_id = window.id();
match WgpuCtx::new_blocking(window.clone()) { match Renderer::new_blocking(window.clone()) {
Ok(wgpu_ctx) => { Ok(mut wgpu_ctx) => {
wgpu_ctx.add_model(CUBE_VERTICES,CUBE_INDICES);
self.windows.insert( self.windows.insert(
window_id, window_id,
WindowContext { WindowContext {

View file

@ -7,6 +7,7 @@ use tracing::{debug, error, info, warn};
use tracing::{level_filters::LevelFilter, subscriber::set_global_default}; use tracing::{level_filters::LevelFilter, subscriber::set_global_default};
use tracing_subscriber::{layer::Filter, util::SubscriberInitExt}; use tracing_subscriber::{layer::Filter, util::SubscriberInitExt};
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
pub mod bint;
pub mod core; pub mod core;
fn init_logger() { fn init_logger() {
@ -26,6 +27,7 @@ fn init_logger() {
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
init_logger(); init_logger();
// bint::main();
if !cfg!(debug_assertions) { if !cfg!(debug_assertions) {
info!("{}", "Debug mode disabled".bright_blue()); info!("{}", "Debug mode disabled".bright_blue());
set_panic_hook(); set_panic_hook();

2
test.mtl Normal file
View file

@ -0,0 +1,2 @@
# Blender 4.2.3 LTS MTL File: 'None'
# www.blender.org

BIN
test.obj (Stored with Git LFS) Normal file

Binary file not shown.