forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
@ -0,0 +1,14 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class AppendTransformNode(LnxLogicTreeNode):
|
||||
"""Appends transform to the given object."""
|
||||
bl_idname = 'LNAppendTransformNode'
|
||||
bl_label = 'Append Transform'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxDynamicSocket', 'Transform')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
@ -0,0 +1,13 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class GetDimensionNode(LnxLogicTreeNode):
|
||||
"""Returns the dimension of the given object."""
|
||||
bl_idname = 'LNGetDimensionNode'
|
||||
bl_label = 'Get Object Dimension'
|
||||
lnx_section = 'dimension'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Dimension')
|
@ -0,0 +1,26 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class GetLocationNode(LnxLogicTreeNode):
|
||||
"""Get the location of the given object in world coordinates.
|
||||
|
||||
@input Parent Relative: If enabled, transforms the world coordinates into object parent local coordinates
|
||||
|
||||
@seeNode Set Object Location
|
||||
@seeNode World Vector to Local Space
|
||||
@seeNode Vector to Object Orientation
|
||||
"""
|
||||
bl_idname = 'LNGetLocationNode'
|
||||
bl_label = 'Get Object Location'
|
||||
lnx_section = 'location'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxBoolSocket', 'Parent Relative')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Location')
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
return NodeReplacement.Identity(self)
|
@ -0,0 +1,91 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class GetRotationNode(LnxLogicTreeNode):
|
||||
"""Returns the current rotation of the given object."""
|
||||
bl_idname = 'LNGetRotationNode'
|
||||
bl_label = 'Get Object Rotation'
|
||||
lnx_section = 'rotation'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_output('LnxRotationSocket', 'Rotation')
|
||||
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Local', 'Local', 'Local'),
|
||||
('Global', 'Global', 'Global')],
|
||||
name='', default='Local')
|
||||
|
||||
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
|
||||
# transition from version 1 to version 2: make rotations their own sockets
|
||||
# this transition is a mess, I know.
|
||||
|
||||
newself = self.id_data.nodes.new('LNGetRotationNode')
|
||||
newself.property0 = 'Local'
|
||||
newnodes = [newself]
|
||||
|
||||
if len(self.outputs[0].links)>0:
|
||||
# euler (radians) needed
|
||||
converter = self.id_data.nodes.new('LNSeparateRotationNode')
|
||||
converter.property0 = "EulerAngles"
|
||||
converter.property1 = "Rad"
|
||||
converter.property2 = "XZY"
|
||||
newnodes.append(converter)
|
||||
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
|
||||
for link in self.outputs[0].links:
|
||||
self.id_data.links.new(converter.outputs[0], link.to_socket)
|
||||
|
||||
if len(self.outputs[4].links)>0 or len(self.outputs[5].links)>0:
|
||||
# quaternion needed
|
||||
converter = self.id_data.nodes.new('LNSeparateRotationNode')
|
||||
converter.property0 = "Quaternion"
|
||||
newnodes.append(converter)
|
||||
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
|
||||
for link in self.outputs[4].links:
|
||||
self.id_data.links.new(converter.outputs[0], link.to_socket)
|
||||
for link in self.outputs[5].links:
|
||||
self.id_data.links.new(converter.outputs[1], link.to_socket)
|
||||
|
||||
if len(self.outputs[1].links)>0 or len(self.outputs[2].links)>0 or len(self.outputs[3].links)>0:
|
||||
# axis/angle needed
|
||||
converter = self.id_data.nodes.new('LNSeparateRotationNode')
|
||||
converter.property0 = "AxisAngle"
|
||||
converter.property1 = "Rad"
|
||||
newnodes.append(converter)
|
||||
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
|
||||
for link in self.outputs[1].links:
|
||||
self.id_data.links.new(converter.outputs[0], link.to_socket)
|
||||
|
||||
if len(self.outputs[3].links)==0 and len(self.outputs[2].links)==0:
|
||||
pass
|
||||
elif len(self.outputs[3].links)==0:
|
||||
for link in self.outputs[2].links:
|
||||
self.id_data.links.new(converter.outputs[1], link.to_socket)
|
||||
elif len(self.outputs[2].links)==0:
|
||||
converter.property1 = 'Deg'
|
||||
for link in self.outputs[3].links:
|
||||
self.id_data.links.new(converter.outputs[1], link.to_socket)
|
||||
else:
|
||||
for link in self.outputs[2].links:
|
||||
self.id_data.links.new(converter.outputs[1], link.to_socket)
|
||||
converter = self.id_data.nodes.new('LNSeparateRotationNode')
|
||||
converter.property0 = "AxisAngle"
|
||||
converter.property1 = "Deg"
|
||||
converter.property2 = "XYZ" # bogus
|
||||
newnodes.append(converter)
|
||||
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
|
||||
for link in self.outputs[3].links:
|
||||
self.id_data.links.new(converter.outputs[1], link.to_socket)
|
||||
|
||||
return newnodes
|
@ -0,0 +1,13 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class GetScaleNode(LnxLogicTreeNode):
|
||||
"""Returns the scale of the given object."""
|
||||
bl_idname = 'LNGetScaleNode'
|
||||
bl_label = 'Get Object Scale'
|
||||
lnx_section = 'scale'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Scale')
|
@ -0,0 +1,14 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class GetTransformNode(LnxLogicTreeNode):
|
||||
"""Returns the transformation of the given object. An object's
|
||||
transform consists of vectors describing its global location,
|
||||
rotation and scale."""
|
||||
bl_idname = 'LNGetTransformNode'
|
||||
bl_label = 'Get Object Transform'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
|
||||
self.add_output('LnxDynamicSocket', 'Transform')
|
@ -0,0 +1,23 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class GetWorldNode(LnxLogicTreeNode):
|
||||
"""Returns the world orientation of the given object."""
|
||||
bl_idname = 'LNGetWorldNode'
|
||||
bl_label = 'Get World Orientation'
|
||||
lnx_section = 'rotation'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Right', 'Right', 'The object right (X) direction'),
|
||||
('Look', 'Look', 'The object look (Y) direction'),
|
||||
('Up', 'Up', 'The object up (Z) direction')],
|
||||
name='', default='Look')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Vector')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
47
leenkx/blender/lnx/logicnode/transform/LN_look_at.py
Normal file
47
leenkx/blender/lnx/logicnode/transform/LN_look_at.py
Normal file
@ -0,0 +1,47 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class LookAtNode(LnxLogicTreeNode):
|
||||
"""Returns *a* rotation that makes something look away from X,Y or Z, and instead look in the 'from->to' direction"""
|
||||
bl_idname = 'LNLookAtNode'
|
||||
bl_label = 'Look At'
|
||||
lnx_section = 'rotation'
|
||||
lnx_version = 2
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('X', ' X', 'X'),
|
||||
('-X', '-X', '-X'),
|
||||
('Y', ' Y', 'Y'),
|
||||
('-Y', '-Y', '-Y'),
|
||||
('Z', ' Z', 'Z'),
|
||||
('-Z', '-Z', '-Z')],
|
||||
name='With', default='Z')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxVectorSocket', 'From Location')
|
||||
self.add_input('LnxVectorSocket', 'To Location')
|
||||
|
||||
self.add_output('LnxRotationSocket', 'Rotation')
|
||||
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
|
||||
# transition from version 1 to version 2: make rotations their own sockets
|
||||
# this transition is a mess, I know.
|
||||
newself = self.id_data.nodes.new('LNLookAtNode')
|
||||
converter = self.id_data.nodes.new('LNSeparateRotationNode')
|
||||
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
|
||||
converter.property0 = 'EulerAngles'
|
||||
converter.property1 = 'Rad'
|
||||
converter.property2 = 'XZY'
|
||||
for link in self.outputs[0].links:
|
||||
self.id_data.links.new(converter.outputs[0], link.to_socket)
|
||||
|
||||
return [newself, converter]
|
105
leenkx/blender/lnx/logicnode/transform/LN_rotate_object.py
Normal file
105
leenkx/blender/lnx/logicnode/transform/LN_rotate_object.py
Normal file
@ -0,0 +1,105 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
from lnx.logicnode.lnx_sockets import LnxRotationSocket as Rotation
|
||||
|
||||
class RotateObjectNode(LnxLogicTreeNode):
|
||||
"""Rotates the given object."""
|
||||
bl_idname = 'LNRotateObjectNode'
|
||||
bl_label = 'Rotate Object'
|
||||
lnx_section = 'rotation'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxRotationSocket', 'Rotation')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0_proxy')
|
||||
|
||||
|
||||
# this property swaperoo is kinda janky-looking, but listen out:
|
||||
# - when you reload an old file, the properties of loaded nodes can be mangled if the node class drops the property or the specific value within the property.
|
||||
# -> to fix this, 'property0' needs to contain the old values so that node replacement can be done decently.
|
||||
# - "but", I hear you ask, "why not make property0 a simple blender property, and create a property0v2 HaxeProperty to be bound to the haxe-time property0?"
|
||||
# -> well, at the time of writing, a HaxeProperty's prop_name is only used for livepatching, not at initial setup, so a freshly-compiled game would get completely borked properties.
|
||||
# solution: have a property0 HaxeProperty contain every possible value, and have a property0_proxy Property be in the UI.
|
||||
|
||||
# NOTE FOR FUTURE MAINTAINERS: the value of the proxy property does **not** matter, only the value of property0 does. When eventually editing this class, you can safely drop the values in the proxy property, and *only* the proxy property.
|
||||
|
||||
def on_proxyproperty_update(self, context=None):
|
||||
self.property0 = self.property0_proxy
|
||||
|
||||
property0_proxy: EnumProperty(
|
||||
items = [('Local', 'Local F.O.R.', 'Frame of reference oriented with the object'),
|
||||
('Global', 'Global/Parent F.O.R.',
|
||||
'Frame of reference oriented with the object\'s parent or the world')],
|
||||
name='', default='Local',
|
||||
update = on_proxyproperty_update
|
||||
)
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items=[('Euler Angles', 'NODE REPLACEMENT ONLY', ''),
|
||||
('Angle Axies (Radians)', 'NODE REPLACEMENT ONLY', ''),
|
||||
('Angle Axies (Degrees)', 'NODE REPLACEMENT ONLY', ''),
|
||||
('Quaternion', 'NODE REPLACEMENT ONLY', ''),
|
||||
('Local', 'Local F.O.R.', 'Frame of reference oriented with the object'),
|
||||
('Global', 'Global/Parent F.O.R.',
|
||||
'Frame of reference oriented with the object\'s parent or the world')
|
||||
],
|
||||
name='', default='Local')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
|
||||
# transition from version 1 to version 2: make rotations their own sockets
|
||||
# this transition is a mess, I know.
|
||||
|
||||
newself = self.id_data.nodes.new('LNRotateObjectNode')
|
||||
inputnode = self.id_data.nodes.new('LNRotationNode')
|
||||
self.id_data.links.new(inputnode.outputs[0], newself.inputs[2])
|
||||
newself.inputs[1].default_value_raw = self.inputs[1].default_value_raw
|
||||
inputnode.inputs[0].default_value = self.inputs[2].default_value
|
||||
inputnode.inputs[1].default_value = self.inputs[3].default_value
|
||||
|
||||
if len(self.inputs[0].links) >0:
|
||||
self.id_data.links.new(self.inputs[0].links[0].from_socket, newself.inputs[0])
|
||||
if len(self.inputs[1].links) >0:
|
||||
self.id_data.links.new(self.inputs[1].links[0].from_socket, newself.inputs[1])
|
||||
if len(self.inputs[2].links) >0:
|
||||
self.id_data.links.new(self.inputs[2].links[0].from_socket, inputnode.inputs[0])
|
||||
if len(self.inputs[3].links) >0:
|
||||
self.id_data.links.new(self.inputs[3].links[0].from_socket, inputnode.inputs[1])
|
||||
|
||||
# first, convert the default value
|
||||
if self.property0 == 'Quaternion':
|
||||
inputnode.property0 = 'Quaternion'
|
||||
elif self.property0 == 'Euler Angles':
|
||||
inputnode.property0 = 'EulerAngles'
|
||||
inputnode.property1 = 'Rad'
|
||||
inputnode.property2 = 'XZY' # legacy order
|
||||
else: # starts with "Angle Axies"
|
||||
inputnode.property0 = 'AxisAngle'
|
||||
if 'Degrees' in self.property0:
|
||||
inputnode.property1 = 'Deg'
|
||||
else:
|
||||
inputnode.property1 = 'Rad'
|
||||
quat = Rotation.convert_to_quaternion(
|
||||
self.inputs[2].default_value,
|
||||
self.inputs[3].default_value,
|
||||
inputnode.property0,
|
||||
inputnode.property1,
|
||||
inputnode.property2
|
||||
)
|
||||
newself.inputs[2].default_value_raw = quat
|
||||
return [newself, inputnode]
|
@ -0,0 +1,60 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SeparateRotationNode(LnxLogicTreeNode):
|
||||
"""Decompose a rotation into one of its mathematical representations"""
|
||||
bl_idname = 'LNSeparateRotationNode'
|
||||
bl_label = 'Separate Rotation'
|
||||
lnx_section = 'rotation'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxRotationSocket', 'Angle')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Euler Angles / Vector XYZ')
|
||||
self.add_output('LnxFloatSocket', 'Angle / W')
|
||||
|
||||
|
||||
def on_property_update(self, context):
|
||||
"""called by the EnumProperty, used to update the node socket labels"""
|
||||
if self.property0 == 'Quaternion':
|
||||
self.outputs[0].name = "Quaternion XYZ"
|
||||
self.outputs[1].name = "Quaternion W"
|
||||
elif self.property0 == 'EulerAngles':
|
||||
self.outputs[0].name = "Euler Angles"
|
||||
self.outputs[1].name = "[unused for Euler output]"
|
||||
elif self.property0 == 'AxisAngle':
|
||||
self.outputs[0].name = "Axis"
|
||||
self.outputs[1].name = "Angle"
|
||||
else:
|
||||
raise ValueError('No nodesocket labels for current input mode: check self-consistancy of LN_separate_rotation.py')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
coll = layout.column(align=True)
|
||||
coll.prop(self, 'property0')
|
||||
if self.property0 in ('EulerAngles','AxisAngle'):
|
||||
coll.prop(self, 'property1')
|
||||
if self.property0=='EulerAngles':
|
||||
coll.prop(self, 'property2')
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('EulerAngles', 'Euler Angles', 'Euler Angles'),
|
||||
('AxisAngle', 'Axis/Angle', 'Axis/Angle'),
|
||||
('Quaternion', 'Quaternion', 'Quaternion')],
|
||||
name='', default='EulerAngles',
|
||||
update=on_property_update)
|
||||
|
||||
property1: HaxeEnumProperty(
|
||||
'property1',
|
||||
items=[('Deg', 'Degrees', 'Degrees'),
|
||||
('Rad', 'Radians', 'Radians')],
|
||||
name='', default='Rad')
|
||||
property2: HaxeEnumProperty(
|
||||
'property2',
|
||||
items=[('XYZ','XYZ','XYZ'),
|
||||
('XZY','XZY (legacy Leenkx euler order)','XZY (legacy Leenkx euler order)'),
|
||||
('YXZ','YXZ','YXZ'),
|
||||
('YZX','YZX','YZX'),
|
||||
('ZXY','ZXY','ZXY'),
|
||||
('ZYX','ZYX','ZYX')],
|
||||
name='', default='XYZ')
|
@ -0,0 +1,45 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SeparateTransformNode(LnxLogicTreeNode):
|
||||
"""Separates the transform of the given object."""
|
||||
bl_idname = 'LNSeparateTransformNode'
|
||||
bl_label = 'Separate Transform'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxDynamicSocket', 'Transform')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Location')
|
||||
self.add_output('LnxRotationSocket', 'Rotation')
|
||||
self.add_output('LnxVectorSocket', 'Scale')
|
||||
|
||||
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
|
||||
# transition from version 1 to version 2: make rotations their own sockets
|
||||
# this transition is a mess, I know.
|
||||
newself = self.id_data.nodes.new('LNSeparateTransformNode')
|
||||
for link in self.outputs[0].links:
|
||||
self.id_data.links.new(newself.outputs[0], link.to_socket)
|
||||
for link in self.outputs[2].links:
|
||||
self.id_data.links.new(newself.outputs[2], link.to_socket)
|
||||
for link in self.inputs[0].links:
|
||||
self.id_data.links.new(link.from_socket, newself.inputs[0])
|
||||
|
||||
ret = [newself]
|
||||
rot_links = self.outputs[1].links
|
||||
if len(rot_links) >0:
|
||||
converter = self.id_data.nodes.new('LNSeparateRotationNode')
|
||||
ret.append(converter)
|
||||
self.id_data.links.new(newself.outputs[1], converter.inputs[0])
|
||||
converter.property0 = 'EulerAngles'
|
||||
converter.property1 = 'Rad'
|
||||
converter.property2 = 'XZY'
|
||||
for link in rot_links:
|
||||
self.id_data.links.new(converter.outputs[0], link.to_socket)
|
||||
|
||||
return ret
|
@ -0,0 +1,28 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SetLocationNode(LnxLogicTreeNode):
|
||||
"""Set the location of the given object in world coordinates.
|
||||
|
||||
@input Parent Relative: If enabled, transforms the world coordinates into object parent local coordinates
|
||||
|
||||
@seeNode Get Object Location
|
||||
@seeNode World Vector to Local Space
|
||||
@seeNode Vector to Object Orientation
|
||||
"""
|
||||
bl_idname = 'LNSetLocationNode'
|
||||
bl_label = 'Set Object Location'
|
||||
lnx_section = 'location'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxVectorSocket', 'Location')
|
||||
self.add_input('LnxBoolSocket', 'Parent Relative')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
return NodeReplacement.Identity(self)
|
@ -0,0 +1,76 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
from lnx.logicnode.lnx_sockets import LnxRotationSocket as Rotation
|
||||
|
||||
class SetRotationNode(LnxLogicTreeNode):
|
||||
"""Sets the rotation of the given object."""
|
||||
bl_idname = 'LNSetRotationNode'
|
||||
bl_label = 'Set Object Rotation'
|
||||
lnx_section = 'rotation'
|
||||
lnx_version = 2
|
||||
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxRotationSocket', 'Rotation')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
|
||||
# transition from version 1 to version 2: make rotations their own sockets
|
||||
# this transition is a mess, I know.
|
||||
|
||||
|
||||
newself = self.id_data.nodes.new('LNRotateObjectNode')
|
||||
inputnode = self.id_data.nodes.new('LNRotationNode')
|
||||
self.id_data.links.new(inputnode.outputs[0], newself.inputs[2])
|
||||
newself.inputs[1].default_value_raw = self.inputs[1].default_value_raw
|
||||
inputnode.inputs[0].default_value = self.inputs[2].default_value
|
||||
inputnode.inputs[1].default_value = self.inputs[3].default_value
|
||||
|
||||
if len(self.inputs[0].links) >0:
|
||||
self.id_data.links.new(self.inputs[0].links[0].from_socket, newself.inputs[0])
|
||||
if len(self.inputs[1].links) >0:
|
||||
self.id_data.links.new(self.inputs[1].links[0].from_socket, newself.inputs[1])
|
||||
if len(self.inputs[2].links) >0:
|
||||
self.id_data.links.new(self.inputs[2].links[0].from_socket, inputnode.inputs[0])
|
||||
if len(self.inputs[3].links) >0:
|
||||
self.id_data.links.new(self.inputs[3].links[0].from_socket, inputnode.inputs[1])
|
||||
|
||||
# first, convert the default value
|
||||
if self.property0 == 'Quaternion':
|
||||
inputnode.property0 = 'Quaternion'
|
||||
elif self.property0 == 'Euler Angles':
|
||||
inputnode.property0 = 'EulerAngles'
|
||||
inputnode.property1 = 'Rad'
|
||||
inputnode.property2 = 'XZY' # legacy order
|
||||
elif self.property0.startswith("Angle Axies "):
|
||||
inputnode.property0 = 'AxisAngle'
|
||||
if 'Degrees' in self.property0:
|
||||
inputnode.property1 = 'Deg'
|
||||
else:
|
||||
inputnode.property1 = 'Rad'
|
||||
else:
|
||||
raise ValueError('nonsensical value {:s} for property0 in SetObjectRotationNode/V1. please report this to the devs.'.format(self.property0))
|
||||
quat = Rotation.convert_to_quaternion(
|
||||
self.inputs[2].default_value,
|
||||
self.inputs[3].default_value,
|
||||
inputnode.property0,
|
||||
inputnode.property1,
|
||||
inputnode.property2
|
||||
)
|
||||
newself.inputs[2].default_value_raw = quat
|
||||
return [newself, inputnode]
|
||||
|
||||
# note: this is unused, but kept here so that the 'property0' field can be read during node replacement
|
||||
property0: EnumProperty(
|
||||
items = [('Euler Angles', '',''),
|
||||
('Angle Axies (Radians)', '', ''),
|
||||
('Angle Axies (Degrees)', '', ''),
|
||||
('Quaternion', '', '')],
|
||||
name='', default='Euler Angles')
|
@ -0,0 +1,15 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SetScaleNode(LnxLogicTreeNode):
|
||||
"""Sets the scale of the given object."""
|
||||
bl_idname = 'LNSetScaleNode'
|
||||
bl_label = 'Set Object Scale'
|
||||
lnx_section = 'scale'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxVectorSocket', 'Scale', default_value=[1.0, 1.0, 1.0])
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
@ -0,0 +1,14 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SetTransformNode(LnxLogicTreeNode):
|
||||
"""Sets the transform of the given object."""
|
||||
bl_idname = 'LNSetTransformNode'
|
||||
bl_label = 'Set Object Transform'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxDynamicSocket', 'Transform')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
13
leenkx/blender/lnx/logicnode/transform/LN_transform_math.py
Normal file
13
leenkx/blender/lnx/logicnode/transform/LN_transform_math.py
Normal file
@ -0,0 +1,13 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class TransformMathNode(LnxLogicTreeNode):
|
||||
"""Operates the two given transform values."""
|
||||
bl_idname = 'LNTransformMathNode'
|
||||
bl_label = 'Transform Math'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxDynamicSocket', 'Transform 1')
|
||||
self.add_input('LnxDynamicSocket', 'Transform 2')
|
||||
|
||||
self.add_output('LnxDynamicSocket', 'Result')
|
@ -0,0 +1,14 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class RotationFromTransformNode(LnxLogicTreeNode):
|
||||
"""Returns rotation matrix from the given transform."""
|
||||
bl_idname = 'LNRotationFromTransformNode'
|
||||
bl_label = 'Transform to Rotation Matrix'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxDynamicSocket', 'Transform')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Vector x')
|
||||
self.add_output('LnxVectorSocket', 'Vector y')
|
||||
self.add_output('LnxVectorSocket', 'Vector z')
|
@ -0,0 +1,40 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class VectorFromTransformNode(LnxLogicTreeNode):
|
||||
"""Returns vector from the given transform."""
|
||||
bl_idname = 'LNVectorFromTransformNode'
|
||||
bl_label = 'Transform to Vector'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxDynamicSocket', 'Transform')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Vector')
|
||||
self.add_output('LnxVectorSocket', 'Quaternion XYZ')
|
||||
self.add_output('LnxFloatSocket', 'Quaternion W')
|
||||
|
||||
def on_property_update(self, context):
|
||||
"""called by the EnumProperty, used to update the node socket labels"""
|
||||
# note: the conditions on len(self.outputs) are take in account "old version" (pre-2020.9) nodes, which only have one output
|
||||
if self.property0 == "Quaternion":
|
||||
self.outputs[0].name = "Quaternion"
|
||||
if len(self.outputs) > 1:
|
||||
self.outputs[1].name = "Quaternion XYZ"
|
||||
self.outputs[2].name = "Quaternion W"
|
||||
else:
|
||||
self.outputs[0].name = "Vector"
|
||||
if len(self.outputs) > 1:
|
||||
self.outputs[1].name = "[quaternion only]"
|
||||
self.outputs[2].name = "[quaternion only]"
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Right', 'Right', 'The transform right (X) direction'),
|
||||
('Look', 'Look', 'The transform look (Y) direction'),
|
||||
('Up', 'Up', 'The transform up (Z) direction'),
|
||||
('Quaternion', 'Quaternion', 'Quaternion')],
|
||||
name='', default='Look',
|
||||
update=on_property_update)
|
@ -0,0 +1,16 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class TranslateObjectNode(LnxLogicTreeNode):
|
||||
"""Translates (moves) the given object using the given vector in world coordinates."""
|
||||
bl_idname = 'LNTranslateObjectNode'
|
||||
bl_label = 'Translate Object'
|
||||
lnx_section = 'location'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxVectorSocket', 'Vector')
|
||||
self.add_input('LnxBoolSocket', 'On Local Axis')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
@ -0,0 +1,18 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TranslateOnLocalAxisNode(LnxLogicTreeNode):
|
||||
"""Translates (moves) the given object using the given vector in the local coordinates."""
|
||||
bl_idname = 'LNTranslateOnLocalAxisNode'
|
||||
bl_label = 'Translate On Local Axis'
|
||||
lnx_section = 'location'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxFloatSocket', 'Speed')
|
||||
self.add_input('LnxIntSocket', 'Forward/Up/Right')
|
||||
self.add_input('LnxBoolSocket', 'Inverse')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
@ -0,0 +1,19 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class VectorToObjectOrientationNode(LnxLogicTreeNode):
|
||||
"""Transform world coordinates into object oriented coordinates (in other words: apply object rotation to it).
|
||||
|
||||
@seeNode World Vector to Object Space
|
||||
@seeNode Get World Orientation
|
||||
@seeNode Vector From Transform
|
||||
"""
|
||||
bl_idname = 'LNVectorToObjectOrientationNode'
|
||||
bl_label = 'Vector to Object Orientation'
|
||||
lnx_section = 'rotation'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxVectorSocket', 'World')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Oriented')
|
@ -0,0 +1,19 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class WorldVectorToLocalSpaceNode(LnxLogicTreeNode):
|
||||
"""Transform world coordinates into object local coordinates.
|
||||
|
||||
@seeNode Vector to Object Orientation
|
||||
@seeNode Get World Orientation
|
||||
@seeNode Vector From Transform
|
||||
"""
|
||||
bl_idname = 'LNWorldVectorToLocalSpaceNode'
|
||||
bl_label = 'World Vector to Local Space'
|
||||
lnx_section = 'location'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketObject', 'Object')
|
||||
self.add_input('LnxVectorSocket', 'World')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Local')
|
8
leenkx/blender/lnx/logicnode/transform/__init__.py
Normal file
8
leenkx/blender/lnx/logicnode/transform/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
from lnx.logicnode.lnx_nodes import add_node_section
|
||||
|
||||
add_node_section(name='default', category='Transform')
|
||||
add_node_section(name='location', category='Transform')
|
||||
add_node_section(name='rotation', category='Transform')
|
||||
add_node_section(name='scale', category='Transform')
|
||||
add_node_section(name='quaternions', category='Transform')
|
||||
add_node_section(name='misc', category='Transform')
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user