Update Files
This commit is contained in:
58
leenkx/blender/lnx/logicnode/math/LN_bitwise_math.py
Normal file
58
leenkx/blender/lnx/logicnode/math/LN_bitwise_math.py
Normal file
@ -0,0 +1,58 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class BitwiseMathNode(LnxLogicTreeNode):
|
||||
"""Perform bitwise math on integer values."""
|
||||
bl_idname = 'LNBitwiseMathNode'
|
||||
bl_label = 'Bitwise Math'
|
||||
lnx_version = 1
|
||||
|
||||
operators = {
|
||||
'negation': '~',
|
||||
'and': '&',
|
||||
'or': '|',
|
||||
'xor': '^',
|
||||
'left_shift': '<<',
|
||||
'right_shift': '>>',
|
||||
'unsigned_right_shift': '>>>'
|
||||
}
|
||||
|
||||
def set_mode(self, context):
|
||||
if self.property0 == 'negation':
|
||||
self.inputs[0].name = 'Operand'
|
||||
self.inputs.remove(self.inputs[1])
|
||||
else:
|
||||
self.inputs[0].name = 'Operand 1'
|
||||
if len(self.inputs) < 2:
|
||||
self.add_input('LnxIntSocket', 'Operand 2')
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items=[
|
||||
('negation', 'Negation (~)', 'Performs bitwise negation on the input, so a 0-bit becomes a 1-bit and vice versa'),
|
||||
None,
|
||||
('and', 'And (&)', 'A bit in the result is 1 if both bits at the same digit in the operands are 1, else it is 0'),
|
||||
('or', 'Or (|)', 'A bit in the result is 1 if at least one bit at the same digit in the operands is 1, else it is 0'),
|
||||
('xor', 'Xor (^)', 'A bit in the result is 1 if exactly one bit at the same digit in the operands is 1, else it is 0'),
|
||||
None,
|
||||
('left_shift', 'Left Shift (<<)', 'Shifts the bits of operand 1 to the left by the amount of operand 2. The result is undefined if operand 2 is negative'),
|
||||
('right_shift', 'Right Shift (>>)', 'Shifts the bits of operand 1 to the right by the amount of operand 2 and keeps the sign of operand 1 (the most significant bit does not change). The result is undefined if operand 2 is negative'),
|
||||
('unsigned_right_shift', 'Unsigned Right Shift (>>>)', 'Shifts the bits of operand 1 to the right by the amount of operand 2, and the most significant bit is set to 0. The result is undefined if operand 2 is negative'),
|
||||
],
|
||||
name='Operation',
|
||||
description='The operation to perform on the input(s)',
|
||||
default='negation',
|
||||
update=set_mode
|
||||
)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxIntSocket', 'Operand 1')
|
||||
self.add_output('LnxIntSocket', 'Result')
|
||||
|
||||
self.set_mode(context)
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0', text='')
|
||||
|
||||
def draw_label(self) -> str:
|
||||
return f'{self.bl_label}: {self.operators[self.property0]}'
|
17
leenkx/blender/lnx/logicnode/math/LN_clamp.py
Normal file
17
leenkx/blender/lnx/logicnode/math/LN_clamp.py
Normal file
@ -0,0 +1,17 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class ClampNode(LnxLogicTreeNode):
|
||||
"""Keeps the value inside the given bound.
|
||||
|
||||
@seeNode Map Range
|
||||
"""
|
||||
bl_idname = 'LNClampNode'
|
||||
bl_label = 'Clamp'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'Value')
|
||||
self.add_input('LnxFloatSocket', 'Min')
|
||||
self.add_input('LnxFloatSocket', 'Max')
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
24
leenkx/blender/lnx/logicnode/math/LN_color_mix.py
Normal file
24
leenkx/blender/lnx/logicnode/math/LN_color_mix.py
Normal file
@ -0,0 +1,24 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class ColorMixNode(LnxLogicTreeNode):
|
||||
"""Mix 2 colors:
|
||||
|
||||
@input Color: Fist color to mix.
|
||||
@input Color: Second color to mix.
|
||||
@input Mix: Mix factor from 0 to 1.
|
||||
|
||||
@output Color: Mixed color.
|
||||
|
||||
@see https://github.com/rvanwijnen/spectral.js 2023 Ronald van Wijnen.
|
||||
"""
|
||||
|
||||
bl_idname = 'LNColorMixNode'
|
||||
bl_label = 'Color Mix'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxColorSocket', 'Color In', default_value=[1.0, 1.0, 1.0, 1.0])
|
||||
self.add_input('LnxColorSocket', 'Color In', default_value=[1.0, 1.0, 1.0, 1.0])
|
||||
self.add_input('LnxFloatSocket', 'Mix', default_value= 0.5)
|
||||
|
||||
self.add_output('LnxColorSocket', 'Color Out', is_var=True)
|
20
leenkx/blender/lnx/logicnode/math/LN_combine_hsv.py
Normal file
20
leenkx/blender/lnx/logicnode/math/LN_combine_hsv.py
Normal file
@ -0,0 +1,20 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class CombineColorNode(LnxLogicTreeNode):
|
||||
"""Combines the given HSVA() components to a color value.
|
||||
If any input is `null`, the respective channel of the output color is set to `0.0`.
|
||||
formula from // https://stackoverflow.com/a/17243070
|
||||
"""
|
||||
bl_idname = 'LNCombineColorHSVNode'
|
||||
bl_label = 'Combine HSVA'
|
||||
lnx_section = 'color'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'H', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'S', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'V', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'A', default_value=1.0)
|
||||
|
||||
self.add_output('LnxColorSocket', 'Color', default_value=[1.0, 1.0, 1.0, 1.0])
|
19
leenkx/blender/lnx/logicnode/math/LN_combine_rgb.py
Normal file
19
leenkx/blender/lnx/logicnode/math/LN_combine_rgb.py
Normal file
@ -0,0 +1,19 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class CombineColorNode(LnxLogicTreeNode):
|
||||
"""Combines the given RGBA (red, green, blue, and alpha) components to a color value.
|
||||
If any input is `null`, the respective channel of the output color is set to `0.0`.
|
||||
"""
|
||||
bl_idname = 'LNCombineColorNode'
|
||||
bl_label = 'Combine RGBA'
|
||||
lnx_section = 'color'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'R', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'G', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'B', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'A', default_value=1.0)
|
||||
|
||||
self.add_output('LnxColorSocket', 'Color', default_value=[1.0, 1.0, 1.0, 1.0])
|
67
leenkx/blender/lnx/logicnode/math/LN_compare.py
Normal file
67
leenkx/blender/lnx/logicnode/math/LN_compare.py
Normal file
@ -0,0 +1,67 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
def remove_extra_inputs(self, context):
|
||||
if not any(p == self.property0 for p in ['Or', 'And']):
|
||||
while len(self.inputs) > self.min_inputs:
|
||||
self.inputs.remove(self.inputs[-1])
|
||||
if self.property0 == 'Between':
|
||||
self.add_input('LnxDynamicSocket', 'Input 3')
|
||||
|
||||
class CompareNode(LnxLogicTreeNode):
|
||||
"""Compares values."""
|
||||
bl_idname = 'LNCompareNode'
|
||||
bl_label = 'Compare'
|
||||
lnx_version = 3
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Equal', 'Equal', 'Equal'),
|
||||
('Not Equal', 'Not Equal', 'Not Equal'),
|
||||
('Almost Equal', 'Almost Equal', 'Almost Equal'),
|
||||
('Greater', 'Greater', 'Greater'),
|
||||
('Greater Equal', 'Greater Equal', 'Greater Equal'),
|
||||
('Less', 'Less', 'Less'),
|
||||
('Less Equal', 'Less Equal', 'Less Equal'),
|
||||
('Between', 'Between', 'Input 1 Between Input 2 and Input 3 inclusive'),
|
||||
('Or', 'Or', 'Or'),
|
||||
('And', 'And', 'And')],
|
||||
name='', default='Equal',
|
||||
update=remove_extra_inputs)
|
||||
min_inputs = 2
|
||||
property1: HaxeFloatProperty('property1', name='Tolerance', description='Precision for float compare', default=0.0001)
|
||||
|
||||
def __init__(self):
|
||||
super(CompareNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxDynamicSocket', 'Value')
|
||||
self.add_input('LnxDynamicSocket', 'Value')
|
||||
|
||||
self.add_output('LnxBoolSocket', 'Bool')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
if self.property0 == 'Almost Equal':
|
||||
layout.prop(self, 'property1')
|
||||
|
||||
if any(p == self.property0 for p in ['Or', 'And']):
|
||||
row = layout.row(align=True)
|
||||
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxDynamicSocket'
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.inputs) == self.min_inputs:
|
||||
column.enabled = False
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 2):
|
||||
raise LookupError()
|
||||
|
||||
if self.lnx_version == 1 or self.lnx_version == 2:
|
||||
return NodeReplacement(
|
||||
'LNGateNode', self.lnx_version, 'LNGateNode', 2,
|
||||
in_socket_mapping={0:0, 1:1, 2:2}, out_socket_mapping={0:0, 1:1}
|
||||
)
|
13
leenkx/blender/lnx/logicnode/math/LN_deg_to_rad.py
Normal file
13
leenkx/blender/lnx/logicnode/math/LN_deg_to_rad.py
Normal file
@ -0,0 +1,13 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class DegToRadNode(LnxLogicTreeNode):
|
||||
"""Converts degrees to radians."""
|
||||
bl_idname = 'LNDegToRadNode'
|
||||
bl_label = 'Deg to Rad'
|
||||
lnx_version = 1
|
||||
lnx_section = 'angle'
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'Degrees')
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Radians')
|
@ -0,0 +1,24 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class DistanceScreenToWorldSpaceNode(LnxLogicTreeNode):
|
||||
"""Gets the distance from given screen coordinates to World coordinates.
|
||||
|
||||
@input Screen X: screen x position.
|
||||
@input Screen Y: screen y position.
|
||||
@input At: World coordinates is a vector position.
|
||||
|
||||
@output Distance At: distance result.
|
||||
"""
|
||||
|
||||
bl_idname = 'LNDistanceScreenToWorldSpaceNode'
|
||||
bl_label = 'Distance Screen to World Space'
|
||||
lnx_section = 'matrix'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxIntSocket', 'Screen X')
|
||||
self.add_input('LnxIntSocket', 'Screen Y')
|
||||
self.add_input('LnxVectorSocket', 'At')
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Distance at')
|
@ -0,0 +1,20 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class FloatDeltaInterpolateNode(LnxLogicTreeNode):
|
||||
"""Linearly interpolate to a new value with specified interpolation `Rate`.
|
||||
@input From: Value to interpolate from.
|
||||
@input To: Value to interpolate to.
|
||||
@input Delta Time: Delta Time.
|
||||
@input Rate: Rate of interpolation.
|
||||
"""
|
||||
bl_idname = 'LNFloatDeltaInterpolateNode'
|
||||
bl_label = 'Float Delta Interpolate'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'From', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'To', default_value=1.0)
|
||||
self.add_input('LnxFloatSocket', 'Delta Time')
|
||||
self.add_input('LnxFloatSocket', 'Rate')
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
18
leenkx/blender/lnx/logicnode/math/LN_key_interpolate.py
Normal file
18
leenkx/blender/lnx/logicnode/math/LN_key_interpolate.py
Normal file
@ -0,0 +1,18 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class KeyInterpolateNode(LnxLogicTreeNode):
|
||||
"""Linearly interpolate to 1.0 if input is true and interpolate to 0.0 if input is false.
|
||||
@input Key State: Interpolate to 1.0 if true and 0.0 if false.
|
||||
@input Init: Initial value in the range 0.0 to 1.0.
|
||||
@input Rate: Rate of interpolation.
|
||||
"""
|
||||
bl_idname = 'LNKeyInterpolateNode'
|
||||
bl_label = 'Key Interpolate Node'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxBoolSocket', 'Key State')
|
||||
self.add_input('LnxFloatSocket', 'Init', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'Rate')
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
19
leenkx/blender/lnx/logicnode/math/LN_map_range.py
Normal file
19
leenkx/blender/lnx/logicnode/math/LN_map_range.py
Normal file
@ -0,0 +1,19 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class MapRangeNode(LnxLogicTreeNode):
|
||||
"""Converts the given value from a range to another range.
|
||||
|
||||
@seeNode Clamp
|
||||
"""
|
||||
bl_idname = 'LNMapRangeNode'
|
||||
bl_label = 'Map Range'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'Value', default_value=1.0)
|
||||
self.add_input('LnxFloatSocket', 'From Min')
|
||||
self.add_input('LnxFloatSocket', 'From Max', default_value=1.0)
|
||||
self.add_input('LnxFloatSocket', 'To Min')
|
||||
self.add_input('LnxFloatSocket', 'To Max', default_value=1.0)
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
138
leenkx/blender/lnx/logicnode/math/LN_math.py
Normal file
138
leenkx/blender/lnx/logicnode/math/LN_math.py
Normal file
@ -0,0 +1,138 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class MathNode(LnxLogicTreeNode):
|
||||
"""Mathematical operations on values."""
|
||||
bl_idname = 'LNMathNode'
|
||||
bl_label = 'Math'
|
||||
lnx_version = 3
|
||||
|
||||
@staticmethod
|
||||
def get_enum_id_value(obj, prop_name, value):
|
||||
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(operation_name):
|
||||
return {
|
||||
'Add': 0,
|
||||
'Subtract': 0,
|
||||
'Multiply': 0,
|
||||
'Divide': 0,
|
||||
'Sine': 1,
|
||||
'Cosine': 1,
|
||||
'Abs': 1,
|
||||
'Tangent': 1,
|
||||
'Arcsine': 1,
|
||||
'Arccosine': 1,
|
||||
'Arctangent': 1,
|
||||
'Logarithm': 1,
|
||||
'Round': 2,
|
||||
'Floor': 1,
|
||||
'Ceil': 1,
|
||||
'Square Root': 1,
|
||||
'Fract': 1,
|
||||
'Exponent': 1,
|
||||
'Max': 2,
|
||||
'Min': 2,
|
||||
'Power': 2,
|
||||
'Arctan2': 2,
|
||||
'Modulo': 2,
|
||||
'Less Than': 2,
|
||||
'Greater Than': 2,
|
||||
'Ping-Pong': 2
|
||||
}.get(operation_name, 0)
|
||||
|
||||
def get_enum(self):
|
||||
return self.get('property0', 0)
|
||||
|
||||
def set_enum(self, value):
|
||||
# Checking the selection of another operation
|
||||
select_current = self.get_enum_id_value(self, 'property0', value)
|
||||
select_prev = self.property0
|
||||
if select_prev != select_current:
|
||||
# Many arguments: Add, Subtract, Multiply, Divide
|
||||
if (self.get_count_in(select_current) == 0):
|
||||
while (len(self.inputs) < 2):
|
||||
self.add_input('LnxFloatSocket', 'Value ' + str(len(self.inputs)))
|
||||
# 2 arguments: Max, Min, Power, Arctan2, Modulo, Less Than, Greater Than, Ping-Pong
|
||||
if (self.get_count_in(select_current) == 2):
|
||||
while (len(self.inputs) > 2):
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
while (len(self.inputs) < 2):
|
||||
self.add_input('LnxFloatSocket', 'Value ' + str(len(self.inputs)))
|
||||
# 1 argument: Sine, Cosine, Abs, Tangent, Arcsine, Arccosine, Arctangent, Logarithm, Round, Floor, Ceil, Square Root, Fract, Exponent
|
||||
if (self.get_count_in(select_current) == 1):
|
||||
while (len(self.inputs) > 1):
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
self['property0'] = value
|
||||
if (self.property0 == 'Round'):
|
||||
self.inputs[1].name = 'Precision'
|
||||
elif (self.property0 == 'Ping-Pong'):
|
||||
self.inputs[1].name = 'Scale'
|
||||
elif (len(self.inputs) > 1): self.inputs[1].name = 'Value 1'
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Add', 'Add', 'Add'),
|
||||
('Multiply', 'Multiply', 'Multiply'),
|
||||
('Sine', 'Sine', 'Sine'),
|
||||
('Cosine', 'Cosine', 'Cosine'),
|
||||
('Max', 'Maximum', 'Max'),
|
||||
('Min', 'Minimum', 'Min'),
|
||||
('Abs', 'Absolute', 'Abs'),
|
||||
('Subtract', 'Subtract', 'Subtract'),
|
||||
('Divide', 'Divide', 'Divide'),
|
||||
('Tangent', 'Tangent', 'Tangent'),
|
||||
('Arcsine', 'Arcsine', 'Arcsine'),
|
||||
('Arccosine', 'Arccosine', 'Arccosine'),
|
||||
('Arctangent', 'Arctangent', 'Arctangent'),
|
||||
('Power', 'Power', 'Power'),
|
||||
('Logarithm', 'Logarithm', 'Logarithm'),
|
||||
('Round', 'Round', 'Round (Value 1 precision of decimal places)'),
|
||||
('Less Than', 'Less Than', 'Less Than'),
|
||||
('Greater Than', 'Greater Than', 'Greater Than'),
|
||||
('Modulo', 'Modulo', 'Modulo'),
|
||||
('Arctan2', 'Arctan2', 'Arctan2'),
|
||||
('Floor', 'Floor', 'Floor'),
|
||||
('Ceil', 'Ceil', 'Ceil'),
|
||||
('Fract', 'Fract', 'Fract'),
|
||||
('Square Root', 'Square Root', 'Square Root'),
|
||||
('Exponent', 'Exponent', 'Exponent'),
|
||||
('Ping-Pong', 'Ping-Pong', 'The output value is moved between 0.0 and the Scale based on the input value')],
|
||||
name='', default='Add', set=set_enum, get=get_enum)
|
||||
|
||||
property1: HaxeBoolProperty('property1', name='Clamp', default=False)
|
||||
|
||||
def __init__(self):
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'Value 0', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'Value 1', default_value=0.0)
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property1')
|
||||
layout.prop(self, 'property0')
|
||||
# Many arguments: Add, Subtract, Multiply, Divide
|
||||
if (self.get_count_in(self.property0) == 0):
|
||||
row = layout.row(align=True)
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_add_input', text='Add Value', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.socket_type = 'LnxFloatSocket'
|
||||
op.name_format = 'Value {0}'
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.inputs) == 2:
|
||||
column.enabled = False
|
||||
|
||||
def draw_label(self) -> str:
|
||||
return f'{self.bl_label}: {self.property0}'
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 2):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
80
leenkx/blender/lnx/logicnode/math/LN_math_expression.py
Normal file
80
leenkx/blender/lnx/logicnode/math/LN_math_expression.py
Normal file
@ -0,0 +1,80 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
import re
|
||||
|
||||
class MathExpressionNode(LnxLogicTreeNode):
|
||||
"""Mathematical operations on values."""
|
||||
bl_idname = 'LNMathExpressionNode'
|
||||
bl_label = 'Math Expression'
|
||||
lnx_version = 2
|
||||
|
||||
num_params: IntProperty(default=2, min=0)
|
||||
|
||||
@staticmethod
|
||||
def get_variable_name(index):
|
||||
return chr( range(ord('a'), ord('z')+1)[index] )
|
||||
|
||||
def set_exp(self, value):
|
||||
self['property2'] = value
|
||||
# TODO: Check expression for errors
|
||||
self['exp_error'] = False
|
||||
|
||||
def get_exp(self):
|
||||
return self.get('property2', 'a + b')
|
||||
|
||||
property0: HaxeBoolProperty('property0', name='Clamp Result', default=False)
|
||||
property1: HaxeIntProperty('property1', name='Number of Params', default=2)
|
||||
property2: HaxeStringProperty('property2', name='', description='Math Expression: +, -, *, /, ^, %, (, ), log(a, b), ln(a), abs(a), max(a,b), min(a,b), sin(a), cos(a), tan(a), cot(a), asin(a), acos(a), atan(a), atan2(a,b), pi(), e()', set=set_exp, get=get_exp)
|
||||
|
||||
def __init__(self):
|
||||
super(MathExpressionNode, self).__init__()
|
||||
self.register_id()
|
||||
|
||||
|
||||
def lnx_init(self, context):
|
||||
|
||||
# OUTPUTS:
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
||||
|
||||
# two default parameters at start
|
||||
self.add_input('LnxFloatSocket', self.get_variable_name(0), default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', self.get_variable_name(1), default_value=0.0)
|
||||
|
||||
def add_sockets(self):
|
||||
if self.num_params < 26:
|
||||
self.add_input('LnxFloatSocket', self.get_variable_name(self.num_params), default_value=0.0)
|
||||
self.num_params += 1
|
||||
self['property1'] = self.num_params
|
||||
|
||||
def remove_sockets(self):
|
||||
if self.num_params > 0:
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
self.num_params -= 1
|
||||
self['property1'] = self.num_params
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
# Clamp Property
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
# Expression Property
|
||||
row = layout.row(align=True)
|
||||
column = row.column(align=True)
|
||||
# TODO:
|
||||
#column.alert = self['exp_error']
|
||||
column.prop(self, 'property2', icon='FORCE_HARMONIC')
|
||||
|
||||
# Button ADD parameter
|
||||
row = layout.row(align=True)
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_call_func', text='Add Param', icon='PLUS', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
op.callback_name = 'add_sockets'
|
||||
if self.num_params == 26:
|
||||
column.enabled = False
|
||||
|
||||
# Button REMOVE parameter
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_call_func', text='', icon='X', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
op.callback_name = 'remove_sockets'
|
||||
if self.num_params == 0:
|
||||
column.enabled = False
|
70
leenkx/blender/lnx/logicnode/math/LN_math_term.py
Normal file
70
leenkx/blender/lnx/logicnode/math/LN_math_term.py
Normal file
@ -0,0 +1,70 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
import re
|
||||
|
||||
class MathTermNode(LnxLogicTreeNode):
|
||||
"""Formula for symbolic Math"""
|
||||
bl_idname = 'LNMathTermNode'
|
||||
bl_label = 'Math Term'
|
||||
lnx_version = 0
|
||||
|
||||
num_params: IntProperty(default=2, min=0)
|
||||
|
||||
property0: HaxeBoolProperty('property0', name='Resolve params', description='Resolve input param values/subterms for output term/transformations', default=False)
|
||||
|
||||
def __init__(self):
|
||||
super(MathTermNode, self).__init__()
|
||||
self.register_id()
|
||||
|
||||
|
||||
def lnx_init(self, context):
|
||||
|
||||
# OUTPUTS:
|
||||
self.add_output('LnxDynamicSocket', 'Math Term')
|
||||
self.add_output('LnxDynamicSocket', 'Simplifyed')
|
||||
self.add_output('LnxDynamicSocket', 'Derivate')
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
||||
self.add_output('LnxStringSocket', 'Error')
|
||||
self.add_output('LnxIntSocket', 'ErrorPos')
|
||||
|
||||
# INPUTS:
|
||||
|
||||
# HOW to setup a Tooltip here and how to put it above the param-add/remove-buttons into layout ?
|
||||
self.add_input('LnxStringSocket', 'Math Term', default_value='a+b')
|
||||
|
||||
# two default parameters at start
|
||||
self.add_input('LnxStringSocket', 'Param 0', default_value='a')
|
||||
self.add_input('LnxDynamicSocket', 'Value / Term 0')
|
||||
|
||||
self.add_input('LnxStringSocket', 'Param 1', default_value='b')
|
||||
self.add_input('LnxDynamicSocket', 'Value / Term 1')
|
||||
|
||||
def add_sockets(self):
|
||||
self.add_input('LnxStringSocket', 'Name ' + str(self.num_params))
|
||||
#self.add_input('LnxFloatSocket', 'Value ' + str(self.num_params))
|
||||
self.add_input('LnxDynamicSocket', 'Value / Term ' + str(self.num_params))
|
||||
self.num_params += 1
|
||||
|
||||
def remove_sockets(self):
|
||||
if self.num_params > 0:
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
self.num_params -= 1
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
# Bind values to params Property
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
# Button ADD parameter
|
||||
row = layout.row(align=True)
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_call_func', text='Add Param', icon='PLUS', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
op.callback_name = 'add_sockets'
|
||||
|
||||
# Button REMOVE parameter
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_call_func', text='', icon='X', emboss=True)
|
||||
op.node_index = self.get_id_str()
|
||||
op.callback_name = 'remove_sockets'
|
||||
if self.num_params == 0:
|
||||
column.enabled = False
|
22
leenkx/blender/lnx/logicnode/math/LN_matrix_math.py
Normal file
22
leenkx/blender/lnx/logicnode/math/LN_matrix_math.py
Normal file
@ -0,0 +1,22 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class MatrixMathNode(LnxLogicTreeNode):
|
||||
"""Multiplies matrices."""
|
||||
bl_idname = 'LNMatrixMathNode'
|
||||
bl_label = 'Matrix Math'
|
||||
lnx_section = 'matrix'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Multiply', 'Multiply', 'Multiply')],
|
||||
name='', default='Multiply')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxDynamicSocket', 'Matrix 1')
|
||||
self.add_input('LnxDynamicSocket', 'Matrix 2')
|
||||
|
||||
self.add_output('LnxDynamicSocket', 'Result')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
43
leenkx/blender/lnx/logicnode/math/LN_mix.py
Normal file
43
leenkx/blender/lnx/logicnode/math/LN_mix.py
Normal file
@ -0,0 +1,43 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class MixNode(LnxLogicTreeNode):
|
||||
"""Interpolates between the two given values."""
|
||||
bl_idname = 'LNMixNode'
|
||||
bl_label = 'Mix'
|
||||
lnx_version = 1
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Linear', 'Linear', 'Linear'),
|
||||
('Sine', 'Sine', 'Sine'),
|
||||
('Quad', 'Quad', 'Quad'),
|
||||
('Cubic', 'Cubic', 'Cubic'),
|
||||
('Quart', 'Quart', 'Quart'),
|
||||
('Quint', 'Quint', 'Quint'),
|
||||
('Expo', 'Expo', 'Expo'),
|
||||
('Circ', 'Circ', 'Circ'),
|
||||
('Back', 'Back', 'Back'),
|
||||
('Bounce', 'Bounce', 'Bounce'),
|
||||
('Elastic', 'Elastic', 'Elastic'),
|
||||
],
|
||||
name='', default='Linear')
|
||||
property1: HaxeEnumProperty(
|
||||
'property1',
|
||||
items = [('In', 'In', 'In'),
|
||||
('Out', 'Out', 'Out'),
|
||||
('InOut', 'InOut', 'InOut'),
|
||||
],
|
||||
name='', default='Out')
|
||||
|
||||
property2: HaxeBoolProperty('property2', name='Clamp', default=False)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'Factor', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'Value 1', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'Value 2', default_value=1.0)
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Result')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property2')
|
||||
layout.prop(self, 'property0')
|
||||
layout.prop(self, 'property1')
|
46
leenkx/blender/lnx/logicnode/math/LN_mix_vector.py
Normal file
46
leenkx/blender/lnx/logicnode/math/LN_mix_vector.py
Normal file
@ -0,0 +1,46 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class VectorMixNode(LnxLogicTreeNode):
|
||||
"""Interpolates between the two given vectors."""
|
||||
bl_idname = 'LNVectorMixNode'
|
||||
bl_label = 'Mix Vector'
|
||||
lnx_section = 'vector'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Linear', 'Linear', 'Linear'),
|
||||
('Sine', 'Sine', 'Sine'),
|
||||
('Quad', 'Quad', 'Quad'),
|
||||
('Cubic', 'Cubic', 'Cubic'),
|
||||
('Quart', 'Quart', 'Quart'),
|
||||
('Quint', 'Quint', 'Quint'),
|
||||
('Expo', 'Expo', 'Expo'),
|
||||
('Circ', 'Circ', 'Circ'),
|
||||
('Back', 'Back', 'Back'),
|
||||
('Bounce', 'Bounce', 'Bounce'),
|
||||
('Elastic', 'Elastic', 'Elastic'),
|
||||
],
|
||||
name='', default='Linear')
|
||||
property1: HaxeEnumProperty(
|
||||
'property1',
|
||||
items = [('In', 'In', 'In'),
|
||||
('Out', 'Out', 'Out'),
|
||||
('InOut', 'InOut', 'InOut'),
|
||||
],
|
||||
name='', default='Out')
|
||||
|
||||
property2: HaxeBoolProperty('property2', name='Clamp', default=False)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'Factor', default_value=0.0)
|
||||
self.add_input('LnxVectorSocket', 'Vector 1', default_value=[0.0, 0.0, 0.0])
|
||||
self.add_input('LnxVectorSocket', 'Vector 2', default_value=[1.0, 1.0, 1.0])
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Result')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property2')
|
||||
layout.prop(self, 'property0')
|
||||
if self.property0 != 'Linear':
|
||||
layout.prop(self, 'property1')
|
369
leenkx/blender/lnx/logicnode/math/LN_quaternion_math.py
Normal file
369
leenkx/blender/lnx/logicnode/math/LN_quaternion_math.py
Normal file
@ -0,0 +1,369 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
from lnx.logicnode.lnx_sockets import LnxRotationSocket as Rotation
|
||||
|
||||
class QuaternionMathNode(LnxLogicTreeNode):
|
||||
"""Mathematical operations on quaternions."""
|
||||
bl_idname = 'LNQuaternionMathNode'
|
||||
bl_label = 'Quaternion Math'
|
||||
bl_description = 'Mathematical operations that can be performed on rotations, when represented as quaternions specifically'
|
||||
lnx_section = 'quaternions'
|
||||
lnx_version = 3
|
||||
|
||||
def ensure_input_socket(self, socket_number, newclass, newname, default_value=None):
|
||||
while len(self.inputs) < socket_number:
|
||||
self.inputs.new('LnxFloatSocket', 'BOGUS')
|
||||
if len(self.inputs) > socket_number:
|
||||
if len(self.inputs[socket_number].links) == 1:
|
||||
source_socket = self.inputs[socket_number].links[0].from_socket
|
||||
else:
|
||||
source_socket = None
|
||||
if (
|
||||
self.inputs[socket_number].bl_idname == newclass \
|
||||
and self.inputs[socket_number].lnx_socket_type != 'NONE'
|
||||
):
|
||||
default_value = self.inputs[socket_number].default_value_raw
|
||||
self.inputs.remove(self.inputs[socket_number])
|
||||
else:
|
||||
source_socket = None
|
||||
|
||||
|
||||
self.inputs.new(newclass, newname)
|
||||
if default_value != None:
|
||||
self.inputs[-1].default_value_raw = default_value
|
||||
self.inputs.move(len(self.inputs)-1, socket_number)
|
||||
if source_socket is not None:
|
||||
self.id_data.links.new(source_socket, self.inputs[socket_number])
|
||||
|
||||
def ensure_output_socket(self, socket_number, newclass, newname):
|
||||
sink_sockets = []
|
||||
while len(self.outputs) < socket_number:
|
||||
self.outputs.new('LnxFloatSocket', 'BOGUS')
|
||||
if len(self.outputs) > socket_number:
|
||||
for link in self.inputs[socket_number].links:
|
||||
sink_sockets.append(link.to_socket)
|
||||
self.inputs.remove(self.inputs[socket_number])
|
||||
|
||||
self.inputs.new(newclass, newname)
|
||||
self.inputs.move(len(self.inputs)-1, socket_number)
|
||||
for socket in sink_sockets:
|
||||
self.id_data.links.new(self.inputs[socket_number], socket)
|
||||
|
||||
@staticmethod
|
||||
def get_enum_id_value(obj, prop_name, value):
|
||||
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(operation_name):
|
||||
return {
|
||||
'Add': 0,
|
||||
'Subtract': 0,
|
||||
'DotProduct': 0,
|
||||
'Multiply': 0,
|
||||
'MultiplyFloats': 0,
|
||||
'Module': 1,
|
||||
'Normalize': 1,
|
||||
'GetEuler': 1,
|
||||
'FromTo': 2,
|
||||
'FromMat': 2,
|
||||
'FromRotationMat': 2,
|
||||
'ToAxisAngle': 2,
|
||||
'Lerp': 3,
|
||||
'Slerp': 3,
|
||||
'FromAxisAngle': 3,
|
||||
'FromEuler': 3
|
||||
}.get(operation_name, 0)
|
||||
|
||||
def get_enum(self):
|
||||
return self.get('property0', 0)
|
||||
|
||||
def set_enum(self, value):
|
||||
# Checking the selection of another operation
|
||||
select_current = self.get_enum_id_value(self, 'property0', value)
|
||||
select_prev = self.property0
|
||||
|
||||
if select_current in ('Add','Subtract','Multiply','DotProduct') \
|
||||
and select_prev in ('Add','Subtract','Multiply','DotProduct'):
|
||||
pass # same as select_current==select_prev for the sockets
|
||||
elif select_prev != select_current:
|
||||
if select_current in ('Add','Subtract','Multiply','DotProduct'):
|
||||
for i in range( max(len(self.inputs)//2 ,2) ):
|
||||
self.ensure_input_socket(2*i, 'LnxVectorSocket', 'Quaternion %d XYZ'%i)
|
||||
self.ensure_input_socket(2*i+1, 'LnxFloatSocket', 'Quaternion %d W'%i, default_value=1.0)
|
||||
if len(self.inputs)%1:
|
||||
self.inputs.remove(self.inputs[len(self.inputs)-1])
|
||||
elif select_current == 'MultiplyFloats':
|
||||
self.ensure_input_socket(0, 'LnxVectorSocket', 'Quaternion XYZ')
|
||||
self.ensure_input_socket(1, 'LnxFloatSocket', 'Quaternion W', default_value=1.0)
|
||||
for i in range( max(len(self.inputs)-2 ,1) ):
|
||||
self.ensure_input_socket(i+2, 'LnxFloatSocket', 'Value %d'%i)
|
||||
elif select_current in ('Module', 'Normalize'):
|
||||
self.ensure_input_socket(0, 'LnxVectorSocket', 'Quaternion XYZ')
|
||||
self.ensure_input_socket(1, 'LnxFloatSocket', 'Quaternion W', default_value=1.0)
|
||||
while len(self.inputs)>2:
|
||||
self.inputs.remove(self.inputs[2])
|
||||
else:
|
||||
raise ValueError('Internal code of LNQuaternionMathNode failed to deal correctly with math operation "%s". Please report this to the developers.' %select_current)
|
||||
|
||||
if select_current in ('Add','Subtract','Multiply','MultiplyFloats','Normalize'):
|
||||
self.outputs[0].name = 'XYZ Out'
|
||||
self.outputs[1].name = 'W Out'
|
||||
else:
|
||||
self.outputs[0].name = '[unused]'
|
||||
self.outputs[1].name = 'Value Out'
|
||||
|
||||
self['property0'] = value
|
||||
self['property0_proxy'] = value
|
||||
|
||||
|
||||
# this property swaperoo is kinda janky-looking, but necessary.
|
||||
# Read more on LN_rotate_object.py
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Add', 'Add', 'Add'),
|
||||
('Subtract', 'Subtract', 'Subtract'),
|
||||
('DotProduct', 'Dot Product', 'Dot Product'),
|
||||
('Multiply', 'Multiply', 'Multiply'),
|
||||
('MultiplyFloats', 'Multiply (Floats)', 'Multiply (Floats)'),
|
||||
('Module', 'Module', 'Module'),
|
||||
('Normalize', 'Normalize', 'Normalize'), #],
|
||||
# NOTE: the unused parts need to exist to be read from an old version from the node.
|
||||
# this is so dumb…
|
||||
('Lerp', 'DO NOT USE',''),
|
||||
('Slerp', 'DO NOT USE',''),
|
||||
('FromTo', 'DO NOT USE',''),
|
||||
('FromMat', 'DO NOT USE',''),
|
||||
('FromRotationMat', 'DO NOT USE',''),
|
||||
('ToAxisAngle', 'DO NOT USE',''),
|
||||
('FromAxisAngle', 'DO NOT USE',''),
|
||||
('FromEuler', 'DO NOT USE',''),
|
||||
('GetEuler', 'DO NOT USE','')],
|
||||
name='', default='Add') #, set=set_enum, get=get_enum)
|
||||
property0_proxy: EnumProperty(
|
||||
items = [('Add', 'Add', 'Add'),
|
||||
('Subtract', 'Subtract', 'Subtract'),
|
||||
('DotProduct', 'Dot Product', 'Dot Product'),
|
||||
('Multiply', 'Multiply', 'Multiply'),
|
||||
('MultiplyFloats', 'Multiply (Floats)', 'Multiply (Floats)'),
|
||||
('Module', 'Module', 'Module'),
|
||||
('Normalize', 'Normalize', 'Normalize')],
|
||||
name='', default='Add', set=set_enum, get=get_enum)
|
||||
|
||||
|
||||
def __init__(self):
|
||||
super(QuaternionMathNode, self).__init__()
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxVectorSocket', 'Quaternion 0 XYZ', default_value=[0.0, 0.0, 0.0])
|
||||
self.add_input('LnxFloatSocket', 'Quaternion 0 W', default_value=1)
|
||||
self.add_input('LnxVectorSocket', 'Quaternion 1 XYZ', default_value=[0.0, 0.0, 0.0])
|
||||
self.add_input('LnxFloatSocket', 'Quaternion 1 W', default_value=1)
|
||||
self.add_output('LnxVectorSocket', 'Result XYZ', default_value=[0.0, 0.0, 0.0])
|
||||
self.add_output('LnxFloatSocket', 'Result W', default_value=1)
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0_proxy') # Operation
|
||||
# Buttons
|
||||
if (self.get_count_in(self.property0) == 0):
|
||||
row = layout.row(align=True)
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_add_input', text='Add Value', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if (self.property0 == 'Add') or (self.property0 == 'Subtract') or (self.property0 == 'Multiply') or (self.property0 == 'DotProduct'):
|
||||
op.name_format = 'Quaternion {0} XYZ;Quaternion {0} W'
|
||||
else:
|
||||
op.name_format = 'Value {0}'
|
||||
if (self.property0 == "MultiplyFloats"):
|
||||
op.socket_type = 'LnxFloatSocket'
|
||||
else:
|
||||
op.socket_type = 'LnxVectorSocket;LnxFloatSocket'
|
||||
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if self.property0 != "MultiplyFloats":
|
||||
op.count = 2
|
||||
op.min_inputs = 4
|
||||
else:
|
||||
op.min_inputs = 2
|
||||
if len(self.inputs) == 4:
|
||||
column.enabled = False
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 2):
|
||||
raise LookupError()
|
||||
|
||||
if self.lnx_version == 1 or self.lnx_version == 2:
|
||||
ret=[]
|
||||
if self.property0 == 'GetEuler':
|
||||
newself = node_tree.nodes.new('LNSeparateRotationNode')
|
||||
ret.append(newself)
|
||||
newself.property0='EulerAngles'
|
||||
newself.property2='XZY'
|
||||
newself.property1='Rad'
|
||||
|
||||
for link in self.inputs[0].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, newself.inputs[0])
|
||||
elif self.property0 == 'FromEuler':
|
||||
newself = node_tree.nodes.new('LNRotationNode')
|
||||
ret.append(newself)
|
||||
preconv = node_tree.nodes.new('LNVectorNode')
|
||||
ret.append(preconv)
|
||||
newself.property0='EulerAngles'
|
||||
newself.property2='XZY'
|
||||
newself.property1='Rad'
|
||||
node_tree.links.new(preconv.outputs[0], newself.inputs[0])
|
||||
|
||||
preconv.inputs[0].default_value = self.inputs[0].default_value
|
||||
for link in self.inputs[0].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, preconv.inputs[0])
|
||||
preconv.inputs[1].default_value = self.inputs[1].default_value
|
||||
for link in self.inputs[1].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, preconv.inputs[1])
|
||||
preconv.inputs[2].default_value = self.inputs[2].default_value
|
||||
for link in self.inputs[2].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, preconv.inputs[2])
|
||||
elif self.property0 == 'ToAxisAngle':
|
||||
newself = node_tree.nodes.new('LNSeparateRotationNode')
|
||||
ret.append(newself)
|
||||
newself.property0='AxisAngle'
|
||||
newself.property1='Rad'
|
||||
|
||||
for link in self.inputs[0].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, newself.inputs[0])
|
||||
elif self.property0 == 'FromAxisAngle':
|
||||
newself = node_tree.nodes.new('LNRotationNode')
|
||||
ret.append(newself)
|
||||
newself.property0='AxisAngle'
|
||||
newself.property1='Rad'
|
||||
|
||||
newself.inputs[0].default_value = self.inputs[1].default_value
|
||||
for link in self.inputs[1].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, newself.inputs[0])
|
||||
newself.inputs[1].default_value = self.inputs[2].default_value
|
||||
for link in self.inputs[2].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, newself.inputs[1])
|
||||
elif self.property0 in ('FromMat','FromRotationMat'):
|
||||
newself = node_tree.nodes.new('LNSeparateTransformNode')
|
||||
ret.append(newself)
|
||||
for link in self.inputs[1].links: # 0 or 1
|
||||
node_tree.links.new(link.from_socket, newself.inputs[0])
|
||||
|
||||
elif self.property0 in ('Lerp','Slerp','FromTo'):
|
||||
newself = node_tree.nodes.new('LNRotationMathNode')
|
||||
ret.append(newself)
|
||||
newself.property0 = self.property0
|
||||
|
||||
for in1, in2 in zip(self.inputs, newself.inputs):
|
||||
if in2.bl_idname == 'LnxRotationSocket':
|
||||
in2.default_value_raw = Rotation.convert_to_quaternion(
|
||||
in1.default_value,0,
|
||||
'EulerAngles','Rad','XZY'
|
||||
)
|
||||
elif in1.bl_idname in ('LnxFloatSocket', 'LnxVectorSocket'):
|
||||
in2.default_value = in1.default_value
|
||||
for link in in1.links:
|
||||
node_tree.links.new(link.from_socket, in2)
|
||||
|
||||
else:
|
||||
newself = node_tree.nodes.new('LNQuaternionMathNode')
|
||||
ret.append(newself)
|
||||
newself.property0 = self.property0
|
||||
|
||||
# convert the inputs… this is going to be hard lmao.
|
||||
i_in_1 = 0
|
||||
i_in_2 = 0
|
||||
while i_in_1 < len(self.inputs):
|
||||
in1 = self.inputs[i_in_1]
|
||||
if in1.bl_idname == 'LnxVectorSocket':
|
||||
# quaternion input: now two sockets, not one.
|
||||
convnode = node_tree.nodes.new('LNSeparateRotationNode')
|
||||
convnode.property0 = 'Quaternion'
|
||||
ret.append(convnode)
|
||||
if i_in_2 >= len(newself.inputs):
|
||||
newself.ensure_input_socket(i_in_2, 'LnxVectorSocket', 'Quaternion %d XYZ'%(i_in_1))
|
||||
newself.ensure_input_socket(i_in_2+1, 'LnxFloatSocket', 'Quaternion %d W'%(i_in_1), 1.0)
|
||||
node_tree.links.new(convnode.outputs[0], newself.inputs[i_in_2])
|
||||
node_tree.links.new(convnode.outputs[1], newself.inputs[i_in_2+1])
|
||||
for link in in1.links:
|
||||
node_tree.links.new(link.from_socket, convnode.inputs[0])
|
||||
i_in_2 +=2
|
||||
i_in_1 +=1
|
||||
elif in1.bl_idname == 'LnxFloatSocket':
|
||||
for link in in1.links:
|
||||
node_tree.links.new(link.from_socket, newself.inputs[i_in_2])
|
||||
i_in_1 +=1
|
||||
i_in_2 +=1
|
||||
else:
|
||||
raise ValueError('get_replacement_node() for is not LNQuaternionMathNode V1->V2 is not prepared to deal with an input socket of type %s. This is a bug to report to the developers' %in1.bl_idname)
|
||||
# #### now that the input has been dealt with, let's deal with the output.
|
||||
if self.property0 in ('FromEuler','FromMat','FromRotationMat','FromAxisAngle','Lerp','Slerp','FromTo'):
|
||||
# the new self returns a rotation
|
||||
for link in self.outputs[0].links:
|
||||
out_sock_i = int( self.property0.endswith('Mat') )
|
||||
node_tree.links.new(newself.outputs[out_sock_i], link.to_socket)
|
||||
elif self.property0 in ('DotProduct','Module'):
|
||||
# new self returns a float
|
||||
for link in self.outputs[1 + 4*int(self.property1)].links:
|
||||
node_tree.links.new(newself.outputs[1], link.to_socket)
|
||||
elif self.property0 in ('GetEuler', 'ToAxisAngle'):
|
||||
# new self returns misc.
|
||||
for link in self.outputs[0].links:
|
||||
node_tree.links.new(newself.outputs[0], link.to_socket)
|
||||
if self.property0 == 'ToAxisAngle':
|
||||
for link in self.outputs[1 + 4*int(self.property1)].links:
|
||||
node_tree.links.new(newself.outputs[1], link.to_socket)
|
||||
if self.property1:
|
||||
xlinks = self.outputs[1].links
|
||||
ylinks = self.outputs[2].links
|
||||
zlinks = self.outputs[3].links
|
||||
if len(xlinks)>0 or len(ylinks)>0 or len(zlinks)>0:
|
||||
conv = node_tree.nodes.new('LNSeparateVectorNode')
|
||||
ret.append(conv)
|
||||
node_tree.links.new(newself.outputs[0], conv.inputs[0])
|
||||
for link in xlinks:
|
||||
node_tree.links.new(conv.outputs[0], link.to_socket)
|
||||
for link in ylinks:
|
||||
node_tree.links.new(conv.outputs[1], link.to_socket)
|
||||
for link in zlinks:
|
||||
node_tree.links.new(conv.outputs[2], link.to_socket)
|
||||
else:
|
||||
# new self returns a proper quaternion XYZ/W
|
||||
outlinks = self.outputs[0].links
|
||||
if len(outlinks)>0:
|
||||
conv = node_tree.nodes.new('LNRotationNode')
|
||||
conv.property0='Quaternion'
|
||||
ret.append(conv)
|
||||
node_tree.links.new(newself.outputs[0], conv.inputs[0])
|
||||
node_tree.links.new(newself.outputs[1], conv.inputs[1])
|
||||
for link in outlinks:
|
||||
node_tree.links.new(conv.outputs[0], link.to_socket)
|
||||
if self.property1:
|
||||
for link in self.outputs[4].links: # for W
|
||||
node_tree.links.new(newself.outputs[1], link.to_socket)
|
||||
xlinks = self.outputs[1].links
|
||||
ylinks = self.outputs[2].links
|
||||
zlinks = self.outputs[3].links
|
||||
if len(xlinks)>0 or len(ylinks)>0 or len(zlinks)>0:
|
||||
conv = node_tree.nodes.new('LNSeparateVectorNode')
|
||||
ret.append(conv)
|
||||
node_tree.links.new(newself.outputs[0], conv.inputs[0])
|
||||
for link in xlinks:
|
||||
node_tree.links.new(conv.outputs[0], link.to_socket)
|
||||
for link in ylinks:
|
||||
node_tree.links.new(conv.outputs[1], link.to_socket)
|
||||
for link in zlinks:
|
||||
node_tree.links.new(conv.outputs[2], link.to_socket)
|
||||
for node in ret: # update the labels on the node's displays
|
||||
if node.bl_idname == 'LNSeparateRotationNode':
|
||||
node.on_property_update(None)
|
||||
elif node.bl_idname == 'LNRotationNode':
|
||||
node.on_property_update(None)
|
||||
elif node.bl_idname == 'LNRotationMathNode':
|
||||
node.on_update_operation(None)
|
||||
elif node.bl_idname == 'LNQuaternionMathNode':
|
||||
node.set_enum(node.get_enum())
|
||||
return ret
|
||||
|
||||
# note: keep property1, so that it is actually readable for node conversion.
|
||||
property1: BoolProperty(name='DEPRECATED', default=False)
|
13
leenkx/blender/lnx/logicnode/math/LN_rad_to_deg.py
Normal file
13
leenkx/blender/lnx/logicnode/math/LN_rad_to_deg.py
Normal file
@ -0,0 +1,13 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class RadToDegNode(LnxLogicTreeNode):
|
||||
"""Converts radians to degrees."""
|
||||
bl_idname = 'LNRadToDegNode'
|
||||
bl_label = 'Rad to Deg'
|
||||
lnx_version = 1
|
||||
lnx_section = 'angle'
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxFloatSocket', 'Radians')
|
||||
|
||||
self.add_output('LnxFloatSocket', 'Degrees')
|
121
leenkx/blender/lnx/logicnode/math/LN_rotation_math.py
Normal file
121
leenkx/blender/lnx/logicnode/math/LN_rotation_math.py
Normal file
@ -0,0 +1,121 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
from mathutils import Vector
|
||||
|
||||
class RotationMathNode(LnxLogicTreeNode):
|
||||
"""Mathematical operations on rotations."""
|
||||
bl_idname = 'LNRotationMathNode'
|
||||
bl_label = 'Rotation Math'
|
||||
bl_description = 'Mathematical operations that can be performed on rotations, no matter their internal representation'
|
||||
lnx_section = 'quaternions'
|
||||
lnx_version = 1
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(operation_name):
|
||||
return {
|
||||
'Inverse': 1,
|
||||
'Normalize': 1,
|
||||
'Compose': 2,
|
||||
'Amplify': 2,
|
||||
'FromTo': 2,
|
||||
#'FromRotationMat': 2,
|
||||
'Lerp': 3,
|
||||
'Slerp': 3,
|
||||
}.get(operation_name, 0)
|
||||
|
||||
def ensure_input_socket(self, socket_number, newclass, newname):
|
||||
while len(self.inputs) < socket_number:
|
||||
self.inputs.new('LnxFloatSocket', 'BOGUS')
|
||||
if len(self.inputs) > socket_number:
|
||||
if len(self.inputs[socket_number].links) == 1:
|
||||
source_socket = self.inputs[socket_number].links[0].from_socket
|
||||
else:
|
||||
source_socket = None
|
||||
self.inputs.remove(self.inputs[socket_number])
|
||||
else:
|
||||
source_socket = None
|
||||
|
||||
|
||||
self.inputs.new(newclass, newname)
|
||||
self.inputs.move(len(self.inputs)-1, socket_number)
|
||||
if source_socket is not None:
|
||||
self.id_data.links.new(source_socket, self.inputs[socket_number])
|
||||
|
||||
def ensure_output_socket(self, socket_number, newclass, newname):
|
||||
sink_sockets = []
|
||||
while len(self.outputs) < socket_number:
|
||||
self.outputs.new('LnxFloatSocket', 'BOGUS')
|
||||
if len(self.outputs) > socket_number:
|
||||
for link in self.inputs[socket_number].links:
|
||||
sink_sockets.append(link.to_socket)
|
||||
self.inputs.remove(self.inputs[socket_number])
|
||||
|
||||
self.inputs.new(newclass, newname)
|
||||
self.inputs.move(len(self.inputs)-1, socket_number)
|
||||
for socket in sink_sockets:
|
||||
self.id_data.links.new(self.inputs[socket_number], socket)
|
||||
|
||||
def on_property_update(self, context):
|
||||
# Checking the selection of another operation
|
||||
|
||||
|
||||
# Rotation as argument 0:
|
||||
if self.property0 in ('Inverse','Normalize','Amplify'):
|
||||
self.ensure_input_socket(0, "LnxRotationSocket", "Rotation")
|
||||
self.ensure_input_socket(1, "LnxFloatSocket", "Amplification factor")
|
||||
elif self.property0 in ('Slerp','Lerp','Compose'):
|
||||
self.ensure_input_socket(0, "LnxRotationSocket", "From")
|
||||
self.ensure_input_socket(1, "LnxRotationSocket", "To")
|
||||
|
||||
if self.property0 == 'Compose':
|
||||
self.inputs[0].name = 'Outer rotation'
|
||||
self.inputs[1].name = 'Inner rotation'
|
||||
else:
|
||||
self.ensure_input_socket(2, "LnxFloatSocket", "Interpolation factor")
|
||||
|
||||
elif self.property0 == 'FromTo':
|
||||
self.ensure_input_socket(0, "LnxVectorSocket", "From")
|
||||
self.ensure_input_socket(1, "LnxVectorSocket", "To")
|
||||
|
||||
# Rotation as argument 1:
|
||||
if self.property0 in ('Compose','Lerp','Slerp'):
|
||||
if self.inputs[1].bl_idname != "LnxRotationSocket":
|
||||
self.replace_input_socket(1, "LnxRotationSocket", "Rotation 2")
|
||||
if self.property0 == 'Compose':
|
||||
self.inputs[1].name = "Inner quaternion"
|
||||
# Float as argument 1:
|
||||
if self.property0 == 'Amplify':
|
||||
if self.inputs[1].bl_idname != 'LnxFloatSocket':
|
||||
self.replace_input_socket(1, "LnxFloatSocket", "Amplification factor")
|
||||
# Vector as argument 1:
|
||||
#if self.property0 == 'FromRotationMat':
|
||||
# # WHAT??
|
||||
# pass
|
||||
|
||||
while len(self.inputs) > self.get_count_in(self.property0):
|
||||
self.inputs.remove(self.inputs[len(self.inputs)-1])
|
||||
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Compose', 'Compose (multiply)', 'compose (multiply) two rotations. Note that order of the composition matters.'),
|
||||
('Amplify', 'Amplify (multiply by float)', 'Amplify or diminish the effect of a rotation'),
|
||||
#('Normalize', 'Normalize', 'Normalize'),
|
||||
('Inverse', 'Get Inverse', 'from r, get the rotation r2 so that " r×r2=r2×r= <no rotation>" '),
|
||||
('Lerp', 'Lerp', 'Linearly interpolation'),
|
||||
('Slerp', 'Slerp', 'Spherical linear interpolation'),
|
||||
('FromTo', 'From To', 'From direction To direction'),
|
||||
#('FromRotationMat', 'From Rotation Mat', 'From Rotation Mat')
|
||||
],
|
||||
name='', default='Compose', update=on_property_update)
|
||||
|
||||
#def __init__(self):
|
||||
# array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxRotationSocket', 'Outer rotation', default_value=(0.0, 0.0, 0.0, 1.0) )
|
||||
self.add_input('LnxRotationSocket', 'Inner rotation', default_value=(0.0, 0.0, 0.0, 1.0) )
|
||||
self.add_output('LnxRotationSocket', 'Result')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0') # Operation
|
@ -0,0 +1,61 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class ScreenToWorldSpaceNode(LnxLogicTreeNode):
|
||||
"""Transforms the given screen coordinates into World coordinates.
|
||||
|
||||
@input Screen X: screen x position.
|
||||
@input Screen Y: screen y position.
|
||||
@input Distance at: distance from camera to the result vector position.
|
||||
Try between 0 and 1.
|
||||
|
||||
@output Screen At: result vector position.
|
||||
@output Screen Word: origin position of the ray emitted from camera.
|
||||
@output Screen Direction: ray direction.
|
||||
"""
|
||||
|
||||
bl_idname = 'LNScreenToWorldSpaceNode'
|
||||
bl_label = 'Screen to World Space'
|
||||
lnx_section = 'matrix'
|
||||
lnx_version = 2
|
||||
max_outputs = 9
|
||||
|
||||
property0: HaxeBoolProperty('property0', name='Separator Out', default=False)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxIntSocket', 'Screen X')
|
||||
self.add_input('LnxIntSocket', 'Screen Y')
|
||||
self.add_input('LnxFloatSocket', 'Distance at', default_value = 0.1)
|
||||
|
||||
self.add_output('LnxVectorSocket', 'At')
|
||||
self.add_output('LnxVectorSocket', 'Origin')
|
||||
self.add_output('LnxVectorSocket', 'Direction')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0') # Separator Out
|
||||
if self.property0:
|
||||
if len(self.outputs) < self.max_outputs:
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Direction vector
|
||||
self.add_output('LnxFloatSocket', 'X') # Origin X
|
||||
self.add_output('LnxFloatSocket', 'Y') # Origin Y
|
||||
self.add_output('LnxFloatSocket', 'Z') # Origin Z
|
||||
self.add_output('LnxVectorSocket', 'Direction') # Vector
|
||||
self.add_output('LnxFloatSocket', 'X') # Direction X
|
||||
self.add_output('LnxFloatSocket', 'Y') # Direction Y
|
||||
self.add_output('LnxFloatSocket', 'Z') # Direction Z
|
||||
else:
|
||||
if len(self.outputs) == self.max_outputs:
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Z
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Y
|
||||
self.outputs.remove(self.outputs.values()[-1]) # X
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Direction
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Z
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Y
|
||||
self.outputs.remove(self.outputs.values()[-1]) # X
|
||||
self.add_output('LnxVectorSocket', 'Direction')
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
20
leenkx/blender/lnx/logicnode/math/LN_separate_hsv.py
Normal file
20
leenkx/blender/lnx/logicnode/math/LN_separate_hsv.py
Normal file
@ -0,0 +1,20 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class SeparateColorHSVNode(LnxLogicTreeNode):
|
||||
"""Splits the given color into its HSVA components (hue, saturation, value, and alpha).
|
||||
If the input color is `null`, the outputs are each set to `0.0`.
|
||||
formula from: https://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
||||
"""
|
||||
bl_idname = 'LNSeparateColorHSVNode'
|
||||
bl_label = 'Separate HSVA'
|
||||
lnx_section = 'color'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxColorSocket', 'Color', default_value=[1.0, 1.0, 1.0, 1.0])
|
||||
|
||||
self.add_output('LnxFloatSocket', 'H')
|
||||
self.add_output('LnxFloatSocket', 'S')
|
||||
self.add_output('LnxFloatSocket', 'V')
|
||||
self.add_output('LnxFloatSocket', 'A')
|
25
leenkx/blender/lnx/logicnode/math/LN_separate_rgb.py
Normal file
25
leenkx/blender/lnx/logicnode/math/LN_separate_rgb.py
Normal file
@ -0,0 +1,25 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class SeparateColorNode(LnxLogicTreeNode):
|
||||
"""Splits the given color into its RGBA components (red, green, blue, and alpha).
|
||||
If the input color is `null`, the outputs are each set to `0.0`.
|
||||
"""
|
||||
bl_idname = 'LNSeparateColorNode'
|
||||
bl_label = 'Separate RGBA'
|
||||
lnx_section = 'color'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxColorSocket', 'Color', default_value=[1.0, 1.0, 1.0, 1.0])
|
||||
|
||||
self.add_output('LnxFloatSocket', 'R')
|
||||
self.add_output('LnxFloatSocket', 'G')
|
||||
self.add_output('LnxFloatSocket', 'B')
|
||||
self.add_output('LnxFloatSocket', 'A')
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
15
leenkx/blender/lnx/logicnode/math/LN_separate_xyz.py
Normal file
15
leenkx/blender/lnx/logicnode/math/LN_separate_xyz.py
Normal file
@ -0,0 +1,15 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SeparateVectorNode(LnxLogicTreeNode):
|
||||
"""Splits the given vector into X, Y and Z."""
|
||||
bl_idname = 'LNSeparateVectorNode'
|
||||
bl_label = 'Separate XYZ'
|
||||
lnx_section = 'vector'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxVectorSocket', 'Vector')
|
||||
|
||||
self.add_output('LnxFloatSocket', 'X')
|
||||
self.add_output('LnxFloatSocket', 'Y')
|
||||
self.add_output('LnxFloatSocket', 'Z')
|
67
leenkx/blender/lnx/logicnode/math/LN_tween_float.py
Normal file
67
leenkx/blender/lnx/logicnode/math/LN_tween_float.py
Normal file
@ -0,0 +1,67 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TweenFloatNode(LnxLogicTreeNode):
|
||||
"""Tween a float value.
|
||||
|
||||
@input Start: Start tweening
|
||||
@input Stop: Stop a tweening. tweening can be re-started via the `Start`input
|
||||
@input From: Tween start value
|
||||
@input To: Tween final value
|
||||
@input Duration: Duartion of the tween in seconds
|
||||
|
||||
@output Out: Executed immidiately after `Start` or `Stop` is called
|
||||
@output Tick: Executed at every time step in the tween duration
|
||||
@output Done: Executed when tween is successfully completed. Not executed if tweening is stopped mid-way
|
||||
@output Value: Current tween value
|
||||
"""
|
||||
bl_idname = 'LNTweenFloatNode'
|
||||
bl_label = 'Tween Float'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Linear', 'Linear', 'Linear'),
|
||||
('SineIn', 'SineIn', 'SineIn'),
|
||||
('SineOut', 'SineOut', 'SineOut'),
|
||||
('SineInOut', 'SineInOut', 'SineInOut'),
|
||||
('QuadIn', 'QuadIn', 'QuadIn'),
|
||||
('QuadOut', 'QuadOut', 'QuadOut'),
|
||||
('QuadInOut', 'QuadInOut', 'QuadInOut'),
|
||||
('CubicIn', 'CubicIn', 'CubicIn'),
|
||||
('CubicOut', 'CubicOut', 'CubicOut'),
|
||||
('CubicInOut', 'CubicInOut', 'CubicInOut'),
|
||||
('QuartIn', 'QuartIn', 'QuartIn'),
|
||||
('QuartOut', 'QuartOut', 'QuartOut'),
|
||||
('QuartInOut', 'QuartInOut', 'QuartInOut'),
|
||||
('QuintIn', 'QuintIn', 'QuintIn'),
|
||||
('QuintOut', 'QuintOut', 'QuintOut'),
|
||||
('QuintInOut', 'QuintInOut', 'QuintInOut'),
|
||||
('ExpoIn', 'ExpoIn', 'ExpoIn'),
|
||||
('ExpoOut', 'ExpoOut', 'ExpoOut'),
|
||||
('ExpoInOut', 'ExpoInOut', 'ExpoInOut'),
|
||||
('CircIn', 'CircIn', 'CircIn'),
|
||||
('CircOut', 'CircOut', 'CircOut'),
|
||||
('CircInOut', 'CircInOut', 'CircInOut'),
|
||||
('BackIn', 'BackIn', 'BackIn'),
|
||||
('BackOut', 'BackOut', 'BackOut'),
|
||||
('BackInOut', 'BackInOut', 'BackInOut')],
|
||||
name='', default='Linear')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'Start')
|
||||
self.add_input('LnxNodeSocketAction', 'Stop')
|
||||
self.add_input('LnxFloatSocket', 'From', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'To', default_value=0.0)
|
||||
self.add_input('LnxFloatSocket', 'Duration', default_value=1.0)
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxNodeSocketAction', 'Tick')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxFloatSocket', 'Value')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
def draw_label(self) -> str:
|
||||
return f'{self.bl_label}: {self.property0}'
|
67
leenkx/blender/lnx/logicnode/math/LN_tween_rotation.py
Normal file
67
leenkx/blender/lnx/logicnode/math/LN_tween_rotation.py
Normal file
@ -0,0 +1,67 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TweenFloatNode(LnxLogicTreeNode):
|
||||
"""Tween rotation.
|
||||
|
||||
@input Start: Start tweening
|
||||
@input Stop: Stop a tweening. tweening can be re-started via the `Start`input
|
||||
@input From: Tween start value
|
||||
@input To: Tween final value
|
||||
@input Duration: Duartion of the tween in seconds
|
||||
|
||||
@output Out: Executed immidiately after `Start` or `Stop` is called
|
||||
@output Tick: Executed at every time step in the tween duration
|
||||
@output Done: Executed when tween is successfully completed. Not executed if tweening is stopped mid-way
|
||||
@output Value: Current tween value
|
||||
"""
|
||||
bl_idname = 'LNTweenRotationNode'
|
||||
bl_label = 'Tween Rotation'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Linear', 'Linear', 'Linear'),
|
||||
('SineIn', 'SineIn', 'SineIn'),
|
||||
('SineOut', 'SineOut', 'SineOut'),
|
||||
('SineInOut', 'SineInOut', 'SineInOut'),
|
||||
('QuadIn', 'QuadIn', 'QuadIn'),
|
||||
('QuadOut', 'QuadOut', 'QuadOut'),
|
||||
('QuadInOut', 'QuadInOut', 'QuadInOut'),
|
||||
('CubicIn', 'CubicIn', 'CubicIn'),
|
||||
('CubicOut', 'CubicOut', 'CubicOut'),
|
||||
('CubicInOut', 'CubicInOut', 'CubicInOut'),
|
||||
('QuartIn', 'QuartIn', 'QuartIn'),
|
||||
('QuartOut', 'QuartOut', 'QuartOut'),
|
||||
('QuartInOut', 'QuartInOut', 'QuartInOut'),
|
||||
('QuintIn', 'QuintIn', 'QuintIn'),
|
||||
('QuintOut', 'QuintOut', 'QuintOut'),
|
||||
('QuintInOut', 'QuintInOut', 'QuintInOut'),
|
||||
('ExpoIn', 'ExpoIn', 'ExpoIn'),
|
||||
('ExpoOut', 'ExpoOut', 'ExpoOut'),
|
||||
('ExpoInOut', 'ExpoInOut', 'ExpoInOut'),
|
||||
('CircIn', 'CircIn', 'CircIn'),
|
||||
('CircOut', 'CircOut', 'CircOut'),
|
||||
('CircInOut', 'CircInOut', 'CircInOut'),
|
||||
('BackIn', 'BackIn', 'BackIn'),
|
||||
('BackOut', 'BackOut', 'BackOut'),
|
||||
('BackInOut', 'BackInOut', 'BackInOut')],
|
||||
name='', default='Linear')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'Start')
|
||||
self.add_input('LnxNodeSocketAction', 'Stop')
|
||||
self.add_input('LnxRotationSocket', 'From')
|
||||
self.add_input('LnxRotationSocket', 'To')
|
||||
self.add_input('LnxFloatSocket', 'Duration', default_value=1.0)
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxNodeSocketAction', 'Tick')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxRotationSocket', 'Value')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
def draw_label(self) -> str:
|
||||
return f'{self.bl_label}: {self.property0}'
|
67
leenkx/blender/lnx/logicnode/math/LN_tween_transform.py
Normal file
67
leenkx/blender/lnx/logicnode/math/LN_tween_transform.py
Normal file
@ -0,0 +1,67 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TweenTransformNode(LnxLogicTreeNode):
|
||||
"""Tween Transform.
|
||||
|
||||
@input Start: Start tweening
|
||||
@input Stop: Stop a tweening. tweening can be re-started via the `Start`input
|
||||
@input From: Tween start value
|
||||
@input To: Tween final value
|
||||
@input Duration: Duartion of the tween in seconds
|
||||
|
||||
@output Out: Executed immidiately after `Start` or `Stop` is called
|
||||
@output Tick: Executed at every time step in the tween duration
|
||||
@output Done: Executed when tween is successfully completed. Not executed if tweening is stopped mid-way
|
||||
@output Value: Current tween value
|
||||
"""
|
||||
bl_idname = 'LNTweenTransformNode'
|
||||
bl_label = 'Tween Transform'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Linear', 'Linear', 'Linear'),
|
||||
('SineIn', 'SineIn', 'SineIn'),
|
||||
('SineOut', 'SineOut', 'SineOut'),
|
||||
('SineInOut', 'SineInOut', 'SineInOut'),
|
||||
('QuadIn', 'QuadIn', 'QuadIn'),
|
||||
('QuadOut', 'QuadOut', 'QuadOut'),
|
||||
('QuadInOut', 'QuadInOut', 'QuadInOut'),
|
||||
('CubicIn', 'CubicIn', 'CubicIn'),
|
||||
('CubicOut', 'CubicOut', 'CubicOut'),
|
||||
('CubicInOut', 'CubicInOut', 'CubicInOut'),
|
||||
('QuartIn', 'QuartIn', 'QuartIn'),
|
||||
('QuartOut', 'QuartOut', 'QuartOut'),
|
||||
('QuartInOut', 'QuartInOut', 'QuartInOut'),
|
||||
('QuintIn', 'QuintIn', 'QuintIn'),
|
||||
('QuintOut', 'QuintOut', 'QuintOut'),
|
||||
('QuintInOut', 'QuintInOut', 'QuintInOut'),
|
||||
('ExpoIn', 'ExpoIn', 'ExpoIn'),
|
||||
('ExpoOut', 'ExpoOut', 'ExpoOut'),
|
||||
('ExpoInOut', 'ExpoInOut', 'ExpoInOut'),
|
||||
('CircIn', 'CircIn', 'CircIn'),
|
||||
('CircOut', 'CircOut', 'CircOut'),
|
||||
('CircInOut', 'CircInOut', 'CircInOut'),
|
||||
('BackIn', 'BackIn', 'BackIn'),
|
||||
('BackOut', 'BackOut', 'BackOut'),
|
||||
('BackInOut', 'BackInOut', 'BackInOut')],
|
||||
name='', default='Linear')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'Start')
|
||||
self.add_input('LnxNodeSocketAction', 'Stop')
|
||||
self.add_input('LnxDynamicSocket', 'From')
|
||||
self.add_input('LnxDynamicSocket', 'To')
|
||||
self.add_input('LnxFloatSocket', 'Duration', default_value=1.0)
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxNodeSocketAction', 'Tick')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxDynamicSocket', 'Value')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
def draw_label(self) -> str:
|
||||
return f'{self.bl_label}: {self.property0}'
|
67
leenkx/blender/lnx/logicnode/math/LN_tween_vector.py
Normal file
67
leenkx/blender/lnx/logicnode/math/LN_tween_vector.py
Normal file
@ -0,0 +1,67 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TweenVectorNode(LnxLogicTreeNode):
|
||||
"""Tween a vector value.
|
||||
|
||||
@input Start: Start tweening
|
||||
@input Stop: Stop a tweening. tweening can be re-started via the `Start`input
|
||||
@input From: Tween start value
|
||||
@input To: Tween final value
|
||||
@input Duration: Duartion of the tween in seconds
|
||||
|
||||
@output Out: Executed immidiately after `Start` or `Stop` is called
|
||||
@output Tick: Executed at every time step in the tween duration
|
||||
@output Done: Executed when tween is successfully completed. Not executed if tweening is stopped mid-way
|
||||
@output Value: Current tween value
|
||||
"""
|
||||
bl_idname = 'LNTweenVectorNode'
|
||||
bl_label = 'Tween Vector'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Linear', 'Linear', 'Linear'),
|
||||
('SineIn', 'SineIn', 'SineIn'),
|
||||
('SineOut', 'SineOut', 'SineOut'),
|
||||
('SineInOut', 'SineInOut', 'SineInOut'),
|
||||
('QuadIn', 'QuadIn', 'QuadIn'),
|
||||
('QuadOut', 'QuadOut', 'QuadOut'),
|
||||
('QuadInOut', 'QuadInOut', 'QuadInOut'),
|
||||
('CubicIn', 'CubicIn', 'CubicIn'),
|
||||
('CubicOut', 'CubicOut', 'CubicOut'),
|
||||
('CubicInOut', 'CubicInOut', 'CubicInOut'),
|
||||
('QuartIn', 'QuartIn', 'QuartIn'),
|
||||
('QuartOut', 'QuartOut', 'QuartOut'),
|
||||
('QuartInOut', 'QuartInOut', 'QuartInOut'),
|
||||
('QuintIn', 'QuintIn', 'QuintIn'),
|
||||
('QuintOut', 'QuintOut', 'QuintOut'),
|
||||
('QuintInOut', 'QuintInOut', 'QuintInOut'),
|
||||
('ExpoIn', 'ExpoIn', 'ExpoIn'),
|
||||
('ExpoOut', 'ExpoOut', 'ExpoOut'),
|
||||
('ExpoInOut', 'ExpoInOut', 'ExpoInOut'),
|
||||
('CircIn', 'CircIn', 'CircIn'),
|
||||
('CircOut', 'CircOut', 'CircOut'),
|
||||
('CircInOut', 'CircInOut', 'CircInOut'),
|
||||
('BackIn', 'BackIn', 'BackIn'),
|
||||
('BackOut', 'BackOut', 'BackOut'),
|
||||
('BackInOut', 'BackInOut', 'BackInOut')],
|
||||
name='', default='Linear')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'Start')
|
||||
self.add_input('LnxNodeSocketAction', 'Stop')
|
||||
self.add_input('LnxVectorSocket', 'From')
|
||||
self.add_input('LnxVectorSocket', 'To')
|
||||
self.add_input('LnxFloatSocket', 'Duration', default_value=1.0)
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxNodeSocketAction', 'Tick')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxVectorSocket', 'Value')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
||||
|
||||
def draw_label(self) -> str:
|
||||
return f'{self.bl_label}: {self.property0}'
|
41
leenkx/blender/lnx/logicnode/math/LN_vector_clamp.py
Normal file
41
leenkx/blender/lnx/logicnode/math/LN_vector_clamp.py
Normal file
@ -0,0 +1,41 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class VectorClampToSizeNode(LnxLogicTreeNode):
|
||||
"""Clamp the vector's value inside the given range and return the result as a new vector.
|
||||
|
||||
@option Clamping Mode: Whether to clamp the length of the vector
|
||||
or the value of each individual component.
|
||||
"""
|
||||
bl_idname = 'LNVectorClampToSizeNode'
|
||||
bl_label = 'Vector Clamp'
|
||||
lnx_section = 'vector'
|
||||
lnx_version = 2
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
name='Clamping Mode', default='length',
|
||||
description='Whether to clamp the length of the vector or the value of each individual component',
|
||||
items=[
|
||||
('length', 'Length', 'Clamp the length (magnitude) of the vector'),
|
||||
('components', 'Components', 'Clamp the individual components of the vector'),
|
||||
]
|
||||
)
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
col = layout.column()
|
||||
col.label(text="Clamping Mode:")
|
||||
col.prop(self, 'property0', expand=True)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxVectorSocket', 'Vector In', default_value=[0.0, 0.0, 0.0])
|
||||
self.add_input('LnxFloatSocket', 'Min')
|
||||
self.add_input('LnxFloatSocket', 'Max')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Vector 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)
|
152
leenkx/blender/lnx/logicnode/math/LN_vector_math.py
Normal file
152
leenkx/blender/lnx/logicnode/math/LN_vector_math.py
Normal file
@ -0,0 +1,152 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class VectorMathNode(LnxLogicTreeNode):
|
||||
"""Mathematical operations on vectors."""
|
||||
bl_idname = 'LNVectorMathNode'
|
||||
bl_label = 'Vector Math'
|
||||
lnx_section = 'vector'
|
||||
lnx_version = 1
|
||||
|
||||
def get_bool(self):
|
||||
return self.get('property1', False)
|
||||
|
||||
def set_bool(self, value):
|
||||
self['property1'] = value
|
||||
if value:
|
||||
if self.property0 in ('Length', 'Distance', 'Dot Product'):
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Distance/Length/Scalar
|
||||
self.add_output('LnxFloatSocket', 'X') # Result X
|
||||
self.add_output('LnxFloatSocket', 'Y') # Result Y
|
||||
self.add_output('LnxFloatSocket', 'Z') # Result Z
|
||||
if self.property0 == 'Length':
|
||||
self.add_output('LnxFloatSocket', 'Length') # Length
|
||||
if self.property0 == 'Distance':
|
||||
self.add_output('LnxFloatSocket', 'Distance') # Distance
|
||||
if self.property0 == 'Dot Product':
|
||||
self.add_output('LnxFloatSocket', 'Scalar') # Scalar
|
||||
else:
|
||||
if self.property0 in ('Length', 'Distance', 'Dot Product') and len(self.outputs) > 1:
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Distance/Length/Scalar
|
||||
# Remove X, Y, Z
|
||||
for i in range(3):
|
||||
if len(self.outputs) > 1:
|
||||
self.outputs.remove(self.outputs.values()[-1])
|
||||
else:
|
||||
break
|
||||
if self.property0 == 'Length':
|
||||
self.add_output('LnxFloatSocket', 'Length') # Length
|
||||
if self.property0 == 'Distance':
|
||||
self.add_output('LnxFloatSocket', 'Distance') # Distance
|
||||
if self.property0 == 'Dot Product':
|
||||
self.add_output('LnxFloatSocket', 'Scalar') # Scalar
|
||||
|
||||
property1: HaxeBoolProperty('property1', name='Separator Out', default=False, set=set_bool, get=get_bool)
|
||||
|
||||
@staticmethod
|
||||
def get_enum_id_value(obj, prop_name, value):
|
||||
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(operation_name):
|
||||
return {
|
||||
'Add': 0,
|
||||
'Subtract': 0,
|
||||
'Average': 0,
|
||||
'Dot Product': 0,
|
||||
'Cross Product': 0,
|
||||
'Multiply': 0,
|
||||
'MultiplyFloats': 0,
|
||||
'Distance': 2,
|
||||
'Reflect': 2,
|
||||
'Normalize': 1,
|
||||
'Length': 1
|
||||
}.get(operation_name, 0)
|
||||
|
||||
def get_enum(self):
|
||||
return self.get('property0', 0)
|
||||
|
||||
def set_enum(self, value):
|
||||
# Checking the selection of another operation
|
||||
select_current = self.get_enum_id_value(self, 'property0', value)
|
||||
select_prev = self.property0
|
||||
if select_prev != select_current:
|
||||
if select_prev in ('Distance', 'Length', 'Dot Product'):
|
||||
self.outputs.remove(self.outputs.values()[-1])
|
||||
# Many arguments: Add, Subtract, Average, Dot Product, Cross Product, Multiply, MultiplyFloats
|
||||
if self.get_count_in(select_current) == 0:
|
||||
if select_current == "MultiplyFloats" or select_prev == "MultiplyFloats":
|
||||
while (len(self.inputs) > 1):
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
if select_current == "MultiplyFloats":
|
||||
self.add_input('LnxFloatSocket', 'Value ' + str(len(self.inputs)))
|
||||
else:
|
||||
while (len(self.inputs) < 2):
|
||||
self.add_input('LnxVectorSocket', 'Value ' + str(len(self.inputs)))
|
||||
if select_current == 'Dot Product':
|
||||
self.add_output('LnxFloatSocket', 'Scalar')
|
||||
# 2 arguments: Distance, Reflect
|
||||
if self.get_count_in(select_current) == 2:
|
||||
count = 2
|
||||
if select_prev == "MultiplyFloats":
|
||||
count = 1
|
||||
while len(self.inputs) > count:
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
while len(self.inputs) < 2:
|
||||
self.add_input('LnxVectorSocket', 'Value ' + str(len(self.inputs)))
|
||||
if select_current == 'Distance':
|
||||
self.add_output('LnxFloatSocket', 'Distance')
|
||||
# 1 argument: Normalize, Length
|
||||
if self.get_count_in(select_current) == 1:
|
||||
while len(self.inputs) > 1:
|
||||
self.inputs.remove(self.inputs.values()[-1])
|
||||
if select_current == 'Length':
|
||||
self.add_output('LnxFloatSocket', 'Length')
|
||||
self['property0'] = value
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items=[('Add', 'Add', 'Add'),
|
||||
('Dot Product', 'Dot Product', 'Dot Product'),
|
||||
('Multiply', 'Multiply', 'Multiply'),
|
||||
('MultiplyFloats', 'Multiply (Floats)', 'Multiply (Floats)'),
|
||||
('Normalize', 'Normalize', 'Normalize'),
|
||||
('Subtract', 'Subtract', 'Subtract'),
|
||||
('Average', 'Average', 'Average'),
|
||||
('Cross Product', 'Cross Product', 'Cross Product'),
|
||||
('Length', 'Length', 'Length'),
|
||||
('Distance', 'Distance', 'Distance'),
|
||||
('Reflect', 'Reflect', 'Reflect')],
|
||||
name='', default='Add', set=set_enum, get=get_enum)
|
||||
|
||||
def __init__(self):
|
||||
array_nodes[str(id(self))] = self
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxVectorSocket', 'Value 0', default_value=[0.0, 0.0, 0.0])
|
||||
self.add_input('LnxVectorSocket', 'Value 1', default_value=[0.0, 0.0, 0.0])
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Result')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property1') # Separator Out
|
||||
layout.prop(self, 'property0') # Operation
|
||||
# Buttons
|
||||
if self.get_count_in(self.property0) == 0:
|
||||
row = layout.row(align=True)
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_add_input', text='Add Value', icon='PLUS', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
op.name_format = 'Value {0}'
|
||||
if self.property0 == "MultiplyFloats":
|
||||
op.socket_type = 'LnxFloatSocket'
|
||||
else:
|
||||
op.socket_type = 'LnxVectorSocket'
|
||||
column = row.column(align=True)
|
||||
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.inputs) == 2:
|
||||
column.enabled = False
|
||||
|
||||
def draw_label(self) -> str:
|
||||
return f'{self.bl_label}: {self.property0}'
|
15
leenkx/blender/lnx/logicnode/math/LN_vector_move_towards.py
Normal file
15
leenkx/blender/lnx/logicnode/math/LN_vector_move_towards.py
Normal file
@ -0,0 +1,15 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class VectorMoveTowardsNode(LnxLogicTreeNode):
|
||||
"""Add a constant value to the given vector until it reach the target vector."""
|
||||
bl_idname = 'LNVectorMoveTowardsNode'
|
||||
bl_label = 'Vector Move Towards'
|
||||
lnx_section = 'vector'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxVectorSocket', 'Vector 1', default_value=[0.0, 0.0, 0.0])
|
||||
self.add_input('LnxVectorSocket', 'Vector 2', default_value=[1.0, 1.0, 1.0])
|
||||
self.add_input('LnxFloatSocket', 'Delta', default_value=0.1)
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Result')
|
@ -0,0 +1,35 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class WorldToScreenSpaceNode(LnxLogicTreeNode):
|
||||
"""Transforms the given world coordinates into screen coordinates,
|
||||
using the active camera or a selected camera."""
|
||||
bl_idname = 'LNWorldToScreenSpaceNode'
|
||||
bl_label = 'World to Screen Space'
|
||||
lnx_section = 'matrix'
|
||||
lnx_version = 2
|
||||
|
||||
def remove_extra_inputs(self, context):
|
||||
while len(self.inputs) > 1:
|
||||
self.inputs.remove(self.inputs[-1])
|
||||
if self.property0 == 'Selected Camera':
|
||||
self.add_input('LnxNodeSocketObject', 'Camera')
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Active Camera', 'Active Camera', 'Active Camera'),
|
||||
('Selected Camera', 'Selected Camera', 'Selected Camera')],
|
||||
name='', default='Active Camera', update=remove_extra_inputs)
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxVectorSocket', 'World')
|
||||
|
||||
self.add_output('LnxVectorSocket', 'Screen')
|
||||
|
||||
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()
|
||||
|
||||
return NodeReplacement.Identity(self)
|
7
leenkx/blender/lnx/logicnode/math/__init__.py
Normal file
7
leenkx/blender/lnx/logicnode/math/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from lnx.logicnode.lnx_nodes import add_node_section
|
||||
|
||||
add_node_section(name='default', category='Math')
|
||||
add_node_section(name='angle', category='Math')
|
||||
add_node_section(name='matrix', category='Math')
|
||||
add_node_section(name='color', category='Math')
|
||||
add_node_section(name='vector', category='Math')
|
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.
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