1
Fork 0
mirror of https://github.com/redstrate/Physis.git synced 2025-04-23 13:17:44 +00:00
physis/src/mtrl.rs

324 lines
7.6 KiB
Rust
Raw Normal View History

// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
2024-04-20 13:17:11 -04:00
#![allow(clippy::unnecessary_fallible_conversions)] // This wrongly trips on binrw code
2022-08-16 11:52:07 -04:00
use std::io::Cursor;
2022-08-11 15:40:14 -04:00
use crate::ByteSpan;
use binrw::{binrw, BinRead, binread};
use crate::common_file_operations::{Half1, Half2, Half3};
#[binrw]
2022-08-11 15:40:14 -04:00
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
2022-08-11 15:40:14 -04:00
struct MaterialFileHeader {
version: u32,
file_size: u16,
data_set_size: u16,
string_table_size: u16,
shader_package_name_offset: u16,
texture_count: u8,
uv_set_count: u8,
color_set_count: u8,
2022-08-16 11:52:07 -04:00
additional_data_size: u8,
2022-08-11 15:40:14 -04:00
}
#[binrw]
2022-08-11 15:40:14 -04:00
#[derive(Debug)]
struct MaterialHeader {
shader_value_list_size: u16,
shader_key_count: u16,
constant_count: u16,
#[br(pad_after = 4)]
2022-08-16 11:52:07 -04:00
sampler_count: u16,
2022-08-11 15:40:14 -04:00
}
#[binrw]
2022-08-11 15:40:14 -04:00
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
2022-08-11 15:40:14 -04:00
struct ColorSet {
name_offset: u16,
#[br(pad_after = 1)]
2022-08-16 11:52:07 -04:00
index: u8,
2022-08-11 15:40:14 -04:00
}
#[binread]
#[derive(Debug)]
#[allow(dead_code)]
struct ColorTableRow {
diffuse_color: Half3,
specular_strength: Half1,
specular_color: Half3,
gloss_strength: Half1,
emissive_color: Half3,
tile_set: u16,
material_repeat: Half2,
material_skew: Half2,
}
#[binread]
#[br(import {set_count: usize})]
2022-08-11 15:40:14 -04:00
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct ColorTable {
#[br(count = set_count)]
data: Vec<ColorTableRow>,
2022-08-11 15:40:14 -04:00
}
#[binread]
#[derive(Debug)]
#[allow(dead_code)]
struct ColorDyeTableRow {
#[br(temp)]
data: u16,
#[br(calc = data >> 5)]
template: u16,
#[br(calc = (data & 0x01) != 0)]
diffuse: bool,
#[br(calc = (data & 0x02) != 0)]
specular: bool,
#[br(calc = (data & 0x04) != 0)]
emissive: bool,
#[br(calc = (data & 0x08) != 0)]
gloss: bool,
#[br(calc = (data & 0x10) != 0)]
specular_strength: bool,
}
#[binread]
2022-08-11 15:40:14 -04:00
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
struct ColorDyeTable {
2022-08-11 15:40:14 -04:00
#[br(count = 16)]
data: Vec<ColorDyeTableRow>,
2022-08-11 15:40:14 -04:00
}
#[binrw]
2024-04-26 22:40:42 -04:00
#[derive(Debug, Clone, Copy)]
2024-04-26 22:36:40 -04:00
#[repr(C)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
2024-01-29 15:07:59 -05:00
pub struct ShaderKey {
pub category: u32,
pub value: u32,
2022-08-11 15:40:14 -04:00
}
#[binrw]
#[derive(Debug, Clone, Copy)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
2024-04-27 12:58:36 -04:00
pub struct ConstantStruct {
2022-08-11 15:40:14 -04:00
constant_id: u32,
value_offset: u16,
2022-08-16 11:52:07 -04:00
value_size: u16,
2022-08-11 15:40:14 -04:00
}
#[derive(Debug, Clone)]
2024-04-27 12:58:36 -04:00
#[repr(C)]
#[allow(dead_code)]
pub struct Constant {
id: u32,
num_values: u32,
values: [f32; 4]
2024-04-27 12:58:36 -04:00
}
2024-01-29 15:07:59 -05:00
// from https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Data/Parsing/MtrlStructs.cs
#[binrw]
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum TextureUsage {
2024-01-29 15:07:59 -05:00
#[brw(magic = 0x88408C04u32)]
Sampler,
#[brw(magic = 0x213CB439u32)]
Sampler0,
#[brw(magic = 0x563B84AFu32)]
Sampler1,
#[brw(magic = 0xFEA0F3D2u32)]
SamplerCatchlight,
#[brw(magic = 0x1E6FEF9Cu32)]
SamplerColorMap0,
#[brw(magic = 0x6968DF0Au32)]
SamplerColorMap1,
#[brw(magic = 0x115306BEu32)]
SamplerDiffuse,
#[brw(magic = 0xF8D7957Au32)]
SamplerEnvMap,
#[brw(magic = 0x8A4E82B6u32)]
SamplerMask,
#[brw(magic = 0x0C5EC1F1u32)]
SamplerNormal,
#[brw(magic = 0xAAB4D9E9u32)]
SamplerNormalMap0,
#[brw(magic = 0xDDB3E97Fu32)]
SamplerNormalMap1,
#[brw(magic = 0x87F6474Du32)]
SamplerReflection,
#[brw(magic = 0x2B99E025u32)]
SamplerSpecular,
#[brw(magic = 0x1BBC2F12u32)]
SamplerSpecularMap0,
#[brw(magic = 0x6CBB1F84u32)]
SamplerSpecularMap1,
#[brw(magic = 0xE6321AFCu32)]
SamplerWaveMap,
#[brw(magic = 0x574E22D6u32)]
SamplerWaveletMap0,
#[brw(magic = 0x20491240u32)]
SamplerWaveletMap1,
#[brw(magic = 0x95E1F64Du32)]
SamplerWhitecapMap,
#[brw(magic = 0x565f8fd8u32)]
2024-04-20 13:18:03 -04:00
UnknownDawntrail1,
2024-01-29 15:07:59 -05:00
}
#[binrw]
#[derive(Debug, Clone, Copy)]
#[repr(C)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
pub struct Sampler {
2024-01-29 15:07:59 -05:00
texture_usage: TextureUsage,
2022-08-11 15:40:14 -04:00
flags: u32, // TODO: unknown
#[br(pad_after = 3)]
texture_index: u8,
}
#[binrw]
2022-08-11 15:40:14 -04:00
#[derive(Debug)]
2022-09-15 16:26:31 -04:00
#[allow(dead_code)]
#[br(little)]
2022-08-11 15:40:14 -04:00
struct MaterialData {
file_header: MaterialFileHeader,
#[br(count = file_header.texture_count)]
offsets: Vec<u32>,
#[br(count = file_header.uv_set_count)]
uv_color_sets: Vec<ColorSet>,
#[br(count = file_header.color_set_count)]
color_sets: Vec<ColorSet>,
#[br(count = file_header.string_table_size)]
#[br(pad_after = file_header.additional_data_size)]
2022-08-11 15:40:14 -04:00
strings: Vec<u8>,
#[br(if(file_header.data_set_size > 0))]
// Dawntrail doubled the amount of color sets.
// The MTRL version is the same (why square enix?) so we check the data set size instead
#[br(args { set_count: if file_header.data_set_size < 2048 { 16 } else { 32 } })]
#[bw(ignore)]
color_set_info: Option<ColorTable>,
2022-08-11 15:40:14 -04:00
#[br(if(file_header.data_set_size >
if file_header.data_set_size < 2048 { 512 } else { 2048 }
))]
#[bw(ignore)]
color_set_due_info: Option<ColorDyeTable>,
2022-08-11 15:40:14 -04:00
header: MaterialHeader,
#[br(count = header.shader_key_count)]
shader_keys: Vec<ShaderKey>,
2022-08-11 15:40:14 -04:00
#[br(count = header.constant_count)]
2024-04-27 12:58:36 -04:00
constants: Vec<ConstantStruct>,
2022-08-11 15:40:14 -04:00
#[br(count = header.sampler_count)]
samplers: Vec<Sampler>,
2022-08-11 15:40:14 -04:00
#[br(count = header.shader_value_list_size / 4)]
shader_values: Vec<f32>,
2022-08-11 15:40:14 -04:00
}
#[derive(Debug)]
pub struct Material {
2024-01-29 15:07:59 -05:00
pub shader_package_name: String,
2022-08-16 11:52:07 -04:00
pub texture_paths: Vec<String>,
2024-04-20 13:18:03 -04:00
pub shader_keys: Vec<ShaderKey>,
pub constants: Vec<Constant>,
pub samplers: Vec<Sampler>,
2022-08-11 15:40:14 -04:00
}
impl Material {
pub fn from_existing(buffer: ByteSpan) -> Option<Material> {
2022-08-11 15:40:14 -04:00
let mut cursor = Cursor::new(buffer);
let mat_data = MaterialData::read(&mut cursor).ok()?;
2024-04-20 13:18:03 -04:00
2022-08-11 15:40:14 -04:00
let mut texture_paths = vec![];
let mut offset = 0;
for _ in 0..mat_data.file_header.texture_count {
let mut string = String::new();
2024-04-20 13:17:11 -04:00
let mut next_char = mat_data.strings[offset] as char;
2022-08-11 15:40:14 -04:00
while next_char != '\0' {
string.push(next_char);
offset += 1;
2024-04-20 13:17:11 -04:00
next_char = mat_data.strings[offset] as char;
2022-08-11 15:40:14 -04:00
}
texture_paths.push(string);
offset += 1;
}
2024-01-29 15:07:59 -05:00
// TODO: move to reusable function
let mut shader_package_name = String::new();
offset = mat_data.file_header.shader_package_name_offset as usize;
let mut next_char = mat_data.strings[offset] as char;
while next_char != '\0' {
shader_package_name.push(next_char);
offset += 1;
2024-04-20 13:17:11 -04:00
next_char = mat_data.strings[offset] as char;
2024-01-29 15:07:59 -05:00
}
2024-04-27 12:58:36 -04:00
let mut constants = Vec::new();
for constant in mat_data.constants {
let mut values: [f32; 4] = [0.0; 4];
// TODO: use mem::size_of
let num_floats = constant.value_size / 4;
for i in 0..num_floats as usize {
values[i] = mat_data.shader_values[(constant.value_offset as usize / 4) + i];
}
2024-04-27 12:58:36 -04:00
constants.push(Constant {
id: constant.constant_id,
num_values: num_floats as u32,
values
2024-04-27 12:58:36 -04:00
});
}
2024-01-29 15:07:59 -05:00
Some(Material {
shader_package_name,
texture_paths,
2024-04-20 13:18:03 -04:00
shader_keys: mat_data.shader_keys,
2024-04-27 12:58:36 -04:00
constants,
samplers: mat_data.samplers
2024-01-29 15:07:59 -05:00
})
2022-08-11 15:40:14 -04:00
}
2022-08-16 11:52:07 -04:00
}
2024-04-16 22:05:59 -04:00
#[cfg(test)]
mod tests {
use std::fs::read;
use std::path::PathBuf;
use super::*;
#[test]
fn test_invalid() {
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("resources/tests");
d.push("random");
// Feeding it invalid data should not panic
Material::from_existing(&read(d).unwrap());
}
}