summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-01-22 00:23:58 +0100
committermetamuffin <metamuffin@disroot.org>2025-01-22 00:23:58 +0100
commit0612ce58890741428f10c73a63bcb417dcd43a9f (patch)
treea06a6d180722cc3adc35d6a8e30789773b56d508 /client
parent13eb6abfe0e766e432a2f08d58fa9f5a6c5026e7 (diff)
downloadweareserver-0612ce58890741428f10c73a63bcb417dcd43a9f.tar
weareserver-0612ce58890741428f10c73a63bcb417dcd43a9f.tar.bz2
weareserver-0612ce58890741428f10c73a63bcb417dcd43a9f.tar.zst
generalize pipeline configuration in preparation for skinning
Diffstat (limited to 'client')
-rw-r--r--client/src/armature.rs21
-rw-r--r--client/src/main.rs1
-rw-r--r--client/src/renderer.rs5
-rw-r--r--client/src/scene_prepare.rs158
-rw-r--r--client/src/scene_render.rs142
-rw-r--r--client/src/shaders/mod.rs21
-rw-r--r--client/src/shaders/vertex_world_skin.wgsl4
7 files changed, 210 insertions, 142 deletions
diff --git a/client/src/armature.rs b/client/src/armature.rs
index d955415..abfc455 100644
--- a/client/src/armature.rs
+++ b/client/src/armature.rs
@@ -1,13 +1,28 @@
-use std::sync::Arc;
+/*
+ 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 <https://www.gnu.org/licenses/>.
+*/
use glam::Mat4;
+use std::sync::Arc;
use weareshared::resources::Armature;
use wgpu::{Buffer, BufferDescriptor, BufferUsages, Device, Queue};
pub struct RArmature {
pub joint_mat_uniform_buffer: Arc<Buffer>,
joint_mat: Vec<Mat4>,
- data: Armature,
+ _data: Armature,
}
impl RArmature {
@@ -19,7 +34,7 @@ impl RArmature {
usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
mapped_at_creation: false,
})),
- data: armature,
+ _data: armature,
joint_mat: vec![],
}
}
diff --git a/client/src/main.rs b/client/src/main.rs
index 66bd21f..e72e3b0 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -28,6 +28,7 @@ pub mod scene_render;
pub mod state;
pub mod ui;
pub mod window;
+pub mod shaders;
use anyhow::Result;
use clap::Parser;
diff --git a/client/src/renderer.rs b/client/src/renderer.rs
index e655420..c69f4bf 100644
--- a/client/src/renderer.rs
+++ b/client/src/renderer.rs
@@ -92,11 +92,10 @@ impl<'a> Renderer<'a> {
let device = Arc::new(device);
let queue = Arc::new(queue);
- let (scene_pipeline, layouts) = ScenePipeline::new(&device, surface_configuration.format);
let scene_prepare = Arc::new(ScenePreparer::new(
device.clone(),
queue.clone(),
- layouts
+ surface_configuration.format,
));
let ui_renderer =
@@ -135,7 +134,7 @@ impl<'a> Renderer<'a> {
let adapter_info = Arc::new(adapter.get_info());
Ok(Self {
- scene_pipeline,
+ scene_pipeline: ScenePipeline,
scene_prepare,
surface,
adapter_info,
diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs
index 4e57e77..4b298a1 100644
--- a/client/src/scene_prepare.rs
+++ b/client/src/scene_prepare.rs
@@ -15,10 +15,11 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{
- armature::{self, RArmature},
+ armature::RArmature,
download::Downloader,
meshops::{generate_normals, generate_tangents, generate_texcoords},
scene_render::SceneBgLayouts,
+ shaders::SceneShaders,
};
use anyhow::Result;
use bytemuck::{Pod, Zeroable};
@@ -38,12 +39,17 @@ use std::{
use weareshared::{
Affine3A,
packets::Resource,
- resources::{Armature, Image, MeshPart, Prefab},
+ resources::{Image, MeshPart, Prefab},
};
use wgpu::{
AddressMode, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource,
- Buffer, BufferUsages, Device, Extent3d, FilterMode, Queue, SamplerDescriptor, Texture,
+ BlendState, Buffer, BufferUsages, ColorTargetState, ColorWrites, CompareFunction,
+ DepthBiasState, DepthStencilState, Device, Extent3d, Face, FilterMode, FragmentState,
+ FrontFace, MultisampleState, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode,
+ PrimitiveState, PrimitiveTopology, PushConstantRange, Queue, RenderPipeline,
+ RenderPipelineDescriptor, SamplerDescriptor, ShaderStages, StencilState, Texture,
TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureViewDescriptor,
+ VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode,
util::{BufferInitDescriptor, DeviceExt, TextureDataOrder},
};
@@ -90,6 +96,8 @@ pub struct ScenePreparer {
device: Arc<Device>,
queue: Arc<Queue>,
layouts: SceneBgLayouts,
+ shaders: SceneShaders,
+ render_format: TextureFormat,
textures: DemandMap<(Resource<Image<'static>>, bool), (Arc<Texture>, Arc<BindGroup>)>,
placeholder_textures: DemandMap<TextureIdentityKind, (Arc<Texture>, Arc<BindGroup>)>,
@@ -100,11 +108,20 @@ pub struct ScenePreparer {
generated_texcoord_buffers: DemandMap<TexcoordBufferSpec, Arc<Buffer>>,
mesh_parts: DemandMap<Resource<MeshPart>, Arc<RMeshPart>>,
materials: DemandMap<Material, Arc<BindGroup>>,
+ pipelines: DemandMap<PipelineConfig, Arc<RenderPipeline>>,
pub prefabs: DemandMap<Resource<Prefab>, Arc<RPrefab>>,
}
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct PipelineConfig {
+ format: TextureFormat,
+ skin: bool,
+ backface_culling: bool,
+}
+
pub struct RPrefab(pub Vec<(Affine3A, Arc<RMeshPart>)>);
pub struct RMeshPart {
+ pub pipeline: Arc<RenderPipeline>,
pub index_count: u32,
pub index: Arc<Buffer>,
pub va_position: Arc<Buffer>,
@@ -157,11 +174,13 @@ struct Material {
}
impl ScenePreparer {
- pub fn new(device: Arc<Device>, queue: Arc<Queue>, layouts: SceneBgLayouts) -> Self {
+ pub fn new(device: Arc<Device>, queue: Arc<Queue>, render_format: TextureFormat) -> Self {
Self {
+ render_format,
+ layouts: SceneBgLayouts::load(&device),
+ shaders: SceneShaders::load(&device),
device,
queue,
- layouts,
index_buffers: DemandMap::new(),
vertex_buffers: DemandMap::new(),
mesh_parts: DemandMap::new(),
@@ -172,6 +191,7 @@ impl ScenePreparer {
generated_normal_buffers: DemandMap::new(),
generated_texcoord_buffers: DemandMap::new(),
materials: DemandMap::new(),
+ pipelines: DemandMap::new(),
}
}
pub fn update(&self, dls: &Downloader) -> Result<usize> {
@@ -347,7 +367,7 @@ impl ScenePreparer {
});
let bind_group = self.device.create_bind_group(&BindGroupDescriptor {
label: Some("material"),
- layout: &self.material_bgl,
+ layout: &self.layouts.material,
entries: &[BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
@@ -355,6 +375,13 @@ impl ScenePreparer {
});
self.materials.insert(spec, Arc::new(bind_group), 0);
}
+ for spec in self.pipelines.needed() {
+ self.pipelines.insert(
+ spec.clone(),
+ Arc::new(spec.create(&self.device, &self.layouts, &self.shaders)),
+ 0,
+ );
+ }
for pres in self.mesh_parts.needed() {
let start = Instant::now();
if let Some(part) = dls.try_get(pres.clone())? {
@@ -461,7 +488,14 @@ impl ScenePreparer {
Some(None)
};
+ let pipeline = self.pipelines.try_get(PipelineConfig {
+ format: self.render_format,
+ skin: false,
+ backface_culling: part.g_double_sided.is_none(),
+ });
+
if let (
+ Some(pipeline),
Some((index, index_count)),
Some(va_normal),
Some(va_tangent),
@@ -474,6 +508,7 @@ impl ScenePreparer {
Some(tex_albedo),
Some(material),
) = (
+ pipeline,
index,
normal,
tangent,
@@ -499,6 +534,7 @@ impl ScenePreparer {
self.mesh_parts.insert(
pres,
Arc::new(RMeshPart {
+ pipeline,
index_count,
index,
va_normal,
@@ -582,6 +618,110 @@ fn create_texture(
(Arc::new(texture), Arc::new(bindgroup))
}
+impl PipelineConfig {
+ 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 {
+ range: 0..((4 * 4 + 3 * 4) * size_of::<f32>() as u32),
+ stages: ShaderStages::VERTEX,
+ }],
+ });
+ 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::<f32>() as u64,
+ attributes: &[VertexAttribute {
+ format: VertexFormat::Float32x3,
+ offset: 0,
+ shader_location: 0,
+ }],
+ },
+ // normal
+ VertexBufferLayout {
+ step_mode: VertexStepMode::Vertex,
+ array_stride: 3 * size_of::<f32>() as u64,
+ attributes: &[VertexAttribute {
+ format: VertexFormat::Float32x3,
+ offset: 0,
+ shader_location: 1,
+ }],
+ },
+ // tangent
+ VertexBufferLayout {
+ step_mode: VertexStepMode::Vertex,
+ array_stride: 3 * size_of::<f32>() as u64,
+ attributes: &[VertexAttribute {
+ format: VertexFormat::Float32x3,
+ offset: 0,
+ shader_location: 2,
+ }],
+ },
+ // texcoord
+ VertexBufferLayout {
+ step_mode: VertexStepMode::Vertex,
+ array_stride: 2 * size_of::<f32>() 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,
+ })
+ }
+}
+
impl<K, V> Widget for &DemandMap<K, V> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
let state = self.inner.read().unwrap();
@@ -618,6 +758,8 @@ impl ScenePreparer {
&self.generated_texcoord_buffers,
);
visit("textures", &self.textures);
+ visit("materials", &self.materials);
+ visit("pipelines", &self.pipelines);
}
}
@@ -644,6 +786,10 @@ impl Widget for &ScenePreparer {
self.generated_texcoord_buffers.ui(ui);
ui.label("textures");
self.textures.ui(ui);
+ ui.label("materials");
+ self.materials.ui(ui);
+ ui.label("pipelines");
+ self.pipelines.ui(ui);
})
.response
}
diff --git a/client/src/scene_render.rs b/client/src/scene_render.rs
index 77217d6..d621ba6 100644
--- a/client/src/scene_render.rs
+++ b/client/src/scene_render.rs
@@ -14,42 +14,28 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+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::{
- BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState,
- BufferBindingType, Color, ColorTargetState, ColorWrites, CommandEncoder, CompareFunction,
- DepthBiasState, DepthStencilState, Device, Face, FragmentState, FrontFace, IndexFormat, LoadOp,
- MultisampleState, Operations, PipelineCompilationOptions, PipelineLayoutDescriptor,
- PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, RenderPassColorAttachment,
- RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline,
- RenderPipelineDescriptor, SamplerBindingType, ShaderStages, StencilState, StoreOp,
- TextureFormat, TextureSampleType, TextureView, TextureViewDimension, VertexAttribute,
- VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, include_wgsl,
+ BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType,
+ BufferBindingType, Color, CommandEncoder, Device, IndexFormat, LoadOp, Operations,
+ RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
+ SamplerBindingType, ShaderStages, StoreOp, TextureSampleType, TextureView,
+ TextureViewDimension,
};
-use crate::scene_prepare::{DemandMap, RPrefab};
-
-pub struct ScenePipeline {
- pipeline: RenderPipeline,
- pipeline_no_cull: RenderPipeline,
-}
+pub struct ScenePipeline;
pub struct SceneBgLayouts {
pub texture: BindGroupLayout,
pub material: BindGroupLayout,
pub joints: BindGroupLayout,
}
-
-impl ScenePipeline {
- pub fn new(device: &Device, format: TextureFormat) -> (Self, SceneBgLayouts) {
- let fragment_pbr = device.create_shader_module(include_wgsl!("shaders/fragment_pbr.wgsl"));
- let vertex_world = device.create_shader_module(include_wgsl!("shaders/vertex_world.wgsl"));
- let _vertex_world_skin =
- device.create_shader_module(include_wgsl!("shaders/vertex_world_skin.wgsl"));
-
- let layouts = SceneBgLayouts {
+impl SceneBgLayouts {
+ pub fn load(device: &Device) -> Self {
+ Self {
texture: device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
@@ -97,105 +83,11 @@ impl ScenePipeline {
}],
label: None,
}),
- };
-
- let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
- label: None,
- bind_group_layouts: &[&layouts.texture, &layouts.texture, &layouts.material],
- push_constant_ranges: &[PushConstantRange {
- range: 0..((4 * 4 + 3 * 4) * size_of::<f32>() as u32),
- stages: ShaderStages::VERTEX,
- }],
- });
- let [pipeline, pipeline_no_cull] = [Some(Face::Back), None].map(|cull_mode| {
- device.create_render_pipeline(&RenderPipelineDescriptor {
- label: None,
- layout: Some(&pipeline_layout),
- fragment: Some(FragmentState {
- module: &fragment_pbr,
- entry_point: Some("main"),
- targets: &[Some(ColorTargetState {
- blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING),
- format,
- write_mask: ColorWrites::all(),
- })],
- compilation_options: PipelineCompilationOptions::default(),
- }),
- vertex: VertexState {
- module: &vertex_world,
- entry_point: Some("main"),
- buffers: &[
- // position
- VertexBufferLayout {
- step_mode: VertexStepMode::Vertex,
- array_stride: 3 * size_of::<f32>() as u64,
- attributes: &[VertexAttribute {
- format: VertexFormat::Float32x3,
- offset: 0,
- shader_location: 0,
- }],
- },
- // normal
- VertexBufferLayout {
- step_mode: VertexStepMode::Vertex,
- array_stride: 3 * size_of::<f32>() as u64,
- attributes: &[VertexAttribute {
- format: VertexFormat::Float32x3,
- offset: 0,
- shader_location: 1,
- }],
- },
- // tangent
- VertexBufferLayout {
- step_mode: VertexStepMode::Vertex,
- array_stride: 3 * size_of::<f32>() as u64,
- attributes: &[VertexAttribute {
- format: VertexFormat::Float32x3,
- offset: 0,
- shader_location: 2,
- }],
- },
- // texcoord
- VertexBufferLayout {
- step_mode: VertexStepMode::Vertex,
- array_stride: 2 * size_of::<f32>() 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,
- 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,
- })
- });
- (
- Self {
- pipeline,
- pipeline_no_cull,
- },
- layouts,
- )
+ }
}
+}
+impl ScenePipeline {
pub fn draw(
&mut self,
commands: &mut CommandEncoder,
@@ -255,13 +147,7 @@ impl ScenePipeline {
];
let model_basis = bytemuck::cast_slice(&model_basis);
- let pipeline = if part.double_sided {
- &self.pipeline_no_cull
- } else {
- &self.pipeline
- };
-
- rpass.set_pipeline(pipeline);
+ 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, &[]);
diff --git a/client/src/shaders/mod.rs b/client/src/shaders/mod.rs
new file mode 100644
index 0000000..42b2164
--- /dev/null
+++ b/client/src/shaders/mod.rs
@@ -0,0 +1,21 @@
+use log::info;
+use wgpu::{Device, ShaderModule, include_wgsl};
+
+pub struct SceneShaders {
+ pub fragment_pbr: ShaderModule,
+ pub vertex_world: ShaderModule,
+ pub vertex_world_skin: ShaderModule,
+}
+
+impl SceneShaders {
+ pub fn load(device: &Device) -> Self {
+ info!("compiling shaders...");
+ let s = Self {
+ fragment_pbr: device.create_shader_module(include_wgsl!("fragment_pbr.wgsl")),
+ vertex_world: device.create_shader_module(include_wgsl!("vertex_world.wgsl")),
+ vertex_world_skin: device.create_shader_module(include_wgsl!("vertex_world_skin.wgsl")),
+ };
+ info!("done");
+ s
+ }
+}
diff --git a/client/src/shaders/vertex_world_skin.wgsl b/client/src/shaders/vertex_world_skin.wgsl
index 86250f1..288950d 100644
--- a/client/src/shaders/vertex_world_skin.wgsl
+++ b/client/src/shaders/vertex_world_skin.wgsl
@@ -33,8 +33,8 @@ struct PushConst {
model_basis: mat3x3<f32>,
}
-var<uniform> joints: array<mat4x4<f32>>;
-var<push_constant> pc: PushConst;
+@group(3) @binding(0) var<uniform> joints: array<mat4x4<f32>, 128>;
+var<push_constant> pc: PushConst;
@vertex
fn main(vi: VertexIn) -> VertexOut {