|
|
|
@ -479,8 +479,7 @@ class AnimationConverter:
|
|
|
|
|
return 0.0, 0.0 |
|
|
|
|
|
|
|
|
|
def make_matrix44_controller(self, fcurves, pos_path: str, scale_path: str, pos_default, scale_default, |
|
|
|
|
*, start: Optional[int] = None, end: Optional[int] = None, |
|
|
|
|
name: str = "") -> Optional[plLeafController]: |
|
|
|
|
*, start: Optional[int] = None, end: Optional[int] = None) -> Optional[plLeafController]: |
|
|
|
|
def convert_matrix_keyframe(**kwargs) -> hsMatrix44: |
|
|
|
|
pos = kwargs[pos_path] |
|
|
|
|
scale = kwargs[scale_path] |
|
|
|
@ -500,7 +499,7 @@ class AnimationConverter:
|
|
|
|
|
channels = { pos_path: 3, scale_path: 3 } |
|
|
|
|
default_values = { pos_path: pos_default, scale_path: scale_default } |
|
|
|
|
keyframes = self._process_fcurves(fcurves, channels, 1, convert_matrix_keyframe, |
|
|
|
|
default_values, start=start, end=end, name=name) |
|
|
|
|
default_values, start=start, end=end) |
|
|
|
|
if not keyframes: |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
@ -509,10 +508,10 @@ class AnimationConverter:
|
|
|
|
|
|
|
|
|
|
def make_pos_controller(self, fcurves, data_path: str, default_xform, |
|
|
|
|
convert: Optional[Callable] = None, *, start: Optional[int] = None, |
|
|
|
|
end: Optional[int] = None, name: str = "") -> Optional[plLeafController]: |
|
|
|
|
end: Optional[int] = None) -> Optional[plLeafController]: |
|
|
|
|
pos_curves = [i for i in fcurves if i.data_path == data_path and i.keyframe_points] |
|
|
|
|
keyframes, bez_chans = self._process_keyframes(pos_curves, 3, default_xform, convert, |
|
|
|
|
start=start, end=end, name=name) |
|
|
|
|
start=start, end=end) |
|
|
|
|
if not keyframes: |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
@ -523,7 +522,7 @@ class AnimationConverter:
|
|
|
|
|
|
|
|
|
|
def make_rot_controller(self, fcurves, rotation_mode: str, default_xform, |
|
|
|
|
convert: Optional[Callable] = None, *, start: Optional[int] = None, |
|
|
|
|
end: Optional[int] = None, name: str = "") -> Union[None, plCompoundController, plLeafController]: |
|
|
|
|
end: Optional[int] = None) -> Union[None, plCompoundController, plLeafController]: |
|
|
|
|
if rotation_mode in {"AXIS_ANGLE", "QUATERNION"}: |
|
|
|
|
rot_curves = [i for i in fcurves if i.data_path == "rotation_{}".format(rotation_mode.lower()) and i.keyframe_points] |
|
|
|
|
if not rot_curves: |
|
|
|
@ -544,7 +543,7 @@ class AnimationConverter:
|
|
|
|
|
# I think that opting into quaternion keyframes is a good enough indication that |
|
|
|
|
# you're OK with that. |
|
|
|
|
keyframes, bez_chans = self._process_keyframes(rot_curves, 4, default_xform, convert, |
|
|
|
|
start=start, end=end, name=name) |
|
|
|
|
start=start, end=end) |
|
|
|
|
if keyframes: |
|
|
|
|
return self._make_quat_controller(keyframes) |
|
|
|
|
else: |
|
|
|
@ -565,7 +564,7 @@ class AnimationConverter:
|
|
|
|
|
|
|
|
|
|
euler_convert = convert_euler_keyframe if rotation_mode != "XYZ" else convert |
|
|
|
|
keyframes, bez_chans = self._process_keyframes(rot_curves, 3, default_xform.to_euler(rotation_mode), |
|
|
|
|
euler_convert, start=start, end=end, name=name) |
|
|
|
|
euler_convert, start=start, end=end) |
|
|
|
|
if keyframes: |
|
|
|
|
# Once again, quaternion keyframes do not support bezier interpolation. Ideally, |
|
|
|
|
# we would just drop support for rotation beziers entirely to simplify all this |
|
|
|
@ -577,11 +576,10 @@ class AnimationConverter:
|
|
|
|
|
|
|
|
|
|
def make_scale_controller(self, fcurves, data_path: str, default_xform, |
|
|
|
|
convert: Optional[Callable] = None, *, start: Optional[int] = None, |
|
|
|
|
end: Optional[int] = None, |
|
|
|
|
name: str = "") -> Optional[plLeafController]: |
|
|
|
|
end: Optional[int] = None) -> Optional[plLeafController]: |
|
|
|
|
scale_curves = [i for i in fcurves if i.data_path == data_path and i.keyframe_points] |
|
|
|
|
keyframes, bez_chans = self._process_keyframes(scale_curves, 3, default_xform, convert, |
|
|
|
|
start=start, end=end, name=name) |
|
|
|
|
start=start, end=end) |
|
|
|
|
if not keyframes: |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
@ -592,9 +590,8 @@ class AnimationConverter:
|
|
|
|
|
def make_scalar_leaf_controller(self, fcurve: bpy.types.FCurve, |
|
|
|
|
convert: Optional[Callable] = None, *, |
|
|
|
|
start: Optional[int] = None, |
|
|
|
|
end: Optional[int] = None, |
|
|
|
|
name: str = "") -> Optional[plLeafController]: |
|
|
|
|
keyframes, bezier = self._process_fcurve(fcurve, convert, start=start, end=end, name=name) |
|
|
|
|
end: Optional[int] = None) -> Optional[plLeafController]: |
|
|
|
|
keyframes, bezier = self._process_fcurve(fcurve, convert, start=start, end=end) |
|
|
|
|
if not keyframes: |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
@ -744,8 +741,7 @@ class AnimationConverter:
|
|
|
|
|
return [keyframes_sorted[i] for i in filtered_indices] |
|
|
|
|
|
|
|
|
|
def _process_fcurve(self, fcurve: bpy.types.FCurve, convert: Optional[Callable] = None, *, |
|
|
|
|
start: Optional[int] = None, end: Optional[int] = None, |
|
|
|
|
name: str = "") -> Tuple[Sequence, AbstractSet]: |
|
|
|
|
start: Optional[int] = None, end: Optional[int] = None) -> Tuple[Sequence, AbstractSet]: |
|
|
|
|
"""Like _process_keyframes, but for one fcurve""" |
|
|
|
|
|
|
|
|
|
# Adapt from incoming single item sequence to a single argument. |
|
|
|
@ -754,7 +750,7 @@ class AnimationConverter:
|
|
|
|
|
else: |
|
|
|
|
single_convert = None |
|
|
|
|
# Can't proxy to _process_fcurves because it only supports linear interoplation. |
|
|
|
|
return self._process_keyframes([fcurve], 1, [0.0], single_convert, start=start, end=end, name=name) |
|
|
|
|
return self._process_keyframes([fcurve], 1, [0.0], single_convert, start=start, end=end) |
|
|
|
|
|
|
|
|
|
def _santize_converted_values(self, num_channels: int, raw_values: Union[Dict, Sequence], convert: Callable): |
|
|
|
|
assert convert is not None |
|
|
|
@ -776,8 +772,7 @@ class AnimationConverter:
|
|
|
|
|
|
|
|
|
|
def _process_fcurves(self, fcurves: Sequence, channels: Dict[str, int], result_channels: int, |
|
|
|
|
convert: Callable, defaults: Dict[str, Union[float, Sequence]], *, |
|
|
|
|
start: Optional[int] = None, end: Optional[int] = None, |
|
|
|
|
name: str = "") -> Sequence: |
|
|
|
|
start: Optional[int] = None, end: Optional[int] = None) -> Sequence: |
|
|
|
|
"""This consumes a sequence of Blender FCurves that map to a single Plasma controller. |
|
|
|
|
Like `_process_keyframes()`, except the converter function is mandatory, and each |
|
|
|
|
Blender `data_path` must have a fixed number of channels. |
|
|
|
@ -843,14 +838,11 @@ class AnimationConverter:
|
|
|
|
|
keyframe.out_tans = [0.0] * result_channels |
|
|
|
|
keyframes[frame_num] = keyframe |
|
|
|
|
|
|
|
|
|
sorted_keyframes = self._sort_and_dedupe_keyframes(keyframes) |
|
|
|
|
if keyframes and not sorted_keyframes and name: |
|
|
|
|
self._exporter().report.warn(f"All keyframes for '{name}' are identical and have been discarded!") |
|
|
|
|
return sorted_keyframes |
|
|
|
|
return self._sort_and_dedupe_keyframes(keyframes) |
|
|
|
|
|
|
|
|
|
def _process_keyframes(self, fcurves, num_channels: int, default_values: Sequence, |
|
|
|
|
convert: Optional[Callable] = None, *, start: Optional[int] = None, |
|
|
|
|
end: Optional[int] = None, name: str = "") -> Tuple[Sequence, AbstractSet]: |
|
|
|
|
end: Optional[int] = None) -> Tuple[Sequence, AbstractSet]: |
|
|
|
|
"""Groups all FCurves for the same frame together""" |
|
|
|
|
keyframe_data = type("KeyFrameData", (), {}) |
|
|
|
|
fps, pi = self._bl_fps, math.pi |
|
|
|
@ -909,10 +901,7 @@ class AnimationConverter:
|
|
|
|
|
keyframes[frame_num] = keyframe |
|
|
|
|
|
|
|
|
|
# Return the keyframes in a sequence sorted by frame number |
|
|
|
|
sorted_keyframes = self._sort_and_dedupe_keyframes(keyframes) |
|
|
|
|
if keyframes and not sorted_keyframes and name: |
|
|
|
|
self._exporter().report.warn(f"All keyframes for '{name}' are identical and have been discarded!") |
|
|
|
|
return (sorted_keyframes, bez_chans) |
|
|
|
|
return (self._sort_and_dedupe_keyframes(keyframes), bez_chans) |
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
def _mgr(self): |
|
|
|
|