formatting

This commit is contained in:
Chance 2025-03-29 20:20:40 -04:00 committed by BitSyndicate
parent 1326b25b3a
commit b192c15e3f
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: 443E4198D6BBA6DE
15 changed files with 540 additions and 626 deletions

Binary file not shown.

View file

@ -6,9 +6,12 @@ use std::time::Instant;
use cgmath::{Deg, Matrix4, Point3, Rad, SquareMatrix, Vector3, perspective};
use futures::executor::block_on;
use thiserror::Error;
use tracing::{error, trace};
use tracing::{error, info, trace};
use wgpu::TextureUsages;
use wgpu::{Backends, InstanceDescriptor, util::DeviceExt};
use wgpu_text::glyph_brush::ab_glyph::FontRef;
use wgpu_text::glyph_brush::{HorizontalAlign, Layout, OwnedSection, OwnedText, VerticalAlign};
use wgpu_text::{BrushBuilder, TextBrush};
use winit::window::Window;
#[derive(Debug, Error)]
@ -129,50 +132,7 @@ impl From<wgpu::RequestDeviceError> for RenderContextError {
}
}
const SHADER_SRC: &str = r#"
struct CameraUniform {
view: mat4x4<f32>,
proj: mat4x4<f32>,
};
struct ModelUniform {
model: mat4x4<f32>,
};
@group(0) @binding(0)
var<uniform> camera: CameraUniform;
@group(1) @binding(0)
var<uniform> model: ModelUniform;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) normal: vec3<f32>,
};
@vertex
fn vs_main(input: VertexInput) -> VertexOutput {
var output: VertexOutput;
let model_pos = model.model * vec4<f32>(input.position, 1.0);
output.clip_position = camera.proj * camera.view * model_pos;
output.normal = input.normal;
return output;
}
@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
let ambient: f32 = 0.2;
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 brightness = ambient + (1.0 - ambient) * diffuse;
return vec4<f32>(0.7 * brightness, 0.7 * brightness, 0.9 * brightness, 1.0);
}
"#;
const SHADER_SRC: &str = include_str!("shader.wgsl");
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
@ -284,7 +244,7 @@ struct Model {
index_buffer: wgpu::Buffer,
uniform_buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup,
index_count: u32, // Changed from vertex_count to index_count
index_count: u32,
transform: Matrix4<f32>,
}
@ -359,6 +319,15 @@ pub struct Renderer<'window> {
start_time: Instant,
last_frame_instant: Instant,
frame_count: u32,
fps: f32,
font_state: FontState,
}
struct FontState {
brush: TextBrush<FontRef<'static>>,
section: OwnedSection,
scale: f32,
color: wgpu::Color,
}
impl<'window> Renderer<'window> {
@ -485,7 +454,6 @@ impl<'window> Renderer<'window> {
multiview: None,
cache: None,
});
let camera = Camera::new(&device, &camera_bind_group_layout, width, height);
@ -508,6 +476,36 @@ impl<'window> Renderer<'window> {
// surface_config.format,
);
let font_bytes = include_bytes!("DejaVuSans.ttf");
let font = FontRef::try_from_slice(font_bytes).map_err(|e| {
RenderContextError::new(
ContextErrorKind::DeviceRequest,
Some("Font loading".into()),
None,
)
})?;
let brush =
BrushBuilder::using_font(font).build(&device, width, height, surface_config.format);
let base_width = 1280.0;
let base_scale = 30.0;
let scale = base_scale * (surface_config.width as f32 / base_width as f32).clamp(0.5, 2.0);
let color = wgpu::Color::WHITE;
let section = OwnedSection::default()
.add_text(OwnedText::new("FPS: 0.00").with_scale(scale).with_color([
color.r as f32,
color.g as f32,
color.b as f32,
color.a as f32,
]))
.with_screen_position((10.0, 10.0))
.with_bounds((base_scale * 200.0, base_scale * 2.0))
.with_layout(
Layout::default()
.h_align(HorizontalAlign::Left)
.v_align(VerticalAlign::Top),
);
Ok(Self {
device,
@ -530,6 +528,13 @@ impl<'window> Renderer<'window> {
frame_count: 0,
depth_texture,
depth_texture_view,
fps: 0f32,
font_state: FontState {
brush,
section,
scale,
color,
},
})
}
@ -549,12 +554,19 @@ impl<'window> Renderer<'window> {
pub fn resize(&mut self, new_size: (u32, u32)) {
let (width, height) = new_size;
let (depth_texture,depth_view) = create_depth_texture(&self.device, width, height);
let (depth_texture, depth_view) = create_depth_texture(&self.device, width, height);
self.surface_config.width = width.max(1);
self.surface_config.height = height.max(1);
self.surface.configure(&self.device, &self.surface_config);
self.depth_texture = depth_texture;
self.depth_texture_view = depth_view;
self.font_state
.brush
.resize_view(width as f32, height as f32, &self.queue);
let base_width = 1280.0;
let base_scale = 30.0;
let scale = base_scale * (width as f32 / base_width as f32).clamp(0.5, 2.0);
self.font_state.scale = scale;
self.camera.resize(width, height);
}
@ -565,7 +577,8 @@ impl<'window> Renderer<'window> {
for (i, model) in self.models.iter_mut().enumerate() {
let angle = Rad(elapsed * 0.8 + i as f32 * 0.3);
model.set_transform(Matrix4::from_angle_x(angle) * Matrix4::from_angle_y(angle));
model.set_transform(Matrix4::from_angle_y(angle));
// model.set_transform(Matrix4::from_angle_x(angle) * Matrix4::from_angle_y(angle));
model.update(&self.queue);
}
@ -587,6 +600,25 @@ impl<'window> Renderer<'window> {
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
let fps_text = format!("FPS: {:.2}", self.fps);
self.font_state.section.text.clear();
self.font_state.section.text.push(
OwnedText::new(fps_text)
.with_scale(self.font_state.scale)
.with_color([
self.font_state.color.r as f32,
self.font_state.color.g as f32,
self.font_state.color.b as f32,
self.font_state.color.a as f32,
]),
);
if let Err(e) = self.font_state.brush.queue(
&self.device,
&self.queue,
&[self.font_state.section.clone()],
) {
error!("Failed to queue text: {}", e);
}
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@ -622,15 +654,35 @@ impl<'window> Renderer<'window> {
render_pass.draw_indexed(0..model.index_count, 0, 0..1);
}
}
{
let mut text_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Text Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
self.font_state.brush.draw(&mut text_pass);
}
self.queue.submit(Some(encoder.finish()));
surface_texture.present();
self.frame_count += 1;
let elapsed_secs = self.last_frame_instant.elapsed().as_secs_f32();
if elapsed_secs >= 1.0 {
let fps = self.frame_count as f32 / elapsed_secs;
trace!("FPS: {:.2}", fps);
// trace!("Renderer FPS: {:.2}", fps);
self.fps = fps;
self.frame_count = 0;
self.last_frame_instant = Instant::now();
}
@ -640,8 +692,14 @@ impl<'window> Renderer<'window> {
self.bg_color = color;
}
pub fn bg_color(&self) -> wgpu::Color {
self.bg_color
pub fn bg_color(&self) -> &wgpu::Color {
&self.bg_color
}
pub fn text_color(&self) -> &wgpu::Color {
&self.font_state.color
}
pub fn set_text_color(&mut self, color: wgpu::Color) {
self.font_state.color = color;
}
}
fn create_depth_texture(
@ -670,4 +728,4 @@ fn create_depth_texture(
let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
(texture, view)
}
}

View file

@ -1,9 +1,11 @@
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use ctx::{Renderer, Vertex};
use tobj::LoadOptions;
use std::env;
use std::fs;
use std::path::PathBuf;
use tobj::{LoadOptions, Model};
use tracing::{debug, error, info, trace, warn};
use wgpu::rwh::HasWindowHandle;
use winit::application::ApplicationHandler;
@ -34,163 +36,51 @@ impl WindowContext<'_> {
#[derive(Default)]
pub struct App<'window> {
windows: HashMap<WindowId, WindowContext<'window>>,
windows: ahash::AHashMap<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,
];
static CUBE_OBJ: &str = "
# Blender 4.2.3 LTS
# www.blender.org
mtllib cube.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 0.625000 0.500000
vt 0.875000 0.500000
vt 0.875000 0.750000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.125000 0.500000
vt 0.375000 0.500000
vt 0.125000 0.750000
s 0
usemtl Material
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/5/2 3/4/2 7/6/2 8/7/2
f 8/8/3 7/9/3 5/10/3 6/11/3
f 6/12/4 2/13/4 4/5/4 8/14/4
f 2/13/5 1/1/5 3/4/5 4/5/5
f 6/11/6 5/10/6 1/1/6 2/13/6
";
impl App<'_> {
fn create_main_window(&mut self, event_loop: &ActiveEventLoop) {
@ -202,9 +92,9 @@ impl App<'_> {
match Renderer::new_blocking(window.clone()) {
Ok(mut wgpu_ctx) => {
let obj = match tobj::load_obj(
"test.obj",
"Pumpkin.obj",
&LoadOptions {
triangulate: true,
triangulate: true,
single_index: true,
..Default::default()
},
@ -215,29 +105,10 @@ impl App<'_> {
panic!()
}
};
let mesh = &obj.0.get(0).unwrap().mesh;
let (combined_vertices, combined_indices) = parse_obj(&obj.0);
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(&combined_vertices, &combined_indices);
wgpu_ctx.add_model(&vertices, &mesh.indices);
// wgpu_ctx.add_model(CUBE_VERTICES,CUBE_INDICES);
self.windows.insert(
window_id,
WindowContext {
@ -289,12 +160,21 @@ impl App<'_> {
fn toggle_background(&mut self, window_id: WindowId) {
if let Some(window_context) = self.windows.get_mut(&window_id) {
let current_color = window_context.ctx.bg_color();
let new_color = match current_color {
wgpu::Color::WHITE => wgpu::Color::BLACK,
wgpu::Color::BLACK => wgpu::Color::WHITE,
&wgpu::Color::WHITE => wgpu::Color::BLACK,
&wgpu::Color::BLACK => wgpu::Color::WHITE,
_ => wgpu::Color::WHITE,
};
let new_text_color = match window_context.ctx.text_color() {
&wgpu::Color::WHITE => wgpu::Color::BLACK,
&wgpu::Color::BLACK => wgpu::Color::WHITE,
_ => wgpu::Color::WHITE,
};
println!("new text color {new_text_color:#?}");
window_context.ctx.set_bg_color(new_color);
window_context.ctx.set_text_color(new_text_color);
debug!("Toggled background color for window {:?}", window_id);
} else {
warn!("No window context for toggling background: {:?}", window_id);
@ -318,7 +198,29 @@ impl App<'_> {
let window_id = window.id();
match Renderer::new_blocking(window.clone()) {
Ok(mut wgpu_ctx) => {
wgpu_ctx.add_model(CUBE_VERTICES,CUBE_INDICES);
{
let mut tmp_path: PathBuf = env::temp_dir();
tmp_path.push("cube.obj");
if let Err(e) = fs::write(&tmp_path, CUBE_OBJ) {
error!("Failed to write cube OBJ to temp: {:?}", e);
}
let load_options = tobj::LoadOptions {
triangulate: true,
single_index: true,
..Default::default()
};
match tobj::load_obj(tmp_path.to_str().unwrap(), &load_options) {
Ok(cube_model) => {
let (cube_vertices, cube_indices) =
parse_obj(&cube_model.0);
wgpu_ctx.add_model(&cube_vertices, &cube_indices);
}
Err(e) => {
error!("Failed to load cube OBJ from temp file: {:?}", e)
}
}
}
self.windows.insert(
window_id,
WindowContext {
@ -375,6 +277,39 @@ impl App<'_> {
}
}
fn parse_obj(obj: &Vec<Model>) -> (Vec<Vertex>, Vec<u32>) {
let mut combined_vertices = Vec::new();
let mut combined_indices = Vec::new();
let mut vertex_offset = 0;
for object in obj {
let mesh: &_ = &object.mesh;
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();
combined_vertices.extend(vertices);
combined_indices.extend(mesh.indices.iter().map(|&index| index + vertex_offset));
vertex_offset += (mesh.positions.len() as u32) / 3;
}
(combined_vertices, combined_indices)
}
impl ApplicationHandler for App<'_> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.windows.is_empty() {

View file

@ -0,0 +1,42 @@
struct CameraUniform {
view: mat4x4<f32>,
proj: mat4x4<f32>,
};
struct ModelUniform {
model: mat4x4<f32>,
};
@group(0) @binding(0)
var<uniform> camera: CameraUniform;
@group(1) @binding(0)
var<uniform> model: ModelUniform;
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) normal: vec3<f32>,
};
@vertex
fn vs_main(input: VertexInput) -> VertexOutput {
var output: VertexOutput;
let model_pos = model.model * vec4<f32>(input.position, 1.0);
output.clip_position = camera.proj * camera.view * model_pos;
output.normal = input.normal;
return output;
}
@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
let ambient: f32 = 0.2;
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 brightness = ambient + (1.0 - ambient) * diffuse;
return vec4<f32>(0.7 * brightness, 0.7 * brightness, 0.9 * brightness, 1.0);
}