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
8use super::RuntimeInstance;
9
10impl RuntimeInstance {
11 pub(super) fn update_world_matrices(&mut self) {
12 self.update_world_matrices_from_eval_order_position(0);
13 }
14
15 pub(super) fn update_world_matrices_from_eval_order_position(&mut self, start_position: usize) {
16 self.update_world_matrices_from_eval_order_position_for_phase(start_position, None);
17 }
18
19 pub(super) fn update_world_matrices_from_eval_order_position_for_phase(
20 &mut self,
21 start_position: usize,
22 phase: Option<bool>,
23 ) {
24 let start_position =
25 self.expand_update_start_for_append_dependencies(start_position, phase);
26 for bone in &self.model.eval_order()[start_position..] {
27 if !self.bone_matches_phase(*bone, phase) {
28 continue;
29 }
30 self.pose.reset_append_transform(*bone);
31 }
32 for position in start_position..self.model.eval_order().len() {
33 let bone = self.model.eval_order()[position];
34 if !self.bone_matches_phase(bone, phase) {
35 continue;
36 }
37 self.update_append_transform_for_bone(bone);
38 self.update_world_matrix_for_bone(bone);
39 }
40 }
41
42 pub(super) fn update_world_matrices_using_current_append_from_eval_order_position(
43 &mut self,
44 start_position: usize,
45 ) {
46 self.update_world_matrices_using_current_append_from_eval_order_position_for_phase(
47 start_position,
48 None,
49 );
50 }
51
52 pub(super) fn update_world_matrices_using_current_append_from_eval_order_position_for_phase(
53 &mut self,
54 start_position: usize,
55 phase: Option<bool>,
56 ) {
57 for position in start_position..self.model.eval_order().len() {
58 let bone = self.model.eval_order()[position];
59 if !self.bone_matches_phase(bone, phase) {
60 continue;
61 }
62 self.update_world_matrix_for_bone(bone);
63 }
64 }
65
66 #[inline]
67 pub(super) fn bone_matches_phase(&self, bone: crate::BoneIndex, phase: Option<bool>) -> bool {
68 phase.is_none_or(|after_physics| self.model.transform_after_physics(bone) == after_physics)
69 }
70
71 pub(super) fn update_append_transform_for_bone(&mut self, bone: crate::BoneIndex) {
72 let Some(append_index) = self.model.append_transform_index(bone) else {
73 return;
74 };
75 let append = self.model.append_transform(append_index);
76 let use_source_append = !append.local
77 && self
78 .model
79 .append_transform_index(append.source_bone)
80 .is_some();
81 let mut source_rotation = if use_source_append {
82 self.pose.append_rotation(append.source_bone)
83 } else {
84 self.pose.local_rotation(append.source_bone)
85 };
86 if use_source_append && self.model.is_ik_link_bone(append.source_bone) {
87 source_rotation =
88 (self.pose.ik_rotation(append.source_bone) * source_rotation).normalize();
89 }
90 let source_position_offset = if use_source_append {
91 self.pose.append_position_offset(append.source_bone)
92 } else {
93 self.pose.local_position_offset(append.source_bone)
94 };
95 let append_output = solve_append_transform(AppendPrimitiveInput {
96 source_position_offset,
97 source_rotation,
98 ratio: append.ratio,
99 affect_rotation: append.affect_rotation,
100 affect_translation: append.affect_translation,
101 });
102 self.pose.set_append_rotation(bone, append_output.rotation);
103 self.pose
104 .set_append_position_offset(bone, append_output.position_offset);
105 }
106
107 pub(super) fn update_world_matrix_for_bone(&mut self, bone: crate::BoneIndex) {
108 #[cfg(test)]
109 {
110 self.world_matrix_bone_update_count += 1;
111 }
112 let mut local_position =
113 self.model.rest_position(bone) + self.pose.local_position_offset(bone);
114 let mut local_rotation = self.pose.local_rotation(bone);
115 let local_scale = self.pose.local_scale(bone);
116
117 if let Some(append_index) = self.model.append_transform_index(bone) {
118 let append = self.model.append_transform(append_index);
119 if append.affect_rotation {
120 local_rotation = (local_rotation * self.pose.append_rotation(bone)).normalize();
121 }
122 if append.affect_translation {
123 local_position += self.pose.append_position_offset(bone);
124 }
125 }
126
127 if let Some(axis) = self.model.fixed_axis_constraint(bone) {
128 local_rotation = constrain_rotation_to_axis(local_rotation, axis);
129 }
130
131 let local_matrix = Mat4::from_scale_rotation_translation(
132 local_scale.into(),
133 local_rotation,
134 local_position.into(),
135 );
136
137 let world_matrix = match self.model.parent_index(bone) {
138 Some(parent) => self.pose.world_matrices()[parent.as_usize()] * local_matrix,
139 None => local_matrix,
140 };
141
142 self.pose.set_world_matrix(bone, world_matrix);
143 self.pose
144 .set_skinning_matrix(bone, world_matrix * self.model.inverse_bind_matrix(bone));
145 }
146
147 pub(super) fn expand_update_start_for_append_dependencies(
148 &self,
149 start_position: usize,
150 phase: Option<bool>,
151 ) -> usize {
152 let mut start = start_position;
153 loop {
154 let mut changed = false;
155 for append in self.model.append_transforms() {
156 if !self.bone_matches_phase(append.target_bone, phase) {
157 continue;
158 }
159 let source_position = self.model.eval_order_position(append.source_bone);
160 let target_position = self.model.eval_order_position(append.target_bone);
161 if source_position >= start && target_position < start {
162 start = target_position;
163 changed = true;
164 }
165 }
166 if !changed {
167 return start;
168 }
169 }
170 }
171
172 #[inline]
173 pub fn world_matrices(&self) -> &[Mat4] {
174 self.pose.world_matrices()
175 }
176
177 #[cfg(test)]
178 pub(super) fn reset_world_matrix_bone_update_count(&mut self) {
179 self.world_matrix_bone_update_count = 0;
180 }
181
182 #[cfg(test)]
183 pub(super) fn world_matrix_bone_update_count(&self) -> usize {
184 self.world_matrix_bone_update_count
185 }
186
187 #[inline]
188 pub fn skinning_matrices(&self) -> &[Mat4] {
189 self.pose.skinning_matrices()
190 }
191}