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}