Motion transfer setup #1

Manually merged
Sybren A. Stüvel merged 18 commits from cgtinker/powership:motion_transfer into main 2023-06-05 12:06:15 +02:00
2 changed files with 77 additions and 84 deletions
Showing only changes of commit e27f71abec - Show all commits

163
nodes.py
View File

@ -724,10 +724,8 @@ class RotateTowards(AbstractPowerShipNode):
# Set the rotation of the control # Set the rotation of the control
vec = Vector((destination - origin)) vec = Vector((destination - origin))
# vec.normalize()
rot = vec.to_track_quat(self.track, self.up) rot = vec.to_track_quat(self.track, self.up)
rot.normalize() self.outputs["Rotation"].default_value = rot.normalized()
self.outputs["Rotation"].default_value = rot
class OffsetRotation(AbstractPowerShipNode): class OffsetRotation(AbstractPowerShipNode):
@ -776,20 +774,15 @@ class MapRange(AbstractPowerShipNode):
self.outputs["Result"].default_value = slope * val + offset self.outputs["Result"].default_value = slope * val + offset
class RotationFromAngle(AbstractPowerShipNode): class AngleFromVectors(AbstractPowerShipNode):
bl_idname = "RotationFromAngle" bl_idname = "AngleFromVectors"
bl_label = "Rotation From Vector Angle" bl_label = "Angle From Vectors"
bl_icon = "EMPTY_ARROWS" bl_icon = "EMPTY_ARROWS"
axis: bpy.props.EnumProperty( # type: ignore
name="Axis",
items=_enum_up_axis_items,
)
angle_type: bpy.props.EnumProperty( # type: ignore angle_type: bpy.props.EnumProperty( # type: ignore
name="Type", name="Type",
items=[ items=[
("DEFAULT", "Default", ""), ("DEFAULT", "Unsigned", ""),
("SIGNED", "Signed", ""), ("SIGNED", "Signed", ""),
], ],
) )
@ -798,13 +791,12 @@ class RotationFromAngle(AbstractPowerShipNode):
self, context: bpy.types.Context, layout: bpy.types.UILayout self, context: bpy.types.Context, layout: bpy.types.UILayout
) -> None: ) -> None:
super().draw_buttons(context, layout) super().draw_buttons(context, layout)
layout.prop(self, "axis")
layout.prop(self, "angle_type") layout.prop(self, "angle_type")
def init(self, context): def init(self, context):
self.add_optional_input_socket("NodeSocketVector", "U") self.add_optional_input_socket("NodeSocketVector", "U")
self.add_optional_input_socket("NodeSocketVector", "V") self.add_optional_input_socket("NodeSocketVector", "V")
self.outputs.new("NodeSocketQuaternion", "Rotation") self.outputs.new("NodeSocketFloat", "Angle")
def execute(self, depsgraph: bpy.types.Depsgraph) -> None: def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
u = self._get_optional_input_value("U", Vector) u = self._get_optional_input_value("U", Vector)
@ -818,10 +810,7 @@ class RotationFromAngle(AbstractPowerShipNode):
else: else:
angle = u.angle(v) angle = u.angle(v)
m = Matrix.Rotation(angle, 3, self.axis) self.outputs["Angle"].default_value = angle
res = m.to_quaternion()
self.outputs["Rotation"].default_value = res
_enum_vector_math_operations = [ _enum_vector_math_operations = [
@ -1006,6 +995,7 @@ class SetBoneNode(AbstractPowerShipNode):
bone_mat_world: Matrix = arm_matrix @ bone.matrix bone_mat_world: Matrix = arm_matrix @ bone.matrix
loc, rot, scale = bone_mat_world.decompose() loc, rot, scale = bone_mat_world.decompose()
if control_location is not None: if control_location is not None:
loc = control_location loc = control_location
if control_rotation is not None: if control_rotation is not None:
@ -1013,24 +1003,27 @@ class SetBoneNode(AbstractPowerShipNode):
if control_scale is not None: if control_scale is not None:
scale = control_scale scale = control_scale
match self.space: # TODO: Fix jittering bone scale which happens
case "WORLD": # esp. when multiple bones are parented to the rotated bone
bone_mat_world = Matrix.LocRotScale(loc, rot, scale) # rounding helps but does not entirely fix the issue.
case "CHANNELS": scale = [round(x, 4) for x in scale]
# Not sure what causes the scale to flip, however...
# The y/z-scale flips on update if multiple rotations are chained,
# this generates jitter when running const updates - flipping seems to temp fix it.
scale = [scale[i] for i in [0, 2, 1]]
v_nil = Vector((0, 0, 0)) v_nil = Vector((0, 0, 0))
bone_rest_rot_scale = bone.bone.matrix_local.copy() bone_rest_rot_scale = bone.bone.matrix_local.copy()
match self.space:
case "WORLD":
bone_mat_world = Matrix.LocRotScale(
loc, rot.normalized(), scale)
loc, rot, scale = bone_mat_world.decompose()
case "CHANNELS":
bone_rest_rot_scale.translation = v_nil bone_rest_rot_scale.translation = v_nil
bone_rest_rot = bone_rest_rot_scale.to_quaternion()
mat_rot_scale = Matrix.LocRotScale( mat_rot_scale = Matrix.LocRotScale(
v_nil, rot, scale) @ bone_rest_rot.to_matrix().to_4x4() v_nil, rot, scale) @ bone_rest_rot_scale
mat_loc = Matrix.Translation(loc) mat_loc = Matrix.Translation(loc)
bone_mat_world = mat_loc @ mat_rot_scale bone_mat_world = mat_loc @ mat_rot_scale
bone.matrix = arm_matrix.inverted() @ bone_mat_world bone.matrix = arm_matrix.inverted() @ bone_mat_world
loc, rot, scale = bone.matrix.decompose()
class TwoBoneIKNode(AbstractPowerShipNode): class TwoBoneIKNode(AbstractPowerShipNode):
@ -1237,61 +1230,61 @@ class ClampNode(AbstractPowerShipNode):
self.outputs["Result"].default_value = clamped self.outputs["Result"].default_value = clamped
class AbstractTwoValueMathNode(AbstractPowerShipNode): # class AbstractTwoValueMathNode(AbstractPowerShipNode):
def init(self, context: bpy.types.Context) -> None: # def init(self, context: bpy.types.Context) -> None:
self.inputs.new("NodeSocketFloat", "A") # self.inputs.new("NodeSocketFloat", "A")
self.inputs.new("NodeSocketFloat", "B") # self.inputs.new("NodeSocketFloat", "B")
self.outputs.new("NodeSocketFloat", "Result") # self.outputs.new("NodeSocketFloat", "Result")
#
def execute(self, depsgraph: bpy.types.Depsgraph) -> None: # def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
a = self._get_input_value("A", float) # a = self._get_input_value("A", float)
b = self._get_input_value("B", float) # b = self._get_input_value("B", float)
self.outputs["Result"].default_value = self.calculate(a, b) # self.outputs["Result"].default_value = self.calculate(a, b)
#
def calculate(self, a: float, b: float) -> float: # def calculate(self, a: float, b: float) -> float:
return 0 # return 0
#
#
class AddNode(AbstractTwoValueMathNode): # class AddNode(AbstractTwoValueMathNode):
"""Add two values""" # """Add two values"""
#
bl_idname = "AddNode" # bl_idname = "AddNode"
bl_label = "Add" # bl_label = "Add"
#
def calculate(self, a: float, b: float) -> float: # def calculate(self, a: float, b: float) -> float:
return a + b # return a + b
#
#
class SubtractNode(AbstractTwoValueMathNode): # class SubtractNode(AbstractTwoValueMathNode):
"""Subtract two values""" # """Subtract two values"""
#
bl_idname = "SubtractNode" # bl_idname = "SubtractNode"
bl_label = "Subtract" # bl_label = "Subtract"
#
def calculate(self, a: float, b: float) -> float: # def calculate(self, a: float, b: float) -> float:
return a - b # return a - b
#
#
class MultiplyNode(AbstractTwoValueMathNode): # class MultiplyNode(AbstractTwoValueMathNode):
"""Multiply two values""" # """Multiply two values"""
#
bl_idname = "MultiplyNode" # bl_idname = "MultiplyNode"
bl_label = "Multiply" # bl_label = "Multiply"
#
def calculate(self, a: float, b: float) -> float: # def calculate(self, a: float, b: float) -> float:
return a * b # return a * b
#
#
class DivideNode(AbstractTwoValueMathNode): # class DivideNode(AbstractTwoValueMathNode):
"""Divide two values; division by zero results in NaN""" # """Divide two values; division by zero results in NaN"""
#
bl_idname = "DivideNode" # bl_idname = "DivideNode"
bl_label = "Divide" # bl_label = "Divide"
#
def calculate(self, a: float, b: float) -> float: # def calculate(self, a: float, b: float) -> float:
if b == 0: # if b == 0:
return float("nan") # return float("nan")
return a / b # return a / b
def _on_num_sockets_change(self: "SequenceNode", context: bpy.types.Context) -> None: def _on_num_sockets_change(self: "SequenceNode", context: bpy.types.Context) -> None:
@ -1394,7 +1387,7 @@ node_categories = [
items=[ items=[
nodeitems_utils.NodeItem("RotateTowards"), nodeitems_utils.NodeItem("RotateTowards"),
nodeitems_utils.NodeItem("OffsetRotation"), nodeitems_utils.NodeItem("OffsetRotation"),
nodeitems_utils.NodeItem("RotationFromAngle"), nodeitems_utils.NodeItem("AngleFromVectors"),
nodeitems_utils.NodeItem("NormalFromPoints"), nodeitems_utils.NodeItem("NormalFromPoints"),
nodeitems_utils.NodeItem("SplitVector"), nodeitems_utils.NodeItem("SplitVector"),
nodeitems_utils.NodeItem("ToVector"), nodeitems_utils.NodeItem("ToVector"),
@ -1439,7 +1432,7 @@ classes = (
SequenceNode, SequenceNode,
# Math Nodes # Math Nodes
RotateTowards, RotateTowards,
RotationFromAngle, AngleFromVectors,
OffsetRotation, OffsetRotation,
NormalFromPoints, NormalFromPoints,
ToVector, ToVector,

Binary file not shown.