mirror of
https://github.com/PrimeDecomp/retrotool.git
synced 2026-07-12 18:18:58 -07:00
Add preliminary CLSN/DCLN conversion
This commit is contained in:
+243
@@ -0,0 +1,243 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
fs::{
|
||||
DirBuilder,
|
||||
File
|
||||
},
|
||||
io::{Cursor, Read, Write},
|
||||
path::PathBuf,
|
||||
string::FromUtf8Error,
|
||||
};
|
||||
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use argh::FromArgs;
|
||||
use binrw::{binrw, BinReaderExt, BinWriterExt, Endian};
|
||||
|
||||
use crate::{
|
||||
format::{
|
||||
chunk::ChunkDescriptor,
|
||||
pack::{K_CHUNK_META, K_FORM_FOOT},
|
||||
rfrm::FormDescriptor,
|
||||
FourCC,
|
||||
},
|
||||
util::{
|
||||
file::map_file,
|
||||
lzss::decompress_buffer,
|
||||
math_classes::{
|
||||
CVector3f, CAABox, COBBox
|
||||
},
|
||||
},};
|
||||
use crate::cmd::txtr::K_CHUNK_HEAD;
|
||||
|
||||
// CAABoxCollisionTree
|
||||
pub const K_FORM_CLSN: FourCC = FourCC(*b"CLSN");
|
||||
// COBBoxCollisionTree
|
||||
pub const K_FORM_DCLN: FourCC = FourCC(*b"DCLN");
|
||||
|
||||
// COBBCollisionTree Header (only used in DCLN)
|
||||
pub const K_CHUNK_INFO: FourCC = FourCC(*b"INFO");
|
||||
|
||||
// Vertex data
|
||||
pub const K_CHUNK_VERT: FourCC = FourCC(*b"VERT");
|
||||
|
||||
// Material data
|
||||
pub const K_CHUNK_MTRL: FourCC = FourCC(*b"MTRL");
|
||||
|
||||
// Triangle data
|
||||
pub const K_CHUNK_TRIS: FourCC = FourCC(*b"TRIS");
|
||||
|
||||
// Octree data
|
||||
pub const K_CHUNK_TREE: FourCC = FourCC(*b"TREE");
|
||||
|
||||
pub const K_CLSN_READER_VERSION: u32 = 11;
|
||||
pub const K_CLSN_WRITER_VERSION: u32 = 22;
|
||||
|
||||
pub const K_DCLN_READER_VERSION: u32 = 9;
|
||||
pub const K_DCLN_WRITER_VERSION: u32 = 18;
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Vertices {
|
||||
pub count: u32,
|
||||
#[br(count = count)]
|
||||
vertices: Vec<CVector3f>
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Materials {
|
||||
pub count: u32,
|
||||
#[br(count = count)]
|
||||
materials: Vec<CCollisionMaterial>
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CCollisionMaterial {
|
||||
orientation: u32,
|
||||
material_type: u32,
|
||||
world_type: u32,
|
||||
behavior_list: u32,
|
||||
filter_list: u32,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CIndexedTriangle {
|
||||
idx1: u32,
|
||||
idx2: u32,
|
||||
idx3: u32,
|
||||
material: u16,
|
||||
unk: u16,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Triangles {
|
||||
count: u32,
|
||||
#[br(count = count)]
|
||||
triangles: Vec<CIndexedTriangle>
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AABoxTreeNode {
|
||||
bounds: CAABox,
|
||||
start: u32,
|
||||
end: u32,
|
||||
unk1: u8,
|
||||
unk2: u8,
|
||||
unk3: u8,
|
||||
unk4: u8,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OBBoxTreeNode {
|
||||
bounds: COBBox,
|
||||
start: u32,
|
||||
end: u32,
|
||||
unk1: u8,
|
||||
unk2: u8,
|
||||
unk3: u8,
|
||||
unk4: u8,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AABoxCollisionTree {
|
||||
count: u32,
|
||||
#[br(count = count)]
|
||||
nodes: Vec<AABoxTreeNode>,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OBBoxCollisionTree {
|
||||
count: u32,
|
||||
#[br(count = count)]
|
||||
nodes: Vec<OBBoxTreeNode>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// process CLSN/DCLN files
|
||||
#[argh(subcommand, name = "collision")]
|
||||
pub struct Args {
|
||||
#[argh(subcommand)]
|
||||
command: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommand {
|
||||
Convert(ConvertArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// converts a CLSN/DCLN to obj
|
||||
#[argh(subcommand, name = "convert")]
|
||||
pub struct ConvertArgs {
|
||||
#[argh(positional)]
|
||||
/// input CLSN/DCLN
|
||||
input: PathBuf,
|
||||
#[argh(positional)]
|
||||
/// output file
|
||||
out: PathBuf,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Convert(c_args) => convert(c_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert(args: ConvertArgs) -> Result<()> {
|
||||
// TODO: Migrate to real model format (glTF?)
|
||||
let data = map_file(&args.input)?;
|
||||
let (form_desc, mut col_data, remain) = FormDescriptor::slice(&data, Endian::Little)?;
|
||||
|
||||
if form_desc.id == K_FORM_DCLN {
|
||||
ensure!(form_desc.version_a == K_DCLN_READER_VERSION);
|
||||
ensure!(form_desc.version_b == K_DCLN_WRITER_VERSION);
|
||||
} else if form_desc.id == K_FORM_CLSN {
|
||||
ensure!(form_desc.version_a == K_CLSN_READER_VERSION);
|
||||
ensure!(form_desc.version_b == K_CLSN_WRITER_VERSION);
|
||||
}
|
||||
|
||||
let (foot_desc, _, remain) = FormDescriptor::slice(remain, Endian::Little)?;
|
||||
ensure!(foot_desc.id == K_FORM_FOOT);
|
||||
ensure!(foot_desc.version_a == 1);
|
||||
ensure!(foot_desc.version_b == 1);
|
||||
ensure!(remain.is_empty());
|
||||
|
||||
let mut bounds: Option<CAABox> = None;
|
||||
let mut vertices: Option<Vertices> = None;
|
||||
let mut materials: Option<Materials> = None;
|
||||
let mut triangles: Option<Triangles> = None;
|
||||
let mut aboxtree: Option<AABoxCollisionTree> = None;
|
||||
let mut oboxtree: Option<OBBoxCollisionTree> = None;
|
||||
|
||||
while !col_data.is_empty() {
|
||||
let (desc, data, remain) = ChunkDescriptor::slice(col_data, Endian::Little)?;
|
||||
if desc.id == K_CHUNK_INFO {
|
||||
bounds = Some(Cursor::new(data).read_type(Endian::Little)?);
|
||||
log::debug!("Bounds: {bounds:#?}");
|
||||
break;
|
||||
} else if desc.id == K_CHUNK_VERT {
|
||||
vertices = Some(Cursor::new(data).read_type(Endian::Little)?);
|
||||
log::debug!("Vertices: {vertices:#?}");
|
||||
} else if desc.id == K_CHUNK_MTRL {
|
||||
materials = Some(Cursor::new(data).read_type(Endian::Little)?);
|
||||
log::debug!("Materials: {materials:#?}");
|
||||
} else if desc.id == K_CHUNK_TRIS {
|
||||
triangles = Some(Cursor::new(data).read_type(Endian::Little)?);
|
||||
log::debug!("Triangles: {triangles:#?}");
|
||||
} else if desc.id == K_CHUNK_TREE {
|
||||
if form_desc.id == K_FORM_CLSN {
|
||||
aboxtree = Some(Cursor::new(data).read_type(Endian::Little)?);
|
||||
log::debug!("Tree: {aboxtree:#?}");
|
||||
} else {
|
||||
oboxtree = Some(Cursor::new(data).read_type(Endian::Little)?);
|
||||
log::debug!("Tree: {oboxtree:#?}");
|
||||
}
|
||||
}
|
||||
col_data = remain;
|
||||
}
|
||||
|
||||
let mut file = File::create(&args.out)?;
|
||||
if vertices.is_some() {
|
||||
let verts = vertices.unwrap();
|
||||
let tris = triangles.unwrap();
|
||||
file.write_fmt(format_args!("# Generated by retrotool, {} vertices, {} triangles\n# Vertices\n", verts.count, tris.count))?;
|
||||
for vertex in verts.vertices.iter() {
|
||||
file.write_fmt(format_args!("v {} {} {}\n", vertex.x, vertex.y, vertex.z))?;
|
||||
}
|
||||
file.write_fmt(format_args!("\n# Triangles\n"))?;
|
||||
for triangle in tris.triangles.iter() {
|
||||
file.write_fmt(format_args!("f {} {} {}\n", triangle.idx1 + 1, triangle.idx2 + 1, triangle.idx3 + 1))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
+8
-17
@@ -24,7 +24,13 @@ use crate::{
|
||||
rfrm::FormDescriptor,
|
||||
FourCC,
|
||||
},
|
||||
util::{file::map_file, lzss::decompress_buffer},
|
||||
util::{
|
||||
file::map_file,
|
||||
lzss::decompress_buffer,
|
||||
math_classes::{
|
||||
CVector3f, CAABox,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Cooked model
|
||||
@@ -333,21 +339,6 @@ pub struct CRenderMesh {
|
||||
pub unk_e: u16, // 64
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CVector3f {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CAABox {
|
||||
pub min: CVector3f,
|
||||
pub max: CVector3f,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SModelHeader {
|
||||
@@ -1567,7 +1558,7 @@ fn convert(args: ConvertArgs) -> Result<()> {
|
||||
_ => bail!("Unsupported data type for NRML"),
|
||||
},
|
||||
id => {
|
||||
// log::debug!("Ignoring material data ID {id:?}");
|
||||
log::debug!("Ignoring material data ID {id:?}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod cmdl;
|
||||
pub mod fmv0;
|
||||
pub mod pak;
|
||||
pub mod txtr;
|
||||
pub mod clsn;
|
||||
|
||||
@@ -19,6 +19,7 @@ pub enum SubCommand {
|
||||
Fmv0(cmd::fmv0::Args),
|
||||
Pak(cmd::pak::Args),
|
||||
Txtr(cmd::txtr::Args),
|
||||
Clsn(cmd::clsn::Args),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@@ -34,6 +35,7 @@ fn main() {
|
||||
SubCommand::Fmv0(args) => cmd::fmv0::run(args),
|
||||
SubCommand::Pak(args) => cmd::pak::run(args),
|
||||
SubCommand::Txtr(args) => cmd::txtr::run(args),
|
||||
SubCommand::Clsn(args) => cmd::clsn::run(args),
|
||||
};
|
||||
if let Err(e) = result {
|
||||
eprintln!("Failed: {e:?}");
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ pub fn write_dds<W: Write>(w: &mut W, head: &STextureHeader, data: Vec<u8>) -> R
|
||||
})?;
|
||||
// FIXME: ddsfile ASTC size calc is broken
|
||||
if !head.format.is_astc() {
|
||||
ensure!(dds.data.len() == data.len());
|
||||
//ensure!(dds.data.len() == data.len());
|
||||
}
|
||||
dds.data = data;
|
||||
dds.write(w)?;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use binrw::{binrw, BinReaderExt, BinWriterExt, Endian};
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CVector3f {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub z: f32,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CAABox {
|
||||
pub min: CVector3f,
|
||||
pub max: CVector3f,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CTransform4f {
|
||||
m00: f32,
|
||||
m01: f32,
|
||||
m02: f32,
|
||||
m03: f32,
|
||||
m10: f32,
|
||||
m11: f32,
|
||||
m12: f32,
|
||||
m13: f32,
|
||||
m20: f32,
|
||||
m21: f32,
|
||||
m22: f32,
|
||||
m23: f32,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct COBBox {
|
||||
xf : CTransform4f,
|
||||
extents: CVector3f,
|
||||
}
|
||||
@@ -2,6 +2,7 @@ pub mod astc;
|
||||
pub mod dds;
|
||||
pub mod file;
|
||||
pub mod lzss;
|
||||
pub mod math_classes;
|
||||
|
||||
/// Creates a fixed-size array reference from a slice.
|
||||
#[macro_export]
|
||||
|
||||
Reference in New Issue
Block a user