1pub mod ik;
27pub mod sprite_anim;
28
29use std::collections::HashMap;
30use crate::math::MathFunction;
31
32#[derive(Debug, Clone)]
36pub enum AnimationCurve {
37 Constant(f32),
39 Keyframes(Vec<(f32, f32)>),
41 MathDriven(MathFunction),
43 BezierKeyframes(Vec<BezierKey>),
45}
46
47#[derive(Debug, Clone)]
48pub struct BezierKey {
49 pub time: f32,
50 pub value: f32,
51 pub in_tangent: f32,
52 pub out_tangent: f32,
53}
54
55impl AnimationCurve {
56 pub fn evaluate(&self, t: f32) -> f32 {
58 let t = t.clamp(0.0, 1.0);
59 match self {
60 AnimationCurve::Constant(v) => *v,
61 AnimationCurve::MathDriven(f) => f.evaluate(t, t),
62 AnimationCurve::Keyframes(keys) => {
63 if keys.is_empty() { return 0.0; }
64 if keys.len() == 1 { return keys[0].1; }
65 let idx = keys.partition_point(|(kt, _)| *kt <= t);
67 if idx == 0 { return keys[0].1; }
68 if idx >= keys.len() { return keys[keys.len()-1].1; }
69 let (t0, v0) = keys[idx-1];
70 let (t1, v1) = keys[idx];
71 let span = (t1 - t0).max(1e-7);
72 let alpha = (t - t0) / span;
73 v0 + (v1 - v0) * alpha
74 }
75 AnimationCurve::BezierKeyframes(keys) => {
76 if keys.is_empty() { return 0.0; }
77 if keys.len() == 1 { return keys[0].value; }
78 let idx = keys.partition_point(|k| k.time <= t);
79 if idx == 0 { return keys[0].value; }
80 if idx >= keys.len() { return keys[keys.len()-1].value; }
81 let k0 = &keys[idx-1];
82 let k1 = &keys[idx];
83 let span = (k1.time - k0.time).max(1e-7);
84 let u = (t - k0.time) / span;
85 let h00 = 2.0*u*u*u - 3.0*u*u + 1.0;
87 let h10 = u*u*u - 2.0*u*u + u;
88 let h01 = -2.0*u*u*u + 3.0*u*u;
89 let h11 = u*u*u - u*u;
90 h00*k0.value + h10*span*k0.out_tangent + h01*k1.value + h11*span*k1.in_tangent
91 }
92 }
93 }
94
95 pub fn constant(v: f32) -> Self { Self::Constant(v) }
97
98 pub fn linear(a: f32, b: f32) -> Self {
100 Self::Keyframes(vec![(0.0, a), (1.0, b)])
101 }
102
103 pub fn from_keys(mut keys: Vec<(f32, f32)>) -> Self {
105 keys.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
106 Self::Keyframes(keys)
107 }
108}
109
110#[derive(Debug, Clone)]
114pub struct AnimationClip {
115 pub name: String,
116 pub duration: f32,
117 pub looping: bool,
118 pub channels: HashMap<String, AnimationCurve>,
120 pub events: Vec<AnimationEvent>,
122}
123
124#[derive(Debug, Clone)]
125pub struct AnimationEvent {
126 pub time: f32,
128 pub tag: String,
130 pub payload: f32,
131}
132
133impl AnimationClip {
134 pub fn new(name: impl Into<String>, duration: f32, looping: bool) -> Self {
135 Self {
136 name: name.into(),
137 duration,
138 looping,
139 channels: HashMap::new(),
140 events: Vec::new(),
141 }
142 }
143
144 pub fn with_channel(mut self, path: impl Into<String>, curve: AnimationCurve) -> Self {
146 self.channels.insert(path.into(), curve);
147 self
148 }
149
150 pub fn with_event(mut self, t: f32, tag: impl Into<String>, payload: f32) -> Self {
152 self.events.push(AnimationEvent { time: t, tag: tag.into(), payload });
153 self
154 }
155
156 pub fn sample(&self, t: f32) -> Vec<(String, f32)> {
158 self.channels.iter()
159 .map(|(path, curve)| (path.clone(), curve.evaluate(t)))
160 .collect()
161 }
162}
163
164#[derive(Debug, Clone)]
168pub enum BlendTreeKind {
169 Linear1D { param: String, thresholds: Vec<f32> },
171 Cartesian2D { param_x: String, param_y: String, positions: Vec<(f32, f32)> },
173 Additive { base_index: usize },
175 Override,
177}
178
179#[derive(Debug, Clone)]
180pub struct BlendTree {
181 pub kind: BlendTreeKind,
182 pub clips: Vec<AnimationClip>,
183 pub weights: Vec<f32>,
184}
185
186impl BlendTree {
187 pub fn compute_weights(&mut self, params: &HashMap<String, ParamValue>) {
189 let n = self.clips.len();
190 if n == 0 { return; }
191 self.weights.resize(n, 0.0);
192
193 match &self.kind {
194 BlendTreeKind::Linear1D { param, thresholds } => {
195 let v = params.get(param).and_then(|p| p.as_float()).unwrap_or(0.0);
196 let thresholds = thresholds.clone();
197 if thresholds.len() != n { return; }
198 let idx = thresholds.partition_point(|&t| t <= v);
200 for w in &mut self.weights { *w = 0.0; }
201 if idx == 0 {
202 self.weights[0] = 1.0;
203 } else if idx >= n {
204 self.weights[n-1] = 1.0;
205 } else {
206 let t0 = thresholds[idx-1];
207 let t1 = thresholds[idx];
208 let span = (t1 - t0).max(1e-7);
209 let alpha = (v - t0) / span;
210 self.weights[idx-1] = 1.0 - alpha;
211 self.weights[idx] = alpha;
212 }
213 }
214 BlendTreeKind::Cartesian2D { param_x, param_y, positions } => {
215 let px = params.get(param_x).and_then(|p| p.as_float()).unwrap_or(0.0);
216 let py = params.get(param_y).and_then(|p| p.as_float()).unwrap_or(0.0);
217 let positions = positions.clone();
218 let dists: Vec<f32> = positions.iter()
220 .map(|(x, y)| ((px - x).powi(2) + (py - y).powi(2)).sqrt().max(1e-6))
221 .collect();
222 let sum: f32 = dists.iter().map(|d| 1.0 / d).sum();
223 for (i, d) in dists.iter().enumerate() {
224 self.weights[i] = (1.0 / d) / sum.max(1e-7);
225 }
226 }
227 BlendTreeKind::Additive { .. } | BlendTreeKind::Override => {
228 }
230 }
231 }
232
233 pub fn sample(&self, t: f32) -> Vec<(String, f32)> {
235 if self.clips.is_empty() { return Vec::new(); }
236 let mut accum: HashMap<String, f32> = HashMap::new();
237 let mut total_weight = 0.0_f32;
238
239 for (clip, &w) in self.clips.iter().zip(self.weights.iter()) {
240 if w < 1e-6 { continue; }
241 total_weight += w;
242 for (path, val) in clip.sample(t) {
243 *accum.entry(path).or_insert(0.0) += val * w;
244 }
245 }
246
247 if total_weight > 1e-6 {
248 for v in accum.values_mut() { *v /= total_weight; }
249 }
250 accum.into_iter().collect()
251 }
252}
253
254#[derive(Debug, Clone)]
258pub enum Condition {
259 FloatGt { param: String, threshold: f32 },
260 FloatLt { param: String, threshold: f32 },
261 FloatGe { param: String, threshold: f32 },
262 FloatLe { param: String, threshold: f32 },
263 FloatEq { param: String, value: f32, tolerance: f32 },
264 BoolTrue { param: String },
265 BoolFalse { param: String },
266 Trigger { param: String },
268 Always,
270 All(Vec<Condition>),
272 Any(Vec<Condition>),
274}
275
276impl Condition {
277 pub fn float_gt(param: impl Into<String>, v: f32) -> Self {
278 Self::FloatGt { param: param.into(), threshold: v }
279 }
280 pub fn float_lt(param: impl Into<String>, v: f32) -> Self {
281 Self::FloatLt { param: param.into(), threshold: v }
282 }
283 pub fn float_ge(param: impl Into<String>, v: f32) -> Self {
284 Self::FloatGe { param: param.into(), threshold: v }
285 }
286 pub fn float_le(param: impl Into<String>, v: f32) -> Self {
287 Self::FloatLe { param: param.into(), threshold: v }
288 }
289 pub fn bool_true(param: impl Into<String>) -> Self {
290 Self::BoolTrue { param: param.into() }
291 }
292 pub fn trigger(param: impl Into<String>) -> Self {
293 Self::Trigger { param: param.into() }
294 }
295
296 pub fn evaluate(&self, params: &HashMap<String, ParamValue>) -> (bool, Vec<String>) {
298 match self {
299 Self::FloatGt { param, threshold } =>
300 (params.get(param).and_then(|p| p.as_float()).unwrap_or(0.0) > *threshold, vec![]),
301 Self::FloatLt { param, threshold } =>
302 (params.get(param).and_then(|p| p.as_float()).unwrap_or(0.0) < *threshold, vec![]),
303 Self::FloatGe { param, threshold } =>
304 (params.get(param).and_then(|p| p.as_float()).unwrap_or(0.0) >= *threshold, vec![]),
305 Self::FloatLe { param, threshold } =>
306 (params.get(param).and_then(|p| p.as_float()).unwrap_or(0.0) <= *threshold, vec![]),
307 Self::FloatEq { param, value, tolerance } => {
308 let v = params.get(param).and_then(|p| p.as_float()).unwrap_or(0.0);
309 ((v - value).abs() <= *tolerance, vec![])
310 }
311 Self::BoolTrue { param } =>
312 (params.get(param).and_then(|p| p.as_bool()).unwrap_or(false), vec![]),
313 Self::BoolFalse { param } =>
314 (!params.get(param).and_then(|p| p.as_bool()).unwrap_or(false), vec![]),
315 Self::Trigger { param } => {
316 let v = params.get(param).and_then(|p| p.as_bool()).unwrap_or(false);
317 if v { (true, vec![param.clone()]) } else { (false, vec![]) }
318 }
319 Self::Always => (true, vec![]),
320 Self::All(conds) => {
321 let mut consumed = Vec::new();
322 for c in conds {
323 let (ok, mut trig) = c.evaluate(params);
324 if !ok { return (false, vec![]); }
325 consumed.append(&mut trig);
326 }
327 (true, consumed)
328 }
329 Self::Any(conds) => {
330 for c in conds {
331 let (ok, trig) = c.evaluate(params);
332 if ok { return (true, trig); }
333 }
334 (false, vec![])
335 }
336 }
337 }
338}
339
340#[derive(Debug, Clone)]
343pub enum ParamValue {
344 Float(f32),
345 Bool(bool),
346 Int(i32),
347}
348
349impl ParamValue {
350 pub fn as_float(&self) -> Option<f32> {
351 match self {
352 Self::Float(v) => Some(*v),
353 Self::Int(v) => Some(*v as f32),
354 _ => None,
355 }
356 }
357 pub fn as_bool(&self) -> Option<bool> {
358 if let Self::Bool(v) = self { Some(*v) } else { None }
359 }
360 pub fn as_int(&self) -> Option<i32> {
361 if let Self::Int(v) = self { Some(*v) } else { None }
362 }
363}
364
365#[derive(Debug, Clone)]
368pub struct Transition {
369 pub from: String,
370 pub to: String,
371 pub condition: Condition,
372 pub duration: f32,
374 pub can_interrupt: bool,
376 pub exit_time: Option<f32>,
378 pub normalized_exit: Option<f32>,
380}
381
382impl Transition {
383 pub fn new(from: impl Into<String>, to: impl Into<String>, cond: Condition) -> Self {
384 Self {
385 from: from.into(),
386 to: to.into(),
387 condition: cond,
388 duration: 0.15,
389 can_interrupt: false,
390 exit_time: None,
391 normalized_exit: None,
392 }
393 }
394
395 pub fn with_duration(mut self, d: f32) -> Self { self.duration = d; self }
396 pub fn interruptible(mut self) -> Self { self.can_interrupt = true; self }
397 pub fn exit_at(mut self, t: f32) -> Self { self.exit_time = Some(t); self }
398 pub fn exit_normalized(mut self, t: f32) -> Self { self.normalized_exit = Some(t); self }
399}
400
401#[derive(Debug, Clone)]
404pub enum StateContent {
405 Clip(AnimationClip),
406 Tree(BlendTree),
407}
408
409#[derive(Debug, Clone)]
410pub struct AnimationState {
411 pub name: String,
412 pub content: StateContent,
413 pub speed: f32,
414 pub mirror: bool,
415 pub cyclic_offset: f32,
416 pub speed_curve: Option<MathFunction>,
418}
419
420impl AnimationState {
421 pub fn clip(clip: AnimationClip) -> Self {
422 let name = clip.name.clone();
423 Self {
424 name,
425 content: StateContent::Clip(clip),
426 speed: 1.0,
427 mirror: false,
428 cyclic_offset: 0.0,
429 speed_curve: None,
430 }
431 }
432
433 pub fn tree(name: impl Into<String>, tree: BlendTree) -> Self {
434 Self {
435 name: name.into(),
436 content: StateContent::Tree(tree),
437 speed: 1.0,
438 mirror: false,
439 cyclic_offset: 0.0,
440 speed_curve: None,
441 }
442 }
443
444 pub fn with_speed(mut self, s: f32) -> Self { self.speed = s; self }
445 pub fn mirrored(mut self) -> Self { self.mirror = true; self }
446
447 pub fn duration(&self) -> f32 {
448 match &self.content {
449 StateContent::Clip(c) => c.duration,
450 StateContent::Tree(t) => t.clips.iter().map(|c| c.duration).fold(0.0, f32::max),
451 }
452 }
453}
454
455#[derive(Debug, Clone)]
459pub struct AnimationLayer {
460 pub name: String,
461 pub weight: f32,
462 pub mask: Vec<String>,
464 pub additive: bool,
465 pub current_state: Option<String>,
467 pub current_time: f32,
468 pub transition: Option<ActiveTransition>,
469}
470
471#[derive(Debug, Clone)]
472pub struct ActiveTransition {
473 pub target_state: String,
474 pub progress: f32, pub duration: f32,
476 pub prev_time: f32,
477 pub prev_state: String,
478}
479
480impl AnimationLayer {
481 pub fn new(name: impl Into<String>) -> Self {
482 Self {
483 name: name.into(),
484 weight: 1.0,
485 mask: Vec::new(),
486 additive: false,
487 current_state: None,
488 current_time: 0.0,
489 transition: None,
490 }
491 }
492
493 pub fn with_weight(mut self, w: f32) -> Self { self.weight = w; self }
494 pub fn with_mask(mut self, mask: Vec<String>) -> Self { self.mask = mask; self }
495 pub fn as_additive(mut self) -> Self { self.additive = true; self }
496}
497
498pub struct AnimatorController {
502 pub states: HashMap<String, AnimationState>,
503 pub transitions: Vec<Transition>,
504 pub params: HashMap<String, ParamValue>,
505 pub layers: Vec<AnimationLayer>,
506 events: Vec<FiredEvent>,
508 pub clips: HashMap<String, AnimationClip>,
510}
511
512#[derive(Debug, Clone)]
513pub struct FiredEvent {
514 pub layer: String,
515 pub state: String,
516 pub tag: String,
517 pub payload: f32,
518}
519
520#[derive(Debug, Default, Clone)]
522pub struct AnimationOutput {
523 pub channels: HashMap<String, f32>,
524}
525
526impl AnimatorController {
527 pub fn new() -> Self {
528 let mut layers = vec![AnimationLayer::new("Base Layer")];
529 layers[0].weight = 1.0;
530 Self {
531 states: HashMap::new(),
532 transitions: Vec::new(),
533 params: HashMap::new(),
534 layers,
535 events: Vec::new(),
536 clips: HashMap::new(),
537 }
538 }
539
540 pub fn add_state(&mut self, state: AnimationState) {
543 self.states.insert(state.name.clone(), state);
544 }
545
546 pub fn add_clip(&mut self, clip: AnimationClip) {
547 self.clips.insert(clip.name.clone(), clip);
548 }
549
550 pub fn add_transition(&mut self, t: Transition) {
551 self.transitions.push(t);
552 }
553
554 pub fn add_layer(&mut self, layer: AnimationLayer) {
555 self.layers.push(layer);
556 }
557
558 pub fn set_float(&mut self, name: &str, v: f32) {
561 self.params.insert(name.to_owned(), ParamValue::Float(v));
562 }
563
564 pub fn set_bool(&mut self, name: &str, v: bool) {
565 self.params.insert(name.to_owned(), ParamValue::Bool(v));
566 }
567
568 pub fn set_int(&mut self, name: &str, v: i32) {
569 self.params.insert(name.to_owned(), ParamValue::Int(v));
570 }
571
572 pub fn set_trigger(&mut self, name: &str) {
574 self.params.insert(name.to_owned(), ParamValue::Bool(true));
575 }
576
577 pub fn get_float(&self, name: &str) -> f32 {
578 self.params.get(name).and_then(|p| p.as_float()).unwrap_or(0.0)
579 }
580
581 pub fn get_bool(&self, name: &str) -> bool {
582 self.params.get(name).and_then(|p| p.as_bool()).unwrap_or(false)
583 }
584
585 pub fn start(&mut self, state_name: &str) {
588 for layer in &mut self.layers {
589 layer.current_state = Some(state_name.to_owned());
590 layer.current_time = 0.0;
591 layer.transition = None;
592 }
593 }
594
595 pub fn start_layer(&mut self, layer_name: &str, state_name: &str) {
596 if let Some(layer) = self.layers.iter_mut().find(|l| l.name == layer_name) {
597 layer.current_state = Some(state_name.to_owned());
598 layer.current_time = 0.0;
599 layer.transition = None;
600 }
601 }
602
603 pub fn tick(&mut self, dt: f32) -> AnimationOutput {
608 let mut output = AnimationOutput::default();
609
610 let mut consumed_triggers: Vec<String> = Vec::new();
612
613 for layer in &mut self.layers {
614 if layer.current_state.is_none() { continue; }
615 let cur_name = layer.current_state.clone().unwrap();
616
617 if let Some(ref mut tr) = layer.transition {
619 tr.progress += dt / tr.duration.max(1e-4);
620 tr.prev_time += dt;
621 if tr.progress >= 1.0 {
622 let new_state = tr.target_state.clone();
624 layer.current_time = 0.0;
625 layer.current_state = Some(new_state);
626 layer.transition = None;
627 }
628 } else {
629 let applicable: Vec<Transition> = self.transitions.iter()
631 .filter(|t| t.from == cur_name || t.from == "*")
632 .cloned()
633 .collect();
634
635 let state_dur = self.states.get(&cur_name).map(|s| s.duration()).unwrap_or(1.0);
636
637 for trans in applicable {
638 if let Some(min_exit) = trans.exit_time {
640 if layer.current_time < min_exit { continue; }
641 }
642 if let Some(norm_exit) = trans.normalized_exit {
643 let norm = if state_dur > 1e-6 { layer.current_time / state_dur } else { 1.0 };
644 if norm < norm_exit { continue; }
645 }
646
647 let (ok, mut trig) = trans.condition.evaluate(&self.params);
648 if ok {
649 consumed_triggers.append(&mut trig);
650 let prev = cur_name.clone();
651 layer.transition = Some(ActiveTransition {
652 target_state: trans.to.clone(),
653 progress: 0.0,
654 duration: trans.duration,
655 prev_time: layer.current_time,
656 prev_state: prev,
657 });
658 break;
659 }
660 }
661
662 if let Some(state) = self.states.get(&cur_name) {
664 let speed_mod = if let Some(ref sf) = state.speed_curve {
665 let norm = if state.duration() > 1e-6 { layer.current_time / state.duration() } else { 0.0 };
666 sf.evaluate(norm, norm)
667 } else {
668 1.0
669 };
670 layer.current_time += dt * state.speed * speed_mod;
671 if state.duration() > 1e-6 {
672 if let StateContent::Clip(ref clip) = state.content {
673 if clip.looping {
674 layer.current_time %= clip.duration.max(1e-4);
675 } else {
676 layer.current_time = layer.current_time.min(clip.duration);
677 }
678 }
679 }
680 }
681 }
682
683 let sample_t = {
685 let dur = self.states.get(layer.current_state.as_deref().unwrap_or(""))
686 .map(|s| s.duration()).unwrap_or(1.0).max(1e-4);
687 (layer.current_time / dur).clamp(0.0, 1.0)
688 };
689
690 if let Some(state) = self.states.get(layer.current_state.as_deref().unwrap_or("")) {
691 let samples = match &state.content {
692 StateContent::Clip(c) => c.sample(sample_t),
693 StateContent::Tree(t) => t.sample(sample_t),
694 };
695 for (path, val) in samples {
696 let entry = output.channels.entry(path).or_insert(0.0);
697 if layer.additive {
698 *entry += val * layer.weight;
699 } else {
700 *entry = *entry * (1.0 - layer.weight) + val * layer.weight;
701 }
702 }
703 }
704 }
705
706 for key in consumed_triggers {
708 self.params.insert(key, ParamValue::Bool(false));
709 }
710
711 output
712 }
713
714 pub fn drain_events(&mut self) -> Vec<FiredEvent> {
716 std::mem::take(&mut self.events)
717 }
718
719 pub fn play(&mut self, state_name: &str) {
721 if let Some(layer) = self.layers.first_mut() {
722 layer.current_state = Some(state_name.to_owned());
723 layer.current_time = 0.0;
724 layer.transition = None;
725 }
726 }
727
728 pub fn cross_fade(&mut self, state_name: &str, duration: f32) {
730 if let Some(layer) = self.layers.first_mut() {
731 let prev = layer.current_state.clone().unwrap_or_default();
732 layer.transition = Some(ActiveTransition {
733 target_state: state_name.to_owned(),
734 progress: 0.0,
735 duration,
736 prev_time: layer.current_time,
737 prev_state: prev,
738 });
739 }
740 }
741
742 pub fn normalized_time(&self) -> f32 {
744 if let Some(layer) = self.layers.first() {
745 if let Some(name) = layer.current_state.as_deref() {
746 if let Some(state) = self.states.get(name) {
747 let dur = state.duration().max(1e-4);
748 return (layer.current_time / dur).clamp(0.0, 1.0);
749 }
750 }
751 }
752 0.0
753 }
754
755 pub fn current_state(&self) -> Option<&str> {
757 self.layers.first()?.current_state.as_deref()
758 }
759
760 pub fn is_transitioning(&self) -> bool {
762 self.layers.first().map(|l| l.transition.is_some()).unwrap_or(false)
763 }
764}
765
766impl Default for AnimatorController {
767 fn default() -> Self { Self::new() }
768}
769
770#[derive(Debug, Clone, Default)]
774pub struct RootMotion {
775 pub delta_position: glam::Vec3,
776 pub delta_rotation: f32,
777 pub delta_scale: glam::Vec3,
778}
779
780impl RootMotion {
781 pub fn from_output(output: &AnimationOutput) -> Self {
782 let get = |key: &str| output.channels.get(key).copied().unwrap_or(0.0);
783 Self {
784 delta_position: glam::Vec3::new(get("root.dx"), get("root.dy"), get("root.dz")),
785 delta_rotation: get("root.dr"),
786 delta_scale: glam::Vec3::ONE,
787 }
788 }
789
790 pub fn is_zero(&self) -> bool {
791 self.delta_position.length_squared() < 1e-10 && self.delta_rotation.abs() < 1e-6
792 }
793}
794
795pub fn mirror_output(output: &mut AnimationOutput) {
799 for (key, val) in &mut output.channels {
800 if key.ends_with(".x") || key.ends_with("_x") || key.contains("left") {
801 *val = -*val;
802 }
803 }
804}
805
806pub fn blend_outputs(a: &AnimationOutput, b: &AnimationOutput, alpha: f32) -> AnimationOutput {
810 let mut out = a.clone();
811 for (key, bv) in &b.channels {
812 let av = a.channels.get(key).copied().unwrap_or(0.0);
813 out.channels.insert(key.clone(), av + (bv - av) * alpha);
814 }
815 out
816}
817
818pub fn add_output(base: &AnimationOutput, additive: &AnimationOutput, weight: f32) -> AnimationOutput {
820 let mut out = base.clone();
821 for (key, av) in &additive.channels {
822 *out.channels.entry(key.clone()).or_insert(0.0) += av * weight;
823 }
824 out
825}