plasma-prp 0.1.0

Read, write, inspect, and manipulate Plasma engine PRP files used by Myst Online: Uru Live
Documentation
//! Interpolation key types — hsScalarKey, hsPoint3Key, hsQuatKey, etc.
//!
//! Each key stores a frame number (u16) and a value. Keys are stored in
//! contiguous arrays within leaf controllers.
//!
//! C++ ref: plInterp/hsKeys.h/.cpp

use std::io::Read;

use anyhow::{Result, bail};

use crate::resource::prp::PlasmaRead;

/// Key type enum values.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum KeyType {
    Unknown = 0,
    Point3 = 1,
    BezPoint3 = 2,
    Scalar = 3,
    BezScalar = 4,
    Scale = 5,
    BezScale = 6,
    Quat = 7,
    CompressedQuat32 = 8,
    CompressedQuat64 = 9,
    MaxKey = 10,
    Matrix33 = 11,
    Matrix44 = 12,
}

impl KeyType {
    pub fn from_u8(v: u8) -> Result<Self> {
        match v {
            0 => Ok(Self::Unknown),
            1 => Ok(Self::Point3),
            2 => Ok(Self::BezPoint3),
            3 => Ok(Self::Scalar),
            4 => Ok(Self::BezScalar),
            5 => Ok(Self::Scale),
            6 => Ok(Self::BezScale),
            7 => Ok(Self::Quat),
            8 => Ok(Self::CompressedQuat32),
            9 => Ok(Self::CompressedQuat64),
            10 => Ok(Self::MaxKey),
            11 => Ok(Self::Matrix33),
            12 => Ok(Self::Matrix44),
            _ => bail!("Unknown key type: {}", v),
        }
    }
}

/// A single animation keyframe with its value.
#[derive(Debug, Clone)]
pub enum KeyFrame {
    Scalar {
        frame: u16,
        value: f32,
    },
    BezScalar {
        frame: u16,
        in_tan: f32,
        out_tan: f32,
        value: f32,
    },
    Point3 {
        frame: u16,
        value: [f32; 3],
    },
    BezPoint3 {
        frame: u16,
        in_tan: [f32; 3],
        out_tan: [f32; 3],
        value: [f32; 3],
    },
    Quat {
        frame: u16,
        value: [f32; 4], // x, y, z, w
    },
    CompressedQuat32 {
        frame: u16,
        data: u32,
    },
    CompressedQuat64 {
        frame: u16,
        data: [u32; 2],
    },
    Scale {
        frame: u16,
        scale: [f32; 3],
        quat: [f32; 4],
    },
    BezScale {
        frame: u16,
        in_tan: [f32; 3],
        out_tan: [f32; 3],
        scale: [f32; 3],
        quat: [f32; 4],
    },
    Matrix44 {
        frame: u16,
        value: [f32; 16],
    },
}

impl KeyFrame {
    pub fn frame(&self) -> u16 {
        match self {
            KeyFrame::Scalar { frame, .. }
            | KeyFrame::BezScalar { frame, .. }
            | KeyFrame::Point3 { frame, .. }
            | KeyFrame::BezPoint3 { frame, .. }
            | KeyFrame::Quat { frame, .. }
            | KeyFrame::CompressedQuat32 { frame, .. }
            | KeyFrame::CompressedQuat64 { frame, .. }
            | KeyFrame::Scale { frame, .. }
            | KeyFrame::BezScale { frame, .. }
            | KeyFrame::Matrix44 { frame, .. } => *frame,
        }
    }
}

