Compare commits

..

3 commits

Author SHA1 Message Date
5afd5b0c1e
fix(err): Improve error handling to reduce crashes
Some checks failed
Build Zenyx ⚡ / 🧪 Run Cargo Tests (push) Successful in 4m23s
Build Zenyx ⚡ / 🏗️ Build aarch64-unknown-linux-gnu (push) Failing after 7m10s
Build Zenyx ⚡ / 🏗️ Build x86_64-apple-darwin (push) Failing after 9m3s
Build Zenyx ⚡ / 🏗️ Build aarch64-apple-darwin (push) Failing after 9m12s
Build Zenyx ⚡ / 🏗️ Build aarch64-pc-windows-msvc (push) Failing after 9m48s
Build Zenyx ⚡ / 🏗️ Build x86_64-pc-windows-msvc (push) Failing after 6m22s
Build Zenyx ⚡ / 🏗️ Build x86_64-unknown-linux-gnu (push) Failing after 5m7s
2025-05-05 17:24:24 -04:00
0e8209f9b9
feat(render): time-based rotation in shaders 2025-04-27 17:22:05 -04:00
22a58044d5
feat(render): basic normal-based lighting 2025-04-27 16:06:59 -04:00
10 changed files with 131 additions and 298 deletions

View file

@ -15,7 +15,7 @@ jobs:
- name: 🚀 Setup & Run tests
run: |
nix-shell -p cargo rustc --run "
nix-shell --run "
cargo test --release --all
"
@ -29,47 +29,41 @@ jobs:
target:
- x86_64-unknown-linux-gnu
- x86_64-pc-windows-msvc
#- x86_64-apple-darwin
- x86_64-apple-darwin
- aarch64-unknown-linux-gnu
- aarch64-pc-windows-msvc
#- aarch64-apple-darwin
- aarch64-apple-darwin
include:
- target: x86_64-unknown-linux-gnu
binary_name: zenyx-x86_64-linux
ext: ""
linker: /lib64/ld-linux-x86-64.so.2
command: zigbuild
args: --target x86_64-unknown-linux-gnu --release
- target: aarch64-unknown-linux-gnu
binary_name: zenyx-aarch64-linux
ext: ""
linker: /lib/ld-linux-aarch64.so.1
command: zigbuild
args: --target aarch64-unknown-linux-gnu --release
- target: x86_64-pc-windows-msvc
binary_name: zenyx-x86_64-windows
ext: ".exe"
linker: ""
command: xwin build
args: --target x86_64-pc-windows-msvc --release
- target: aarch64-pc-windows-msvc
binary_name: zenyx-aarch64-windows
ext: ".exe"
linker: ""
command: xwin build
args: --target aarch64-pc-windows-msvc --release
#- target: x86_64-apple-darwin
# binary_name: zenyx-x86_64-macos
# ext: ""
# linker: ""
#command: zigbuild
#args: --target x86_64-apple-darwin --release
#target: aarch64-apple-darwin
#binary_name: zenyx-aarch64-macos
#ext: ""
#linker: ""
#command: zigbuild
#args: --target aarch64-apple-darwin --release
- target: x86_64-apple-darwin
binary_name: zenyx-x86_64-macos
ext: ""
command: zigbuild
args: --target x86_64-apple-darwin --release
- target: aarch64-apple-darwin
binary_name: zenyx-aarch64-macos
ext: ""
command: zigbuild
args: --target aarch64-apple-darwin --release
steps:
- name: 📥 Checkout source
@ -86,14 +80,10 @@ jobs:
mkdir -p artifacts
cp target/${{ matrix.target }}/release/zenyx${{ matrix.ext }} artifacts/${{ matrix.binary_name }}${{ matrix.ext }}
- name: 🛠️ Patchelf
if: ${{
matrix.target == 'x86_64-unknown-linux-gnu' ||
matrix.target == 'aarch64-unknown-linux-gnu'
}}
- name: 🛠️ Pathelf
if: ${{ matrix.target }} == x86_64-unknown-linux-gnu || ${{ matrix.target }} == aarch64-unknown-linux-gnu
run: |
nix run --extra-experimental-features nix-command --extra-experimental-features flakes nixpkgs#patchelf -- --set-interpreter ${{ matrix.linker }} \
artifacts/${{ matrix.binary_name }}${{ matrix.ext }}
nix run nixpkgs#patchelf -- --set-interpreter /lib64/ld-linux-x86-64.so.2 artifacts/${{ matrix.binary_name }}${{ matrix.ext }}
- name: ⬆️ Upload artifact
uses: https://code.forgejo.org/forgejo/upload-artifact@v4

