1use std::sync::Arc;
2
3use glam::Quat;
4
5use crate::ik_primitive::ChainLinkState;
6use crate::{AnimationClip, ModelArena, PoseArena};
7
8mod ik;
9mod morph;
10mod world;
11
12#[cfg(test)]
13use crate::ik_primitive::{
14 LimitedAxesLinkStepInput, PlaneLinkStepInput, axis_vec, decompose_euler_xyz, euler_xyz_to_quat,
15 limit_axis_bounds, quat_to_rotation_mat3, signed_projected_angle, solve_limited_axes_link_step,
16 solve_plane_link_step,
17};
18
19#[derive(Debug)]
20struct IkScratch {
21 links: Vec<crate::IkLink>,
22 base_rotations: Vec<Quat>,
23 base_ik_rotations: Vec<Quat>,
24 ik_rotations: Vec<Quat>,
25 best_ik_rotations: Vec<Quat>,
26 chain_states: Vec<ChainLinkState>,
27}
28
29impl IkScratch {
30 fn new(model: &ModelArena) -> Self {
31 let max_links = model
32 .ik_solvers()
33 .iter()
34 .map(|s| s.links.len())
35 .max()
36 .unwrap_or(0);
37 IkScratch {
38 links: Vec::with_capacity(max_links),
39 base_rotations: Vec::with_capacity(max_links),
40 base_ik_rotations: Vec::with_capacity(max_links),
41 ik_rotations: Vec::with_capacity(max_links),
42 best_ik_rotations: Vec::with_capacity(max_links),
43 chain_states: Vec::with_capacity(max_links),
44 }
45 }
46}
47
48#[derive(Debug)]
49struct MorphScratch {
50 expanded_weights: Vec<f32>,
51}
52
53impl MorphScratch {
54 fn new(morph_count: usize) -> Self {
55 Self {
56 expanded_weights: vec![0.0; morph_count],
57 }
58 }
59}
60
61#[derive(Clone, Copy, Debug, Default, PartialEq)]
62pub struct IkSolverRuntimeStats {
63 pub solver_evaluations: u64,
64 pub configured_iterations: u64,
65 pub executed_iterations: u64,
66 pub tolerance_precheck_breaks: u64,
67 pub tolerance_post_iteration_breaks: u64,
68 pub rollback_breaks: u64,
69 pub max_iteration_exhaustions: u64,
70 pub link_visits: u64,
71 pub link_steps: u64,
72 pub final_distance_sum: f64,
73 pub final_distance_max: f32,
74 pub exhausted_final_distance_sum: f64,
75 pub exhausted_final_distance_max: f32,
76}
77
78impl IkSolverRuntimeStats {
79 fn reset(&mut self) {
80 *self = Self::default();
81 }
82}
83
84#[derive(Clone, Copy, Debug, PartialEq)]
85pub struct IkSolveOptions {
86 pub tolerance: f32,
87 pub max_iterations_cap: Option<u32>,
88}
89
90impl Default for IkSolveOptions {
91 fn default() -> Self {
92 Self {
93 tolerance: 0.0,
94 max_iterations_cap: None,
95 }
96 }
97}
98
99#[derive(Debug)]
100pub struct RuntimeInstance {
101 model: Arc<ModelArena>,
102 pose: PoseArena,
103 ik_scratch: IkScratch,
104 morph_scratch: MorphScratch,
105 ik_stats: Vec<IkSolverRuntimeStats>,
106 #[cfg(test)]
107 world_matrix_bone_update_count: usize,
108}
109
110impl RuntimeInstance {
111 pub fn new(model: Arc<ModelArena>) -> Self {
112 let morph_count = model.morph_count() as usize;
113 Self::new_with_morph_count(model, morph_count)
114 }
115
116 pub fn new_with_morph_count(model: Arc<ModelArena>, morph_count: usize) -> Self {
117 let ik_count = model.ik_count();
118 Self::new_with_counts(model, morph_count, ik_count)
119 }
120
121 pub fn new_with_counts(model: Arc<ModelArena>, morph_count: usize, ik_count: usize) -> Self {
122 let morph_count = morph_count.max(model.morph_count() as usize);
123 let pose = PoseArena::new_with_counts(model.bone_count(), morph_count, ik_count);
124 let ik_scratch = IkScratch::new(&model);
125 let morph_scratch = MorphScratch::new(morph_count);
126 let ik_stats = vec![IkSolverRuntimeStats::default(); model.ik_count()];
127 Self {
128 model,
129 pose,
130 ik_scratch,
131 morph_scratch,
132 ik_stats,
133 #[cfg(test)]
134 world_matrix_bone_update_count: 0,
135 }
136 }
137
138 #[inline]
139 pub fn model(&self) -> &ModelArena {
140 &self.model
141 }
142
143 #[inline]
144 pub fn pose(&self) -> &PoseArena {
145 &self.pose
146 }
147
148 #[inline]
149 pub fn pose_mut(&mut self) -> &mut PoseArena {
150 &mut self.pose
151 }
152
153 pub fn evaluate_current_pose(&mut self) {
154 self.pose.reset_ik_rotations();
155 self.evaluate_current_pose_ordered(IkSolveOptions::default());
156 }
157
158 pub fn evaluate_current_pose_with_ik_options(&mut self, options: IkSolveOptions) {
159 self.pose.reset_ik_rotations();
160 self.evaluate_current_pose_ordered(options);
161 }
162
163 pub fn evaluate_current_pose_without_ik(&mut self) {
167 self.pose.reset_ik_rotations();
168 self.update_world_matrices();
169 }
170
171 fn evaluate_current_pose_ordered(&mut self, options: IkSolveOptions) {
172 self.pose.reset_append_transforms();
173 self.update_world_matrices_using_current_append_from_eval_order_position(0);
174
175 for after_physics in [false, true] {
176 for position in 0..self.model.eval_order().len() {
177 let bone = self.model.eval_order()[position];
178 if self.model.transform_after_physics(bone) != after_physics {
179 continue;
180 }
181
182 if self.model.append_transform_index(bone).is_some() {
183 self.pose.reset_append_transform(bone);
184 self.update_append_transform_for_bone(bone);
185 }
186 self.update_world_matrix_for_bone(bone);
187
188 for ik_index in 0..self.model.ik_count() {
189 if self.model.ik_solvers()[ik_index].ik_bone == bone {
190 self.solve_ik_solver(ik_index, options, after_physics);
191 }
192 }
193 }
194 }
195 self.update_world_matrices_using_current_append_from_eval_order_position(0);
196 }
197
198 pub fn evaluate_rest_pose(&mut self) {
199 self.pose.reset_local_pose();
200 self.evaluate_current_pose();
201 }
202
203 pub fn evaluate_clip_frame(&mut self, clip: &AnimationClip, frame: f32) {
204 clip.apply_to_pose(frame, &mut self.pose);
205 self.expand_morphs();
206 self.evaluate_current_pose();
207 }
208
209 pub fn evaluate_clip_frame_with_ik_options(
210 &mut self,
211 clip: &AnimationClip,
212 frame: f32,
213 options: IkSolveOptions,
214 ) {
215 clip.apply_to_pose(frame, &mut self.pose);
216 self.expand_morphs();
217 self.evaluate_current_pose_with_ik_options(options);
218 }
219
220 pub fn evaluate_clip_frame_without_ik(&mut self, clip: &AnimationClip, frame: f32) {
225 clip.apply_to_pose(frame, &mut self.pose);
226 self.expand_morphs();
227 self.pose.reset_ik_rotations();
228 self.update_world_matrices();
229 }
230
231 pub fn reset_ik_runtime_stats(&mut self) {
232 for stats in &mut self.ik_stats {
233 stats.reset();
234 }
235 }
236
237 pub fn ik_runtime_stats(&self) -> &[IkSolverRuntimeStats] {
238 &self.ik_stats
239 }
240
241 #[inline]
242 pub fn ik_enabled(&self) -> &[u8] {
243 self.pose.ik_enabled()
244 }
245}
246
247#[cfg(test)]
248mod tests;