mmd_anim_runtime/runtime/
world.rs1use glam::Mat4;
2
3use crate::{
4 append_primitive::{AppendPrimitiveInput, solve_append_transform},
5 ik_primitive::constrain_rotation_to_axis,
6};
7
8#[cfg(test)]
9use super::WorldMatrixBoneUpdateCategory;
10
11use super::RuntimeInstance;
12
13impl RuntimeInstance {
14 pub(super) fn update_world_matrices(&mut self) {
15 self.update_world_matrices_from_eval_order_position(0);
16 }
17
18 pub(super) fn update_world_matrices_from_eval_order_position(&mut self, start_position: usize) {
19 self.update_world_matrices_from_eval_order_position_for_phase(start_position, None);
20 }
21
22 pub(super) fn update_world_matrices_from_eval_order_position_for_phase(
23 &mut self,
24 start_position: usize,
25 phase: Option<bool>,
26 ) {
27 let start_position =
28 self.expand_update_start_for_append_dependencies(start_position, phase);
29 match phase {
30 None => {
31 for bone in &self.model.eval_order()[start_position..] {
32 self.pose.reset_append_transform(*bone);
33 }
34 for position in start_position..self.model.eval_order().len() {
35 let bone = self.model.eval_order()[position];
36 self.update_append_transform_for_bone(bone);
37 self.update_world_matrix_for_bone(bone);
38 }
39 }
40 Some(after_physics) => {
41 let phase_bone_count = self.model.eval_order_for_phase(after_physics).len();
42 for phase_index in 0..phase_bone_count {
43 let bone = self.model.eval_order_for_phase(after_physics)[phase_index];
44 if self.model.eval_order_position(bone) < start_position {
45 continue;
46 }
47 self.pose.reset_append_transform(bone);
48 }
49 for phase_index in 0..phase_bone_count {
50 let bone = self.model.eval_order_for_phase(after_physics)[phase_index];
51 if self.model.eval_order_position(bone) < start_position {
52 continue;
53 }
54 self.update_append_transform_for_bone(bone);
55 self.update_world_matrix_for_bone(bone);
56 }
57 }
58 }
59 }
60
61 pub(super) fn update_world_matrices_using_current_append_from_eval_order_position(
62 &mut self,
63 start_position: usize,
64 ) {
65 self.update_world_matrices_using_current_append_from_eval_order_position_for_phase(
66 start_position,
67 None,
68 );
69 }
70
71 pub(super) fn update_world_matrices_using_current_append_from_eval_order_position_for_phase(
72 &mut self,
73 start_position: usize,
74 phase: Option<bool>,
75 ) {
76 match phase {
77 None => {
78 for position in start_position..self.model.eval_order().len() {
79 let bone = self.model.eval_order()[position];
80 self.update_world_matrix_for_bone(bone);
81 }
82 }
83 Some(after_physics) => {
84 let phase_bone_count = self.model.eval_order_for_phase(after_physics).len();
85 for phase_index in 0..phase_bone_count {
86 let bone = self.model.eval_order_for_phase(after_physics)[phase_index];
87 if self.model.eval_order_position(bone) < start_position {
88 continue;
89 }
90 self.update_world_matrix_for_bone(bone);
91 }
92 }
93 }
94 }
95
96 #[inline]
97 pub(super) fn bone_matches_phase(&self, bone: crate::BoneIndex, phase: Option<bool>) -> bool {
98 phase.is_none_or(|after_physics| self.model.transform_after_physics(bone) == after_physics)
99 }
100
101 pub(super) fn update_append_transform_for_bone(&mut self, bone: crate::BoneIndex) {
102 let mut visiting = Vec::new();
103 self.update_append_transform_for_bone_inner(bone, &mut visiting);
104 }
105
106 fn update_append_transform_for_bone_inner(
107 &mut self,
108 bone: crate::BoneIndex,
109 visiting: &mut Vec<crate::BoneIndex>,
110 ) {
111 let Some(append_index) = self.model.append_transform_index(bone) else {
112 return;
113 };
114 if visiting.contains(&bone) {
115 return;
116 }
117 visiting.push(bone);
118 let append = self.model.append_transform(append_index);
119 let source_bone = append.source_bone;
120 let ratio = append.ratio;
121 let affect_rotation = append.affect_rotation;
122 let affect_translation = append.affect_translation;
123 let use_source_append =
124 !append.local && self.model.append_transform_index(source_bone).is_some();
125 if use_source_append {
126 self.update_append_transform_for_bone_inner(source_bone, visiting);
127 }
128 let mut source_rotation = if use_source_append {
129 self.pose.append_rotation(source_bone)
130 } else {
131 self.pose.local_rotation(source_bone)
132 };
133 if use_source_append && self.model.is_ik_link_bone(source_bone) {
134 source_rotation = (self.pose.ik_rotation(source_bone) * source_rotation).normalize();
135 }
136 let source_position_offset = if use_source_append {
137 self.pose.append_position_offset(source_bone)
138 } else {
139 self.pose.local_position_offset(source_bone)
140 };
141 let append_output = solve_append_transform(AppendPrimitiveInput {
142 source_position_offset,
143 source_rotation,
144 ratio,
145 affect_rotation,
146 affect_translation,
147 });
148 self.pose.set_append_rotation(bone, append_output.rotation);
149 self.pose
150 .set_append_position_offset(bone, append_output.position_offset);
151 visiting.pop();
152 }
153
154 pub(super) fn update_world_matrix_for_bone(&mut self, bone: crate::BoneIndex) {
155 #[cfg(test)]
156 {
157 self.world_matrix_bone_update_count += 1;
158 match self.world_matrix_bone_update_category {
159 WorldMatrixBoneUpdateCategory::LeadingBookend => {
160 self.world_matrix_bone_update_leading_bookend_count += 1;
161 }
162 WorldMatrixBoneUpdateCategory::PhaseLoop => {
163 self.world_matrix_bone_update_phase_loop_count += 1;
164 }
165 WorldMatrixBoneUpdateCategory::TrailingBookend => {
166 self.world_matrix_bone_update_trailing_bookend_count += 1;
167 }
168 WorldMatrixBoneUpdateCategory::IkLinkChange => {
169 self.world_matrix_bone_update_ik_link_change_count += 1;
170 }
171 WorldMatrixBoneUpdateCategory::Other => {
172 self.world_matrix_bone_update_other_count += 1;
173 }
174 }
175 }
176 let mut local_position =
177 self.model.rest_position(bone) + self.pose.local_position_offset(bone);
178 let mut local_rotation = self.pose.local_rotation(bone);
179 let local_scale = self.pose.local_scale(bone);
180
181 if let Some(append_index) = self.model.append_transform_index(bone) {
182 let append = self.model.append_transform(append_index);
183 if append.affect_rotation {
184 local_rotation = (local_rotation * self.pose.append_rotation(bone)).normalize();
185 }
186 if append.affect_translation {
187 local_position += self.pose.append_position_offset(bone);
188 }
189 }
190
191 if let Some(axis) = self.model.fixed_axis_constraint(bone) {
192 local_rotation = constrain_rotation_to_axis(local_rotation, axis);
193 }
194
195 let local_matrix = Mat4::from_scale_rotation_translation(
196 local_scale.into(),
197 local_rotation,
198 local_position.into(),
199 );
200
201 let world_matrix = match self.model.parent_index(bone) {
202 Some(parent) => self.pose.world_matrices()[parent.as_usize()] * local_matrix,
203 None => local_matrix,
204 };
205
206 self.pose.set_world_matrix(bone, world_matrix);
207 self.pose
208 .set_skinning_matrix(bone, world_matrix * self.model.inverse_bind_matrix(bone));
209 }
210
211 pub(super) fn expand_update_start_for_append_dependencies(
212 &self,
213 start_position: usize,
214 phase: Option<bool>,
215 ) -> usize {
216 let mut start = start_position;
217 loop {
218 let mut changed = false;
219 for append in self.model.append_transforms() {
220 if !self.bone_matches_phase(append.target_bone, phase) {
221 continue;
222 }
223 let source_position = self.model.eval_order_position(append.source_bone);
224 let target_position = self.model.eval_order_position(append.target_bone);
225 if source_position >= start && target_position < start {
226 start = target_position;
227 changed = true;
228 }
229 }
230 if !changed {
231 return start;
232 }
233 }
234 }
235
236 #[inline]
237 pub fn world_matrices(&self) -> &[Mat4] {
238 self.pose.world_matrices()
239 }
240
241 #[cfg(test)]
242 pub(super) fn set_world_matrix_bone_update_category(
243 &mut self,
244 category: WorldMatrixBoneUpdateCategory,
245 ) {
246 self.world_matrix_bone_update_category = category;
247 }
248
249 #[cfg(test)]
250 pub(super) fn reset_world_matrix_bone_update_count(&mut self) {
251 self.world_matrix_bone_update_count = 0;
252 self.world_matrix_bone_update_leading_bookend_count = 0;
253 self.world_matrix_bone_update_phase_loop_count = 0;
254 self.world_matrix_bone_update_trailing_bookend_count = 0;
255 self.world_matrix_bone_update_ik_link_change_count = 0;
256 self.world_matrix_bone_update_other_count = 0;
257 self.world_matrix_bone_update_category = WorldMatrixBoneUpdateCategory::Other;
258 }
259
260 #[cfg(test)]
261 pub(super) fn world_matrix_bone_update_count(&self) -> usize {
262 self.world_matrix_bone_update_count
263 }
264
265 #[cfg(test)]
266 pub(super) fn world_matrix_bone_update_leading_bookend_count(&self) -> usize {
267 self.world_matrix_bone_update_leading_bookend_count
268 }
269
270 #[cfg(test)]
271 pub(super) fn world_matrix_bone_update_phase_loop_count(&self) -> usize {
272 self.world_matrix_bone_update_phase_loop_count
273 }
274
275 #[cfg(test)]
276 pub(super) fn world_matrix_bone_update_trailing_bookend_count(&self) -> usize {
277 self.world_matrix_bone_update_trailing_bookend_count
278 }
279
280 #[cfg(test)]
281 pub(super) fn world_matrix_bone_update_ik_link_change_count(&self) -> usize {
282 self.world_matrix_bone_update_ik_link_change_count
283 }
284
285 #[cfg(test)]
286 pub(super) fn world_matrix_bone_update_other_count(&self) -> usize {
287 self.world_matrix_bone_update_other_count
288 }
289
290 #[inline]
291 pub fn skinning_matrices(&self) -> &[Mat4] {
292 self.pose.skinning_matrices()
293 }
294}