diff --git a/Cargo.lock b/Cargo.lock index 77318c1..baaf280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3707,6 +3707,7 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" name = "zenyx" version = "0.1.0" dependencies = [ + "ahash", "allocator-api2", "build-print", "bytemuck", diff --git a/Cargo.toml b/Cargo.toml index 9cb8778..0f99368 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/build.rs b/build.rs index c7aec40..1f63293 100644 --- a/build.rs +++ b/build.rs @@ -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() diff --git a/shaders/shader.frag b/shaders/shader.frag index c9ffc77..cee14db 100644 --- a/shaders/shader.frag +++ b/shaders/shader.frag @@ -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); } diff --git a/shaders/shader.vert b/shaders/shader.vert index 79c95f0..620b7a3 100644 --- a/shaders/shader.vert +++ b/shaders/shader.vert @@ -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); } diff --git a/src/main.rs b/src/main.rs index 0105e37..e0fdb9a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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) -> 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() } diff --git a/src/model/mod.rs b/src/model/mod.rs index 23bb69b..ea2b72b 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -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();