1use glam::Vec3;
15use std::f32::consts::{PI, TAU};
16
17#[derive(Debug, Clone)]
23pub enum DeformationMode {
24 Breathe {
26 amplitude: f32,
27 frequency: f32,
28 },
29 Wave {
31 direction: Vec3,
32 amplitude: f32,
33 wavelength: f32,
34 speed: f32,
35 },
36 Twist {
38 axis: Vec3,
39 strength: f32,
40 falloff_start: f32,
41 falloff_end: f32,
42 },
43 Melt {
45 rate: f32,
46 gravity_dir: Vec3,
47 noise_scale: f32,
48 },
49 Explode {
51 strength: f32,
52 center: Vec3,
53 noise_scale: f32,
54 },
55 Ripple {
57 center: Vec3,
58 amplitude: f32,
59 wavelength: f32,
60 speed: f32,
61 decay: f32,
62 },
63 Fold {
65 plane_point: Vec3,
66 plane_normal: Vec3,
67 angle: f32,
68 sharpness: f32,
69 },
70 Shatter {
72 center: Vec3,
73 strength: f32,
74 fragment_seed: u32,
75 gravity: Vec3,
76 },
77 NoiseDisplace {
79 amplitude: f32,
80 frequency: f32,
81 speed: f32,
82 },
83 Pinch {
85 axis_point: Vec3,
86 axis_dir: Vec3,
87 strength: f32,
88 radius: f32,
89 },
90 Inflate {
92 amount: f32,
93 },
94 Taper {
96 axis: Vec3,
97 start: f32,
98 end: f32,
99 scale_at_end: f32,
100 },
101}
102
103#[derive(Clone)]
105pub struct Deformation {
106 pub mode: DeformationMode,
107 pub weight: f32,
109 pub active: bool,
111 pub falloff: FalloffFunction,
113 pub falloff_center: Vec3,
115 pub falloff_radius: f32,
117}
118
119impl Deformation {
120 pub fn new(mode: DeformationMode) -> Self {
122 Self {
123 mode,
124 weight: 1.0,
125 active: true,
126 falloff: FalloffFunction::None,
127 falloff_center: Vec3::ZERO,
128 falloff_radius: f32::MAX,
129 }
130 }
131
132 pub fn with_weight(mut self, weight: f32) -> Self {
134 self.weight = weight;
135 self
136 }
137
138 pub fn with_falloff(mut self, center: Vec3, radius: f32, falloff: FalloffFunction) -> Self {
140 self.falloff_center = center;
141 self.falloff_radius = radius;
142 self.falloff = falloff;
143 self
144 }
145
146 fn compute_falloff(&self, position: Vec3) -> f32 {
148 if matches!(self.falloff, FalloffFunction::None) {
149 return 1.0;
150 }
151 let dist = (position - self.falloff_center).length();
152 if dist > self.falloff_radius {
153 return 0.0;
154 }
155 let t = dist / self.falloff_radius;
156 match self.falloff {
157 FalloffFunction::None => 1.0,
158 FalloffFunction::Linear => 1.0 - t,
159 FalloffFunction::Smooth => {
160 let s = 1.0 - t;
161 s * s * (3.0 - 2.0 * s)
162 }
163 FalloffFunction::Exponential { decay } => (-t * decay).exp(),
164 FalloffFunction::Gaussian { sigma } => (-(t * t) / (2.0 * sigma * sigma)).exp(),
165 FalloffFunction::InverseSquare => 1.0 / (1.0 + t * t),
166 }
167 }
168
169 pub fn apply(&self, position: Vec3, normal: Vec3, time: f32) -> Vec3 {
171 if !self.active || self.weight < 1e-6 {
172 return position;
173 }
174
175 let falloff = self.compute_falloff(position) * self.weight;
176 if falloff < 1e-6 {
177 return position;
178 }
179
180 let displacement = match self.mode.clone() {
181 DeformationMode::Breathe { amplitude, frequency } => {
182 let scale = (time * frequency * TAU).sin() * amplitude;
183 normal * scale
184 }
185 DeformationMode::Wave { direction, amplitude, wavelength, speed } => {
186 let dir = direction.normalize_or_zero();
187 let phase = position.dot(dir) / wavelength - time * speed;
188 let disp = (phase * TAU).sin() * amplitude;
189 normal * disp
190 }
191 DeformationMode::Twist { axis, strength, falloff_start, falloff_end } => {
192 let axis_n = axis.normalize_or_zero();
193 let proj = axis_n * position.dot(axis_n);
194 let radial = position - proj;
195 let t_along = position.dot(axis_n);
196
197 let twist_amount = if falloff_end > falloff_start {
198 let local_t = ((t_along - falloff_start) / (falloff_end - falloff_start))
199 .clamp(0.0, 1.0);
200 local_t * strength * time
201 } else {
202 strength * time
203 };
204
205 let angle = twist_amount;
206 let cos_a = angle.cos();
207 let sin_a = angle.sin();
208
209 let rotated = radial * cos_a
211 + axis_n.cross(radial) * sin_a
212 + axis_n * axis_n.dot(radial) * (1.0 - cos_a);
213
214 (proj + rotated) - position
215 }
216 DeformationMode::Melt { rate, gravity_dir, noise_scale } => {
217 let g = gravity_dir.normalize_or_zero();
218 let droop = rate * time * time * 0.5;
219 let height_factor = position.dot(-g).max(0.0);
221 let noise = simple_noise_3d(position * noise_scale) * 0.5 + 0.5;
222 g * droop * height_factor * noise
223 }
224 DeformationMode::Explode { strength, center, noise_scale } => {
225 let dir = (position - center).normalize_or_zero();
226 let dist = (position - center).length();
227 let noise = simple_noise_3d(position * noise_scale) * 0.5 + 0.5;
228 let t_exp = (time * strength).min(10.0);
229 dir * t_exp * (1.0 + noise * 0.5) * (1.0 + dist * 0.1)
230 }
231 DeformationMode::Ripple { center, amplitude, wavelength, speed, decay } => {
232 let dist = (position - center).length();
233 let wave = ((dist / wavelength - time * speed) * TAU).sin();
234 let attenuation = (-dist * decay).exp();
235 normal * wave * amplitude * attenuation
236 }
237 DeformationMode::Fold { plane_point, plane_normal, angle, sharpness } => {
238 let pn = plane_normal.normalize_or_zero();
239 let to_point = position - plane_point;
240 let signed_dist = to_point.dot(pn);
241
242 if signed_dist > 0.0 {
243 let fold_factor = (signed_dist * sharpness).min(1.0);
245 let fold_angle = angle * fold_factor * time.min(1.0);
246
247 let cos_a = fold_angle.cos();
249 let sin_a = fold_angle.sin();
250 let proj = pn * signed_dist;
251 let in_plane = to_point - proj;
252
253 let folded = in_plane + pn * (signed_dist * cos_a);
254 let cross = pn.cross(in_plane.normalize_or_zero()) * signed_dist * sin_a;
255
256 (folded + cross + plane_point) - position
257 } else {
258 Vec3::ZERO
259 }
260 }
261 DeformationMode::Shatter { center, strength, fragment_seed, gravity } => {
262 let dir = (position - center).normalize_or_zero();
263 let hash = hash_vec3(position, fragment_seed);
265 let frag_dir = Vec3::new(
266 (hash & 0xFF) as f32 / 128.0 - 1.0,
267 ((hash >> 8) & 0xFF) as f32 / 128.0 - 1.0,
268 ((hash >> 16) & 0xFF) as f32 / 128.0 - 1.0,
269 ).normalize_or_zero();
270
271 let t = time * strength;
272 let explosion = (dir + frag_dir * 0.5) * t;
273 let grav = gravity * t * t * 0.5;
274 explosion + grav
275 }
276 DeformationMode::NoiseDisplace { amplitude, frequency, speed } => {
277 let sample_pos = position * frequency + Vec3::splat(time * speed);
278 let n = simple_noise_3d(sample_pos);
279 normal * n * amplitude
280 }
281 DeformationMode::Pinch { axis_point, axis_dir, strength, radius } => {
282 let axis = axis_dir.normalize_or_zero();
283 let to_point = position - axis_point;
284 let along = axis * to_point.dot(axis);
285 let radial = to_point - along;
286 let dist = radial.length();
287 if dist < radius && dist > 1e-6 {
288 let t = 1.0 - dist / radius;
289 let t = t * t; -radial.normalize_or_zero() * t * strength * time.min(1.0)
291 } else {
292 Vec3::ZERO
293 }
294 }
295 DeformationMode::Inflate { amount } => {
296 normal * amount * time.min(1.0)
297 }
298 DeformationMode::Taper { axis, start, end, scale_at_end } => {
299 let axis_n = axis.normalize_or_zero();
300 let t_along = position.dot(axis_n);
301 let range = end - start;
302 if range.abs() < 1e-6 {
303 return position;
304 }
305 let local_t = ((t_along - start) / range).clamp(0.0, 1.0);
306 let scale = 1.0 + (scale_at_end - 1.0) * local_t * time.min(1.0);
307 let proj = axis_n * t_along;
308 let radial = position - proj;
309 (proj + radial * scale) - position
310 }
311 };
312
313 position + displacement * falloff
314 }
315}
316
317#[derive(Debug, Clone, Copy)]
319pub enum FalloffFunction {
320 None,
322 Linear,
324 Smooth,
326 Exponential { decay: f32 },
328 Gaussian { sigma: f32 },
330 InverseSquare,
332}
333
334pub struct DeformationStack {
340 pub deformations: Vec<Deformation>,
341 pub time: f32,
342 pub master_weight: f32,
344}
345
346impl DeformationStack {
347 pub fn new() -> Self {
349 Self {
350 deformations: Vec::new(),
351 time: 0.0,
352 master_weight: 1.0,
353 }
354 }
355
356 pub fn push(&mut self, deformation: Deformation) {
358 self.deformations.push(deformation);
359 }
360
361 pub fn remove(&mut self, index: usize) {
363 if index < self.deformations.len() {
364 self.deformations.remove(index);
365 }
366 }
367
368 pub fn clear(&mut self) {
370 self.deformations.clear();
371 }
372
373 pub fn set_time(&mut self, time: f32) {
375 self.time = time;
376 }
377
378 pub fn tick(&mut self, dt: f32) {
380 self.time += dt;
381 }
382
383 pub fn apply(&self, mut position: Vec3, normal: Vec3) -> Vec3 {
385 if self.master_weight < 1e-6 {
386 return position;
387 }
388 for deform in &self.deformations {
389 let deformed = deform.apply(position, normal, self.time);
390 position = position + (deformed - position) * self.master_weight;
391 }
392 position
393 }
394
395 pub fn apply_to_positions(&self, positions: &mut [Vec3], normals: &[Vec3]) {
397 if self.master_weight < 1e-6 || self.deformations.is_empty() {
398 return;
399 }
400 for (i, pos) in positions.iter_mut().enumerate() {
401 let normal = normals.get(i).copied().unwrap_or(Vec3::Y);
402 *pos = self.apply(*pos, normal);
403 }
404 }
405
406 pub fn compute_displaced(&self, positions: &[Vec3], normals: &[Vec3]) -> Vec<Vec3> {
408 positions.iter().enumerate().map(|(i, &pos)| {
409 let normal = normals.get(i).copied().unwrap_or(Vec3::Y);
410 self.apply(pos, normal)
411 }).collect()
412 }
413
414 pub fn active_count(&self) -> usize {
416 self.deformations.iter().filter(|d| d.active).count()
417 }
418
419 pub fn set_weight(&mut self, index: usize, weight: f32) {
421 if let Some(d) = self.deformations.get_mut(index) {
422 d.weight = weight;
423 }
424 }
425
426 pub fn set_active(&mut self, index: usize, active: bool) {
428 if let Some(d) = self.deformations.get_mut(index) {
429 d.active = active;
430 }
431 }
432}
433
434impl Default for DeformationStack {
435 fn default() -> Self { Self::new() }
436}
437
438pub struct MorphTarget {
444 pub base_positions: Vec<Vec3>,
446 pub target_positions: Vec<Vec3>,
448 pub base_normals: Vec<Vec3>,
450 pub target_normals: Vec<Vec3>,
452 pub blend: f32,
454 pub speed: f32,
456 pub ping_pong: bool,
458 direction: f32,
460}
461
462impl MorphTarget {
463 pub fn new(base_positions: Vec<Vec3>, target_positions: Vec<Vec3>) -> Self {
465 let len = base_positions.len();
466 Self {
467 base_positions,
468 target_positions,
469 base_normals: vec![Vec3::Y; len],
470 target_normals: vec![Vec3::Y; len],
471 blend: 0.0,
472 speed: 1.0,
473 ping_pong: false,
474 direction: 1.0,
475 }
476 }
477
478 pub fn with_normals(
480 mut self,
481 base_normals: Vec<Vec3>,
482 target_normals: Vec<Vec3>,
483 ) -> Self {
484 self.base_normals = base_normals;
485 self.target_normals = target_normals;
486 self
487 }
488
489 pub fn with_speed(mut self, speed: f32) -> Self {
491 self.speed = speed;
492 self
493 }
494
495 pub fn with_ping_pong(mut self, ping_pong: bool) -> Self {
497 self.ping_pong = ping_pong;
498 self
499 }
500
501 pub fn tick(&mut self, dt: f32) {
503 self.blend += self.direction * self.speed * dt;
504
505 if self.ping_pong {
506 if self.blend >= 1.0 {
507 self.blend = 1.0;
508 self.direction = -1.0;
509 } else if self.blend <= 0.0 {
510 self.blend = 0.0;
511 self.direction = 1.0;
512 }
513 } else {
514 self.blend = self.blend.clamp(0.0, 1.0);
515 }
516 }
517
518 pub fn set_blend(&mut self, blend: f32) {
520 self.blend = blend.clamp(0.0, 1.0);
521 }
522
523 pub fn compute_positions(&self) -> Vec<Vec3> {
525 let t = self.blend;
526 let len = self.base_positions.len().min(self.target_positions.len());
527 (0..len).map(|i| {
528 self.base_positions[i] * (1.0 - t) + self.target_positions[i] * t
529 }).collect()
530 }
531
532 pub fn compute_normals(&self) -> Vec<Vec3> {
534 let t = self.blend;
535 let len = self.base_normals.len().min(self.target_normals.len());
536 (0..len).map(|i| {
537 (self.base_normals[i] * (1.0 - t) + self.target_normals[i] * t).normalize_or_zero()
538 }).collect()
539 }
540
541 pub fn vertex_count(&self) -> usize {
543 self.base_positions.len()
544 }
545
546 pub fn is_complete(&self) -> bool {
548 self.blend <= 0.0 || self.blend >= 1.0
549 }
550}
551
552pub struct MultiMorphTarget {
554 pub base_positions: Vec<Vec3>,
556 pub targets: Vec<Vec<Vec3>>,
558 pub weights: Vec<f32>,
560}
561
562impl MultiMorphTarget {
563 pub fn new(base_positions: Vec<Vec3>) -> Self {
564 Self {
565 base_positions,
566 targets: Vec::new(),
567 weights: Vec::new(),
568 }
569 }
570
571 pub fn add_target(&mut self, target: Vec<Vec3>) {
573 self.targets.push(target);
574 self.weights.push(0.0);
575 }
576
577 pub fn set_weight(&mut self, index: usize, weight: f32) {
579 if let Some(w) = self.weights.get_mut(index) {
580 *w = weight.clamp(0.0, 1.0);
581 }
582 }
583
584 pub fn compute_positions(&self) -> Vec<Vec3> {
586 let len = self.base_positions.len();
587 let mut result = self.base_positions.clone();
588
589 for (target_idx, target) in self.targets.iter().enumerate() {
590 let w = self.weights.get(target_idx).copied().unwrap_or(0.0);
591 if w < 1e-6 { continue; }
592 for i in 0..len.min(target.len()) {
593 let delta = target[i] - self.base_positions[i];
594 result[i] += delta * w;
595 }
596 }
597
598 result
599 }
600}
601
602pub struct WaveSimulation {
611 pub width: usize,
613 pub height: usize,
615 pub current: Vec<f32>,
617 pub previous: Vec<f32>,
619 pub speed: f32,
621 pub damping: f32,
623 pub fixed_boundaries: bool,
625}
626
627impl WaveSimulation {
628 pub fn new(width: usize, height: usize, speed: f32) -> Self {
630 let size = width * height;
631 Self {
632 width,
633 height,
634 current: vec![0.0; size],
635 previous: vec![0.0; size],
636 speed,
637 damping: 0.01,
638 fixed_boundaries: true,
639 }
640 }
641
642 pub fn with_damping(mut self, damping: f32) -> Self {
644 self.damping = damping;
645 self
646 }
647
648 pub fn with_fixed_boundaries(mut self, fixed: bool) -> Self {
650 self.fixed_boundaries = fixed;
651 self
652 }
653
654 pub fn reset(&mut self) {
656 self.current.fill(0.0);
657 self.previous.fill(0.0);
658 }
659
660 pub fn displace(&mut self, x: usize, y: usize, amount: f32) {
662 if x < self.width && y < self.height {
663 self.current[y * self.width + x] += amount;
664 }
665 }
666
667 pub fn impulse(&mut self, cx: f32, cy: f32, radius: f32, amplitude: f32) {
669 let r2 = radius * radius;
670 let min_x = ((cx - radius).floor() as i32).max(0) as usize;
671 let max_x = ((cx + radius).ceil() as i32).min(self.width as i32 - 1) as usize;
672 let min_y = ((cy - radius).floor() as i32).max(0) as usize;
673 let max_y = ((cy + radius).ceil() as i32).min(self.height as i32 - 1) as usize;
674
675 for y in min_y..=max_y {
676 for x in min_x..=max_x {
677 let dx = x as f32 - cx;
678 let dy = y as f32 - cy;
679 let d2 = dx * dx + dy * dy;
680 if d2 < r2 {
681 let falloff = 1.0 - d2 / r2;
682 let falloff = falloff * falloff;
683 self.current[y * self.width + x] += amplitude * falloff;
684 }
685 }
686 }
687 }
688
689 pub fn oscillator(&mut self, x: usize, y: usize, time: f32, frequency: f32, amplitude: f32) {
691 if x < self.width && y < self.height {
692 self.current[y * self.width + x] = (time * frequency * TAU).sin() * amplitude;
693 }
694 }
695
696 pub fn step(&mut self, dt: f32) {
698 let c2 = self.speed * self.speed * dt * dt;
699 let damp = 1.0 - self.damping;
700
701 let mut next = vec![0.0f32; self.width * self.height];
702
703 let w = self.width;
704 let h = self.height;
705
706 for y in 1..h - 1 {
707 for x in 1..w - 1 {
708 let idx = y * w + x;
709 let laplacian = self.current[idx - 1]
710 + self.current[idx + 1]
711 + self.current[idx - w]
712 + self.current[idx + w]
713 - 4.0 * self.current[idx];
714
715 next[idx] = (2.0 * self.current[idx] - self.previous[idx]
716 + c2 * laplacian) * damp;
717 }
718 }
719
720 if !self.fixed_boundaries {
721 for x in 0..w {
723 next[x] = next[w + x]; next[(h - 1) * w + x] = next[(h - 2) * w + x]; }
726 for y in 0..h {
727 next[y * w] = next[y * w + 1]; next[y * w + w - 1] = next[y * w + w - 2]; }
730 }
731
732 self.previous = std::mem::replace(&mut self.current, next);
733 }
734
735 pub fn get(&self, x: usize, y: usize) -> f32 {
737 if x < self.width && y < self.height {
738 self.current[y * self.width + x]
739 } else {
740 0.0
741 }
742 }
743
744 pub fn sample(&self, x: f32, y: f32) -> f32 {
746 let fx = x.clamp(0.0, (self.width - 1) as f32);
747 let fy = y.clamp(0.0, (self.height - 1) as f32);
748 let ix = fx as usize;
749 let iy = fy as usize;
750 let sx = fx - ix as f32;
751 let sy = fy - iy as f32;
752
753 let ix1 = (ix + 1).min(self.width - 1);
754 let iy1 = (iy + 1).min(self.height - 1);
755
756 let v00 = self.current[iy * self.width + ix];
757 let v10 = self.current[iy * self.width + ix1];
758 let v01 = self.current[iy1 * self.width + ix];
759 let v11 = self.current[iy1 * self.width + ix1];
760
761 let top = v00 * (1.0 - sx) + v10 * sx;
762 let bottom = v01 * (1.0 - sx) + v11 * sx;
763 top * (1.0 - sy) + bottom * sy
764 }
765
766 pub fn normal_at(&self, x: usize, y: usize, scale: f32) -> Vec3 {
768 let h_left = if x > 0 { self.get(x - 1, y) } else { self.get(x, y) };
769 let h_right = if x + 1 < self.width { self.get(x + 1, y) } else { self.get(x, y) };
770 let h_down = if y > 0 { self.get(x, y - 1) } else { self.get(x, y) };
771 let h_up = if y + 1 < self.height { self.get(x, y + 1) } else { self.get(x, y) };
772
773 let dx = (h_right - h_left) * scale;
774 let dy = (h_up - h_down) * scale;
775
776 Vec3::new(-dx, 2.0, -dy).normalize()
777 }
778
779 pub fn total_energy(&self) -> f32 {
781 self.current.iter().map(|&v| v * v).sum::<f32>()
782 }
783
784 pub fn to_displacements(&self) -> Vec<f32> {
787 self.current.clone()
788 }
789
790 pub fn apply_to_grid(&self, positions: &mut [Vec3], grid_width: usize, grid_height: usize) {
792 let scale_x = (self.width - 1) as f32 / (grid_width - 1).max(1) as f32;
793 let scale_y = (self.height - 1) as f32 / (grid_height - 1).max(1) as f32;
794
795 for gy in 0..grid_height {
796 for gx in 0..grid_width {
797 let idx = gy * grid_width + gx;
798 if idx < positions.len() {
799 let wx = gx as f32 * scale_x;
800 let wy = gy as f32 * scale_y;
801 positions[idx].y += self.sample(wx, wy);
802 }
803 }
804 }
805 }
806}
807
808#[derive(Debug, Clone, Copy)]
814pub struct DeformKeyframe {
815 pub time: f32,
816 pub value: f32,
817}
818
819impl DeformKeyframe {
820 pub fn new(time: f32, value: f32) -> Self { Self { time, value } }
821}
822
823#[derive(Debug, Clone, Copy)]
825pub enum KeyframeInterp {
826 Linear,
828 Smooth,
830 Step,
832 Cubic,
834}
835
836pub struct KeyframeAnimator {
838 pub keyframes: Vec<DeformKeyframe>,
839 pub interpolation: KeyframeInterp,
840 pub looping: bool,
842 pub time: f32,
844}
845
846impl KeyframeAnimator {
847 pub fn new() -> Self {
849 Self {
850 keyframes: Vec::new(),
851 interpolation: KeyframeInterp::Linear,
852 looping: false,
853 time: 0.0,
854 }
855 }
856
857 pub fn add_key(&mut self, time: f32, value: f32) {
859 self.keyframes.push(DeformKeyframe::new(time, value));
860 self.keyframes.sort_by(|a, b| a.time.partial_cmp(&b.time).unwrap_or(std::cmp::Ordering::Equal));
861 }
862
863 pub fn with_interpolation(mut self, interp: KeyframeInterp) -> Self {
865 self.interpolation = interp;
866 self
867 }
868
869 pub fn with_looping(mut self, looping: bool) -> Self {
871 self.looping = looping;
872 self
873 }
874
875 pub fn tick(&mut self, dt: f32) {
877 self.time += dt;
878
879 if self.looping && self.keyframes.len() >= 2 {
880 let duration = self.keyframes.last().unwrap().time - self.keyframes.first().unwrap().time;
881 if duration > 0.0 {
882 let start = self.keyframes.first().unwrap().time;
883 self.time = start + ((self.time - start) % duration);
884 }
885 }
886 }
887
888 pub fn evaluate(&self) -> f32 {
890 self.evaluate_at(self.time)
891 }
892
893 pub fn evaluate_at(&self, time: f32) -> f32 {
895 if self.keyframes.is_empty() {
896 return 0.0;
897 }
898 if self.keyframes.len() == 1 {
899 return self.keyframes[0].value;
900 }
901
902 if time <= self.keyframes.first().unwrap().time {
904 return self.keyframes.first().unwrap().value;
905 }
906 if time >= self.keyframes.last().unwrap().time {
907 return self.keyframes.last().unwrap().value;
908 }
909
910 let mut idx = 0;
911 for (i, kf) in self.keyframes.iter().enumerate() {
912 if kf.time > time {
913 idx = i;
914 break;
915 }
916 }
917 if idx == 0 { idx = 1; }
918
919 let kf0 = &self.keyframes[idx - 1];
920 let kf1 = &self.keyframes[idx];
921 let t = (time - kf0.time) / (kf1.time - kf0.time);
922
923 match self.interpolation {
924 KeyframeInterp::Linear => {
925 kf0.value + (kf1.value - kf0.value) * t
926 }
927 KeyframeInterp::Smooth => {
928 let s = t * t * (3.0 - 2.0 * t);
929 kf0.value + (kf1.value - kf0.value) * s
930 }
931 KeyframeInterp::Step => {
932 if t < 0.5 { kf0.value } else { kf1.value }
933 }
934 KeyframeInterp::Cubic => {
935 let v0 = if idx >= 2 { self.keyframes[idx - 2].value } else { kf0.value };
937 let v1 = kf0.value;
938 let v2 = kf1.value;
939 let v3 = if idx + 1 < self.keyframes.len() {
940 self.keyframes[idx + 1].value
941 } else {
942 kf1.value
943 };
944 catmull_rom(v0, v1, v2, v3, t)
945 }
946 }
947 }
948
949 pub fn duration(&self) -> f32 {
951 if self.keyframes.len() < 2 {
952 return 0.0;
953 }
954 self.keyframes.last().unwrap().time - self.keyframes.first().unwrap().time
955 }
956
957 pub fn is_finished(&self) -> bool {
959 if self.looping { return false; }
960 if self.keyframes.is_empty() { return true; }
961 self.time >= self.keyframes.last().unwrap().time
962 }
963
964 pub fn reset(&mut self) {
966 self.time = if self.keyframes.is_empty() {
967 0.0
968 } else {
969 self.keyframes.first().unwrap().time
970 };
971 }
972}
973
974impl Default for KeyframeAnimator {
975 fn default() -> Self { Self::new() }
976}
977
978pub struct Vec3KeyframeAnimator {
980 pub x: KeyframeAnimator,
981 pub y: KeyframeAnimator,
982 pub z: KeyframeAnimator,
983}
984
985impl Vec3KeyframeAnimator {
986 pub fn new() -> Self {
987 Self {
988 x: KeyframeAnimator::new(),
989 y: KeyframeAnimator::new(),
990 z: KeyframeAnimator::new(),
991 }
992 }
993
994 pub fn add_key(&mut self, time: f32, value: Vec3) {
995 self.x.add_key(time, value.x);
996 self.y.add_key(time, value.y);
997 self.z.add_key(time, value.z);
998 }
999
1000 pub fn tick(&mut self, dt: f32) {
1001 self.x.tick(dt);
1002 self.y.tick(dt);
1003 self.z.tick(dt);
1004 }
1005
1006 pub fn evaluate(&self) -> Vec3 {
1007 Vec3::new(self.x.evaluate(), self.y.evaluate(), self.z.evaluate())
1008 }
1009
1010 pub fn set_time(&mut self, time: f32) {
1011 self.x.time = time;
1012 self.y.time = time;
1013 self.z.time = time;
1014 }
1015}
1016
1017impl Default for Vec3KeyframeAnimator {
1018 fn default() -> Self { Self::new() }
1019}
1020
1021fn simple_noise_3d(p: Vec3) -> f32 {
1027 let n = p.x * 127.1 + p.y * 311.7 + p.z * 74.7;
1029 (n.sin() * 43758.5453).fract() * 2.0 - 1.0
1030}
1031
1032fn hash_vec3(p: Vec3, seed: u32) -> u32 {
1034 let x = (p.x * 73.0 + 37.0) as u32;
1035 let y = (p.y * 157.0 + 59.0) as u32;
1036 let z = (p.z * 241.0 + 83.0) as u32;
1037 let mut h = seed;
1038 h = h.wrapping_mul(31).wrapping_add(x);
1039 h = h.wrapping_mul(31).wrapping_add(y);
1040 h = h.wrapping_mul(31).wrapping_add(z);
1041 h ^= h >> 16;
1042 h = h.wrapping_mul(0x45d9f3b);
1043 h ^= h >> 16;
1044 h
1045}
1046
1047fn catmull_rom(v0: f32, v1: f32, v2: f32, v3: f32, t: f32) -> f32 {
1049 let t2 = t * t;
1050 let t3 = t2 * t;
1051 0.5 * ((2.0 * v1)
1052 + (-v0 + v2) * t
1053 + (2.0 * v0 - 5.0 * v1 + 4.0 * v2 - v3) * t2
1054 + (-v0 + 3.0 * v1 - 3.0 * v2 + v3) * t3)
1055}
1056
1057pub struct DeformationPresets;
1063
1064impl DeformationPresets {
1065 pub fn gentle_breathe() -> Deformation {
1067 Deformation::new(DeformationMode::Breathe {
1068 amplitude: 0.05,
1069 frequency: 0.5,
1070 })
1071 }
1072
1073 pub fn ocean_wave() -> Deformation {
1075 Deformation::new(DeformationMode::Wave {
1076 direction: Vec3::X,
1077 amplitude: 0.3,
1078 wavelength: 5.0,
1079 speed: 1.0,
1080 })
1081 }
1082
1083 pub fn slow_twist() -> Deformation {
1085 Deformation::new(DeformationMode::Twist {
1086 axis: Vec3::Y,
1087 strength: 0.5,
1088 falloff_start: -1.0,
1089 falloff_end: 1.0,
1090 })
1091 }
1092
1093 pub fn melt_down() -> Deformation {
1095 Deformation::new(DeformationMode::Melt {
1096 rate: 1.0,
1097 gravity_dir: Vec3::NEG_Y,
1098 noise_scale: 2.0,
1099 })
1100 }
1101
1102 pub fn explosion() -> Deformation {
1104 Deformation::new(DeformationMode::Explode {
1105 strength: 2.0,
1106 center: Vec3::ZERO,
1107 noise_scale: 1.0,
1108 })
1109 }
1110
1111 pub fn water_ripple(center: Vec3) -> Deformation {
1113 Deformation::new(DeformationMode::Ripple {
1114 center,
1115 amplitude: 0.1,
1116 wavelength: 0.5,
1117 speed: 3.0,
1118 decay: 0.5,
1119 })
1120 }
1121
1122 pub fn paper_fold() -> Deformation {
1124 Deformation::new(DeformationMode::Fold {
1125 plane_point: Vec3::ZERO,
1126 plane_normal: Vec3::X,
1127 angle: PI * 0.5,
1128 sharpness: 5.0,
1129 })
1130 }
1131
1132 pub fn shatter() -> Deformation {
1134 Deformation::new(DeformationMode::Shatter {
1135 center: Vec3::ZERO,
1136 strength: 3.0,
1137 fragment_seed: 42,
1138 gravity: Vec3::new(0.0, -9.8, 0.0),
1139 })
1140 }
1141
1142 pub fn organic_wobble() -> Deformation {
1144 Deformation::new(DeformationMode::NoiseDisplace {
1145 amplitude: 0.1,
1146 frequency: 3.0,
1147 speed: 1.0,
1148 })
1149 }
1150
1151 pub fn inflate(amount: f32) -> Deformation {
1153 Deformation::new(DeformationMode::Inflate { amount })
1154 }
1155}
1156
1157#[cfg(test)]
1162mod tests {
1163 use super::*;
1164
1165 #[test]
1166 fn breathe_deformation() {
1167 let d = Deformation::new(DeformationMode::Breathe {
1168 amplitude: 1.0,
1169 frequency: 1.0,
1170 });
1171 let pos = Vec3::new(1.0, 0.0, 0.0);
1172 let normal = Vec3::new(1.0, 0.0, 0.0);
1173
1174 let at_zero = d.apply(pos, normal, 0.0);
1175 let at_quarter = d.apply(pos, normal, 0.25);
1176 assert!((at_zero - pos).length() < 1e-3);
1178 assert!((at_quarter - pos).length() > 0.5);
1180 }
1181
1182 #[test]
1183 fn deformation_stack() {
1184 let mut stack = DeformationStack::new();
1185 stack.push(Deformation::new(DeformationMode::Inflate { amount: 1.0 }));
1186 stack.set_time(1.0);
1187
1188 let pos = Vec3::new(1.0, 0.0, 0.0);
1189 let normal = Vec3::new(1.0, 0.0, 0.0);
1190 let result = stack.apply(pos, normal);
1191
1192 assert!((result.x - 2.0).abs() < 1e-3);
1193 }
1194
1195 #[test]
1196 fn morph_target_blend() {
1197 let base = vec![Vec3::ZERO; 4];
1198 let target = vec![Vec3::ONE; 4];
1199 let mut morph = MorphTarget::new(base, target);
1200
1201 morph.set_blend(0.5);
1202 let positions = morph.compute_positions();
1203 assert!((positions[0] - Vec3::splat(0.5)).length() < 1e-5);
1204 }
1205
1206 #[test]
1207 fn morph_target_pingpong() {
1208 let base = vec![Vec3::ZERO; 1];
1209 let target = vec![Vec3::ONE; 1];
1210 let mut morph = MorphTarget::new(base, target)
1211 .with_speed(2.0)
1212 .with_ping_pong(true);
1213
1214 for _ in 0..20 {
1215 morph.tick(0.1);
1216 }
1217 assert!(morph.blend >= 0.0 && morph.blend <= 1.0);
1219 }
1220
1221 #[test]
1222 fn wave_simulation_basic() {
1223 let mut wave = WaveSimulation::new(32, 32, 1.0);
1224 wave.impulse(16.0, 16.0, 3.0, 1.0);
1225 assert!(wave.total_energy() > 0.0);
1226
1227 for _ in 0..10 {
1228 wave.step(0.016);
1229 }
1230 assert!(wave.total_energy() > 0.0);
1232 }
1233
1234 #[test]
1235 fn wave_simulation_damping() {
1236 let mut wave = WaveSimulation::new(16, 16, 1.0).with_damping(0.1);
1237 wave.impulse(8.0, 8.0, 2.0, 1.0);
1238 let initial_energy = wave.total_energy();
1239
1240 for _ in 0..100 {
1241 wave.step(0.016);
1242 }
1243 assert!(wave.total_energy() < initial_energy);
1245 }
1246
1247 #[test]
1248 fn keyframe_animator() {
1249 let mut anim = KeyframeAnimator::new();
1250 anim.add_key(0.0, 0.0);
1251 anim.add_key(1.0, 10.0);
1252 anim.add_key(2.0, 5.0);
1253
1254 anim.time = 0.5;
1255 assert!((anim.evaluate() - 5.0).abs() < 1e-3);
1256
1257 anim.time = 1.0;
1258 assert!((anim.evaluate() - 10.0).abs() < 1e-3);
1259 }
1260
1261 #[test]
1262 fn keyframe_looping() {
1263 let mut anim = KeyframeAnimator::new().with_looping(true);
1264 anim.add_key(0.0, 0.0);
1265 anim.add_key(1.0, 1.0);
1266
1267 anim.time = 0.0;
1268 anim.tick(1.5);
1269 assert!(anim.time >= 0.0 && anim.time <= 1.0);
1271 }
1272
1273 #[test]
1274 fn falloff_functions() {
1275 let d = Deformation::new(DeformationMode::Inflate { amount: 1.0 })
1276 .with_falloff(Vec3::ZERO, 10.0, FalloffFunction::Linear);
1277
1278 let center_falloff = d.compute_falloff(Vec3::ZERO);
1280 assert!((center_falloff - 1.0).abs() < 1e-5);
1281
1282 let edge_falloff = d.compute_falloff(Vec3::new(10.0, 0.0, 0.0));
1284 assert!(edge_falloff.abs() < 1e-5);
1285
1286 let outside = d.compute_falloff(Vec3::new(15.0, 0.0, 0.0));
1288 assert!(outside.abs() < 1e-5);
1289 }
1290
1291 #[test]
1292 fn deformation_presets() {
1293 let _ = DeformationPresets::gentle_breathe();
1295 let _ = DeformationPresets::ocean_wave();
1296 let _ = DeformationPresets::slow_twist();
1297 let _ = DeformationPresets::melt_down();
1298 let _ = DeformationPresets::explosion();
1299 let _ = DeformationPresets::water_ripple(Vec3::ZERO);
1300 let _ = DeformationPresets::paper_fold();
1301 let _ = DeformationPresets::shatter();
1302 let _ = DeformationPresets::organic_wobble();
1303 let _ = DeformationPresets::inflate(1.0);
1304 }
1305
1306 #[test]
1307 fn multi_morph() {
1308 let base = vec![Vec3::ZERO; 4];
1309 let mut mm = MultiMorphTarget::new(base);
1310 mm.add_target(vec![Vec3::X; 4]);
1311 mm.add_target(vec![Vec3::Y; 4]);
1312
1313 mm.set_weight(0, 0.5);
1314 mm.set_weight(1, 0.3);
1315
1316 let result = mm.compute_positions();
1317 assert!((result[0].x - 0.5).abs() < 1e-5);
1318 assert!((result[0].y - 0.3).abs() < 1e-5);
1319 }
1320}