/* wearechat - generic multiplayer game with voip Copyright (C) 2025 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use crate::scene_prepare::{DemandMap, RPrefab}; use glam::{EulerRot, Mat3, Mat4}; use std::sync::Arc; use weareshared::{packets::Resource, resources::Prefab, tree::SceneTree}; use wgpu::{ Color, CommandEncoder, IndexFormat, LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, ShaderStages, StoreOp, TextureView, }; pub struct ScenePipeline; impl ScenePipeline { pub fn draw( &mut self, commands: &mut CommandEncoder, target: &TextureView, depth: &TextureView, scene: &SceneTree, prefabs: &DemandMap, Arc>, projection: Mat4, ) { let mut rpass = commands.begin_render_pass(&RenderPassDescriptor { label: None, color_attachments: &[Some(RenderPassColorAttachment { view: target, resolve_target: None, ops: Operations { store: StoreOp::Store, load: LoadOp::Clear(Color { r: 0.01, g: 0.01, b: 0.01, a: 1., }), }, })], depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { view: &depth, depth_ops: Some(Operations { load: LoadOp::Clear(1.), store: StoreOp::Store, }), stencil_ops: None, }), ..Default::default() }); for ob in scene.objects.values() { let prefab_projection = projection * Mat4::from_translation(ob.pos.into()) * Mat4::from_mat3(Mat3::from_euler( EulerRot::YXZ, ob.rot.x, ob.rot.y, ob.rot.z, )); if let Some(prefab) = prefabs.try_get(ob.res.clone()) { for (affine, part) in &prefab.0 { let part_projection = prefab_projection * Mat4::from_translation(affine.translation.into()) * Mat4::from_mat3a(affine.matrix3); let projection = part_projection.to_cols_array().map(|v| v.to_le_bytes()); let mb = affine.matrix3.to_cols_array(); // TODO apply object rotation // add padding for gpu mat3x3 repr let model_basis = [ mb[0], mb[1], mb[2], 0., // mb[3], mb[4], mb[5], 0., // mb[6], mb[7], mb[8], 0., // ]; let model_basis = bytemuck::cast_slice(&model_basis); rpass.set_pipeline(&part.pipeline); rpass.set_bind_group(0, &*part.tex_albedo, &[]); rpass.set_bind_group(1, &*part.tex_normal, &[]); rpass.set_bind_group(2, &*part.material, &[]); rpass.set_push_constants(ShaderStages::VERTEX, 0, projection.as_flattened()); rpass.set_push_constants(ShaderStages::VERTEX, 64, model_basis); rpass.set_index_buffer(part.index.slice(..), IndexFormat::Uint32); rpass.set_vertex_buffer(0, part.va_position.slice(..)); rpass.set_vertex_buffer(1, part.va_normal.slice(..)); rpass.set_vertex_buffer(2, part.va_tangent.slice(..)); rpass.set_vertex_buffer(3, part.va_texcoord.slice(..)); rpass.draw_indexed(0..part.index_count, 0, 0..1); } } } } }