1
Cargo.lock generated
View file

@ -3707,6 +3707,7 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
name = "zenyx"
version = "0.1.0"
dependencies = [
"ahash",
"allocator-api2",
"build-print",
"bytemuck",

View file

@ -45,7 +45,7 @@ cgmath = "0.18.0"
image = "0.25.6"
smol = "2.0.2"
winit = { version = "0.30.9" }
winit = "0.30.9"
terminator = "0.3.2"
thiserror = "2.0.12"
tobj = "4.0.3"
@ -55,10 +55,10 @@ vulkano = "0.35.1"
wgpu = { version = "25.0.0", features = ["spirv"] }
zlog.workspace = true
allocator-api2 = "0.2.21"
ahash = "0.8.11"
[target.aarch64-linux-android.dependencies]
winit = { version = "0.30.9", features = ["android-native-activity"] }
[build-dependencies]
build-print = "0.1.1"
bytemuck = "1.22.0"

View file

@ -3,7 +3,7 @@ use naga::{
ShaderStage,
back::spv::{self, WriterFlags},
front::glsl::{self, Options as GlslOptions, ParseErrors},
valid::{ValidationError, ValidationFlags, Validator},
valid::{Capabilities, ValidationError, ValidationFlags, Validator},
};
use std::{
env, fs,
@ -62,9 +62,12 @@ fn compile_shader(path: &Path, out_dir: &Path) -> Result<(), BuildError> {
let module = glsl::Frontend::default()
.parse(&GlslOptions::from(stage), &src)
.map_err(|e| (ext.clone(), e))?;
let info = Validator::new(ValidationFlags::all(), Default::default())
.validate(&module)
.map_err(|e| (ext.clone(), e.into_inner()))?;
let info = Validator::new(
ValidationFlags::all(),
Capabilities::default().union(Capabilities::PUSH_CONSTANT),
)
.validate(&module)
.map_err(|e| (ext.clone(), e.into_inner()))?;
let mut writer = spv::Writer::new(&spv::Options {
flags: WriterFlags::empty(),
..Default::default()

View file

@ -1,10 +1,20 @@
#version 450
layout(location = 0) in vec2 tex_coords;
layout(location = 1) in vec4 normal;
layout(set = 1, binding = 0) uniform texture2D t_diffuse;
layout(set = 1, binding = 1) uniform sampler s_diffuse;
layout(location = 0) out vec4 out_color;
// layout(group = 0, binding = 0) out texture2D;
void main() {
float ambient = 0.2;
vec3 light_dir = normalize(vec3(0.5, 1.0, 0.5));
float diffuse = clamp(dot(normalize(vec3(normal.x, normal.y, normal.z)), light_dir), 0.0, 1.0);
float brightness = ambient + (1.0 - ambient) * diffuse;
// out_color = vec4(normal.x, normal.y, normal.z, 1.0);
// out_color = vec3(1.0,.0.0)
out_color = texture(sampler2D(t_diffuse, s_diffuse), tex_coords);
out_color.r = clamp(out_color.r * 0.6 * brightness, 0.0, 1.0);
out_color.g = clamp(out_color.g * 0.6 * brightness, 0.0, 1.0);
out_color.b = clamp(out_color.b * 0.9 * brightness, 0.0, 1.0);
}

View file

@ -2,14 +2,36 @@
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
layout(location = 2) in vec3 normal_in;
layout(location = 3) in vec2 tex_coords;
layout(location = 0) out vec2 tex_coord;
layout(location = 1) out vec4 normal;
layout(push_constant) uniform float TIME;
layout(set = 0, binding = 0) uniform UniformBufferObject {
mat4x4 projection;
} view;
void main() {
gl_Position = view.projection * vec4(position, 1.0);
tex_coord = tex_coords;
// gl_Position
// out_color = color;
mat4 rotation(float lerp) {
return mat4(cos(lerp), -sin(lerp), 0., 0.,
sin(lerp), cos(lerp), 0., 0.,
0., 0., 1., 0.,
0., 0., 0., 1.);
}
void main() {
float sum_val = sin(TIME / 5);
mat4 model = transpose(mat4(
1.0, 0.0, 0.0, sum_val,
0.0, 1.0, 0.0, sum_val,
0.0, 0.0, 1.0, sum_val,
0.0, 0.0, 0.0, 1.0) * mat4((sin(TIME) + 1.0) / 2, 0.0, 0.0, 0.0,
0.0, (sin(TIME) + 1.0) / 2, 0.0, 0.0,
0.0, 0.0, (sin(TIME) + 1.0) / 2, 0.0,
0.0, 0.0, 0.0, 1) * rotation(TIME * 2.5));
gl_Position = view.projection * model * vec4(position, 1.0);
tex_coord = tex_coords * abs(sin(TIME / 4));
// tex_coord.x = tex_coord.x * sin(TIME / 2);
// tex_coord.y = tex_coord.y * cos(TIME / 2);
normal = model * vec4(normal_in, 1.0);
}

View file

@ -1,6 +1,3 @@
/// Collections types for Zenyx
///
/// - [`SparseSet`]
mod sparse_set;
pub use sparse_set::SparseSet;

View file

@ -10,36 +10,17 @@ use bytemuck::Contiguous;
const SPARSE_PAGESIZE: usize = (1 << 10) * 4;
type SparsePage<A> = Option<(Box<[Option<NonZeroUsize>; SPARSE_PAGESIZE], A>, usize)>;
/// A sparse set for fast lookup of large indices.
///
/// The sparse allocator is mainly used for bulk allocations in the system's page size
/// for the lookup array. It will also be used for the array of pointers into those
/// bulk allocations. Additionally it will be used for the reverse map that generates keys
/// from the value in the internal packed array.
///
/// The packed allocator will exclusively be used to store the values of type `T`.
///
/// All operations on this datastructure, meaning insertion, lookup, and deletion, are `O(1)`.
///
/// This data structure does not in any way guarantee ordering of the values on
/// its own.
#[derive(Hash)]
pub struct SparseSet<T, PackedAlloc = Global, SparseAlloc = Global>
where
PackedAlloc: Allocator,
SparseAlloc: Allocator,
{
/// The paginated array of keys. The value at the key is an index into the dense array minus
/// one where the value corresponding to that key is stored.
sparse: Vec<SparsePage<SparseAlloc>, SparseAlloc>,
/// The dense array where the values corresponding to the keys are stored.
dense: Vec<T, PackedAlloc>,
/// The reverse map to get the index in the sparse array from the index in the dense array.
dense_to_id: Vec<usize, SparseAlloc>,
}
impl<T> SparseSet<T> {
/// Creates a new [`SparseSet`] with the global allocator.
pub const fn new() -> Self {
Self {
sparse: Vec::new(),
@ -49,45 +30,11 @@ impl<T> SparseSet<T> {
}
}
impl<T> Default for SparseSet<T> {
fn default() -> Self {
Self::new()
}
}
impl<T, PackedAlloc, SparseAlloc> core::fmt::Debug for SparseSet<T, PackedAlloc, SparseAlloc>
where
T: core::fmt::Debug,
PackedAlloc: Allocator,
SparseAlloc: Allocator,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map()
.entries(self.dense_to_id.iter().zip(self.dense.iter()))
.finish()
}
}
impl<T, PackedAlloc, SparseAlloc> SparseSet<T, PackedAlloc, SparseAlloc>
where
PackedAlloc: Allocator,
SparseAlloc: Allocator + Clone,
{
/// Inserts an element into the sparse set with the key `id`. This will
/// return the previous value if it already exists.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
///
/// sparse_set.insert(10, 123);
/// assert_eq!(sparse_set.get(10), Some(&123));
///
/// let prev = sparse_set.insert(10, 9);
/// assert_eq!(prev, Some(123));
/// assert_eq!(sparse_set.get(10), Some(&9));
/// ```
pub fn insert(&mut self, id: usize, value: T) -> Option<T> {
match self.get_dense_idx(id) {
Some(idx) => {
@ -105,39 +52,15 @@ where
}
}
/// Gets the value with the key `id`.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
///
/// sparse_set.insert(10, 123);
/// assert_eq!(sparse_set.get(10), Some(&123));
/// ```
pub fn get(&self, id: usize) -> Option<&T> {
self.dense.get(self.get_dense_idx(id)?)
}
/// Gets the value with the key `id` mutably.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
///
/// sparse_set.insert(10, 123);
/// let value = sparse_set.get_mut(10).unwrap();
/// *value = 0;
/// assert_eq!(sparse_set.get(10), Some(&0));
/// ```
pub fn get_mut(&mut self, id: usize) -> Option<&mut T> {
let idx = self.get_dense_idx(id)?;
self.dense.get_mut(idx)
}
/// Sets the dense index of an `key` to `idx`. This will remove said index
/// if it is [`None`].
fn set_dense_idx(&mut self, id: usize, idx: Option<usize>) {
let page = id / SPARSE_PAGESIZE;
let sparse_index = id % SPARSE_PAGESIZE;
@ -161,15 +84,6 @@ where
}
}
/// Gets the index in the dense array for a key `id`.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
/// sparse_set.insert(10, 123);
/// assert_eq!(sparse_set.values()[sparse_set.get_dense_idx(10).unwrap()], 123);
/// ```
pub fn get_dense_idx(&self, id: usize) -> Option<usize> {
let page = id / SPARSE_PAGESIZE;
let sparse_index = id % SPARSE_PAGESIZE;
@ -177,8 +91,6 @@ where
page.0[sparse_index].map(|idx| idx.into_integer() - 1)
}
/// This reduces the usage count for a page in the sparse array, deallocating
/// it if it is not used anymore.
fn reduce_page_usage_count(&mut self, id: usize) {
let page = id / SPARSE_PAGESIZE;
let Some(usage) = &mut self.sparse[page] else {
@ -191,7 +103,6 @@ where
}
}
/// Increase the page usage count for a page in the sparse array.
fn increase_page_usage_count(&mut self, id: usize) {
let page = id / SPARSE_PAGESIZE;
if page >= self.sparse.len() {
@ -203,18 +114,6 @@ where
usage.1 += 1;
}
/// Removes the value with the key `id` from the sparse set, returning the
/// value if it existed.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
///
/// sparse_set.insert(10, 123);
/// assert_eq!(sparse_set.remove(10), Some(123));
/// assert_eq!(sparse_set.remove(10), None);
/// ```
pub fn remove(&mut self, id: usize) -> Option<T> {
let index = self.get_dense_idx(id)?;
if self.dense.is_empty() {
@ -230,126 +129,26 @@ where
Some(previous)
}
/// Returns if there are values in this sparse set.
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
/// assert!(sparse_set.is_empty());
/// sparse_set.insert(10, 123);
/// assert!(!sparse_set.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the number of values in this sparse set.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
/// sparse_set.insert(10, 123);
/// sparse_set.insert(10, 9);
/// sparse_set.insert(11, 10);
/// assert_eq!(sparse_set.len(), 2);
/// ```
pub fn len(&self) -> usize {
self.dense.len()
}
/// Checks if the sparse set contains a value with key `id`.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
/// assert!(!sparse_set.contains(10));
/// sparse_set.insert(10, 123);
/// assert!(sparse_set.contains(10));
/// ```
pub fn contains(&self, id: usize) -> bool {
self.get_dense_idx(id).is_some()
}
/// Gets the keys of all values in the sparse set. This method does not provide
/// any ordering guarantees other than the keys contained corresponding to
/// the values with the same index returned by [`Self::values`].
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
/// assert!(sparse_set.keys().is_empty());
/// sparse_set.insert(10, 10);
/// sparse_set.insert(9, 10);
/// sparse_set.insert(11, 10);
///
/// assert_eq!(sparse_set.keys(), &[10, 9, 11]);
/// sparse_set.remove(10);
/// assert_eq!(sparse_set.keys(), &[11, 9]);
/// ```
pub fn keys(&self) -> &[usize] {
&self.dense_to_id
}
/// Gets all values in the sparse set, the corresponding `key` is at the same
/// position in the slice returned by [`Self::keys`].
///
/// Otherwise there are no ordering guarantees.
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
/// assert!(sparse_set.values().is_empty());
/// sparse_set.insert(10, 10);
/// sparse_set.insert(9, 9);
/// sparse_set.insert(11, 11);
///
/// assert_eq!(sparse_set.values(), &[10, 9, 11]);
/// sparse_set.remove(10);
/// assert_eq!(sparse_set.values(), &[11, 9]);
/// ```
pub fn values(&self) -> &[T] {
&self.dense
}
/// Mutable version of [`Self::keys`].
///
/// ```
/// use zenyx::collections::SparseSet;
///
/// let mut sparse_set: SparseSet<u32> = SparseSet::new();
/// assert!(sparse_set.values().is_empty());
/// sparse_set.insert(10, 10);
/// sparse_set.insert(9, 9);
/// sparse_set.insert(11, 11);
///
/// let dense_of_9 = sparse_set.get_dense_idx(9).unwrap();
/// let dense_of_10 = sparse_set.get_dense_idx(10).unwrap();
///
/// let values = sparse_set.values_mut();
/// values[dense_of_10] = 9;
/// values[dense_of_9] = 10;
///
/// assert_eq!(sparse_set.get(10), Some(&9));
/// assert_eq!(sparse_set.get(9), Some(&10));
/// ```
pub fn values_mut(&mut self) -> &mut [T] {
&mut self.dense
}
/// Creates a new [`SparseSet`] with the values allocated by the `packed_alloc`
/// and everything else, as described in the top level documentation for [`SparseSet`]
/// in the `sparse_alloc`.
///
/// ```
/// use zenyx::collections::SparseSet;
/// use allocator_api2::alloc::Global;
///
/// let sparse_set = SparseSet::<u32>::new_in(Global, Global);
/// ```
pub fn new_in(packed_alloc: PackedAlloc, sparse_alloc: SparseAlloc) -> Self {
Self {
dense: Vec::new_in(packed_alloc),
@ -363,16 +162,6 @@ impl<T, PackedAlloc> SparseSet<T, PackedAlloc>
where
PackedAlloc: Allocator,
{
/// Creates a new [`SparseSet`] with the values allocated by the `packed_alloc`
/// Everything else, as described in the top level documentation for [`SparseSet`]
/// is allocated using the global allocator.
///
/// ```
/// use zenyx::collections::SparseSet;
/// use allocator_api2::alloc::Global;
///
/// let sparse_set = SparseSet::<u32>::new_in_packed(Global);
/// ```
pub const fn new_in_packed(packed_alloc: PackedAlloc) -> Self {
Self {
sparse: Vec::new(),
@ -447,10 +236,7 @@ mod tests {
assert_eq!(sparse_set.remove(SPARSE_PAGESIZE + 2).unwrap(), 3);
assert_eq!(sparse_set.sparse[1].as_ref().unwrap().1, 2);
assert_eq!(
sparse_set.keys(),
[10, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]
);
assert_eq!(sparse_set.keys(), [10, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]);
assert_eq!(sparse_set.values(), [1, 2, 2, 1, 2]);
assert_eq!(sparse_set.remove(SPARSE_PAGESIZE + 1).unwrap(), 2);
@ -463,44 +249,25 @@ mod tests {
assert_eq!(sparse_set.keys(), [10, 11, 12]);
assert_eq!(sparse_set.values(), [1, 2, 2]);
sparse_set.insert(SPARSE_PAGESIZE, 1);
sparse_set.insert(SPARSE_PAGESIZE + 1, 2);
sparse_set.insert(SPARSE_PAGESIZE + 2, 3);
assert_eq!(sparse_set.remove(10).unwrap(), 1);
assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 2);
// swap-remove
assert_eq!(
sparse_set.keys(),
[
SPARSE_PAGESIZE + 2,
11,
12,
SPARSE_PAGESIZE,
SPARSE_PAGESIZE + 1
]
);
// swap-remove
assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, 11, 12, SPARSE_PAGESIZE, SPARSE_PAGESIZE + 1]);
assert_eq!(sparse_set.values(), [3, 2, 2, 1, 2]);
assert_eq!(sparse_set.remove(11).unwrap(), 2);
assert_eq!(sparse_set.sparse[0].as_ref().unwrap().1, 1);
assert_eq!(
sparse_set.keys(),
[
SPARSE_PAGESIZE + 2,
SPARSE_PAGESIZE + 1,
12,
SPARSE_PAGESIZE
]
);
assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, 12, SPARSE_PAGESIZE]);
assert_eq!(sparse_set.values(), [3, 2, 2, 1]);
assert_eq!(sparse_set.remove(12).unwrap(), 2);
assert!(sparse_set.sparse[0].is_none());
assert_eq!(
sparse_set.keys(),
[SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, SPARSE_PAGESIZE]
);
assert_eq!(sparse_set.keys(), [SPARSE_PAGESIZE + 2, SPARSE_PAGESIZE + 1, SPARSE_PAGESIZE]);
assert_eq!(sparse_set.values(), [3, 2, 1]);
}
}

View file

@ -3,13 +3,14 @@ use std::collections::BTreeMap;
use std::io::BufReader;
use std::sync::Arc;
use std::time::Instant;
use tracing::info;
use tracing::{error, info};
use wgpu::util::DeviceExt;
use wgpu::{
Backends, FragmentState, IndexFormat, Instance, InstanceDescriptor, PipelineCompilationOptions,
Backends, Features, FragmentState, IndexFormat, Instance, InstanceDescriptor, Limits,
PipelineCompilationOptions, PushConstantRange, ShaderStages,
};
use winit::application::ApplicationHandler;
use winit::event::{ElementState, MouseButton};
use winit::event::{ElementState, MouseButton, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;
@ -48,6 +49,9 @@ struct WgpuRenderer<'surface> {
camera_bind_group: wgpu::BindGroup,
pumpkin: model::Model,
delta: f32,
time_elapsed: f32,
start_time: Instant,
last_frame_time: Instant,
default_texture: wgpu::BindGroup,
}
@ -85,6 +89,11 @@ impl WgpuRenderer<'_> {
});
rpass.set_pipeline(&self.render_pipeline);
rpass.set_bind_group(0, &self.camera_bind_group, &[]);
rpass.set_push_constants(
ShaderStages::VERTEX,
0,
&bytemuck::cast_slice(&[self.time_elapsed]),
);
for mesh in &self.pumpkin.meshes {
let bind_group = mesh
@ -99,9 +108,12 @@ impl WgpuRenderer<'_> {
}
}
self.queue.submit(Some(encoder.finish()));
let elapsed_time = std::time::Instant::elapsed(&self.start_time);
self.time_elapsed = elapsed_time.as_secs_f32();
// info!("{}", self.time_elapsed);
let delta_time = std::time::Instant::now() - self.last_frame_time;
self.delta = delta_time.as_secs_f32();
info!("{}", self.delta);
// info!("{}", self.delta);
surface_texture.present();
self.last_frame_time = std::time::Instant::now();
}
@ -153,17 +165,30 @@ impl WgpuState {
}
async fn create_renderer<'surface>(&self, window: Arc<Window>) -> WgpuRenderer<'surface> {
let surface = self.instance.create_surface(window.clone()).unwrap();
let surface = match self.instance.create_surface(window.clone()) {
Ok(surface) => surface,
Err(e) => {
panic!("{e}")
}
};
let adapter = self
.instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface),
..Default::default()
})
.await
.unwrap();
let device_descriptor = wgpu::DeviceDescriptor::default();
let device_descriptor = wgpu::DeviceDescriptor {
required_features: Features::PUSH_CONSTANTS,
required_limits: Limits {
max_push_constant_size: 4,
..Default::default()
},
..Default::default()
};
let (device, queue) = adapter.request_device(&device_descriptor).await.unwrap();
let size = window.inner_size();
@ -212,7 +237,10 @@ impl WgpuState {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Pipeline Layout"),
bind_group_layouts: &[&camera_bind_group_layout, &texture_bind_group_layout],
push_constant_ranges: &[],
push_constant_ranges: &[PushConstantRange {
stages: ShaderStages::VERTEX,
range: 0..4,
}],
});
let vert_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
@ -299,10 +327,10 @@ impl WgpuState {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
desired_maximum_frame_latency: 3,
};
static PUMPKIN: &[u8] = include_bytes!("../Pumpkin.obj");
surface.configure(&device, &surface_config);
let pumpkin = model::Model::load_obj(
&mut BufReader::new(std::fs::File::open("Pumpkin.obj").unwrap()),
&mut BufReader::new(PUMPKIN),
&device,
&queue,
&texture_bind_group_layout,
@ -373,6 +401,7 @@ impl WgpuState {
let render_pipeline = device.create_render_pipeline(&pipeline_descriptor);
WgpuRenderer {
time_elapsed: 0.0,
surface,
surface_config,
depth_texture,
@ -386,6 +415,8 @@ impl WgpuState {
depth_texture_view,
pumpkin,
default_texture,
start_time: std::time::Instant::now(),
delta: 0f32,
last_frame_time: Instant::now(),
}
@ -427,15 +458,15 @@ impl ApplicationHandler for App<'_> {
event: winit::event::WindowEvent,
) {
match event {
winit::event::WindowEvent::RedrawRequested => {
let window_ctx = self.windows.get_mut(&window_id).unwrap();
window_ctx.renderer.draw()
}
winit::event::WindowEvent::RedrawRequested => match self.windows.get_mut(&window_id) {
Some(window_ctx) => {
window_ctx.renderer.draw();
window_ctx.request_redraw();
}
None => self.resumed(event_loop),
},
winit::event::WindowEvent::CloseRequested => {
let _ = self.windows.remove(&window_id);
if self.windows.is_empty() {
event_loop.exit();
}
}
winit::event::WindowEvent::MouseInput { state, button, .. } => {
if button == MouseButton::Left && state == ElementState::Pressed {
@ -474,6 +505,11 @@ impl ApplicationHandler for App<'_> {
.configure(&window_ctx.renderer.device, &new_config)
}
}
WindowEvent::Destroyed => {
if self.windows.is_empty() {
event_loop.exit();
}
}
_ => (),
}
}
@ -527,12 +563,16 @@ pub fn run_app(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), termi
#[unsafe(no_mangle)]
#[cfg(target_os = "android")]
extern "C" fn android_main(app: AndroidApp) {
use android_logger::Config;
use android_logger::FilterBuilder;
use tracing::level_filters::LevelFilter;
use winit::event_loop::EventLoopBuilder;
use winit::platform::android::EventLoopBuilderExtAndroid;
let event_loop = EventLoopBuilder::default()
.with_android_app(app)
.build()
.unwrap();
android_logger::init_once(Config::default().with_tag("Zenyx"));
run_app(event_loop).unwrap()
}

View file

@ -1,8 +1,11 @@
use std::io::{BufRead, BufReader};
use std::{
collections::HashMap,
io::{BufRead, BufReader},
};
use crate::texture::Texture;
use cgmath::{Vector2, Vector3, Zero};
use tobj::Model as tModel;
use tobj::{MTLLoadResult, Model as tModel};
use wgpu::util::DeviceExt;
pub struct Model {
@ -38,7 +41,7 @@ impl Model {
single_index: true,
..Default::default()
},
|p| tobj::load_mtl_buf(&mut BufReader::new(std::fs::File::open(p).unwrap())),
|_| MTLLoadResult::Ok((vec![], ahash::AHashMap::new())),
)
.unwrap();