/// Read an array of keys of the given type.
pub fn read_keys(reader: &mut impl Read, key_type: KeyType, count: u32) -> Result<Vec<KeyFrame>> {
    let mut keys = Vec::with_capacity(count as usize);

    for _ in 0..count {
        let frame = reader.read_u16()?;
        let key = match key_type {
            KeyType::Scalar => KeyFrame::Scalar {
                frame,
                value: reader.read_f32()?,
            },
            KeyType::BezScalar => KeyFrame::BezScalar {
                frame,
                in_tan: reader.read_f32()?,
                out_tan: reader.read_f32()?,
                value: reader.read_f32()?,
            },
            KeyType::Point3 => KeyFrame::Point3 {
                frame,
                value: [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ],
            },
            KeyType::BezPoint3 => KeyFrame::BezPoint3 {
                frame,
                in_tan: [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ],
                out_tan: [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ],
                value: [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ],
            },
            KeyType::Quat => KeyFrame::Quat {
                frame,
                value: [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ],
            },
            KeyType::CompressedQuat32 => KeyFrame::CompressedQuat32 {
                frame,
                data: reader.read_u32()?,
            },
            KeyType::CompressedQuat64 => KeyFrame::CompressedQuat64 {
                frame,
                data: [reader.read_u32()?, reader.read_u32()?],
            },
            KeyType::Scale => {
                let scale = [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ];
                let quat = [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ];
                KeyFrame::Scale { frame, scale, quat }
            }
            KeyType::BezScale => {
                let in_tan = [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ];
                let out_tan = [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ];
                let scale = [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ];
                let quat = [
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                    reader.read_f32()?,
                ];
                KeyFrame::BezScale {
                    frame,
                    in_tan,
                    out_tan,
                    scale,
                    quat,
                }
            }
            KeyType::Matrix44 => {
                // hsMatrix44::Read — flag byte, then 16 floats if non-identity
                let flag = reader.read_u8()?;
                let value = if flag == 0 {
                    [
                        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,
                    ]
                } else {
                    let mut m = [0f32; 16];
                    for val in &mut m {
                        *val = reader.read_f32()?;
                    }
                    m
                };
                KeyFrame::Matrix44 { frame, value }
            }
            KeyType::MaxKey => {
                // hsAffineParts: translation (3 floats) + quat (4 floats) +
                // scale quat (4 floats) + scale (3 floats) + det sign (float)
                // = 15 floats total
                for _ in 0..15 {
                    reader.read_f32()?;
                }
                KeyFrame::Scale {
                    frame,
                    scale: [1.0, 1.0, 1.0],
                    quat: [0.0, 0.0, 0.0, 1.0],
                }
            }
            KeyType::Unknown => {
                bail!("Cannot read unknown key type");
            }
            _ => {
                bail!("Unsupported key type: {:?}", key_type);
            }
        };
        keys.push(key);
    }

    Ok(keys)
}

/// Decompress a 32-bit compressed quaternion.
pub fn decompress_quat32(data: u32) -> [f32; 4] {
    let comp = (data >> 30) as usize;
    let scale = 0.707107f32 * 2.0 / 1023.0;

    let a = ((data >> 20) & 0x3FF) as f32 * scale - 0.707107;
    let b = ((data >> 10) & 0x3FF) as f32 * scale - 0.707107;
    let c = (data & 0x3FF) as f32 * scale - 0.707107;
    let d = (1.0 - a * a - b * b - c * c).max(0.0).sqrt();

    match comp {
        0 => [d, a, b, c],
        1 => [a, d, b, c],
        2 => [a, b, d, c],
        _ => [a, b, c, d],
    }
}

/// Decompress a 64-bit compressed quaternion.
pub fn decompress_quat64(data: [u32; 2]) -> [f32; 4] {
    let comp = (data[0] >> 30) as usize;
    let scale20 = 0.707107f32 * 2.0 / 1048575.0;
    let scale21 = 0.707107f32 * 2.0 / 2097151.0;

    let a = ((data[0] >> 10) & 0xFFFFF) as f32 * scale20 - 0.707107;
    let b_hi = (data[0] & 0x3FF) as u32;
    let b_lo = (data[1] >> 21) as u32;
    let b = ((b_hi << 11) | b_lo) as f32 * scale21 - 0.707107;
    let c = (data[1] & 0x1FFFFF) as f32 * scale21 - 0.707107;
    let d = (1.0 - a * a - b * b - c * c).max(0.0).sqrt();

    match comp {
        0 => [d, a, b, c],
        1 => [a, d, b, c],
        2 => [a, b, d, c],
        _ => [a, b, c, d],
    }
}