/* 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 wgpu::{ BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, BufferBindingType, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState, Device, Face, FragmentState, FrontFace, MultisampleState, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, RenderPipeline, RenderPipelineDescriptor, SamplerBindingType, ShaderStages, StencilState, TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, }; use crate::shaders::SceneShaders; use super::PipelineSpec; pub struct SceneBgLayouts { pub texture: BindGroupLayout, pub material: BindGroupLayout, pub joints: BindGroupLayout, } impl SceneBgLayouts { pub fn load(device: &Device) -> Self { Self { texture: device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[ BindGroupLayoutEntry { binding: 0, count: None, visibility: ShaderStages::FRAGMENT, ty: BindingType::Texture { sample_type: TextureSampleType::Float { filterable: true }, view_dimension: TextureViewDimension::D2, multisampled: false, }, }, BindGroupLayoutEntry { binding: 1, count: None, visibility: ShaderStages::FRAGMENT, ty: BindingType::Sampler(SamplerBindingType::Filtering), }, ], label: None, }), material: device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { binding: 0, count: None, visibility: ShaderStages::FRAGMENT, ty: BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None, }, }], label: None, }), joints: device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { binding: 0, count: None, visibility: ShaderStages::VERTEX, ty: BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None, }, }], label: None, }), } } } impl PipelineSpec { pub fn create( &self, device: &Device, layouts: &SceneBgLayouts, shaders: &SceneShaders, ) -> RenderPipeline { let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: None, bind_group_layouts: &[&layouts.texture, &layouts.texture, &layouts.material], push_constant_ranges: &[ PushConstantRange { // 4x4 view projections // 3x3(+1 pad) model basis // 3(+1 pad) camera position range: 0..((4 * 4 + 3 * 4) * size_of::() as u32), stages: ShaderStages::VERTEX, }, PushConstantRange { range: ((4 * 4 + 3 * 4) * size_of::() as u32) ..(4 * 4 + 3 * 4 + 4) * size_of::() as u32, stages: ShaderStages::FRAGMENT, }, ], }); device.create_render_pipeline(&RenderPipelineDescriptor { label: None, layout: Some(&pipeline_layout), fragment: Some(FragmentState { module: &shaders.fragment_pbr, entry_point: Some("main"), targets: &[Some(ColorTargetState { blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING), format: self.format, write_mask: ColorWrites::all(), })], compilation_options: PipelineCompilationOptions::default(), }), vertex: VertexState { module: if self.skin { &shaders.vertex_world_skin } else { &shaders.vertex_world }, entry_point: Some("main"), buffers: &[ // position VertexBufferLayout { step_mode: VertexStepMode::Vertex, array_stride: 3 * size_of::() as u64, attributes: &[VertexAttribute { format: VertexFormat::Float32x3, offset: 0, shader_location: 0, }], }, // normal VertexBufferLayout { step_mode: VertexStepMode::Vertex, array_stride: 3 * size_of::() as u64, attributes: &[VertexAttribute { format: VertexFormat::Float32x3, offset: 0, shader_location: 1, }], }, // tangent VertexBufferLayout { step_mode: VertexStepMode::Vertex, array_stride: 3 * size_of::() as u64, attributes: &[VertexAttribute { format: VertexFormat::Float32x3, offset: 0, shader_location: 2, }], }, // texcoord VertexBufferLayout { step_mode: VertexStepMode::Vertex, array_stride: 2 * size_of::() as u64, attributes: &[VertexAttribute { format: VertexFormat::Float32x2, offset: 0, shader_location: 3, }], }, ], compilation_options: PipelineCompilationOptions::default(), }, primitive: PrimitiveState { topology: PrimitiveTopology::TriangleList, front_face: FrontFace::Ccw, cull_mode: if self.backface_culling { Some(Face::Back) } else { None }, polygon_mode: PolygonMode::Fill, ..Default::default() }, depth_stencil: Some(DepthStencilState { depth_write_enabled: true, depth_compare: CompareFunction::Less, format: TextureFormat::Depth32Float, bias: DepthBiasState::default(), stencil: StencilState::default(), }), multisample: MultisampleState::default(), multiview: None, cache: None, }) } }