1use banished_formats::skeleton::Transform;
6
7pub const MAX_BONES: usize = 52;
10pub const PALETTE_BYTES: u64 = (MAX_BONES * 64) as u64;
13
14pub const SKIN_WGSL: &str = r#"
16const MAX_BONES: u32 = 52u;
17struct Skin {
18 bones: array<mat4x4<f32>, MAX_BONES>,
19};
20@group(2) @binding(0) var<uniform> skin: Skin;
21
22fn skin_matrix(weights: vec4<f32>, bones: vec4<u32>) -> mat4x4<f32> {
23 return skin.bones[bones.x] * weights.x + skin.bones[bones.y] * weights.y
24 + skin.bones[bones.z] * weights.z + skin.bones[bones.w] * weights.w;
25}
26
27// A skinned point with w = 1. The shipped weights sum to 253..255/255, so the blended matrix's w
28// is not 1; the original blends `R·p + t` per bone and projects the result as a point.
29fn skin_point(skin_m: mat4x4<f32>, p: vec3<f32>) -> vec4<f32> {
30 return vec4<f32>((skin_m * vec4<f32>(p, 1.0)).xyz, 1.0);
31}
32"#;
33
34pub fn skinned_attributes() -> [wgpu::VertexAttribute; 5] {
37 let at = |format, offset, shader_location| wgpu::VertexAttribute {
38 format,
39 offset,
40 shader_location,
41 };
42 use wgpu::VertexFormat::{Sint16x4, Uint8x4, Unorm8x4};
43 [
44 at(Sint16x4, 0, 0),
45 at(Sint16x4, 8, 1),
46 at(Unorm8x4, 16, 2),
47 at(Unorm8x4, 24, 3),
48 at(Uint8x4, 28, 4),
49 ]
50}
51
52pub fn palette_matrices(palette: &[Transform]) -> Vec<[f32; 16]> {
54 if palette.len() > MAX_BONES {
55 log::warn!(
56 "{} bones exceed the palette size {MAX_BONES}",
57 palette.len()
58 );
59 }
60 let mut m: Vec<[f32; 16]> = palette
61 .iter()
62 .take(MAX_BONES)
63 .map(Transform::to_matrix)
64 .collect();
65 m.resize(MAX_BONES, Transform::IDENTITY.to_matrix());
66 m
67}
68
69pub fn skin_layout(device: &wgpu::Device, dynamic: bool) -> wgpu::BindGroupLayout {
71 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
72 label: Some("skin"),
73 entries: &[wgpu::BindGroupLayoutEntry {
74 binding: 0,
75 visibility: wgpu::ShaderStages::VERTEX,
76 ty: wgpu::BindingType::Buffer {
77 ty: wgpu::BufferBindingType::Uniform,
78 has_dynamic_offset: dynamic,
79 min_binding_size: wgpu::BufferSize::new(PALETTE_BYTES),
80 },
81 count: None,
82 }],
83 })
84}