use crate::{Result, core::Vector2};
use super::{
Moc3CountInfo, Moc3Header, Moc3SectionOffsets,
compose::{
ComposedDeformer, ComposedDeformers, ComposedRotation, ComposedWarp,
apply_composed_parent, parent_opacity_accum, parent_rotation_angle, parent_scale_accum,
},
keyform_bindings::{Moc3KeyformBindings, Moc3KeyformSlot},
parse::{
invalid_moc3, read_bool_section, read_f32_section, read_f32_section_or_default,
read_i32_section, to_usize,
},
};
const DEFORMER_PARENT_DEFORMER_INDICES_SLOT: usize = 16;
const DEFORMER_TYPES_SLOT: usize = 17;
const DEFORMER_SPECIFIC_INDICES_SLOT: usize = 18;
const WARP_KEYFORM_BINDING_BAND_INDICES_SLOT: usize = 19;
const WARP_KEYFORM_BEGIN_INDICES_SLOT: usize = 20;
const WARP_KEYFORM_COUNTS_SLOT: usize = 21;
const WARP_VERTEX_COUNTS_SLOT: usize = 22;
const WARP_ROWS_SLOT: usize = 23;
const WARP_COLS_SLOT: usize = 24;
const ROTATION_KEYFORM_BINDING_BAND_INDICES_SLOT: usize = 25;
const ROTATION_KEYFORM_BEGIN_INDICES_SLOT: usize = 26;
const ROTATION_KEYFORM_COUNTS_SLOT: usize = 27;
const ROTATION_BASE_ANGLES_SLOT: usize = 28;
const WARP_KEYFORM_OPACITIES_SLOT: usize = 59;
const WARP_KEYFORM_POSITION_BEGIN_INDICES_SLOT: usize = 60;
const ROTATION_KEYFORM_OPACITIES_SLOT: usize = 61;
const ROTATION_KEYFORM_ANGLES_SLOT: usize = 62;
const ROTATION_KEYFORM_ORIGIN_XS_SLOT: usize = 63;
const ROTATION_KEYFORM_ORIGIN_YS_SLOT: usize = 64;
const ROTATION_KEYFORM_SCALES_SLOT: usize = 65;
const ROTATION_KEYFORM_REFLECT_XS_SLOT: usize = 66;
const ROTATION_KEYFORM_REFLECT_YS_SLOT: usize = 67;
const KEYFORM_POSITION_XYS_SLOT: usize = 71;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Moc3DeformerKind {
Warp,
Rotation,
}
impl Moc3DeformerKind {
fn from_raw(value: i32) -> Option<Self> {
match value {
0 => Some(Self::Warp),
1 => Some(Self::Rotation),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct InterpolatedRotation {
angle_degrees: f32,
translation: Vector2,
scale: f32,
flip_x: bool,
flip_y: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Moc3Deformers {
parent_deformer_indices: Vec<i32>,
deformer_kinds: Vec<Moc3DeformerKind>,
specific_indices: Vec<i32>,
warp_keyform_binding_band_indices: Vec<i32>,
warp_keyform_begin_indices: Vec<i32>,
warp_keyform_counts: Vec<i32>,
warp_vertex_counts: Vec<i32>,
warp_rows: Vec<i32>,
warp_cols: Vec<i32>,
warp_keyform_opacities: Vec<f32>,
rotation_keyform_binding_band_indices: Vec<i32>,
rotation_keyform_begin_indices: Vec<i32>,
rotation_keyform_counts: Vec<i32>,
rotation_base_angles: Vec<f32>,
warp_keyform_position_begin_indices: Vec<i32>,
rotation_keyform_angles: Vec<f32>,
rotation_keyform_origin_xs: Vec<f32>,
rotation_keyform_origin_ys: Vec<f32>,
rotation_keyform_scales: Vec<f32>,
rotation_keyform_reflect_xs: Vec<bool>,
rotation_keyform_reflect_ys: Vec<bool>,
rotation_keyform_opacities: Vec<f32>,
keyform_position_xys: Vec<f32>,
}
impl Moc3Deformers {
pub fn parse(bytes: &[u8]) -> Result<Self> {
let header = Moc3Header::parse(bytes)?;
let offsets = Moc3SectionOffsets::parse(bytes)?;
let counts = Moc3CountInfo::parse(bytes)?;
let endianness = header.endianness();
let deformer_count = to_usize(counts.deformers(), "deformer count")?;
let warp_count = to_usize(counts.warp_deformers(), "warp deformer count")?;
let rotation_count = to_usize(counts.rotation_deformers(), "rotation deformer count")?;
let warp_keyform_count = to_usize(
counts.warp_deformer_keyforms(),
"warp deformer keyform count",
)?;
let rotation_keyform_count = to_usize(
counts.rotation_deformer_keyforms(),
"rotation deformer keyform count",
)?;
let deformer_types = read_i32_section(
bytes,
&offsets,
DEFORMER_TYPES_SLOT,
deformer_count,
endianness,
)?;
let deformer_kinds = deformer_types
.iter()
.copied()
.map(|value| {
Moc3DeformerKind::from_raw(value)
.ok_or_else(|| invalid_moc3(format!("unsupported deformer type {value}")))
})
.collect::<Result<Vec<_>>>()?;
Ok(Self {
parent_deformer_indices: read_i32_section(
bytes,
&offsets,
DEFORMER_PARENT_DEFORMER_INDICES_SLOT,
deformer_count,
endianness,
)?,
deformer_kinds,
specific_indices: read_i32_section(
bytes,
&offsets,
DEFORMER_SPECIFIC_INDICES_SLOT,
deformer_count,
endianness,
)?,
warp_keyform_binding_band_indices: read_i32_section(
bytes,
&offsets,
WARP_KEYFORM_BINDING_BAND_INDICES_SLOT,
warp_count,
endianness,
)?,
warp_keyform_begin_indices: read_i32_section(
bytes,
&offsets,
WARP_KEYFORM_BEGIN_INDICES_SLOT,
warp_count,
endianness,
)?,
warp_keyform_counts: read_i32_section(
bytes,
&offsets,
WARP_KEYFORM_COUNTS_SLOT,
warp_count,
endianness,
)?,
warp_vertex_counts: read_i32_section(
bytes,
&offsets,
WARP_VERTEX_COUNTS_SLOT,
warp_count,
endianness,
)?,
warp_rows: read_i32_section(bytes, &offsets, WARP_ROWS_SLOT, warp_count, endianness)?,
warp_cols: read_i32_section(bytes, &offsets, WARP_COLS_SLOT, warp_count, endianness)?,
warp_keyform_opacities: read_f32_section_or_default(
bytes,
&offsets,
WARP_KEYFORM_OPACITIES_SLOT,
warp_keyform_count,
endianness,
1.0,
)?,
rotation_keyform_binding_band_indices: read_i32_section(
bytes,
&offsets,
ROTATION_KEYFORM_BINDING_BAND_INDICES_SLOT,
rotation_count,
endianness,
)?,
rotation_keyform_begin_indices: read_i32_section(
bytes,
&offsets,
ROTATION_KEYFORM_BEGIN_INDICES_SLOT,
rotation_count,
endianness,
)?,
rotation_keyform_counts: read_i32_section(
bytes,
&offsets,
ROTATION_KEYFORM_COUNTS_SLOT,
rotation_count,
endianness,
)?,
rotation_base_angles: read_f32_section(
bytes,
&offsets,
ROTATION_BASE_ANGLES_SLOT,
rotation_count,
endianness,
)?,
warp_keyform_position_begin_indices: read_i32_section(
bytes,
&offsets,
WARP_KEYFORM_POSITION_BEGIN_INDICES_SLOT,
warp_keyform_count,
endianness,
)?,
rotation_keyform_angles: read_f32_section(
bytes,
&offsets,
ROTATION_KEYFORM_ANGLES_SLOT,
rotation_keyform_count,
endianness,
)?,
rotation_keyform_origin_xs: read_f32_section(
bytes,
&offsets,
ROTATION_KEYFORM_ORIGIN_XS_SLOT,
rotation_keyform_count,
endianness,
)?,
rotation_keyform_origin_ys: read_f32_section(
bytes,
&offsets,
ROTATION_KEYFORM_ORIGIN_YS_SLOT,
rotation_keyform_count,
endianness,
)?,
rotation_keyform_scales: read_f32_section(
bytes,
&offsets,
ROTATION_KEYFORM_SCALES_SLOT,
rotation_keyform_count,
endianness,
)?,
rotation_keyform_reflect_xs: read_bool_section(
bytes,
&offsets,
ROTATION_KEYFORM_REFLECT_XS_SLOT,
rotation_keyform_count,
endianness,
)?,
rotation_keyform_reflect_ys: read_bool_section(
bytes,
&offsets,
ROTATION_KEYFORM_REFLECT_YS_SLOT,
rotation_keyform_count,
endianness,
)?,
rotation_keyform_opacities: read_f32_section_or_default(
bytes,
&offsets,
ROTATION_KEYFORM_OPACITIES_SLOT,
rotation_keyform_count,
endianness,
1.0,
)?,
keyform_position_xys: read_f32_section(
bytes,
&offsets,
KEYFORM_POSITION_XYS_SLOT,
to_usize(counts.keyform_positions(), "keyform position count")?,
endianness,
)?,
})
}
pub(super) fn compose(
&self,
bindings: &Moc3KeyformBindings,
parameter_values: &[f32],
) -> Option<ComposedDeformers> {
let count = self.deformer_kinds.len();
let mut order: Vec<usize> = (0..count).collect();
order.sort_by_key(|&idx| self.deformer_depth(idx));
let mut composed: Vec<Option<ComposedDeformer>> = vec![None; count];
for idx in order {
let parent = *self.parent_deformer_indices.get(idx)?;
let specific = usize::try_from(*self.specific_indices.get(idx)?).ok()?;
let composed_deformer = match *self.deformer_kinds.get(idx)? {
Moc3DeformerKind::Warp => {
let mut grid =
self.interpolated_warp_grid(specific, bindings, parameter_values)?;
let cols = usize::try_from(*self.warp_cols.get(specific)?).ok()?;
let rows = usize::try_from(*self.warp_rows.get(specific)?).ok()?;
for point in &mut grid {
*point = apply_composed_parent(&composed, parent, *point)?;
}
let scale_accum = parent_scale_accum(&composed, parent);
let opacity =
self.interpolated_warp_opacity(specific, bindings, parameter_values)?;
let opacity_accum = opacity * parent_opacity_accum(&composed, parent);
ComposedDeformer::Warp(ComposedWarp {
grid,
cols,
rows,
scale_accum,
opacity_accum,
})
}
Moc3DeformerKind::Rotation => {
let rotation =
self.interpolated_rotation(specific, bindings, parameter_values)?;
let origin = apply_composed_parent(&composed, parent, rotation.translation)?;
let parent_angle = parent_rotation_angle(
&composed,
parent,
origin,
rotation.translation,
)?;
let scale_accum = parent_scale_accum(&composed, parent);
let opacity =
self.interpolated_rotation_opacity(specific, bindings, parameter_values)?;
let opacity_accum = opacity * parent_opacity_accum(&composed, parent);
ComposedDeformer::Rotation(ComposedRotation {
origin,
angle_degrees: rotation.angle_degrees + parent_angle.to_degrees(),
scale: rotation.scale * scale_accum,
flip_x: rotation.flip_x,
flip_y: rotation.flip_y,
scale_accum: rotation.scale * scale_accum,
opacity_accum,
})
}
};
*composed.get_mut(idx)? = Some(composed_deformer);
}
Some(ComposedDeformers::new(
composed.into_iter().collect::<Option<Vec<_>>>()?,
))
}
fn deformer_depth(&self, index: usize) -> usize {
let mut depth = 0usize;
let mut current = index;
loop {
let parent = self
.parent_deformer_indices
.get(current)
.copied()
.unwrap_or(-1);
if parent < 0 {
break;
}
current = match usize::try_from(parent) {
Ok(value) => value,
Err(_) => break,
};
depth += 1;
if depth > self.parent_deformer_indices.len() {
break;
}
}
depth
}
fn warp_keyform_slots(
&self,
warp_index: usize,
bindings: &Moc3KeyformBindings,
parameter_values: &[f32],
) -> Option<Vec<Moc3KeyformSlot>> {
let keyform_count = usize::try_from(*self.warp_keyform_counts.get(warp_index)?).ok()?;
bindings.keyform_slots(
*self.warp_keyform_binding_band_indices.get(warp_index)?,
keyform_count,
parameter_values,
)
}
fn rotation_keyform_slots(
&self,
rotation_index: usize,
bindings: &Moc3KeyformBindings,
parameter_values: &[f32],
) -> Option<Vec<Moc3KeyformSlot>> {
let keyform_count =
usize::try_from(*self.rotation_keyform_counts.get(rotation_index)?).ok()?;
bindings.keyform_slots(
*self
.rotation_keyform_binding_band_indices
.get(rotation_index)?,
keyform_count,
parameter_values,
)
}
fn interpolated_warp_grid(
&self,
warp_index: usize,
bindings: &Moc3KeyformBindings,
parameter_values: &[f32],
) -> Option<Vec<Vector2>> {
let slots = self.warp_keyform_slots(warp_index, bindings, parameter_values)?;
let begin = usize::try_from(*self.warp_keyform_begin_indices.get(warp_index)?).ok()?;
let vertex_count = usize::try_from(*self.warp_vertex_counts.get(warp_index)?).ok()?;
let mut grid = vec![Vector2::default(); vertex_count];
for slot in slots {
let keyform_index = begin.checked_add(slot.local_index)?;
let source = self.warp_grid(warp_index, keyform_index)?;
if source.len() != grid.len() {
return None;
}
for (target, source) in grid.iter_mut().zip(source) {
*target = Vector2::new(
target.x() + source.x() * slot.weight,
target.y() + source.y() * slot.weight,
);
}
}
Some(grid)
}
fn interpolated_rotation(
&self,
rotation_index: usize,
bindings: &Moc3KeyformBindings,
parameter_values: &[f32],
) -> Option<InterpolatedRotation> {
let slots = self.rotation_keyform_slots(rotation_index, bindings, parameter_values)?;
let begin =
usize::try_from(*self.rotation_keyform_begin_indices.get(rotation_index)?).ok()?;
let mut angle = 0.0f32;
let mut translation = Vector2::default();
let mut scale = 0.0f32;
let mut flip_x = 0.0f32;
let mut flip_y = 0.0f32;
for slot in slots {
let keyform_index = begin.checked_add(slot.local_index)?;
angle += *self.rotation_keyform_angles.get(keyform_index)? * slot.weight;
translation = Vector2::new(
translation.x()
+ *self.rotation_keyform_origin_xs.get(keyform_index)? * slot.weight,
translation.y()
+ *self.rotation_keyform_origin_ys.get(keyform_index)? * slot.weight,
);
scale += *self.rotation_keyform_scales.get(keyform_index)? * slot.weight;
flip_x += u8::from(*self.rotation_keyform_reflect_xs.get(keyform_index)?) as f32
* slot.weight;
flip_y += u8::from(*self.rotation_keyform_reflect_ys.get(keyform_index)?) as f32
* slot.weight;
}
Some(InterpolatedRotation {
angle_degrees: *self.rotation_base_angles.get(rotation_index)? + angle,
translation,
scale,
flip_x: interpolate_bool(flip_x),
flip_y: interpolate_bool(flip_y),
})
}
fn interpolated_warp_opacity(
&self,
warp_index: usize,
bindings: &Moc3KeyformBindings,
parameter_values: &[f32],
) -> Option<f32> {
let slots = self.warp_keyform_slots(warp_index, bindings, parameter_values)?;
let begin = usize::try_from(*self.warp_keyform_begin_indices.get(warp_index)?).ok()?;
let mut opacity = 0.0f32;
for slot in slots {
let keyform_index = begin.checked_add(slot.local_index)?;
opacity += *self.warp_keyform_opacities.get(keyform_index)? * slot.weight;
}
Some(opacity)
}
fn interpolated_rotation_opacity(
&self,
rotation_index: usize,
bindings: &Moc3KeyformBindings,
parameter_values: &[f32],
) -> Option<f32> {
let slots = self.rotation_keyform_slots(rotation_index, bindings, parameter_values)?;
let begin =
usize::try_from(*self.rotation_keyform_begin_indices.get(rotation_index)?).ok()?;
let mut opacity = 0.0f32;
for slot in slots {
let keyform_index = begin.checked_add(slot.local_index)?;
opacity += *self.rotation_keyform_opacities.get(keyform_index)? * slot.weight;
}
Some(opacity)
}
fn warp_grid(&self, warp_index: usize, keyform_index: usize) -> Option<Vec<Vector2>> {
let start = usize::try_from(
*self
.warp_keyform_position_begin_indices
.get(keyform_index)?,
)
.ok()?;
let vertex_count = usize::try_from(*self.warp_vertex_counts.get(warp_index)?).ok()?;
let len = vertex_count.checked_mul(2)?;
let values = self
.keyform_position_xys
.get(start..start.checked_add(len)?)?;
Some(
values
.chunks_exact(2)
.map(|xy| Vector2::new(xy[0], xy[1]))
.collect(),
)
}
}
fn interpolate_bool(value: f32) -> bool {
(value + 0.001).trunc() != 0.0
}