Skip to main content

minetest_gltf/model/
animation.rs

1// Based on https://whoisryosuke.com/blog/2022/importing-gltf-with-wgpu-and-rust
2// You can thank ryosuke for this information.
3
4use std::error::Error;
5
6use ahash::AHashMap;
7use glam::{Quat, Vec3};
8use gltf::{animation::util, buffer::Data, Gltf};
9use log::error;
10
11use crate::minetest_gltf::MinetestGLTF;
12
13/// Raw animation data. Unionized.
14pub enum Keyframes {
15  /// Translation raw data.
16  Translation(Vec<Vec3>),
17  /// Rotation raw data.
18  Rotation(Vec<Quat>),
19  /// Scale raw data.
20  Scale(Vec<Vec3>),
21  /// Morph Target Weights raw data.
22  Weights(Vec<f32>),
23  /// An absolute failure that shows something blew up.
24  Explosion,
25}
26
27/// Container containing raw TRS animation data for a node (bone).
28#[derive(Default)]
29pub struct BoneAnimationChannel {
30  /// Translation data.
31  pub translations: Vec<Vec3>,
32  /// Translation timestamp data.
33  pub translation_timestamps: Vec<f32>,
34
35  /// Rotation data.
36  pub rotations: Vec<Quat>,
37  /// Rotation timestamp data.
38  pub rotation_timestamps: Vec<f32>,
39
40  /// Scale data.
41  pub scales: Vec<Vec3>,
42  /// Scale timestamp data.
43  pub scale_timestamps: Vec<f32>,
44
45  /// Weight data.
46  pub weights: Vec<f32>,
47  /// Not sure why you'll need this but it's here.
48  ///
49  /// Weight timestamp data.
50  pub weight_timestamps: Vec<f32>,
51}
52
53impl BoneAnimationChannel {
54  ///
55  /// Create new bone animation.
56  ///
57  pub(crate) fn new() -> Self {
58    BoneAnimationChannel {
59      translations: vec![],
60      translation_timestamps: vec![],
61      rotations: vec![],
62      rotation_timestamps: vec![],
63      scales: vec![],
64      scale_timestamps: vec![],
65      weights: vec![],
66      weight_timestamps: vec![],
67    }
68  }
69}
70
71///
72/// We need a comparable data set. Cast this this thing 0.00001 f32 5 precision points into 1 i32
73///
74fn into_precision(x: f32) -> i32 {
75  (x * 100_000.0) as i32
76}
77
78pub(crate) fn grab_animations(
79  gltf_data: Gltf,
80  buffers: Vec<Data>,
81  file_name: &str,
82) -> AHashMap<i32, BoneAnimationChannel> {
83  // We always want the animation data as well.
84  // You can thank: https://whoisryosuke.com/blog/2022/importing-gltf-with-wgpu-and-rust
85  let mut bone_animation_channels: AHashMap<i32, BoneAnimationChannel> = AHashMap::new();
86
87  // ? We are mimicking minetest C++ and only getting the first animation.
88  if let Some(first_animation) = gltf_data.animations().next() {
89    // ? Now we want to get all channels which contains node (bone) TRS data in random order.
90    for (channel_index, channel) in first_animation.channels().enumerate() {
91      let reader = channel.reader(|buffer| Some(&buffers[buffer.index()]));
92
93      // * If the timestamp accessor is sparse, or something has gone horribly wrong, it's a static model.
94      let result_timestamps = if let Some(inputs) = reader.read_inputs() {
95        match inputs {
96            gltf::accessor::Iter::Standard(times) => {
97              let times: Vec<f32> = times.collect();
98              // println!("Time: {}", times.len());
99              // dbg!(times);
100              Ok(times)
101            }
102            gltf::accessor::Iter::Sparse(_) => Err(format!(
103              "minetest-gltf: Sparse keyframes not supported. Model: [{}]. Model will not be animated.",
104              file_name
105            )),
106          }
107      } else {
108        Err(format!("minetest-gltf: No animation data detected in animation channel [{}]. [{}] is probably a broken model. Model will not be animated.", channel_index, file_name))
109      };
110
111      // * If something blows up when parsing the model animations, it's now a static model.
112      match result_timestamps {
113        Ok(timestamps) => {
114          let keyframes = if let Some(outputs) = reader.read_outputs() {
115            // More advanced control flow and boilerplate reduction for when something
116            // that's not implemented blows up.
117            let mut blew_up = false;
118            let mut generic_failure = |data_type: &str, implementation_type: &str| {
119              error!(
120                "Minetest_gltf: {} is not implemented for animation {}.",
121                data_type, implementation_type
122              );
123              bone_animation_channels.clear();
124              blew_up = true;
125              Keyframes::Explosion
126            };
127
128            let keyframe_result = match outputs {
129              util::ReadOutputs::Translations(translation) => {
130                Keyframes::Translation(translation.map(Vec3::from_array).collect())
131              }
132
133              util::ReadOutputs::Rotations(rotation) => match rotation {
134                util::Rotations::I8(_rotation) => generic_failure("i8", "rotation"),
135                util::Rotations::U8(_rotation) => generic_failure("u8", "rotation"),
136                util::Rotations::I16(_rotation) => generic_failure("i16", "rotation"),
137                util::Rotations::U16(_rotation) => generic_failure("u16", "rotation"),
138                util::Rotations::F32(rotation) => Keyframes::Rotation(
139                  rotation
140                    .map(|rot| {
141                      Quat::from_array({
142                        let mut returning_array: [f32; 4] = [0.0, 0.0, 0.0, 0.0];
143                        for (i, v) in rot.iter().enumerate() {
144                          returning_array[i] = *v;
145                        }
146                        returning_array
147                      })
148                    })
149                    .collect(),
150                ),
151              },
152              util::ReadOutputs::Scales(scale) => {
153                Keyframes::Scale(scale.map(Vec3::from_array).collect())
154              }
155              util::ReadOutputs::MorphTargetWeights(target_weight) => match target_weight {
156                util::MorphTargetWeights::I8(_weights) => {
157                  generic_failure("i8", "morph weight targets")
158                }
159                util::MorphTargetWeights::U8(_weights) => {
160                  generic_failure("u8", "morph weight targets")
161                }
162                util::MorphTargetWeights::I16(_weights) => {
163                  generic_failure("i16", "morph weight targets")
164                }
165                util::MorphTargetWeights::U16(_weights) => {
166                  generic_failure("u16", "morph weight targets")
167                }
168                util::MorphTargetWeights::F32(weights) => {
169                  let mut container: Vec<f32> = vec![];
170
171                  // There can be a bug in the iterator given due to how rust GLTF works, we want to drop out when the end is hit.
172                  // This prevents an infinite loop.
173                  let limit = weights.len();
174                  for (index, value) in weights.enumerate() {
175                    container.push(value);
176                    // Bail out.
177                    if index >= limit {
178                      break;
179                    }
180                  }
181                  Keyframes::Weights(container)
182                }
183              },
184            };
185
186            // And now we capture if this thing failed and stop it if it did.
187            if blew_up {
188              break;
189            }
190
191            keyframe_result
192          } else {
193            // * Something blew up, it's now a static model.
194            error!(
195                "minetest-gltf: Unknown keyframe in model [{}]. This model is probably corrupted. Model will not be animated.",
196                file_name
197              );
198            bone_animation_channels.clear();
199            break;
200          };
201
202          let bone_id = channel.target().node().index() as i32;
203
204          let enable_debug_spam = false;
205
206          if enable_debug_spam {
207            println!("found target bone: {}", bone_id);
208          }
209
210          match keyframes {
211            Keyframes::Translation(translations) => {
212              let animation_channel = bone_animation_channels.entry(bone_id).or_default();
213
214              // * If the animation already has translation for this node (bone), that means that something has gone horribly wrong.
215              if !animation_channel.translations.is_empty() {
216                error!("minetest-gltf: Attempted to overwrite node (bone) channel [{}]'s translation animation data! Model [{}] is broken! This is now a static model.", bone_id, file_name);
217                bone_animation_channels.clear();
218                break;
219              }
220
221              // * If the translation animation channel data does not match the length of timestamp data, it blew up.
222              if translations.len() != timestamps.len() {
223                error!(
224                    "minetest-gltf: Mismatched node (bone) translations length in channel [{}] of model [{}]. [{}] translation compared to [{}] timestamps. This is now a static model.", 
225                    bone_id,
226                    file_name,
227                    translations.len(),
228                    timestamps.len());
229
230                bone_animation_channels.clear();
231                break;
232              }
233
234              animation_channel.translations = translations;
235              animation_channel.translation_timestamps = timestamps;
236            }
237
238            Keyframes::Rotation(rotations) => {
239              let animation_channel = bone_animation_channels.entry(bone_id).or_default();
240
241              // * If the animation already has rotation for this node (bone), that means that something has gone horribly wrong.
242              if !animation_channel.rotations.is_empty() {
243                error!("minetest-gltf: Attempted to overwrite node (bone) channel [{}]'s rotation animation data! Model [{}] is broken! This is now a static model.", bone_id, file_name);
244                bone_animation_channels.clear();
245                break;
246              }
247
248              // * If the rotations animation channel data does not match the length of timestamp data, it blew up.
249              if rotations.len() != timestamps.len() {
250                error!(
251                    "minetest-gltf: Mismatched node (bone) rotations length in channel [{}] of model [{}]. [{}] rotation compared to [{}] timestamps. This is now a static model.", 
252                    bone_id,
253                    file_name,
254                    rotations.len(),
255                    timestamps.len());
256
257                bone_animation_channels.clear();
258                break;
259              }
260
261              animation_channel.rotations = rotations;
262              animation_channel.rotation_timestamps = timestamps;
263            }
264            Keyframes::Scale(scales) => {
265              let gotten_animation_channel = bone_animation_channels.entry(bone_id).or_default();
266
267              // * If the animation already has scale for this node (bone), that means that something has gone horribly wrong.
268              if !gotten_animation_channel.scales.is_empty() {
269                error!("minetest-gltf: Attempted to overwrite node (bone) channel [{}]'s scale animation data! Model [{}] is broken! This is now a static model", bone_id, file_name);
270                bone_animation_channels.clear();
271                break;
272              }
273
274              // * If the scales animation channel data does not match the length of timestamp data, it blew up.
275              if scales.len() != timestamps.len() {
276                error!(
277                    "minetest-gltf: Mismatched node (bone) scales length in channel [{}] of model [{}]. [{}] scale compared to [{}] timestamps. This is now a static model.", 
278                    bone_id,
279                    file_name,
280                    scales.len(),
281                    timestamps.len());
282
283                bone_animation_channels.clear();
284                break;
285              }
286
287              gotten_animation_channel.scales = scales;
288              gotten_animation_channel.scale_timestamps = timestamps;
289            }
290            Keyframes::Weights(weights) => {
291              let gotten_animation_channel = bone_animation_channels.entry(bone_id).or_default();
292
293              // * If the animation already has weight for this node (bone), that means that something has gone horribly wrong.
294              if !gotten_animation_channel.weights.is_empty() {
295                error!("minetest-gltf: Attempted to overwrite node (bone) channel [{}]'s weight animation data! Model [{}] is broken! This is now a static model", bone_id, file_name);
296                bone_animation_channels.clear();
297                break;
298              }
299
300              // ? We don't do a timestamp comparison here because weights probably shouldn't have timestamp data anyways??
301
302              gotten_animation_channel.weights = weights;
303              gotten_animation_channel.weight_timestamps = timestamps;
304            }
305
306            Keyframes::Explosion => {
307              panic!("minetest-gltf: Explosion was somehow reached in animation!");
308            }
309          }
310        }
311
312        // * Something blew up, it's now a static model.
313        Err(e) => {
314          error!("{}", e);
315          bone_animation_channels.clear();
316          break;
317        }
318      }
319    }
320  }
321
322  bone_animation_channels
323}
324
325pub(crate) fn finalize_animations(
326  minetest_gltf: &mut MinetestGLTF,
327  gltf_data: Gltf,
328  buffers: Vec<Data>,
329  file_name: &str,
330) -> Result<(), Box<dyn Error + Send + Sync>> {
331  // We're going to take the raw data.
332  let bone_animations = grab_animations(gltf_data, buffers, file_name);
333
334  // Then finalize it.
335  // (finalization is interpolating the frames so they're all equal distance from eachother in the scale of time.)
336
337  // Chuck this into a scope so we can have immutable values.
338  let (_min_time, max_time, min_distance) = {
339    let mut min_time_worker = 0.0;
340    let mut max_time_worker = 0.0;
341    let mut min_distance_worker = f32::MAX;
342
343    for (_id, animation) in &bone_animations {
344      // A closure so I don't have to type this out 4 times.
345      let mut devolve_timestamp_data = |raw_timestamps: &Vec<f32>| {
346        let mut old_timestamp = f32::MIN;
347        for timestamp in raw_timestamps {
348          // Time distance data.
349          if *timestamp - old_timestamp < min_distance_worker {
350            min_distance_worker = *timestamp - old_timestamp;
351          }
352
353          // Min time data.
354          if timestamp < &min_time_worker {
355            min_time_worker = *timestamp;
356          }
357          // Max time data.
358          if timestamp > &max_time_worker {
359            max_time_worker = *timestamp;
360          }
361
362          old_timestamp = *timestamp;
363        }
364      };
365
366      // Translation timestamps.
367      devolve_timestamp_data(&animation.translation_timestamps);
368
369      // Rotation timestamps.
370      devolve_timestamp_data(&animation.rotation_timestamps);
371
372      // Scale timestamps.
373      devolve_timestamp_data(&animation.rotation_timestamps);
374
375      // Weight timestamps.
376      devolve_timestamp_data(&animation.weight_timestamps);
377    }
378
379    (min_time_worker, max_time_worker, min_distance_worker)
380  };
381
382  // Now we need a triple checker variable.
383  // We need to make sure that all the channels have this many frames.
384  // This will also work as an iterator.
385  // Timestamps start at 0.0. That's why it's + 1. It's a zero counted container.
386  let required_frames = (max_time / min_distance).round() as usize + 1;
387
388  // println!(
389  //   "min_time: {}\nmax_time: {}\nmin_distance: {}\nrequired_frames: {}",
390  //   min_time, max_time, min_distance, required_frames
391  // );
392
393  let enable_timestamp_spam = false;
394
395  if enable_timestamp_spam {
396    for i in 0..required_frames {
397      println!("test: {}", i as f32 * min_distance);
398    }
399  }
400
401  // Now we finalize all animation channels.
402  let mut finalized_bone_animations: AHashMap<i32, BoneAnimationChannel> = AHashMap::new();
403
404  for (id, animation) in &bone_animations {
405    // ! This is going to get a bit complicated.
406    // ! Like, extremely complicated.
407
408    // Add a channel to the current id in the finalized animations container.
409    let mut new_finalized_channel = BoneAnimationChannel::new();
410
411    // ? ////////////////////////////////////////////////////////////
412    // ?            TRANSLATIONS
413    // ? ////////////////////////////////////////////////////////////
414
415    // Final check for translation equality.
416    if animation.translation_timestamps.len() != animation.translations.len() {
417      return Err(format!("Unequal animation translation lengths in channel {}.", id).into());
418    }
419
420    if animation.translation_timestamps.is_empty() {
421      // error!("hit none");
422      // If it's blank, we want to polyfill in default data.
423      for i in 0..required_frames {
424        new_finalized_channel
425          .translation_timestamps
426          .push(i as f32 * min_distance);
427        new_finalized_channel
428          .translations
429          .push(Vec3::new(0.0, 0.0, 0.0));
430      }
431    } else if animation.translation_timestamps.len() == 1 {
432      // If there's only one, we can simply use the one translation point as the entire translation animation.
433      // error!("hit one");
434      let polyfill = match animation.translations.first() {
435        Some(translation) => translation,
436        None => panic!("translation was already checked, why did this panic!? 1"),
437      };
438
439      for i in 0..required_frames {
440        new_finalized_channel
441          .translation_timestamps
442          .push(i as f32 * min_distance);
443        new_finalized_channel.translations.push(*polyfill);
444      }
445    } else {
446      // Now if we can't polyfill with the easiest data set,
447      // we're going to have to get creative.
448
449      // error!("Hit another?");
450      // println!("got: {}", animation.translation_timestamps.len());
451      // println!("got: {}", animation.translations.len());
452
453      let mut raw_add = false;
454
455      // Let's see if we can take the easist route with start to finish polyfill.
456      match animation.translation_timestamps.first() {
457        Some(first_timestamp) => {
458          if into_precision(*first_timestamp) == 0 {
459            match animation.translation_timestamps.last() {
460              Some(last_timestamp) => {
461                if into_precision(*last_timestamp) == into_precision(max_time) {
462                  raw_add = true;
463                }
464              }
465              None => panic!("translation was already checked, why did this panic!? 2"),
466            }
467          }
468        }
469        None => panic!("translation was already checked, why did this panic!? 3"),
470      }
471
472      // Now if we can raw add let's see if we can just dump the raw frames in because they're finalized.
473      if raw_add && animation.translation_timestamps.len() == required_frames {
474        // We can!
475        // error!("OKAY TO RAW ADD!");
476        new_finalized_channel.translation_timestamps = animation.translation_timestamps.clone();
477        new_finalized_channel.translations = animation.translations.clone();
478      } else if raw_add && animation.translation_timestamps.len() == 2 {
479        // But if we only have the start and finish, we now have to polyfill between beginning and end.
480        // error!("POLYFILLING FROM START TO FINISH!");
481        let start = match animation.translations.first() {
482          Some(start) => start,
483          None => panic!("translation was already checked, why did this panic!? 4"),
484        };
485        let finish = match animation.translations.last() {
486          Some(finish) => finish,
487          None => panic!("translation was already checked, why did this panic!? 5"),
488        };
489
490        for i in 0..required_frames {
491          // 0.0 to 1.0.
492          let current_percentile = i as f32 / (required_frames - 1) as f32;
493          // 0.0 to X max time.
494          let current_stamp = current_percentile * max_time;
495
496          // println!("current: {}", current_stamp);
497
498          let result = start.lerp(*finish, current_percentile);
499
500          // println!("result: {:?}", result);
501
502          new_finalized_channel
503            .translation_timestamps
504            .push(current_stamp);
505          new_finalized_channel.translations.push(result);
506        }
507      } else {
508        // And if we can't do either of those, now we have to brute force our way through the polyfill calculations. :(
509
510        // To begin this atrocity let's start by grabbing the current size of the animation container.
511        let old_frame_size = animation.translation_timestamps.len();
512
513        // This gives me great pain.
514        for i in 0..required_frames {
515          // 0.0 to 1.0.
516          let current_percentile = i as f32 / (required_frames - 1) as f32;
517          // 0.0 to X max time.
518          let current_stamp = current_percentile * max_time;
519          // 5 points of precision integral positioning.
520          let precise_stamp = into_precision(current_stamp);
521
522          // Okay now that we got our data, let's see if this model has it.
523          // We need index ONLY cause we have to walk back and forth.
524          // There might be a logic thing missing in here. If you find it. Halp.
525          // ? Fun begins here.
526          let mut found_frame_key = None;
527
528          // Let's find if we have a frame that already exists in the animation.
529          for i in 0..old_frame_size {
530            let gotten = animation.translation_timestamps[i];
531
532            let gotten_precise = into_precision(gotten);
533
534            // We got lucky and found an existing frame! :D
535            if gotten_precise == precise_stamp {
536              found_frame_key = Some(i);
537              break;
538            }
539
540            // And if this loop completes and we didn't find anything. We gotta get creative.
541          }
542
543          // If it's none we now have to either interpolate this thing or we have to insert it.
544          if found_frame_key.is_none() {
545            // If there's no starting keyframe.
546            // First of all, why is this allowed?
547            // Second of all, polyfill from the next available frame.
548            // We know this thing has more than 2 available frames at this point.
549            if precise_stamp == 0 {
550              new_finalized_channel
551                .translation_timestamps
552                .push(current_stamp);
553              // If this crashes, there's something truly horrible that has happened.
554              new_finalized_channel
555                .translations
556                .push(animation.translations[1]);
557            } else {
558              // Else we're going to have to figure this mess out.
559              // ! Here is where the program performance just tanks.
560
561              // ? So we have no direct frame, we have to find out 2 things:
562              // ? 1.) The leading frame.
563              // ? 2.) The following frame.
564              // ? Then we have to interpolate them together.
565
566              // This is an option because if it's none, we have to brute force with animation frame 0.
567              let mut leading_frame = None;
568
569              for i in 0..old_frame_size {
570                let gotten = animation.translation_timestamps[i];
571
572                let gotten_precise = into_precision(gotten);
573
574                // Here we check for a frame that is less than goal.
575                // aka, the leading frame.
576                // We already checked if it's got an equal to frame, there's only unequal to frames now.
577                // We need to let this keep going until it overshoots or else it won't be accurate.
578                if gotten_precise < precise_stamp {
579                  leading_frame = Some(i);
580                } else {
581                  // We overshot, now time to abort.
582                  break;
583                }
584              }
585
586              // ! If we have no leading leading frame is now whatever is first.
587              if leading_frame.is_none() {
588                leading_frame = Some(0);
589              }
590
591              // This is an option because if it's none, we have to brute force with animation frame 0.
592              let mut following_frame = None;
593
594              for i in 0..old_frame_size {
595                let gotten = animation.translation_timestamps[i];
596
597                let gotten_precise = into_precision(gotten);
598
599                // Here we check for a frame that is less than goal.
600                // aka, the leading frame.
601                // We already checked if it's got an equal to frame, there's only unequal to frames now.
602                // We need to let this keep going until it overshoots or else it won't be accurate.
603                if gotten_precise > precise_stamp {
604                  following_frame = Some(i);
605                }
606
607                // Can't do a logic gate in the previous statement. If it's found then break.
608                if following_frame.is_some() {
609                  break;
610                }
611              }
612
613              // ? If it's none, the safe fallback is to just equalize the start and finish, which is extremely wrong.
614              if following_frame.is_none() {
615                following_frame = leading_frame;
616              }
617
618              // Now we do the interpolation.
619              // This isn't perfect, but it's something.
620              match leading_frame {
621                Some(leader) => match following_frame {
622                  Some(follower) => {
623                    let lead_timestamp = animation.translation_timestamps[leader];
624                    let lead_translation = animation.translations[leader];
625
626                    let follow_timestamp = animation.translation_timestamps[follower];
627                    let follow_translation = animation.translations[follower];
628
629                    // This is a simple zeroing out of the scales.
630                    let scale = follow_timestamp - lead_timestamp;
631
632                    // Shift the current timestamp into the range of our work.
633                    let shifted_stamp = current_stamp - lead_timestamp;
634
635                    // Get it into 0.0 - 1.0.
636                    let finalized_percentile = shifted_stamp / scale;
637
638                    // println!("finalized: {}", finalized_percentile);
639
640                    let finalized_translation_interpolation =
641                      lead_translation.lerp(follow_translation, finalized_percentile);
642
643                    // Now we finally push the interpolated translation into the finalized animation channel.
644                    new_finalized_channel
645                      .translations
646                      .push(finalized_translation_interpolation);
647                    new_finalized_channel
648                      .translation_timestamps
649                      .push(current_stamp);
650                  }
651                  None => panic!("how?!"),
652                },
653                None => panic!("how?!"),
654              }
655            }
656          } else {
657            // ! We found a keyframe! :D
658            // If it's some we have an existing good frame, work with it.
659            let key = match found_frame_key {
660              Some(key) => key,
661              None => panic!("how is that even possible?!"),
662            };
663
664            // This should never blow up. That's immutable data it's working with, within range!
665            new_finalized_channel
666              .translation_timestamps
667              .push(animation.translation_timestamps[key]);
668
669            new_finalized_channel
670              .translations
671              .push(animation.translations[key]);
672          }
673
674          // println!("test: {:?}", found_frame_key);
675
676          // println!("{} {}", current_stamp, precise_stamp);
677        }
678
679        // panic!("minetest-gltf: This translation logic branch is disabled because I have no model that has this available yet. If this is hit. Give me your model.")
680      }
681    }
682
683    if new_finalized_channel.translation_timestamps.len()
684      != new_finalized_channel.translations.len()
685    {
686      panic!("BLEW UP! Mismatched translation lengths.");
687    }
688    if new_finalized_channel.translation_timestamps.len() != required_frames {
689      panic!(
690        "BLEW UP! translation frames Expected: {} got: {}",
691        required_frames,
692        new_finalized_channel.translation_timestamps.len()
693      );
694    }
695
696    // println!("t: {:?}", new_finalized_channel.translations);
697    // println!("t: {:?}", new_finalized_channel.translation_timestamps);
698
699    // println!("-=-=-=-=-");
700
701    // ? ////////////////////////////////////////////////////////////
702    // ?            ROTATIONS
703    // ? ////////////////////////////////////////////////////////////
704
705    // Final check for rotation equality.
706    if animation.rotation_timestamps.len() != animation.rotations.len() {
707      return Err(format!("Unequal animation rotation lengths in channel {}.", id).into());
708    }
709
710    if animation.rotation_timestamps.is_empty() {
711      // error!("hit none");
712      // If it's blank, we want to polyfill in default data.
713      for i in 0..required_frames {
714        new_finalized_channel
715          .rotation_timestamps
716          .push(i as f32 * min_distance);
717        new_finalized_channel.rotations.push(Quat::IDENTITY);
718      }
719    } else if animation.rotation_timestamps.len() == 1 {
720      // If there's only one, we can simply use the one rotation point as the entire rotation animation.
721      // error!("hit one");
722      let polyfill = match animation.rotations.first() {
723        Some(rotation) => rotation,
724        None => panic!("rotation was already checked, why did this panic!? 1"),
725      };
726
727      for i in 0..required_frames {
728        new_finalized_channel
729          .rotation_timestamps
730          .push(i as f32 * min_distance);
731        new_finalized_channel.rotations.push(*polyfill);
732      }
733    } else {
734      // Now if we can't polyfill with the easiest data set,
735      // we're going to have to get creative.
736
737      // error!("Hit another?");
738      // println!("got: {}", animation.rotation_timestamps.len());
739      // println!("got: {}", animation.rotations.len());
740
741      let mut raw_add = false;
742
743      // Let's see if we can take the easist route with start to finish polyfill.
744      match animation.rotation_timestamps.first() {
745        Some(first_timestamp) => {
746          if into_precision(*first_timestamp) == 0 {
747            match animation.rotation_timestamps.last() {
748              Some(last_timestamp) => {
749                if into_precision(*last_timestamp) == into_precision(max_time) {
750                  raw_add = true;
751                }
752              }
753              None => panic!("rotation was already checked, why did this panic!? 2"),
754            }
755          }
756        }
757        None => panic!("rotation was already checked, why did this panic!? 3"),
758      }
759
760      // Now if we can raw add let's see if we can just dump the raw frames in because they're finalized.
761      if raw_add && animation.rotation_timestamps.len() == required_frames {
762        // We can!
763        // error!("OKAY TO RAW ADD!");
764        new_finalized_channel.rotation_timestamps = animation.rotation_timestamps.clone();
765        new_finalized_channel.rotations = animation.rotations.clone();
766      } else if raw_add && animation.rotation_timestamps.len() == 2 {
767        // But if we only have the start and finish, we now have to polyfill between beginning and end.
768        // error!("POLYFILLING FROM START TO FINISH!");
769        let start = match animation.rotations.first() {
770          Some(start) => start,
771          None => panic!("rotation was already checked, why did this panic!? 4"),
772        };
773        let finish = match animation.rotations.last() {
774          Some(finish) => finish,
775          None => panic!("rotation was already checked, why did this panic!? 5"),
776        };
777
778        for i in 0..required_frames {
779          // 0.0 to 1.0.
780          let current_percentile = i as f32 / (required_frames - 1) as f32;
781          // 0.0 to X max time.
782          let current_stamp = current_percentile * max_time;
783
784          // println!("current: {}", current_stamp);
785
786          let result = start.lerp(*finish, current_percentile);
787
788          // println!("result: {:?}", result);
789
790          new_finalized_channel
791            .rotation_timestamps
792            .push(current_stamp);
793          new_finalized_channel.rotations.push(result);
794        }
795      } else {
796        // And if we can't do either of those, now we have to brute force our way through the polyfill calculations. :(
797
798        // To begin this atrocity let's start by grabbing the current size of the animation container.
799        let old_frame_size = animation.rotation_timestamps.len();
800
801        // This gives me great pain.
802        for i in 0..required_frames {
803          // 0.0 to 1.0.
804          let current_percentile = i as f32 / (required_frames - 1) as f32;
805          // 0.0 to X max time.
806          let current_stamp = current_percentile * max_time;
807          // 5 points of precision integral positioning.
808          let precise_stamp = into_precision(current_stamp);
809
810          // Okay now that we got our data, let's see if this model has it.
811          // We need index ONLY cause we have to walk back and forth.
812          // There might be a logic thing missing in here. If you find it. Halp.
813          // ? Fun begins here.
814          let mut found_frame_key = None;
815
816          // Let's find if we have a frame that already exists in the animation.
817          for i in 0..old_frame_size {
818            let gotten = animation.rotation_timestamps[i];
819
820            let gotten_precise = into_precision(gotten);
821
822            // We got lucky and found an existing frame! :D
823            if gotten_precise == precise_stamp {
824              found_frame_key = Some(i);
825              break;
826            }
827
828            // And if this loop completes and we didn't find anything. We gotta get creative.
829          }
830
831          // If it's none we now have to either interpolate this thing or we have to insert it.
832          if found_frame_key.is_none() {
833            // If there's no starting keyframe.
834            // First of all, why is this allowed?
835            // Second of all, polyfill from the next available frame.
836            // We know this thing has more than 2 available frames at this point.
837            if precise_stamp == 0 {
838              new_finalized_channel
839                .rotation_timestamps
840                .push(current_stamp);
841              // If this crashes, there's something truly horrible that has happened.
842              new_finalized_channel.rotations.push(animation.rotations[1]);
843            } else {
844              // Else we're going to have to figure this mess out.
845              // ! Here is where the program performance just tanks.
846
847              // ? So we have no direct frame, we have to find out 2 things:
848              // ? 1.) The leading frame.
849              // ? 2.) The following frame.
850              // ? Then we have to interpolate them together.
851
852              // This is an option because if it's none, we have to brute force with animation frame 0.
853              let mut leading_frame = None;
854
855              for i in 0..old_frame_size {
856                let gotten = animation.rotation_timestamps[i];
857
858                let gotten_precise = into_precision(gotten);
859
860                // Here we check for a frame that is less than goal.
861                // aka, the leading frame.
862                // We already checked if it's got an equal to frame, there's only unequal to frames now.
863                // We need to let this keep going until it overshoots or else it won't be accurate.
864                if gotten_precise < precise_stamp {
865                  leading_frame = Some(i);
866                } else {
867                  // We overshot, now time to abort.
868                  break;
869                }
870              }
871
872              // ! If we have no leading leading frame is now whatever is first.
873              if leading_frame.is_none() {
874                leading_frame = Some(0);
875              }
876
877              // This is an option because if it's none, we have to brute force with animation frame 0.
878              let mut following_frame = None;
879
880              for i in 0..old_frame_size {
881                let gotten = animation.rotation_timestamps[i];
882
883                let gotten_precise = into_precision(gotten);
884
885                // Here we check for a frame that is less than goal.
886                // aka, the leading frame.
887                // We already checked if it's got an equal to frame, there's only unequal to frames now.
888                // We need to let this keep going until it overshoots or else it won't be accurate.
889                if gotten_precise > precise_stamp {
890                  following_frame = Some(i);
891                }
892
893                // Can't do a logic gate in the previous statement. If it's found then break.
894                if following_frame.is_some() {
895                  break;
896                }
897              }
898
899              // ? If it's none, the safe fallback is to just equalize the start and finish, which is extremely wrong.
900              if following_frame.is_none() {
901                following_frame = leading_frame;
902              }
903
904              // Now we do the interpolation.
905              // This isn't perfect, but it's something.
906              match leading_frame {
907                Some(leader) => match following_frame {
908                  Some(follower) => {
909                    let lead_timestamp = animation.rotation_timestamps[leader];
910                    let lead_rotation = animation.rotations[leader];
911
912                    let follow_timestamp = animation.rotation_timestamps[follower];
913                    let follow_rotation = animation.rotations[follower];
914
915                    // This is a simple zeroing out of the scales.
916                    let scale = follow_timestamp - lead_timestamp;
917
918                    // Shift the current timestamp into the range of our work.
919                    let shifted_stamp = current_stamp - lead_timestamp;
920
921                    // Get it into 0.0 - 1.0.
922                    let finalized_percentile = shifted_stamp / scale;
923
924                    // println!("finalized: {}", finalized_percentile);
925
926                    let finalized_rotation_interpolation =
927                      lead_rotation.lerp(follow_rotation, finalized_percentile);
928
929                    // Now we finally push the interpolated rotation into the finalized animation channel.
930                    new_finalized_channel
931                      .rotations
932                      .push(finalized_rotation_interpolation);
933                    new_finalized_channel
934                      .rotation_timestamps
935                      .push(current_stamp);
936                  }
937                  None => panic!("how?!"),
938                },
939                None => panic!("how?!"),
940              }
941            }
942          } else {
943            // ! We found a keyframe! :D
944            // If it's some we have an existing good frame, work with it.
945            let key = match found_frame_key {
946              Some(key) => key,
947              None => panic!("how is that even possible?!"),
948            };
949
950            // This should never blow up. That's immutable data it's working with, within range!
951            new_finalized_channel
952              .rotation_timestamps
953              .push(animation.rotation_timestamps[key]);
954
955            new_finalized_channel
956              .rotations
957              .push(animation.rotations[key]);
958          }
959
960          // println!("test: {:?}", found_frame_key);
961
962          // println!("{} {}", current_stamp, precise_stamp);
963        }
964
965        // panic!("minetest-gltf: This rotation logic branch is disabled because I have no model that has this available yet. If this is hit. Give me your model.")
966      }
967    }
968
969    if new_finalized_channel.rotation_timestamps.len() != new_finalized_channel.rotations.len() {
970      panic!("BLEW UP! Mismatched rotation lengths.");
971    }
972    if new_finalized_channel.rotation_timestamps.len() != required_frames {
973      panic!(
974        "BLEW UP! rotation frames Expected: {} got: {}",
975        required_frames,
976        new_finalized_channel.rotation_timestamps.len()
977      );
978    }
979
980    // println!("t: {:?}", new_finalized_channel.rotations);
981    // println!("t: {:?}", new_finalized_channel.rotation_timestamps);
982
983    // ? ////////////////////////////////////////////////////////////
984    // ?            SCALES
985    // ? ////////////////////////////////////////////////////////////
986
987    // Final check for scale equality.
988    if animation.scale_timestamps.len() != animation.scales.len() {
989      return Err(format!("Unequal animation scale lengths in channel {}.", id).into());
990    }
991
992    if animation.scale_timestamps.is_empty() {
993      // error!("hit none");
994      // If it's blank, we want to polyfill in default data.
995      for i in 0..required_frames {
996        new_finalized_channel
997          .scale_timestamps
998          .push(i as f32 * min_distance);
999        new_finalized_channel.scales.push(Vec3::new(1.0, 1.0, 1.0));
1000      }
1001    } else if animation.scale_timestamps.len() == 1 {
1002      // If there's only one, we can simply use the one scale point as the entire scale animation.
1003      // error!("hit one");
1004      let polyfill = match animation.scales.first() {
1005        Some(scale) => scale,
1006        None => panic!("scale was already checked, why did this panic!? 1"),
1007      };
1008
1009      for i in 0..required_frames {
1010        new_finalized_channel
1011          .scale_timestamps
1012          .push(i as f32 * min_distance);
1013        new_finalized_channel.scales.push(*polyfill);
1014      }
1015    } else {
1016      // Now if we can't polyfill with the easiest data set,
1017      // we're going to have to get creative.
1018
1019      // error!("Hit another?");
1020      // println!("got: {}", animation.scale_timestamps.len());
1021      // println!("got: {}", animation.scales.len());
1022
1023      let mut raw_add = false;
1024
1025      // Let's see if we can take the easist route with start to finish polyfill.
1026      match animation.scale_timestamps.first() {
1027        Some(first_timestamp) => {
1028          if into_precision(*first_timestamp) == 0 {
1029            match animation.scale_timestamps.last() {
1030              Some(last_timestamp) => {
1031                if into_precision(*last_timestamp) == into_precision(max_time) {
1032                  raw_add = true;
1033                }
1034              }
1035              None => panic!("scale was already checked, why did this panic!? 2"),
1036            }
1037          }
1038        }
1039        None => panic!("scale was already checked, why did this panic!? 3"),
1040      }
1041
1042      // Now if we can raw add let's see if we can just dump the raw frames in because they're finalized.
1043      if raw_add && animation.scale_timestamps.len() == required_frames {
1044        // We can!
1045        // error!("OKAY TO RAW ADD!");
1046        new_finalized_channel.scale_timestamps = animation.scale_timestamps.clone();
1047        new_finalized_channel.scales = animation.scales.clone();
1048      } else if raw_add && animation.scale_timestamps.len() == 2 {
1049        // But if we only have the start and finish, we now have to polyfill between beginning and end.
1050        // error!("POLYFILLING FROM START TO FINISH!");
1051        let start = match animation.scales.first() {
1052          Some(start) => start,
1053          None => panic!("scale was already checked, why did this panic!? 4"),
1054        };
1055        let finish = match animation.scales.last() {
1056          Some(finish) => finish,
1057          None => panic!("scale was already checked, why did this panic!? 5"),
1058        };
1059
1060        for i in 0..required_frames {
1061          // 0.0 to 1.0.
1062          let current_percentile = i as f32 / (required_frames - 1) as f32;
1063          // 0.0 to X max time.
1064          let current_stamp = current_percentile * max_time;
1065
1066          // println!("current: {}", current_stamp);
1067
1068          let result = start.lerp(*finish, current_percentile);
1069
1070          // println!("result: {:?}", result);
1071
1072          new_finalized_channel.scale_timestamps.push(current_stamp);
1073          new_finalized_channel.scales.push(result);
1074        }
1075      } else {
1076        // And if we can't do either of those, now we have to brute force our way through the polyfill calculations. :(
1077
1078        // To begin this atrocity let's start by grabbing the current size of the animation container.
1079        let old_frame_size = animation.scale_timestamps.len();
1080
1081        // This gives me great pain.
1082        for i in 0..required_frames {
1083          // 0.0 to 1.0.
1084          let current_percentile = i as f32 / (required_frames - 1) as f32;
1085          // 0.0 to X max time.
1086          let current_stamp = current_percentile * max_time;
1087          // 5 points of precision integral positioning.
1088          let precise_stamp = into_precision(current_stamp);
1089
1090          // Okay now that we got our data, let's see if this model has it.
1091          // We need index ONLY cause we have to walk back and forth.
1092          // There might be a logic thing missing in here. If you find it. Halp.
1093          // ? Fun begins here.
1094          let mut found_frame_key = None;
1095
1096          // Let's find if we have a frame that already exists in the animation.
1097          for i in 0..old_frame_size {
1098            let gotten = animation.scale_timestamps[i];
1099
1100            let gotten_precise = into_precision(gotten);
1101
1102            // We got lucky and found an existing frame! :D
1103            if gotten_precise == precise_stamp {
1104              found_frame_key = Some(i);
1105              break;
1106            }
1107
1108            // And if this loop completes and we didn't find anything. We gotta get creative.
1109          }
1110
1111          // If it's none we now have to either interpolate this thing or we have to insert it.
1112          if found_frame_key.is_none() {
1113            // If there's no starting keyframe.
1114            // First of all, why is this allowed?
1115            // Second of all, polyfill from the next available frame.
1116            // We know this thing has more than 2 available frames at this point.
1117            if precise_stamp == 0 {
1118              new_finalized_channel.scale_timestamps.push(current_stamp);
1119              // If this crashes, there's something truly horrible that has happened.
1120              new_finalized_channel.scales.push(animation.scales[1]);
1121            } else {
1122              // Else we're going to have to figure this mess out.
1123              // ! Here is where the program performance just tanks.
1124
1125              // ? So we have no direct frame, we have to find out 2 things:
1126              // ? 1.) The leading frame.
1127              // ? 2.) The following frame.
1128              // ? Then we have to interpolate them together.
1129
1130              // This is an option because if it's none, we have to brute force with animation frame 0.
1131              let mut leading_frame = None;
1132
1133              for i in 0..old_frame_size {
1134                let gotten = animation.scale_timestamps[i];
1135
1136                let gotten_precise = into_precision(gotten);
1137
1138                // Here we check for a frame that is less than goal.
1139                // aka, the leading frame.
1140                // We already checked if it's got an equal to frame, there's only unequal to frames now.
1141                // We need to let this keep going until it overshoots or else it won't be accurate.
1142                if gotten_precise < precise_stamp {
1143                  leading_frame = Some(i);
1144                } else {
1145                  // We overshot, now time to abort.
1146                  break;
1147                }
1148              }
1149
1150              // ! If we have no leading leading frame is now whatever is first.
1151              if leading_frame.is_none() {
1152                leading_frame = Some(0);
1153              }
1154
1155              // This is an option because if it's none, we have to brute force with animation frame 0.
1156              let mut following_frame = None;
1157
1158              for i in 0..old_frame_size {
1159                let gotten = animation.scale_timestamps[i];
1160
1161                let gotten_precise = into_precision(gotten);
1162
1163                // Here we check for a frame that is less than goal.
1164                // aka, the leading frame.
1165                // We already checked if it's got an equal to frame, there's only unequal to frames now.
1166                // We need to let this keep going until it overshoots or else it won't be accurate.
1167                if gotten_precise > precise_stamp {
1168                  following_frame = Some(i);
1169                }
1170
1171                // Can't do a logic gate in the previous statement. If it's found then break.
1172                if following_frame.is_some() {
1173                  break;
1174                }
1175              }
1176
1177              // ? If it's none, the safe fallback is to just equalize the start and finish, which is extremely wrong.
1178              if following_frame.is_none() {
1179                following_frame = leading_frame;
1180              }
1181
1182              // Now we do the interpolation.
1183              // This isn't perfect, but it's something.
1184              match leading_frame {
1185                Some(leader) => match following_frame {
1186                  Some(follower) => {
1187                    let lead_timestamp = animation.scale_timestamps[leader];
1188                    let lead_scale = animation.scales[leader];
1189
1190                    let follow_timestamp = animation.scale_timestamps[follower];
1191                    let follow_scale = animation.scales[follower];
1192
1193                    // This is a simple zeroing out of the scales.
1194                    let scale = follow_timestamp - lead_timestamp;
1195
1196                    // Shift the current timestamp into the range of our work.
1197                    let shifted_stamp = current_stamp - lead_timestamp;
1198
1199                    // Get it into 0.0 - 1.0.
1200                    let finalized_percentile = shifted_stamp / scale;
1201
1202                    // println!("finalized: {}", finalized_percentile);
1203
1204                    let finalized_scale_interpolation =
1205                      lead_scale.lerp(follow_scale, finalized_percentile);
1206
1207                    // Now we finally push the interpolated scale into the finalized animation channel.
1208                    new_finalized_channel
1209                      .scales
1210                      .push(finalized_scale_interpolation);
1211                    new_finalized_channel.scale_timestamps.push(current_stamp);
1212                  }
1213                  None => panic!("how?!"),
1214                },
1215                None => panic!("how?!"),
1216              }
1217            }
1218          } else {
1219            // ! We found a keyframe! :D
1220            // If it's some we have an existing good frame, work with it.
1221            let key = match found_frame_key {
1222              Some(key) => key,
1223              None => panic!("how is that even possible?!"),
1224            };
1225
1226            // This should never blow up. That's immutable data it's working with, within range!
1227            new_finalized_channel
1228              .scale_timestamps
1229              .push(animation.scale_timestamps[key]);
1230
1231            new_finalized_channel.scales.push(animation.scales[key]);
1232          }
1233
1234          // println!("test: {:?}", found_frame_key);
1235
1236          // println!("{} {}", current_stamp, precise_stamp);
1237        }
1238
1239        // panic!("minetest-gltf: This scale logic branch is disabled because I have no model that has this available yet. If this is hit. Give me your model.")
1240      }
1241    }
1242
1243    if new_finalized_channel.scale_timestamps.len() != new_finalized_channel.scales.len() {
1244      panic!("BLEW UP! Mismatched scale lengths.");
1245    }
1246    if new_finalized_channel.scale_timestamps.len() != required_frames {
1247      panic!(
1248        "BLEW UP! scale frames Expected: {} got: {}",
1249        required_frames,
1250        new_finalized_channel.scale_timestamps.len()
1251      );
1252    }
1253
1254    // println!("t: {:?}", new_finalized_channel.scales);
1255    // println!("t: {:?}", new_finalized_channel.scale_timestamps);
1256
1257    // println!("-=-=-=-=-");
1258
1259    // Finally add it in.
1260    // println!("Adding in channel: {}", id);
1261    finalized_bone_animations.insert(*id, new_finalized_channel);
1262  }
1263
1264  // Then insert the finalized data here.
1265  minetest_gltf.bone_animations = Some(finalized_bone_animations);
1266  minetest_gltf.is_animated = true;
1267
1268  Ok(())
1269}