1use banished_formats::skeleton::{local_bind, world_transforms, Transform};
6use banished_formats::types::{DrawRange, GraphicsMesh, MaterialInstance, MeshPart};
7
8use crate::material::MaterialKind;
9
10#[derive(Debug, Clone)]
12pub struct RangeMaterial {
13 pub kind: MaterialKind,
15 pub diffuse: Option<String>,
17 pub snow: Option<String>,
19 pub damage: Option<String>,
21 pub skinned: bool,
23 pub rigid_bone: Option<usize>,
25 pub rigid: Transform,
27}
28
29pub fn material_set(mesh: &GraphicsMesh, set: usize) -> Option<&[Option<String>]> {
31 mesh.material_instances
32 .get(set)
33 .or_else(|| mesh.material_instances.first())
34 .map(Vec::as_slice)
35}
36
37pub fn range_material(
40 world: &[Transform],
41 set: Option<&[Option<String>]>,
42 range: &DrawRange,
43 resolve: &mut impl FnMut(&str) -> Option<MaterialInstance>,
44) -> RangeMaterial {
45 let instance = set
46 .and_then(|s| s.get(range.material_index as usize).cloned().flatten())
47 .and_then(|r| resolve(&r));
48 let texture = |slot: &str| {
49 instance
50 .as_ref()
51 .and_then(|mi| mi.texture(slot).map(str::to_owned))
52 };
53 let skinned = range.bone < 0;
54 let rigid_bone = usize::try_from(range.bone).ok();
55 RangeMaterial {
56 kind: MaterialKind::of(instance.as_ref().and_then(|mi| mi.material.as_deref())),
57 diffuse: texture("diffuse"),
58 snow: texture("snow"),
59 damage: texture("damage"),
60 skinned,
61 rigid_bone,
62 rigid: rigid_bone
63 .and_then(|b| world.get(b).copied())
64 .unwrap_or(Transform::IDENTITY),
65 }
66}
67
68pub fn bind_world(mesh: &GraphicsMesh) -> Vec<Transform> {
70 let locals: Vec<Transform> = mesh.bones.iter().map(local_bind).collect();
71 world_transforms(&mesh.bones, &locals)
72}
73
74pub fn animation_bone(part: &MeshPart) -> Option<usize> {
77 let mut nodes = part
78 .ranges
79 .iter()
80 .filter(|r| r.index_count > 0)
81 .map(|r| r.bone);
82 nodes
83 .next()
84 .filter(|&first| first >= 0 && nodes.all(|bone| bone == first))
85 .map(|bone| bone as usize)
86}