99 lines
2.9 KiB
GDScript
99 lines
2.9 KiB
GDScript
@tool
|
|
class_name BoneFlipper
|
|
extends SkeletonModifier3D
|
|
|
|
@export var flip: bool = false
|
|
|
|
@export_enum(" ") var _bones_to_flip: Array[String] = []
|
|
@export var _flip_all_bones: bool = true
|
|
@export_enum(" ") var _bones_to_exclude: Array[String] = []
|
|
@export var _bone_prefixes_to_exclude: Array[String] = []
|
|
|
|
var _skeleton: Skeleton3D
|
|
|
|
|
|
func _validate_property(property: Dictionary) -> void:
|
|
if property.name in ["_bones_to_flip", "_bones_to_exclude"]:
|
|
if _skeleton:
|
|
property.hint = PROPERTY_HINT_TYPE_STRING
|
|
property.hint_string = (
|
|
"%d/%d:%s"
|
|
% [
|
|
TYPE_STRING,
|
|
PROPERTY_HINT_ENUM,
|
|
_skeleton.get_concatenated_bone_names()
|
|
]
|
|
)
|
|
|
|
|
|
func _ready() -> void:
|
|
_skeleton = get_skeleton()
|
|
assert(_skeleton, str(self) + ": _skeleton missing!")
|
|
|
|
for bone_name in _bones_to_flip:
|
|
var bone_idx := _skeleton.find_bone(bone_name)
|
|
assert(bone_idx != -1, str(self) + ": bone " + bone_name + " missing!")
|
|
for bone_name in _bones_to_exclude:
|
|
var bone_idx := _skeleton.find_bone(bone_name)
|
|
assert(bone_idx != -1, str(self) + ": bone " + bone_name + " missing!")
|
|
|
|
|
|
func _process_modification() -> void:
|
|
if !_skeleton:
|
|
return
|
|
|
|
if !flip:
|
|
for bone_idx in range(_skeleton.get_bone_count()):
|
|
_skeleton.set_bone_pose(bone_idx, _skeleton.get_bone_pose(bone_idx))
|
|
return
|
|
|
|
if _flip_all_bones:
|
|
for bone_idx in range(_skeleton.get_bone_count()):
|
|
_flip_bone_if_not_excluded(bone_idx)
|
|
else:
|
|
for bone_name in _bones_to_flip:
|
|
var bone_idx := _skeleton.find_bone(bone_name)
|
|
if bone_idx == -1:
|
|
continue
|
|
_flip_bone(bone_idx, bone_name)
|
|
|
|
|
|
func _flip_bone_if_not_excluded(bone_idx: int) -> void:
|
|
var bone_name := _skeleton.get_bone_name(bone_idx)
|
|
if bone_name in _bones_to_exclude:
|
|
return
|
|
for bone_prefix in _bone_prefixes_to_exclude:
|
|
if bone_prefix and bone_name.begins_with(bone_prefix):
|
|
return
|
|
_flip_bone(bone_idx, bone_name)
|
|
|
|
|
|
func _flip_bone(bone_idx: int, bone_name: String) -> void:
|
|
var pose: Transform3D = _skeleton.get_bone_pose(bone_idx)
|
|
|
|
if bone_name.ends_with("_R") or (bone_name.ends_with("_L") and !_flip_all_bones):
|
|
var other_side_bone_name: String = (
|
|
bone_name.replace("_R", "_L")
|
|
if bone_name.ends_with("_R")
|
|
else bone_name.replace("_L", "_R")
|
|
)
|
|
var other_side_bone_idx := _skeleton.find_bone(other_side_bone_name)
|
|
if other_side_bone_idx == -1:
|
|
return
|
|
var other_side_pose: Transform3D = _skeleton.get_bone_pose(other_side_bone_idx)
|
|
_mirror_bone_transform(bone_idx, other_side_pose)
|
|
_mirror_bone_transform(other_side_bone_idx, pose)
|
|
elif !bone_name.ends_with("_R") and !bone_name.ends_with("_L"):
|
|
_mirror_bone_transform(bone_idx, pose)
|
|
|
|
|
|
func _mirror_bone_transform(bone_idx: int, pose: Transform3D) -> void:
|
|
pose.origin.x = -pose.origin.x
|
|
var pose_rot := pose.basis.get_euler()
|
|
var pose_scale := pose.basis.get_scale()
|
|
pose_rot.y = -pose_rot.y
|
|
pose_rot.z = -pose_rot.z
|
|
pose.basis = Basis.from_euler(pose_rot)
|
|
pose.basis = pose.basis.scaled(pose_scale)
|
|
_skeleton.set_bone_pose(bone_idx, pose)
|