Skip to main content

banished_gfx/
material.rs

1//! Model material classification and the character outfit atlas, shared by the viewer and the
2//! game so both draw a material the same way.
3
4/// Which ported model shader a range uses, from its material instance's base material.
5#[derive(Clone, Copy, PartialEq, Eq, Debug)]
6pub enum MaterialKind {
7    /// `Material\Opaque`, `OpaqueInWater` (`OpaqueMaterial_ps`).
8    Opaque,
9    /// `Material\OpaqueSnow`: roofs collect snow where the diffuse alpha allows.
10    OpaqueSnow,
11    /// `Material\AlphaTest`: opaque with a 0.5 alpha cut.
12    AlphaTest,
13    /// `Material\Foliage`: pines sway with the wind and hold snow on upward faces.
14    Foliage,
15    /// `Material\Billboard`: leaf cards expanded towards the camera (`BillboardMaterial_vs/ps`).
16    Billboard,
17    /// `Material\OpaqueSkin\OpaqueCharacterMaterial`: skinned characters textured from an atlas
18    /// of [`CHARACTER_OUTFITS`] outfits (`NormalSkin_charactervs`, then `Normal_ps`).
19    Character,
20}
21
22impl MaterialKind {
23    /// The kind of a range from the path of its material instance's base material.
24    pub fn of(base: Option<&str>) -> MaterialKind {
25        let base = base.unwrap_or_default().replace('/', "\\");
26        if base.contains("OpaqueCharacterMaterial") {
27            MaterialKind::Character
28        } else if base.contains("\\Billboard\\") {
29            MaterialKind::Billboard
30        } else if base.contains("\\Foliage\\") {
31            MaterialKind::Foliage
32        } else if base.contains("\\OpaqueSnow\\") {
33            MaterialKind::OpaqueSnow
34        } else if base.contains("\\AlphaTest\\") {
35            MaterialKind::AlphaTest
36        } else {
37            MaterialKind::Opaque
38        }
39    }
40}
41
42/// `NormalSkin_charactervs` (`c5.z = 0.125`): the character texture holds 8 outfits side by side.
43pub const CHARACTER_OUTFITS: f32 = 8.0;
44
45// The outfit is citizen +0x34; the shader reads it from c44.w.
46/// The texture transform of a citizen's outfit, as a scale and offset for `uv * xy + zw`. The
47/// character texture holds 8 outfits side by side, and the shader computes the diffuse U as
48/// `(u + outfit) / 8`, where `u` is the vertex texture coordinate (`/512`) and `outfit` is 0 to
49/// 7.
50pub fn character_uv_transform(outfit: u32) -> [f32; 4] {
51    [
52        1.0 / CHARACTER_OUTFITS,
53        1.0,
54        outfit as f32 / CHARACTER_OUTFITS,
55        0.0,
56    ]
57}