plasma_prp/material/
layer.rs1use std::io::Read;
6
7use anyhow::Result;
8
9use crate::core::synched_object::SynchedObjectData;
10use crate::core::uoid::{Uoid, read_key_uoid};
11use crate::resource::prp::PlasmaRead;
12
13use super::state::MatState;
14
15#[derive(Debug, Clone, Copy, Default)]
17pub struct Color {
18 pub r: f32,
19 pub g: f32,
20 pub b: f32,
21 pub a: f32,
22}
23
24impl Color {
25 pub fn read(reader: &mut impl Read) -> Result<Self> {
26 Ok(Self {
27 r: reader.read_f32()?,
28 g: reader.read_f32()?,
29 b: reader.read_f32()?,
30 a: reader.read_f32()?,
31 })
32 }
33
34 pub fn white() -> Self {
35 Self {
36 r: 1.0,
37 g: 1.0,
38 b: 1.0,
39 a: 1.0,
40 }
41 }
42}
43
44fn read_matrix44(reader: &mut impl Read) -> Result<[f32; 16]> {
46 let flag = reader.read_u8()?;
47 if flag == 0 {
48 return Ok([
49 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
50 ]);
51 }
52 let mut m = [0f32; 16];
53 for val in &mut m {
54 *val = reader.read_f32()?;
55 }
56 Ok(m)
57}
58
59#[allow(dead_code)]
61pub mod uvw_src {
62 pub const PASS_THRU: u32 = 0x00000000;
63 pub const IDX_MASK: u32 = 0x0000FFFF;
64 pub const NORMAL: u32 = 0x00010000;
65 pub const POSITION: u32 = 0x00020000;
66 pub const REFLECT: u32 = 0x00030000;
67}
68
69#[derive(Debug, Clone)]
71pub struct LayerData {
72 pub self_key: Option<Uoid>,
74 pub synched: SynchedObjectData,
75 pub underlay: Option<Uoid>,
76
77 pub state: MatState,
79 pub transform: [f32; 16],
80 pub preshade_color: Color,
81 pub runtime_color: Color,
82 pub ambient_color: Color,
83 pub specular_color: Color,
84 pub uvw_src: u32,
85 pub opacity: f32,
86 pub lod_bias: f32,
87 pub specular_power: f32,
88 pub texture: Option<Uoid>,
89 pub vertex_shader: Option<Uoid>,
90 pub pixel_shader: Option<Uoid>,
91 pub bump_env_xfm: [f32; 16],
92}
93
94impl LayerData {
95 pub fn read(reader: &mut impl Read) -> Result<Self> {
97 let self_key = read_key_uoid(reader)?;
99 let synched = SynchedObjectData::read(reader)?;
100 let underlay = read_key_uoid(reader)?;
102
103 let state = MatState::read(reader)?;
105
106 let transform = read_matrix44(reader)?;
107 let preshade_color = Color::read(reader)?;
108 let runtime_color = Color::read(reader)?;
109 let ambient_color = Color::read(reader)?;
110 let specular_color = Color::read(reader)?;
111
112 let uvw_src = reader.read_u32()?;
113 let opacity = reader.read_f32()?;
114 let lod_bias = reader.read_f32()?;
115 let specular_power = reader.read_f32()?;
116
117 let texture = read_key_uoid(reader)?;
118 let vertex_shader = read_key_uoid(reader)?;
119 let pixel_shader = read_key_uoid(reader)?;
120
121 let bump_env_xfm = read_matrix44(reader)?;
122
123 Ok(Self {
124 self_key,
125 synched,
126 underlay,
127 state,
128 transform,
129 preshade_color,
130 runtime_color,
131 ambient_color,
132 specular_color,
133 uvw_src,
134 opacity,
135 lod_bias,
136 specular_power,
137 texture,
138 vertex_shader,
139 pixel_shader,
140 bump_env_xfm,
141 })
142 }
143
144 pub fn texture_name(&self) -> Option<&str> {
146 self.texture.as_ref().map(|u| u.object_name.as_str())
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::core::class_index::ClassIndex;
154 use crate::resource::prp::PrpPage;
155 use std::io::Cursor;
156 use std::path::Path;
157
158 #[test]
159 fn test_parse_cleft_layers() {
160 let path = Path::new("../../Plasma/staging/client/dat/Cleft_District_Cleft.prp");
161 if !path.exists() {
162 eprintln!("Skipping test: {:?} not found", path);
163 return;
164 }
165
166 let page = PrpPage::from_file(path).unwrap();
167 let layer_keys: Vec<_> = page.keys_of_type(ClassIndex::PL_LAYER);
168
169 let mut parsed = 0;
170 let mut with_texture = 0;
171 for key in &layer_keys {
172 if let Some(data) = page.object_data(key) {
173 let mut cursor = Cursor::new(data);
174 let _ = cursor.read_i16().unwrap();
175
176 match LayerData::read(&mut cursor) {
177 Ok(layer) => {
178 parsed += 1;
179 if layer.texture.is_some() {
180 with_texture += 1;
181 }
182 assert_eq!(
183 layer.self_key.as_ref().unwrap().object_name,
184 key.object_name
185 );
186 assert!(
188 layer.opacity >= 0.0 && layer.opacity <= 1.0,
189 "Invalid opacity {} for {}",
190 layer.opacity,
191 key.object_name
192 );
193 }
194 Err(e) => {
195 panic!("Failed to parse layer '{}': {}", key.object_name, e);
196 }
197 }
198 }
199 }
200
201 eprintln!(
202 "Parsed {} layers ({} with textures) from Cleft",
203 parsed, with_texture
204 );
205 assert!(parsed > 0);
206 assert!(with_texture > 0);
207 }
208}