Skip to main content

plasma_prp/material/
layer_animation.rs

1//! plLayerAnimation — time-based UV scroll, opacity pulse, color cycling.
2//!
3//! C++ ref: plSurface/plLayerAnimation.h/.cpp
4
5use crate::core::uoid::Uoid;
6
7/// Layer animation flags.
8#[allow(dead_code)]
9pub mod layer_anim_flags {
10    pub const UV_SCROLL: u32 = 0x01;
11    pub const OPACITY_PULSE: u32 = 0x02;
12    pub const COLOR_CYCLE: u32 = 0x04;
13    pub const TRANSFORM: u32 = 0x08;
14}
15
16/// Parsed plLayerAnimation data.
17#[derive(Debug, Clone)]
18pub struct LayerAnimationData {
19    pub self_key: Option<Uoid>,
20    pub anim_name: String,
21    pub flags: u32,
22    /// UV scroll speed (texels per second).
23    pub uv_scroll_speed: [f32; 2],
24    /// Opacity range for pulsing.
25    pub opacity_min: f32,
26    pub opacity_max: f32,
27    /// Time convert for driving the animation clock.
28    pub atc_key: Option<Uoid>,
29}
30
31impl Default for LayerAnimationData {
32    fn default() -> Self {
33        Self {
34            self_key: None,
35            anim_name: String::new(),
36            flags: 0,
37            uv_scroll_speed: [0.0, 0.0],
38            opacity_min: 0.0,
39            opacity_max: 1.0,
40            atc_key: None,
41        }
42    }
43}
44
45/// Parsed plLayerSDLAnimation data — driven by SDL state variable.
46#[derive(Debug, Clone)]
47pub struct LayerSDLAnimationData {
48    pub base: LayerAnimationData,
49    pub sdl_var_name: String,
50}
51
52/// Evaluate a color at a given time by linear interpolation of keyframes.
53/// C++ ref: plLeafController::Interp(time, hsColorRGBA*) delegates to Point3 interp
54/// (plController.cpp:221-228). Color keys store [r, g, b] as Point3 (x, y, z).
55/// Returns None if no keyframes exist.
56#[allow(dead_code)]
57pub fn evaluate_color_at_time(keys: &[(f32, [f32; 3])], time: f32) -> Option<[f32; 3]> {
58    if keys.is_empty() { return None; }
59    if keys.len() == 1 { return Some(keys[0].1); }
60
61    if time <= keys[0].0 {
62        return Some(keys[0].1);
63    }
64    if time >= keys.last().unwrap().0 {
65        return Some(keys.last().unwrap().1);
66    }
67    for w in keys.windows(2) {
68        if time >= w[0].0 && time <= w[1].0 {
69            let frac = (time - w[0].0) / (w[1].0 - w[0].0).max(0.0001);
70            return Some([
71                w[0].1[0] + (w[1].1[0] - w[0].1[0]) * frac,
72                w[0].1[1] + (w[1].1[1] - w[0].1[1]) * frac,
73                w[0].1[2] + (w[1].1[2] - w[0].1[2]) * frac,
74            ]);
75        }
76    }
77    Some(keys.last().unwrap().1)
78}
79
80/// Evaluate opacity at a given time by linear interpolation of keyframes.
81/// C++ ref: plLayerAnimation.cpp:185 — opacity *= 1.e-2f (keyframe values are 0-100 range).
82#[allow(dead_code)]
83pub fn evaluate_opacity_at_time(keys: &[(f32, f32)], time: f32) -> f32 {
84    if keys.is_empty() { return 1.0; }
85    if keys.len() == 1 { return keys[0].1 * 0.01; }
86
87    let raw = if time <= keys[0].0 {
88        keys[0].1
89    } else if time >= keys.last().unwrap().0 {
90        keys.last().unwrap().1
91    } else {
92        let mut val = keys.last().unwrap().1;
93        for w in keys.windows(2) {
94            if time >= w[0].0 && time <= w[1].0 {
95                let frac = (time - w[0].0) / (w[1].0 - w[0].0).max(0.0001);
96                val = w[0].1 + (w[1].1 - w[0].1) * frac;
97                break;
98            }
99        }
100        val
101    };
102    (raw * 0.01).clamp(0.0, 1.0)
103}