1use anyhow::{Result, bail};
9use std::io::{Read, Seek};
10use crate::resource::prp::PlasmaRead;
11use crate::core::uoid::{Uoid, read_key_uoid};
12
13#[derive(Debug, Clone, Copy)]
16#[repr(C)]
17pub struct ClusterVertex {
18 pub position: [f32; 4],
19 pub normal: [f32; 4],
20 pub color: [f32; 4],
21 pub uv: [f32; 2],
22 pub uv2: [f32; 2],
23 pub uv3: [f32; 2],
24 pub flags: f32,
25 pub _pad: f32,
26 pub uv4: [f32; 2],
27 pub uv5: [f32; 2],
28}
29
30#[derive(Debug)]
32pub struct ClusterGroup {
33 pub template: SpanTemplate,
34 pub material_uoid: Option<Uoid>,
35 pub clusters: Vec<Cluster>,
36}
37
38#[derive(Debug)]
40pub struct SpanTemplate {
41 pub format: u16,
42 pub num_verts: u16,
43 pub num_tris: u16,
44 pub stride: usize,
45 pub vert_data: Vec<u8>,
46 pub index_data: Vec<u16>,
47}
48
49#[derive(Debug)]
51pub struct Cluster {
52 pub encoding: SpanEncoding,
53 pub instances: Vec<SpanInstance>,
54}
55
56#[derive(Debug, Clone, Copy)]
58pub struct SpanEncoding {
59 pub code: u8,
60 pub pos_scale: f32,
61}
62
63#[derive(Debug)]
65pub struct SpanInstance {
66 pub l2w: [[f32; 4]; 3],
67 pub pos_delta: Option<Vec<u8>>,
68 pub col: Option<Vec<u8>>,
69}
70
71const POS_NONE: u8 = 0x0;
73const POS_888: u8 = 0x1;
74const POS_161616: u8 = 0x2;
75const POS_101010: u8 = 0x4;
76const POS_008: u8 = 0x8;
77const POS_MASK: u8 = POS_888 | POS_161616 | POS_101010 | POS_008;
78
79const COL_NONE: u16 = 0x0;
81const COL_A8: u16 = 0x10;
82const COL_I8: u16 = 0x20;
83const COL_AI88: u16 = 0x40;
84const COL_RGB888: u16 = 0x80;
85const COL_ARGB8888: u16 = 0x100;
86const COL_MASK: u16 = COL_A8 | COL_I8 | COL_AI88 | COL_RGB888 | COL_ARGB8888;
87
88const TMPL_POS: u16 = 0x1;
90const TMPL_NORM: u16 = 0x2;
91const TMPL_COLOR: u16 = 0x4;
92const TMPL_WGTIDX: u16 = 0x8;
93const TMPL_UVW_MASK: u16 = 0xF0;
94const TMPL_WEIGHT_MASK: u16 = 0x300;
95const TMPL_COLOR2: u16 = 0x400;
96
97impl SpanEncoding {
98 fn pos_stride(&self) -> usize {
99 match self.code & POS_MASK {
100 POS_888 => 3,
101 POS_161616 => 6,
102 POS_101010 => 4,
103 POS_008 => 1,
104 _ => 0,
105 }
106 }
107
108 fn col_stride(&self) -> usize {
109 let code = self.code as u16;
110 match code & COL_MASK {
111 COL_A8 | COL_I8 => 1,
112 COL_AI88 => 2,
113 COL_RGB888 => 3,
114 COL_ARGB8888 => 4,
115 _ => 0,
116 }
117 }
118}
119
120impl SpanTemplate {
121 pub fn num_uvws(&self) -> usize { ((self.format & TMPL_UVW_MASK) >> 4) as usize }
122 fn num_weights(&self) -> usize { ((self.format & TMPL_WEIGHT_MASK) >> 8) as usize }
123 fn has_pos(&self) -> bool { self.format & TMPL_POS != 0 }
124 fn has_norm(&self) -> bool { self.format & TMPL_NORM != 0 }
125 fn has_color(&self) -> bool { self.format & TMPL_COLOR != 0 }
126 fn has_color2(&self) -> bool { self.format & TMPL_COLOR2 != 0 }
127 fn has_wgt_idx(&self) -> bool { self.format & TMPL_WGTIDX != 0 }
128
129 fn calc_stride(format: u16) -> usize {
130 let mut s = 0usize;
131 if format & TMPL_POS != 0 { s += 12; } let num_weights = ((format & TMPL_WEIGHT_MASK) >> 8) as usize;
133 s += num_weights * 4; if format & TMPL_WGTIDX != 0 { s += 4; } if format & TMPL_NORM != 0 { s += 12; } if format & TMPL_COLOR != 0 { s += 4; } if format & TMPL_COLOR2 != 0 { s += 4; } let num_uvws = ((format & TMPL_UVW_MASK) >> 4) as usize;
139 s += num_uvws * 12; s
141 }
142
143 fn pos_offset(&self) -> usize { 0 }
145 fn norm_offset(&self) -> usize {
146 let mut o = if self.has_pos() { 12 } else { 0 };
147 o += self.num_weights() * 4;
148 if self.has_wgt_idx() { o += 4; }
149 o
150 }
151 fn color_offset(&self) -> usize {
152 self.norm_offset() + if self.has_norm() { 12 } else { 0 }
153 }
154 fn color2_offset(&self) -> usize {
155 self.color_offset() + if self.has_color() { 4 } else { 0 }
156 }
157 fn uvw_offset(&self) -> usize {
158 self.color2_offset() + if self.has_color2() { 4 } else { 0 }
159 }
160}
161
162impl ClusterGroup {
163 pub fn read(reader: &mut (impl Read + Seek)) -> Result<Self> {
166 let _self_key = read_key_uoid(reader)?;
168
169 let template = SpanTemplate::read(reader)?;
171
172 let material_uoid = read_key_uoid(reader)?;
174
175 let num_clusters = reader.read_u32()? as usize;
177 let mut clusters = Vec::with_capacity(num_clusters);
178 for _ in 0..num_clusters {
179 clusters.push(Cluster::read(reader, template.num_verts)?);
180 }
181
182 let num_regions = reader.read_u32()? as usize;
184 for _ in 0..num_regions {
185 let _ = read_key_uoid(reader)?;
186 }
187
188 let num_lights = reader.read_u32()? as usize;
190 for _ in 0..num_lights {
191 let _ = read_key_uoid(reader)?;
192 }
193
194 let _min_dist = reader.read_f32()?;
196 let _max_dist = reader.read_f32()?;
197
198 let _render_level = reader.read_u32()?;
200
201 let _ = read_key_uoid(reader)?;
203
204 let total_insts: usize = clusters.iter().map(|c| c.instances.len()).sum();
205 log::debug!("ClusterGroup: {} clusters, {} total instances, template: {} verts, {} tris, format=0x{:X}",
206 clusters.len(), total_insts, template.num_verts, template.num_tris, template.format);
207
208 Ok(Self {
209 template,
210 material_uoid,
211 clusters,
212 })
213 }
214
215 pub fn unpack_meshes(&self) -> Vec<(Vec<ClusterVertex>, Vec<u16>)> {
219 let templ = &self.template;
220 let verts_per_inst = templ.num_verts as usize;
221 let indices_per_inst = templ.num_tris as usize * 3;
222
223 let mut result = Vec::new();
224
225 for cluster in &self.clusters {
226 let num_insts = cluster.instances.len();
227 if num_insts == 0 { continue; }
228
229 let mut all_verts = Vec::with_capacity(verts_per_inst * num_insts);
230 let mut all_indices = Vec::with_capacity(indices_per_inst * num_insts);
231 let mut idx_offset = 0u16;
232
233 for inst in &cluster.instances {
234 for &idx in &templ.index_data {
236 all_indices.push(idx + idx_offset);
237 }
238 idx_offset += verts_per_inst as u16;
239
240 let l2w = &inst.l2w;
242 let m: [f32; 16] = [
243 l2w[0][0], l2w[0][1], l2w[0][2], l2w[0][3],
244 l2w[1][0], l2w[1][1], l2w[1][2], l2w[1][3],
245 l2w[2][0], l2w[2][1], l2w[2][2], l2w[2][3],
246 0.0, 0.0, 0.0, 1.0,
247 ];
248
249 let w2l_t = transpose_inverse_3x3(&m);
252
253 let enc = &cluster.encoding;
255 let pos_stride = enc.pos_stride();
256 let col_stride = enc.col_stride();
257
258 for vi in 0..verts_per_inst {
259 let base = vi * templ.stride;
260
261 let pos_off = templ.pos_offset();
263 let (mut px, mut py, mut pz) = if templ.has_pos() && base + pos_off + 12 <= templ.vert_data.len() {
264 let o = base + pos_off;
265 (
266 f32::from_le_bytes(templ.vert_data[o..o+4].try_into().unwrap()),
267 f32::from_le_bytes(templ.vert_data[o+4..o+8].try_into().unwrap()),
268 f32::from_le_bytes(templ.vert_data[o+8..o+12].try_into().unwrap()),
269 )
270 } else {
271 (0.0, 0.0, 0.0)
272 };
273
274 if let Some(ref pos_data) = inst.pos_delta {
276 let delta_off = vi * pos_stride;
277 let (dx, dy, dz) = decode_pos_delta(enc, pos_data, delta_off);
278 px += dx;
279 py += dy;
280 pz += dz;
281 }
282
283 let wx = px * m[0] + py * m[1] + pz * m[2] + m[3];
285 let wy = px * m[4] + py * m[5] + pz * m[6] + m[7];
286 let wz = px * m[8] + py * m[9] + pz * m[10] + m[11];
287
288 let norm_off = templ.norm_offset();
290 let (nx, ny, nz) = if templ.has_norm() && base + norm_off + 12 <= templ.vert_data.len() {
291 let o = base + norm_off;
292 (
293 f32::from_le_bytes(templ.vert_data[o..o+4].try_into().unwrap()),
294 f32::from_le_bytes(templ.vert_data[o+4..o+8].try_into().unwrap()),
295 f32::from_le_bytes(templ.vert_data[o+8..o+12].try_into().unwrap()),
296 )
297 } else {
298 (0.0, 1.0, 0.0)
299 };
300
301 let wnx = nx * w2l_t[0] + ny * w2l_t[1] + nz * w2l_t[2];
303 let wny = nx * w2l_t[3] + ny * w2l_t[4] + nz * w2l_t[5];
304 let wnz = nx * w2l_t[6] + ny * w2l_t[7] + nz * w2l_t[8];
305 let nlen = (wnx*wnx + wny*wny + wnz*wnz).sqrt().max(1e-10);
306
307 let (r, g, b, a) = if templ.has_color() {
309 let col_off = templ.color_offset();
310 let o = base + col_off;
311 if o + 4 <= templ.vert_data.len() {
312 let mut c = u32::from_le_bytes(templ.vert_data[o..o+4].try_into().unwrap());
313 if let Some(ref col_data) = inst.col {
315 c = decode_color(enc, col_data, vi * col_stride, c);
316 }
317 let ca = ((c >> 24) & 0xFF) as f32 / 255.0;
319 let cr = ((c >> 16) & 0xFF) as f32 / 255.0;
320 let cg = ((c >> 8) & 0xFF) as f32 / 255.0;
321 let cb = (c & 0xFF) as f32 / 255.0;
322 (cr, cg, cb, ca)
323 } else {
324 (1.0, 1.0, 1.0, 1.0)
325 }
326 } else {
327 (1.0, 1.0, 1.0, 1.0)
328 };
329
330 let uvw_off = templ.uvw_offset();
332 let (u, v) = if templ.num_uvws() > 0 {
333 let o = base + uvw_off;
334 if o + 8 <= templ.vert_data.len() {
335 (
336 f32::from_le_bytes(templ.vert_data[o..o+4].try_into().unwrap()),
337 f32::from_le_bytes(templ.vert_data[o+4..o+8].try_into().unwrap()),
338 )
339 } else {
340 (0.0, 0.0)
341 }
342 } else {
343 (0.0, 0.0)
344 };
345
346 let (u2, v2) = if templ.num_uvws() > 1 {
348 let o = base + uvw_off + 12; if o + 8 <= templ.vert_data.len() {
350 (
351 f32::from_le_bytes(templ.vert_data[o..o+4].try_into().unwrap()),
352 f32::from_le_bytes(templ.vert_data[o+4..o+8].try_into().unwrap()),
353 )
354 } else {
355 (0.0, 0.0)
356 }
357 } else {
358 (0.0, 0.0)
359 };
360
361 all_verts.push(ClusterVertex {
362 position: [wx, wy, wz, 0.0],
363 normal: [wnx / nlen, wny / nlen, wnz / nlen, 0.0],
364 color: [r, g, b, a],
365 uv: [u, v],
366 uv2: [u2, v2],
367 uv3: [0.0, 0.0],
368 flags: 2.0,
370 _pad: 0.0,
371 uv4: [0.0, 0.0],
372 uv5: [0.0, 0.0],
373 });
374 }
375 }
376
377 if !all_verts.is_empty() && !all_indices.is_empty() {
378 result.push((all_verts, all_indices));
379 }
380 }
381
382 result
383 }
384}
385
386impl SpanTemplate {
387 fn read(reader: &mut (impl Read + Seek)) -> Result<Self> {
388 let num_verts = reader.read_u16()?;
389 let format = reader.read_u16()?;
390 let num_tris = reader.read_u16()?;
391
392 let stride = Self::calc_stride(format);
393 let vert_size = num_verts as usize * stride;
394 let idx_size = num_tris as usize * 3;
395
396 let mut vert_data = vec![0u8; vert_size];
397 reader.read_exact(&mut vert_data)?;
398
399 let mut index_data = Vec::with_capacity(idx_size);
400 for _ in 0..idx_size {
401 index_data.push(reader.read_u16()?);
402 }
403
404 Ok(Self { format, num_verts, num_tris, stride, vert_data, index_data })
405 }
406}
407
408impl Cluster {
409 fn read(reader: &mut (impl Read + Seek), num_template_verts: u16) -> Result<Self> {
410 let code = reader.read_u8()?;
412 let pos_scale = reader.read_f32()?;
413 let encoding = SpanEncoding { code, pos_scale };
414
415 let num_verts = num_template_verts as usize;
416 let num_insts = reader.read_u32()? as usize;
417 let mut instances = Vec::with_capacity(num_insts);
418
419 let pos_stride = encoding.pos_stride();
420 let col_stride = encoding.col_stride();
421
422 for _ in 0..num_insts {
423 let mut l2w = [[0.0f32; 4]; 3];
425 for row in &mut l2w {
426 for col in row.iter_mut() {
427 *col = reader.read_f32()?;
428 }
429 }
430
431 let pos_delta = if pos_stride > 0 {
433 let size = num_verts * pos_stride;
434 let mut data = vec![0u8; size];
435 reader.read_exact(&mut data)?;
436 Some(data)
437 } else {
438 None
439 };
440
441 let col = if col_stride > 0 {
443 let size = num_verts * col_stride;
444 let mut data = vec![0u8; size];
445 reader.read_exact(&mut data)?;
446 Some(data)
447 } else {
448 None
449 };
450
451 instances.push(SpanInstance { l2w, pos_delta, col });
452 }
453
454 Ok(Self { encoding, instances })
455 }
456}
457
458fn decode_pos_delta(enc: &SpanEncoding, data: &[u8], offset: usize) -> (f32, f32, f32) {
461 match enc.code & POS_MASK {
462 POS_888 => {
463 if offset + 3 <= data.len() {
464 let dx = data[offset] as i8 as f32 * enc.pos_scale;
465 let dy = data[offset + 1] as i8 as f32 * enc.pos_scale;
466 let dz = data[offset + 2] as i8 as f32 * enc.pos_scale;
467 (dx, dy, dz)
468 } else { (0.0, 0.0, 0.0) }
469 }
470 POS_161616 => {
471 if offset + 6 <= data.len() {
472 let dx = i16::from_le_bytes(data[offset..offset+2].try_into().unwrap()) as f32 * enc.pos_scale;
473 let dy = i16::from_le_bytes(data[offset+2..offset+4].try_into().unwrap()) as f32 * enc.pos_scale;
474 let dz = i16::from_le_bytes(data[offset+4..offset+6].try_into().unwrap()) as f32 * enc.pos_scale;
475 (dx, dy, dz)
476 } else { (0.0, 0.0, 0.0) }
477 }
478 POS_101010 => {
479 if offset + 4 <= data.len() {
480 let packed = u32::from_le_bytes(data[offset..offset+4].try_into().unwrap());
481 let dx = (packed & 0x3F) as f32 * enc.pos_scale;
482 let dy = ((packed >> 10) & 0x3F) as f32 * enc.pos_scale;
483 let dz = ((packed >> 20) & 0x3F) as f32 * enc.pos_scale;
484 (dx, dy, dz)
485 } else { (0.0, 0.0, 0.0) }
486 }
487 POS_008 => {
488 if offset < data.len() {
489 let dz = data[offset] as i8 as f32 * enc.pos_scale;
490 (0.0, 0.0, dz)
491 } else { (0.0, 0.0, 0.0) }
492 }
493 _ => (0.0, 0.0, 0.0),
494 }
495}
496
497fn decode_color(enc: &SpanEncoding, data: &[u8], offset: usize, template_color: u32) -> u32 {
500 let code = enc.code as u16;
501 match code & COL_MASK {
502 COL_A8 => {
503 if offset < data.len() {
504 (template_color & 0x00FFFFFF) | ((data[offset] as u32) << 24)
505 } else { template_color }
506 }
507 COL_I8 => {
508 if offset < data.len() {
509 let v = data[offset] as u32;
510 (template_color & 0xFF000000) | (v << 16) | (v << 8) | v
511 } else { template_color }
512 }
513 COL_AI88 => {
514 if offset + 2 <= data.len() {
515 let val = u16::from_le_bytes(data[offset..offset+2].try_into().unwrap());
516 let col = (val & 0xFF) as u32;
517 let alpha = ((val & 0xFF00) >> 8) as u32;
518 (alpha << 24) | (col << 16) | (col << 8) | col
519 } else { template_color }
520 }
521 COL_RGB888 => {
522 if offset + 3 <= data.len() {
523 (template_color & 0xFF000000)
524 | ((data[offset] as u32) << 16)
525 | ((data[offset + 1] as u32) << 8)
526 | (data[offset + 2] as u32)
527 } else { template_color }
528 }
529 COL_ARGB8888 => {
530 if offset + 4 <= data.len() {
531 u32::from_le_bytes(data[offset..offset+4].try_into().unwrap())
532 } else { template_color }
533 }
534 _ => template_color,
535 }
536}
537
538fn transpose_inverse_3x3(m: &[f32; 16]) -> [f32; 9] {
541 let a = m[0]; let b = m[1]; let c = m[2];
543 let d = m[4]; let e = m[5]; let f = m[6];
544 let g = m[8]; let h = m[9]; let i = m[10];
545
546 let det = a*(e*i - f*h) - b*(d*i - f*g) + c*(d*h - e*g);
547 if det.abs() < 1e-10 {
548 return [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0];
549 }
550 let inv_det = 1.0 / det;
551
552 [
558 (e*i - f*h) * inv_det, (f*g - d*i) * inv_det, (d*h - e*g) * inv_det,
559 (c*h - b*i) * inv_det, (a*i - c*g) * inv_det, (b*g - a*h) * inv_det,
560 (b*f - c*e) * inv_det, (c*d - a*f) * inv_det, (a*e - b*d) * inv_det,
561 ]
562}