use std::io::Read;
use anyhow::Result;
use crate::core::synched_object::SynchedObjectData;
use crate::core::uoid::{Uoid, read_key_uoid};
use crate::resource::prp::PlasmaRead;
use super::state::MatState;
#[derive(Debug, Clone, Copy, Default)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Color {
pub fn read(reader: &mut impl Read) -> Result<Self> {
Ok(Self {
r: reader.read_f32()?,
g: reader.read_f32()?,
b: reader.read_f32()?,
a: reader.read_f32()?,
})
}
pub fn white() -> Self {
Self {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
}
}
}
fn read_matrix44(reader: &mut impl Read) -> Result<[f32; 16]> {
let flag = reader.read_u8()?;
if flag == 0 {
return Ok([
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,
]);
}
let mut m = [0f32; 16];
for val in &mut m {
*val = reader.read_f32()?;
}
Ok(m)
}
#[allow(dead_code)]
pub mod uvw_src {
pub const PASS_THRU: u32 = 0x00000000;
pub const IDX_MASK: u32 = 0x0000FFFF;
pub const NORMAL: u32 = 0x00010000;
pub const POSITION: u32 = 0x00020000;
pub const REFLECT: u32 = 0x00030000;
}
#[derive(Debug, Clone)]
pub struct LayerData {
pub self_key: Option<Uoid>,
pub synched: SynchedObjectData,
pub underlay: Option<Uoid>,
pub state: MatState,
pub transform: [f32; 16],
pub preshade_color: Color,
pub runtime_color: Color,
pub ambient_color: Color,
pub specular_color: Color,
pub uvw_src: u32,
pub opacity: f32,
pub lod_bias: f32,
pub specular_power: f32,
pub texture: Option<Uoid>,
pub vertex_shader: Option<Uoid>,
pub pixel_shader: Option<Uoid>,
pub bump_env_xfm: [f32; 16],
}
impl LayerData {
pub fn read(reader: &mut impl Read) -> Result<Self> {
let self_key = read_key_uoid(reader)?;
let synched = SynchedObjectData::read(reader)?;
let underlay = read_key_uoid(reader)?;
let state = MatState::read(reader)?;
let transform = read_matrix44(reader)?;
let preshade_color = Color::read(reader)?;
let runtime_color = Color::read(reader)?;
let ambient_color = Color::read(reader)?;
let specular_color = Color::read(reader)?;
let uvw_src = reader.read_u32()?;
let opacity = reader.read_f32()?;
let lod_bias = reader.read_f32()?;
let specular_power = reader.read_f32()?;
let texture = read_key_uoid(reader)?;
let vertex_shader = read_key_uoid(reader)?;
let pixel_shader = read_key_uoid(reader)?;
let bump_env_xfm = read_matrix44(reader)?;
Ok(Self {
self_key,
synched,
underlay,
state,
transform,
preshade_color,
runtime_color,
ambient_color,
specular_color,
uvw_src,
opacity,
lod_bias,
specular_power,
texture,
vertex_shader,
pixel_shader,
bump_env_xfm,
})
}
pub fn texture_name(&self) -> Option<&str> {
self.texture.as_ref().map(|u| u.object_name.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::class_index::ClassIndex;
use crate::resource::prp::PrpPage;
use std::io::Cursor;
use std::path::Path;
#[test]
fn test_parse_cleft_layers() {
let path = Path::new("../../Plasma/staging/client/dat/Cleft_District_Cleft.prp");
if !path.exists() {
eprintln!("Skipping test: {:?} not found", path);
return;
}
let page = PrpPage::from_file(path).unwrap();
let layer_keys: Vec<_> = page.keys_of_type(ClassIndex::PL_LAYER);
let mut parsed = 0;
let mut with_texture = 0;
for key in &layer_keys {
if let Some(data) = page.object_data(key) {
let mut cursor = Cursor::new(data);
let _ = cursor.read_i16().unwrap();
match LayerData::read(&mut cursor) {
Ok(layer) => {
parsed += 1;
if layer.texture.is_some() {
with_texture += 1;
}
assert_eq!(
layer.self_key.as_ref().unwrap().object_name,
key.object_name
);
assert!(
layer.opacity >= 0.0 && layer.opacity <= 1.0,
"Invalid opacity {} for {}",
layer.opacity,
key.object_name
);
}
Err(e) => {
panic!("Failed to parse layer '{}': {}", key.object_name, e);
}
}
}
}
eprintln!(
"Parsed {} layers ({} with textures) from Cleft",
parsed, with_texture
);
assert!(parsed > 0);
assert!(with_texture > 0);
}
}