Motion transfer setup #1
@ -146,6 +146,7 @@ def register() -> None:
|
|||||||
|
|
||||||
def unregister() -> None:
|
def unregister() -> None:
|
||||||
bpy.app.handlers.depsgraph_update_post.remove(_on_depsgraph_update_post)
|
bpy.app.handlers.depsgraph_update_post.remove(_on_depsgraph_update_post)
|
||||||
|
bpy.app.handlers.frame_change_post.remove(_on_frame_changed_post)
|
||||||
|
|
||||||
del bpy.types.Scene.powership_mode
|
del bpy.types.Scene.powership_mode
|
||||||
|
|
||||||
|
343
nodes.py
343
nodes.py
@ -67,10 +67,7 @@ class PowerShipNodeTree(bpy.types.NodeTree):
|
|||||||
|
|
||||||
def _prepare_nodes(self) -> None:
|
def _prepare_nodes(self) -> None:
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
try:
|
node.reset_run()
|
||||||
node.reset_run()
|
|
||||||
except AttributeError:
|
|
||||||
print(node)
|
|
||||||
|
|
||||||
def _run_from_node(
|
def _run_from_node(
|
||||||
self, depsgraph: bpy.types.Depsgraph, start_node: "AbstractPowerShipNode"
|
self, depsgraph: bpy.types.Depsgraph, start_node: "AbstractPowerShipNode"
|
||||||
@ -234,7 +231,6 @@ class AbstractPowerShipNode(bpy.types.Node):
|
|||||||
|
|
||||||
def run(self, depsgraph: bpy.types.Depsgraph) -> None:
|
def run(self, depsgraph: bpy.types.Depsgraph) -> None:
|
||||||
assert not self.has_run, "a node can only run once, reset it first"
|
assert not self.has_run, "a node can only run once, reset it first"
|
||||||
|
|
||||||
if self.mute:
|
if self.mute:
|
||||||
# Skip execution of this node, it's muted.
|
# Skip execution of this node, it's muted.
|
||||||
self._first_input_to_output()
|
self._first_input_to_output()
|
||||||
@ -278,7 +274,6 @@ class AbstractPowerShipNode(bpy.types.Node):
|
|||||||
|
|
||||||
def _first_input_to_output(self) -> None:
|
def _first_input_to_output(self) -> None:
|
||||||
"""Copy the first input's default value to the output, if the sockets are compatible."""
|
"""Copy the first input's default value to the output, if the sockets are compatible."""
|
||||||
|
|
||||||
if not self.inputs or not self.outputs:
|
if not self.inputs or not self.outputs:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -592,8 +587,6 @@ class ToVector(AbstractPowerShipNode):
|
|||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
|
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "X")
|
self.add_optional_input_socket("NodeSocketFloat", "X")
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "Y")
|
self.add_optional_input_socket("NodeSocketFloat", "Y")
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "Z")
|
self.add_optional_input_socket("NodeSocketFloat", "Z")
|
||||||
@ -621,8 +614,6 @@ class SplitVector(AbstractPowerShipNode):
|
|||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
|
|
||||||
self.add_optional_input_socket("NodeSocketVector", "Vector")
|
self.add_optional_input_socket("NodeSocketVector", "Vector")
|
||||||
self.outputs.new("NodeSocketFloat", "X")
|
self.outputs.new("NodeSocketFloat", "X")
|
||||||
self.outputs.new("NodeSocketFloat", "Y")
|
self.outputs.new("NodeSocketFloat", "Y")
|
||||||
@ -630,10 +621,6 @@ class SplitVector(AbstractPowerShipNode):
|
|||||||
|
|
||||||
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
||||||
v = self._get_optional_input_value("Vector", Vector)
|
v = self._get_optional_input_value("Vector", Vector)
|
||||||
|
|
||||||
if not v:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.outputs["X"].default_value = v.x
|
self.outputs["X"].default_value = v.x
|
||||||
self.outputs["Y"].default_value = v.y
|
self.outputs["Y"].default_value = v.y
|
||||||
self.outputs["Z"].default_value = v.z
|
self.outputs["Z"].default_value = v.z
|
||||||
@ -647,8 +634,6 @@ class Distance(AbstractPowerShipNode):
|
|||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
|
|
||||||
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("NodeSocketFloat", "Float")
|
self.outputs.new("NodeSocketFloat", "Float")
|
||||||
@ -671,8 +656,6 @@ class NormalFromPoints(AbstractPowerShipNode):
|
|||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
|
|
||||||
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.add_optional_input_socket("NodeSocketVector", "W")
|
self.add_optional_input_socket("NodeSocketVector", "W")
|
||||||
@ -682,83 +665,26 @@ class NormalFromPoints(AbstractPowerShipNode):
|
|||||||
u = self._get_optional_input_value("U", Vector)
|
u = self._get_optional_input_value("U", Vector)
|
||||||
v = self._get_optional_input_value("V", Vector)
|
v = self._get_optional_input_value("V", Vector)
|
||||||
w = self._get_optional_input_value("W", Vector)
|
w = self._get_optional_input_value("W", Vector)
|
||||||
|
|
||||||
if not (u and v and w):
|
|
||||||
return
|
|
||||||
|
|
||||||
a = v - u
|
a = v - u
|
||||||
b = w - u
|
b = w - u
|
||||||
normal = a.cross(b)
|
normal = a.cross(b)
|
||||||
self.outputs["Result"].default_value = normal
|
self.outputs["Result"].default_value = normal
|
||||||
|
|
||||||
|
|
||||||
class UpAxisSocket(bpy.types.NodeSocket):
|
_enum_up_axis_items = (
|
||||||
'''Custom node socket type'''
|
('X', "X", ""),
|
||||||
bl_idname = 'UpAxisSocket'
|
('Y', "Y", ""),
|
||||||
bl_label = "Up Axis Socket"
|
('Z', "Z", ""),
|
||||||
link_limit = 0
|
)
|
||||||
|
|
||||||
# Enum items list
|
_enum_track_axis_items = (
|
||||||
axis_items = (
|
('X', "X", ""),
|
||||||
('X', "X", ""),
|
('Y', "Y", ""),
|
||||||
('Y', "Y", ""),
|
('Z', "Z", ""),
|
||||||
('Z', "Z", ""),
|
('-X', "-X", ""),
|
||||||
)
|
('-Y', "-Y", ""),
|
||||||
# default_value = "UP"
|
('-Z', "-Z", ""),
|
||||||
|
)
|
||||||
default_value: bpy.props.EnumProperty(
|
|
||||||
name="Axis",
|
|
||||||
description="",
|
|
||||||
items=axis_items,
|
|
||||||
default='X',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Optional function for drawing the socket input value
|
|
||||||
def draw(self, context, layout, node, text):
|
|
||||||
if self.is_output or self.is_linked:
|
|
||||||
layout.label(text=text)
|
|
||||||
else:
|
|
||||||
layout.prop(self, "default_value", text=text)
|
|
||||||
|
|
||||||
def draw_color(self, context, node):
|
|
||||||
return (1.0, 0.4, 0.216, 0.5)
|
|
||||||
|
|
||||||
|
|
||||||
class TrackAxisSocket(bpy.types.NodeSocket):
|
|
||||||
'''Custom node socket type'''
|
|
||||||
bl_idname = 'TrackAxisSocket'
|
|
||||||
bl_label = "Track Axis Socket"
|
|
||||||
link_limit = 0
|
|
||||||
|
|
||||||
# Enum items list
|
|
||||||
axis_items = (
|
|
||||||
('X', "X", ""),
|
|
||||||
('Y', "Y", ""),
|
|
||||||
('Z', "Z", ""),
|
|
||||||
('-X', "-X", ""),
|
|
||||||
('-Y', "-Y", ""),
|
|
||||||
('-Z', "-Z", ""),
|
|
||||||
)
|
|
||||||
# default_value = "UP"
|
|
||||||
|
|
||||||
default_value: bpy.props.EnumProperty(
|
|
||||||
name="Axis",
|
|
||||||
description="",
|
|
||||||
items=axis_items,
|
|
||||||
default='Y',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Optional function for drawing the socket input value
|
|
||||||
def draw(self, context, layout, node, text):
|
|
||||||
if self.is_output or self.is_linked:
|
|
||||||
layout.label(text=text)
|
|
||||||
else:
|
|
||||||
layout.prop(self, "default_value", text=text)
|
|
||||||
|
|
||||||
# Socket color
|
|
||||||
|
|
||||||
def draw_color(self, context, node):
|
|
||||||
return (1.0, 0.4, 0.216, 0.5)
|
|
||||||
|
|
||||||
|
|
||||||
class RotateTowards(AbstractPowerShipNode):
|
class RotateTowards(AbstractPowerShipNode):
|
||||||
@ -768,12 +694,24 @@ class RotateTowards(AbstractPowerShipNode):
|
|||||||
bl_label = "Rotate Towards"
|
bl_label = "Rotate Towards"
|
||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
|
track: bpy.props.EnumProperty( # type: ignore
|
||||||
|
name="Track",
|
||||||
|
items=_enum_track_axis_items,
|
||||||
|
)
|
||||||
|
|
||||||
|
up: bpy.props.EnumProperty( # type: ignore
|
||||||
|
name="Up",
|
||||||
|
items=_enum_up_axis_items,
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw_buttons(
|
||||||
|
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
super().draw_buttons(context, layout)
|
||||||
|
layout.prop(self, "up")
|
||||||
|
layout.prop(self, "track")
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
|
|
||||||
self.add_optional_input_socket("UpAxisSocket", "Up")
|
|
||||||
self.add_optional_input_socket("TrackAxisSocket", "Track")
|
|
||||||
|
|
||||||
self.add_optional_input_socket("NodeSocketVector", "Origin")
|
self.add_optional_input_socket("NodeSocketVector", "Origin")
|
||||||
self.add_optional_input_socket("NodeSocketVector", "Destination")
|
self.add_optional_input_socket("NodeSocketVector", "Destination")
|
||||||
self.outputs.new("NodeSocketQuaternion", "Rotation")
|
self.outputs.new("NodeSocketQuaternion", "Rotation")
|
||||||
@ -781,16 +719,14 @@ class RotateTowards(AbstractPowerShipNode):
|
|||||||
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
||||||
origin = self._get_optional_input_value("Origin", Vector)
|
origin = self._get_optional_input_value("Origin", Vector)
|
||||||
destination = self._get_optional_input_value("Destination", Vector)
|
destination = self._get_optional_input_value("Destination", Vector)
|
||||||
|
|
||||||
track = self._get_input_value("Track", str)
|
|
||||||
up = self._get_input_value("Up", str)
|
|
||||||
if not (origin and destination):
|
if not (origin and destination):
|
||||||
return
|
return Quaternion()
|
||||||
|
|
||||||
# Set the rotation of the control
|
# Set the rotation of the control
|
||||||
vec = Vector((destination - origin))
|
vec = Vector((destination - origin))
|
||||||
vec.normalize()
|
# vec.normalize()
|
||||||
rot = vec.to_track_quat(track, up)
|
rot = vec.to_track_quat(self.track, self.up)
|
||||||
|
rot.normalize()
|
||||||
self.outputs["Rotation"].default_value = rot
|
self.outputs["Rotation"].default_value = rot
|
||||||
|
|
||||||
|
|
||||||
@ -800,9 +736,9 @@ class OffsetRotation(AbstractPowerShipNode):
|
|||||||
bl_idname = "OffsetRotation"
|
bl_idname = "OffsetRotation"
|
||||||
bl_label = "Offset Rotation"
|
bl_label = "Offset Rotation"
|
||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
default_value = Quaternion()
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
self.add_optional_input_socket("NodeSocketQuaternion", "Base")
|
self.add_optional_input_socket("NodeSocketQuaternion", "Base")
|
||||||
self.add_optional_input_socket("NodeSocketQuaternion", "Offset")
|
self.add_optional_input_socket("NodeSocketQuaternion", "Offset")
|
||||||
self.outputs.new("NodeSocketQuaternion", "Rotation")
|
self.outputs.new("NodeSocketQuaternion", "Rotation")
|
||||||
@ -810,10 +746,6 @@ class OffsetRotation(AbstractPowerShipNode):
|
|||||||
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
||||||
base = self._get_optional_input_value("Base", Quaternion)
|
base = self._get_optional_input_value("Base", Quaternion)
|
||||||
offset = self._get_optional_input_value("Offset", Quaternion)
|
offset = self._get_optional_input_value("Offset", Quaternion)
|
||||||
|
|
||||||
if not (base and offset):
|
|
||||||
return
|
|
||||||
|
|
||||||
self.outputs["Rotation"].default_value = base @ offset
|
self.outputs["Rotation"].default_value = base @ offset
|
||||||
|
|
||||||
|
|
||||||
@ -825,8 +757,6 @@ class MapRange(AbstractPowerShipNode):
|
|||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
|
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "Value")
|
self.add_optional_input_socket("NodeSocketFloat", "Value")
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "From Min")
|
self.add_optional_input_socket("NodeSocketFloat", "From Min")
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "From Max")
|
self.add_optional_input_socket("NodeSocketFloat", "From Max")
|
||||||
@ -841,55 +771,37 @@ class MapRange(AbstractPowerShipNode):
|
|||||||
tmin = self._get_optional_input_value("Value", float)
|
tmin = self._get_optional_input_value("Value", float)
|
||||||
tmax = self._get_optional_input_value("Value", float)
|
tmax = self._get_optional_input_value("Value", float)
|
||||||
|
|
||||||
if not (val and fmin and fmax and tmin and tmax):
|
|
||||||
return
|
|
||||||
|
|
||||||
slope = (tmax - tmin) / (fmax - fmin)
|
slope = (tmax - tmin) / (fmax - fmin)
|
||||||
offset = tmin - slope * fmin
|
offset = tmin - slope * fmin
|
||||||
self.outputs["Result"].default_value = slope * val + offset
|
self.outputs["Result"].default_value = slope * val + offset
|
||||||
|
|
||||||
|
|
||||||
class AngleTypeSocket(bpy.types.NodeSocket):
|
|
||||||
bl_idname = 'AngleTypeSocket'
|
|
||||||
bl_label = "Angle Type"
|
|
||||||
link_limit = 0
|
|
||||||
|
|
||||||
# Enum items list
|
|
||||||
ops_items = (
|
|
||||||
('SIGNED', "Signed", ""),
|
|
||||||
('UNSIGNED', "Default", ""),
|
|
||||||
)
|
|
||||||
# default_value = "UP"
|
|
||||||
|
|
||||||
default_value: bpy.props.EnumProperty(
|
|
||||||
name="Operation",
|
|
||||||
description="",
|
|
||||||
items=ops_items,
|
|
||||||
default='UNSIGNED',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Optional function for drawing the socket input value
|
|
||||||
def draw(self, context, layout, node, text):
|
|
||||||
if self.is_output or self.is_linked:
|
|
||||||
layout.label(text=text)
|
|
||||||
else:
|
|
||||||
layout.prop(self, "default_value", text=text)
|
|
||||||
|
|
||||||
# Socket color
|
|
||||||
|
|
||||||
def draw_color(self, context, node):
|
|
||||||
return (1.0, 0.4, 0.216, 0.5)
|
|
||||||
|
|
||||||
|
|
||||||
class RotationFromAngle(AbstractPowerShipNode):
|
class RotationFromAngle(AbstractPowerShipNode):
|
||||||
bl_idname = "RotationFromAngle"
|
bl_idname = "RotationFromAngle"
|
||||||
bl_label = "Rotation From Vector Angle"
|
bl_label = "Rotation From Vector Angle"
|
||||||
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
|
||||||
|
name="Type",
|
||||||
|
items=[
|
||||||
|
("DEFAULT", "Default", ""),
|
||||||
|
("SIGNED", "Signed", ""),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw_buttons(
|
||||||
|
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
super().draw_buttons(context, layout)
|
||||||
|
layout.prop(self, "axis")
|
||||||
|
layout.prop(self, "angle_type")
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
self.add_optional_input_socket("UpAxisSocket", "Axis")
|
|
||||||
self.add_optional_input_socket("AngleTypeSocket", "Type")
|
|
||||||
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("NodeSocketQuaternion", "Rotation")
|
||||||
@ -897,60 +809,28 @@ class RotationFromAngle(AbstractPowerShipNode):
|
|||||||
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)
|
||||||
v = self._get_optional_input_value("V", Vector)
|
v = self._get_optional_input_value("V", Vector)
|
||||||
axis = self._get_input_value("Axis", str)
|
signed = self.angle_type == 'SIGNED'
|
||||||
angle_type = self._get_input_value("Type", str)
|
|
||||||
signed = angle_type == 'SIGNED'
|
|
||||||
|
|
||||||
if not (u and v):
|
|
||||||
return
|
|
||||||
|
|
||||||
angle = 0
|
angle = 0
|
||||||
|
|
||||||
if not (u.length == 0 or v.length == 0):
|
if not (u.length == 0 or v.length == 0):
|
||||||
if signed:
|
if signed:
|
||||||
angle = u.angle_signed(v)
|
angle = u.angle_signed(v)
|
||||||
else:
|
else:
|
||||||
angle = u.angle(v)
|
angle = u.angle(v)
|
||||||
|
|
||||||
m = Matrix.Rotation(angle, 3, axis)
|
m = Matrix.Rotation(angle, 3, self.axis)
|
||||||
res = m.to_quaternion()
|
res = m.to_quaternion()
|
||||||
|
|
||||||
self.outputs["Rotation"].default_value = res
|
self.outputs["Rotation"].default_value = res
|
||||||
|
|
||||||
|
|
||||||
class MathOperationSocket(bpy.types.NodeSocket):
|
_enum_vector_math_operations = [
|
||||||
bl_idname = 'MathOperationSocket'
|
('ADD', "Add", ""),
|
||||||
bl_label = "Operations"
|
('SUBSTRACT', "Substract", ""),
|
||||||
link_limit = 0
|
('MULTIPLY', "Mutliply", ""),
|
||||||
|
('DIVIDE', "Divide", ""),
|
||||||
# Enum items list
|
('CROSS', "Cross", ""),
|
||||||
ops_items = (
|
]
|
||||||
('ADD', "Add", ""),
|
|
||||||
('SUBSTRACT', "Substract", ""),
|
|
||||||
('MULTIPLY', "Mutliply", ""),
|
|
||||||
('DIVIDE', "Divide", ""),
|
|
||||||
('CROSS', "Cross", ""),
|
|
||||||
)
|
|
||||||
# default_value = "UP"
|
|
||||||
|
|
||||||
default_value: bpy.props.EnumProperty(
|
|
||||||
name="Operation",
|
|
||||||
description="",
|
|
||||||
items=ops_items,
|
|
||||||
default='ADD',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Optional function for drawing the socket input value
|
|
||||||
def draw(self, context, layout, node, text):
|
|
||||||
if self.is_output or self.is_linked:
|
|
||||||
layout.label(text=text)
|
|
||||||
else:
|
|
||||||
layout.prop(self, "default_value", text=text)
|
|
||||||
|
|
||||||
# Socket color
|
|
||||||
|
|
||||||
def draw_color(self, context, node):
|
|
||||||
return (1.0, 0.4, 0.216, 0.5)
|
|
||||||
|
|
||||||
|
|
||||||
class VectorMath(AbstractPowerShipNode):
|
class VectorMath(AbstractPowerShipNode):
|
||||||
@ -960,9 +840,18 @@ class VectorMath(AbstractPowerShipNode):
|
|||||||
bl_label = "Vector Math"
|
bl_label = "Vector Math"
|
||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
|
operation: bpy.props.EnumProperty( # type: ignore
|
||||||
|
name="Operation",
|
||||||
|
items=_enum_vector_math_operations,
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw_buttons(
|
||||||
|
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
super().draw_buttons(context, layout)
|
||||||
|
layout.prop(self, "operation")
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
self.add_optional_input_socket("MathOperationSocket", "Operation")
|
|
||||||
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("NodeSocketVector", "Result")
|
self.outputs.new("NodeSocketVector", "Result")
|
||||||
@ -970,12 +859,8 @@ class VectorMath(AbstractPowerShipNode):
|
|||||||
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)
|
||||||
v = self._get_optional_input_value("V", Vector)
|
v = self._get_optional_input_value("V", Vector)
|
||||||
mode = self._get_input_value("Operation", str)
|
|
||||||
|
|
||||||
if not (u and v):
|
match self.operation:
|
||||||
return
|
|
||||||
|
|
||||||
match mode:
|
|
||||||
case 'ADD':
|
case 'ADD':
|
||||||
res = u+v
|
res = u+v
|
||||||
case 'MULTIPLY':
|
case 'MULTIPLY':
|
||||||
@ -983,15 +868,24 @@ class VectorMath(AbstractPowerShipNode):
|
|||||||
case 'SUBSTRACT':
|
case 'SUBSTRACT':
|
||||||
res = u-v
|
res = u-v
|
||||||
case 'DIVIDE':
|
case 'DIVIDE':
|
||||||
res = Vector([x/y for x, y in zip(u, v)])
|
res = Vector([x/y if y != 0.0 else 0.0 for x, y in zip(u, v)])
|
||||||
case 'CROSS':
|
case 'CROSS':
|
||||||
res = u.cross(v)
|
res = u.cross(v)
|
||||||
case _:
|
case _:
|
||||||
|
print("Vector math operation not found:", self.operation)
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
self.outputs["Result"].default_value = res
|
self.outputs["Result"].default_value = res
|
||||||
|
|
||||||
|
|
||||||
|
_enum_math_operations = [
|
||||||
|
('ADD', "Add", ""),
|
||||||
|
('SUBSTRACT', "Substract", ""),
|
||||||
|
('MULTIPLY', "Mutliply", ""),
|
||||||
|
('DIVIDE', "Divide", ""),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Math(AbstractPowerShipNode):
|
class Math(AbstractPowerShipNode):
|
||||||
"""Sets the location and/or rotation of the 3D cursor"""
|
"""Sets the location and/or rotation of the 3D cursor"""
|
||||||
|
|
||||||
@ -999,9 +893,18 @@ class Math(AbstractPowerShipNode):
|
|||||||
bl_label = "Math"
|
bl_label = "Math"
|
||||||
bl_icon = "EMPTY_ARROWS"
|
bl_icon = "EMPTY_ARROWS"
|
||||||
|
|
||||||
|
operation: bpy.props.EnumProperty( # type: ignore
|
||||||
|
name="Operation",
|
||||||
|
items=_enum_math_operations,
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw_buttons(
|
||||||
|
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
super().draw_buttons(context, layout)
|
||||||
|
layout.prop(self, "operation")
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
|
||||||
self.add_optional_input_socket("MathOperationSocket", "Operation")
|
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "U")
|
self.add_optional_input_socket("NodeSocketFloat", "U")
|
||||||
self.add_optional_input_socket("NodeSocketFloat", "V")
|
self.add_optional_input_socket("NodeSocketFloat", "V")
|
||||||
self.outputs.new("NodeSocketFloat", "Result")
|
self.outputs.new("NodeSocketFloat", "Result")
|
||||||
@ -1009,12 +912,8 @@ class Math(AbstractPowerShipNode):
|
|||||||
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
def execute(self, depsgraph: bpy.types.Depsgraph) -> None:
|
||||||
u = self._get_optional_input_value("U", float)
|
u = self._get_optional_input_value("U", float)
|
||||||
v = self._get_optional_input_value("V", float)
|
v = self._get_optional_input_value("V", float)
|
||||||
mode = self._get_input_value("Operation", str)
|
|
||||||
|
|
||||||
if not (u and v):
|
match self.operation:
|
||||||
return
|
|
||||||
|
|
||||||
match mode:
|
|
||||||
case 'ADD':
|
case 'ADD':
|
||||||
res = u+v
|
res = u+v
|
||||||
case 'MULTIPLY':
|
case 'MULTIPLY':
|
||||||
@ -1022,9 +921,9 @@ class Math(AbstractPowerShipNode):
|
|||||||
case 'SUBSTRACT':
|
case 'SUBSTRACT':
|
||||||
res = u-v
|
res = u-v
|
||||||
case 'DIVIDE':
|
case 'DIVIDE':
|
||||||
res = u/v
|
res = u/v if v != 0 else 0
|
||||||
case _:
|
case _:
|
||||||
print("MODE NOT FOUND:", mode)
|
print("Math operation not found:", self.operation)
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
self.outputs["Result"].default_value = res
|
self.outputs["Result"].default_value = res
|
||||||
@ -1062,6 +961,17 @@ class SetBoneNode(AbstractPowerShipNode):
|
|||||||
bl_label = "Set Bone"
|
bl_label = "Set Bone"
|
||||||
bl_icon = "BONE_DATA"
|
bl_icon = "BONE_DATA"
|
||||||
|
|
||||||
|
space: bpy.props.EnumProperty( # type: ignore
|
||||||
|
name="Space",
|
||||||
|
items=_enum_control_space,
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw_buttons(
|
||||||
|
self, context: bpy.types.Context, layout: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
super().draw_buttons(context, layout)
|
||||||
|
layout.prop(self, "space")
|
||||||
|
|
||||||
def init(self, context):
|
def init(self, context):
|
||||||
self.add_execution_sockets()
|
self.add_execution_sockets()
|
||||||
|
|
||||||
@ -1103,15 +1013,22 @@ class SetBoneNode(AbstractPowerShipNode):
|
|||||||
if control_scale is not None:
|
if control_scale is not None:
|
||||||
scale = control_scale
|
scale = control_scale
|
||||||
|
|
||||||
v_nil = Vector((0, 0, 0))
|
match self.space:
|
||||||
bone_rest_rot_scale = bone.bone.matrix_local.copy()
|
case "WORLD":
|
||||||
bone_rest_rot_scale.translation = v_nil
|
bone_mat_world = Matrix.LocRotScale(loc, rot, scale)
|
||||||
|
case "CHANNELS":
|
||||||
mat_rot_scale = Matrix.LocRotScale(
|
# Not sure what causes the scale to flip, however...
|
||||||
v_nil, rot, scale) @ bone_rest_rot_scale
|
# 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.
|
||||||
mat_loc = Matrix.Translation(loc)
|
scale = [scale[i] for i in [0, 2, 1]]
|
||||||
bone_mat_world = mat_loc @ mat_rot_scale
|
v_nil = Vector((0, 0, 0))
|
||||||
|
bone_rest_rot_scale = bone.bone.matrix_local.copy()
|
||||||
|
bone_rest_rot_scale.translation = v_nil
|
||||||
|
bone_rest_rot = bone_rest_rot_scale.to_quaternion()
|
||||||
|
mat_rot_scale = Matrix.LocRotScale(
|
||||||
|
v_nil, rot, scale) @ bone_rest_rot.to_matrix().to_4x4()
|
||||||
|
mat_loc = Matrix.Translation(loc)
|
||||||
|
bone_mat_world = mat_loc @ mat_rot_scale
|
||||||
|
|
||||||
bone.matrix = arm_matrix.inverted() @ bone_mat_world
|
bone.matrix = arm_matrix.inverted() @ bone_mat_world
|
||||||
|
|
||||||
@ -1510,10 +1427,6 @@ classes = (
|
|||||||
# Socket types:
|
# Socket types:
|
||||||
NodeSocketExecute,
|
NodeSocketExecute,
|
||||||
NodeSocketQuaternion,
|
NodeSocketQuaternion,
|
||||||
MathOperationSocket,
|
|
||||||
TrackAxisSocket,
|
|
||||||
UpAxisSocket,
|
|
||||||
AngleTypeSocket,
|
|
||||||
# Nodes:
|
# Nodes:
|
||||||
ForwardSolveNode,
|
ForwardSolveNode,
|
||||||
BackwardSolveNode,
|
BackwardSolveNode,
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user