Repe [T3DU] Update - 31f26d171bba0355ce2a77031e3aad4c64dbc7e9

This commit is contained in:
2026-09-04 10:46:13 -07:00
parent c915312901
commit 0460b9d587
482 changed files with 27797 additions and 3226 deletions

View File

@ -64,13 +64,14 @@ class NodeType(Enum):
SPEAKER = 5
DECAL = 6
PROBE = 7
CURVE = 8
@classmethod
def get_bobject_type(cls, bobject: bpy.types.Object) -> "NodeType":
"""Returns the NodeType enum member belonging to the type of
the given blender object."""
if bobject.type == "MESH":
if bobject.data.polygons or bobject.data.edges or bobject.data.vertices:
if bobject.data.polygons: #or bobject.data.edges or bobject.data.vertices:
return cls.MESH
elif bobject.type in ('FONT', 'META', 'CURVE'): # FIXME: curves with meshes shouldn't be used in modifiers for now.
if bobject.type == 'CURVE':
@ -89,12 +90,14 @@ class NodeType(Enum):
return cls.SPEAKER
elif bobject.type == "LIGHT_PROBE":
return cls.PROBE
elif bobject.type == "CURVE":
return cls.CURVE
return cls.EMPTY
STRUCT_IDENTIFIER = ("object", "bone_object", "mesh_object",
"light_object", "camera_object", "speaker_object",
"decal_object", "probe_object")
"decal_object", "probe_object", "curve_object")
# Internal target names for single FCurve data paths
FCURVE_TARGET_NAMES = {
@ -161,6 +164,7 @@ class LeenkxExporter:
self.bobject_array: Dict[bpy.types.Object, Dict[str, Union[NodeType, str]]] = {}
self.bobject_bone_array = {}
self.mesh_array = {}
self.curve_array = {}
self.light_array = {}
self.probe_array = {}
self.camera_array = {}
@ -405,7 +409,7 @@ class LeenkxExporter:
o['transform'] = {'values': LeenkxExporter.write_matrix(matrix_local)}
# Animated transform
if bobject.animation_data is not None and bobject.type != "ARMATURE":
if bobject.animation_data is not None and bobject.type != "ARMATURE" and bobject.lnx_animation_enabled:
action = bobject.animation_data.action
if action is not None:
@ -987,7 +991,7 @@ class LeenkxExporter:
out_object = self.object_to_lnx_object_dict[bobject]
out_object['type'] = STRUCT_IDENTIFIER[object_type.value]
out_object['name'] = bobject_ref["structName"]
out_object['name'] = lnx.utils.safestr(bobject_ref["structName"])
if bobject.parent_type == "BONE":
out_object['parent_bone'] = bobject.parent_bone
@ -1064,7 +1068,7 @@ class LeenkxExporter:
_num_verts = len(_verts)
_all_co = np.empty(_num_verts * 3, dtype='<f8')
_verts.foreach_get('co', _all_co)
_all_str = _all_co.astype('U32').tolist()
_all_f = _all_co.tolist()
_num_groups = len(bobject.vertex_groups)
_vg_idx = [[] for _ in range(_num_groups)]
for v in _verts:
@ -1079,9 +1083,9 @@ class LeenkxExporter:
_values = []
for vi in indices:
base = vi * 3
_values.append(_all_str[base])
_values.append(_all_str[base + 1])
_values.append(_all_str[base + 2])
_values.append(_all_f[base])
_values.append(_all_f[base + 1])
_values.append(_all_f[base + 2])
else:
_values = []
out_object['vertex_groups'].append({
@ -1100,16 +1104,24 @@ class LeenkxExporter:
if len(bobject.lnx_propertylist) > 0:
out_object['properties'] = []
for proplist_item in bobject.lnx_propertylist:
# Check if the property is a collection (array type).
if proplist_item.type_prop == 'array':
# Convert the collection to a list.
array_type = proplist_item.array_item_type
collection_value = getattr(proplist_item, 'array_prop')
property_name = array_type + '_prop'
value = [str(getattr(item, property_name)) for item in collection_value]
if array_type == 'vector':
value = []
for item in collection_value:
v = getattr(item, property_name)
value.append([float(v[0]), float(v[1]), float(v[2])])
else:
value = [getattr(item, property_name) for item in collection_value]
elif proplist_item.type_prop == 'vector':
v = getattr(proplist_item, 'vector_prop')
value = [float(v[0]), float(v[1]), float(v[2])]
else:
# Handle other types of properties.
value = getattr(proplist_item, proplist_item.type_prop + '_prop')
out_property = {
@ -1163,6 +1175,9 @@ class LeenkxExporter:
# Decal flag
if mat is not None and mat.lnx_decal:
out_object['type'] = 'decal_object'
if mat is not None:
if 'Rot' in bobject.lnx_instanced:
mat.lnx_instanced_rot_type = bobject.lnx_instanced_rot_type
# No material, mimic cycles and assign default
if len(out_object['material_refs']) == 0:
self.use_default_material(bobject, out_object)
@ -1228,15 +1243,32 @@ class LeenkxExporter:
self.speaker_array[objref]["objectTable"].append(bobject)
out_object['data_ref'] = self.speaker_array[objref]["structName"]
elif object_type is NodeType.CURVE:
if objref not in self.curve_array:
self.curve_array[objref] = {"structName" : objname, "objectTable" : [bobject]}
else:
self.curve_array[objref]["objectTable"].append(bobject)
out_object['data_ref'] = lnx.utils.safestr(self.curve_array[objref]["structName"])
out_object['material_refs'] = []
for i in range(len(bobject.material_slots)):
mat = self.slot_to_material(bobject, bobject.material_slots[i])
# Export ref
self.export_material_ref(bobject, mat, i, out_object)
# Decal flag
if mat is not None and mat.lnx_decal:
out_object['type'] = 'decal_object'
# Export the transform. If object is animated, then animation tracks are exported here
if bobject.type != 'ARMATURE' and bobject.animation_data is not None:
action = bobject.animation_data.action
export_actions = [action]
for track in bobject.animation_data.nla_tracks:
if track.strips is None:
if track.strips is None or track.mute:
continue
for strip in track.strips:
if strip.action is None or strip.action in export_actions:
if strip.action is None or strip.action in export_actions or strip.mute:
continue
export_actions.append(strip.action)
orig_action = action
@ -1265,7 +1297,7 @@ class LeenkxExporter:
bone_translation_pose = pose_bone.tail - pose_bone.head
out_object['parent_bone_tail_pose'] = [bone_translation_pose[0], bone_translation_pose[1], bone_translation_pose[2]]
if bobject.type == 'ARMATURE' and bobject.data is not None:
if lnx.utils.get_rp().lnx_skin != 'Off' and bobject.type == 'ARMATURE' and bobject.data is not None:
# Armature data
bdata = bobject.data
# Reference start action
@ -1290,10 +1322,10 @@ class LeenkxExporter:
# armature object to deform with
if hasattr(adata, 'nla_tracks') and adata.nla_tracks is not None:
for track in adata.nla_tracks:
if track.strips is None:
if track.strips is None or track.mute:
continue
for strip in track.strips:
if strip.action is None:
if strip.action is None or strip.mute:
continue
if strip.action.name == action.name:
continue
@ -2196,6 +2228,86 @@ class LeenkxExporter:
except ReferenceError:
pass
def export_curve(self, object_ref):
"""Exports a single curve object."""
table = object_ref[1]["objectTable"]
bobject = table[0]
curve_id = lnx.utils.safestr(object_ref[1]["structName"])
world = bpy.data.worlds['Lnx']
if world.lnx_verbose_output:
print('Exporting curve ' + lnx.utils.asset_name(bobject.data))
curve_data = bobject.data
out_curve = {
'name': curve_id,
'object': bobject.name,
'splines': [],
'strength': curve_data.lnx_draw_strength,
'color': [curve_data.lnx_draw_color[0], curve_data.lnx_draw_color[1], curve_data.lnx_draw_color[2], curve_data.lnx_draw_color[3]]
}
for spline in curve_data.splines:
if spline.type == 'BEZIER':
current_spline = {
'closed': spline.use_cyclic_u,
'resolution': spline.resolution_u,
'points': [],
'material_index': spline.material_index,
}
for bezier_point in spline.bezier_points:
point_data = {
'co': [bezier_point.co.x, bezier_point.co.y, bezier_point.co.z],
'handle_left': [bezier_point.handle_left.x, bezier_point.handle_left.y, bezier_point.handle_left.z],
'handle_right': [bezier_point.handle_right.x, bezier_point.handle_right.y, bezier_point.handle_right.z]
}
current_spline['points'].append(point_data)
out_curve['splines'].append(current_spline)
active_keys = [kb for kb in bobject.data.shape_keys.key_blocks if not kb.mute] if bobject.data.shape_keys else []
if len(active_keys) == 0:
temp_obj = bobject.to_mesh()
if len(temp_obj.polygons) > 0:
temp_ref = [object_ref[0], {
"objectTable": table,
"structName": lnx.utils.safestr(curve_id)
}]
self.export_mesh(temp_ref)
bobject.to_mesh_clear()
else:
out_curve['shape_keys'] = []
for kb in bobject.data.shape_keys.key_blocks:
key_data = {
'name': kb.name,
'value': kb.value,
'points': []
}
for p in kb.data:
key_data['points'].append({
'co': p.co[:],
'handle_left': p.handle_left[:],
'handle_right': p.handle_right[:]
})
out_curve['shape_keys'].append(key_data)
mat_count = len(bobject.material_slots)
if mat_count > 0:
out_curve['material_refs'] = []
for i in range(mat_count):
mat = self.slot_to_material(bobject, bobject.material_slots[i])
self.export_material_ref(bobject, mat, i, out_curve)
self.output['curve_datas'].append(out_curve)
def export_light(self, object_ref):
"""Exports a single light object."""
rpdat = lnx.utils.get_rp()
@ -2288,11 +2400,11 @@ class LeenkxExporter:
o = {'name': objectRef[1]["structName"]}
bo = objectRef[0]
if bo.type == 'GRID':
if bo.type in ('GRID', 'VOLUME'):
o['type'] = 'grid'
elif bo.type == 'PLANAR':
elif bo.type in ('PLANAR', 'PLANE'):
o['type'] = 'planar'
else:
else: # (CUBEMAP, SPHERE)
o['type'] = 'cubemap'
self.output['probe_datas'].append(o)
@ -2348,6 +2460,7 @@ class LeenkxExporter:
if bobject.type == 'CAMERA':
self.output['camera_ref'] = asset_name
self.has_spawning_camera = True
asset_name = bobject.name
out_collection['object_refs'].append(asset_name)
@ -2731,7 +2844,7 @@ class LeenkxExporter:
'lifetime_random': psettings.lifetime_random,
'emit_from': emit_from,
# Velocity
# 'normal_factor': psettings.normal_factor,
'normal_factor': psettings.normal_factor,
# 'tangent_factor': psettings.tangent_factor,
# 'tangent_phase': psettings.tangent_phase,
'object_align_factor': (
@ -2815,20 +2928,19 @@ class LeenkxExporter:
def export_world(self):
"""Exports the world of the current scene."""
world = self.scene.world
for world in bpy.data.worlds:
if world is not None:
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
if world is not None:
world_name = lnx.utils.safestr(lnx.utils.asset_name(world) if world.library else world.name)
if world_name not in self.world_array:
self.world_array.append(world_name)
out_world = {'name': world_name}
if world_name not in self.world_array:
self.world_array.append(world_name)
out_world = {'name': world_name}
self.post_export_world(world, out_world)
self.output['world_datas'].append(out_world)
self.post_export_world(world, out_world)
self.output['world_datas'].append(out_world)
elif lnx.utils.get_rp().rp_background == 'World':
log.warn(f'Scene "{self.scene.name}" is missing a world, some render targets will not be cleared')
elif lnx.utils.get_rp().rp_background == 'World':
log.warn(f'Scene "{self.scene.name}" is missing a world, some render targets will not be cleared')
def export_objects(self, scene):
"""Exports all supported blender objects.
@ -2847,6 +2959,7 @@ class LeenkxExporter:
self.output['light_datas'] = []
self.output['camera_datas'] = []
self.output['speaker_datas'] = []
self.output['curve_datas'] = []
for light_ref in self.light_array.items():
self.export_light(light_ref)
@ -2867,6 +2980,9 @@ class LeenkxExporter:
for lightprobe_object in self.probe_array.items():
self.export_probe(lightprobe_object)
for curve_ref in self.curve_array.items():
self.export_curve(curve_ref)
self.output['mesh_datas'] = []
for mesh_ref in self.mesh_array.items():
self.export_mesh(mesh_ref)
@ -2896,9 +3012,9 @@ class LeenkxExporter:
self.process_bobject(bobject)
# Softbody needs connected triangles, use optimized
# geometry export
for mod in bobject.modifiers:
"""for mod in bobject.modifiers:
if mod.type in ('CLOTH', 'SOFT_BODY'):
LeenkxExporter.optimize_enabled = True
LeenkxExporter.optimize_enabled = True"""
self.process_skinned_meshes()
@ -3029,6 +3145,8 @@ class LeenkxExporter:
self.export_scene_traits()
self.export_scene_properties()
self.export_canvas_themes()
# Write embedded data references
@ -3145,6 +3263,14 @@ class LeenkxExporter:
instanced_type = 4
instanced_data = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0]
attr_data_block = []
for attr in bobject.lnx_instanced_attrs:
attr_data_block.extend([0.0] * attr.size)
instanced_data.extend(attr_data_block)
if lnx.utils.export_morph_targets(bobject):
instanced_data.extend([0.0, 0.0, 0.0, 0.0])
for child in bobject.children:
if not child.lnx_export or child.hide_render:
continue
@ -3163,6 +3289,9 @@ class LeenkxExporter:
instanced_data.append(scale.x)
instanced_data.append(scale.y)
instanced_data.append(scale.z)
instanced_data.extend(attr_data_block)
if lnx.utils.export_morph_targets(child):
instanced_data.extend([0.0, 0.0, 0.0, 0.0])
break
# Instance render collections with same children?
@ -3338,9 +3467,9 @@ class LeenkxExporter:
# Phys traits
if phys_enabled:
for modifier in bobject.modifiers:
if modifier.type in ('CLOTH', 'SOFT_BODY'):
if modifier.type in ('CLOTH', 'SOFT_BODY') and modifier.show_render:
self.add_softbody_mod(o, bobject, modifier)
elif modifier.type == 'HOOK':
elif modifier.type == 'HOOK' and modifier.show_render:
self.add_hook_mod(o, bobject, modifier.object.name, modifier.vertex_group)
# Rigid body constraint
@ -3392,7 +3521,7 @@ class LeenkxExporter:
if bone:
out_constraint['bone'] = bobject.name
if hasattr(constraint, 'target') and constraint.target is not None:
if constraint.type == 'COPY_LOCATION':
if constraint.type in ('COPY_LOCATION', 'COPY_ROTATION'):
out_constraint['target'] = constraint.target.name
out_constraint['use_x'] = constraint.use_x
out_constraint['use_y'] = constraint.use_y
@ -3402,9 +3531,38 @@ class LeenkxExporter:
out_constraint['invert_z'] = constraint.invert_z
out_constraint['use_offset'] = constraint.use_offset
out_constraint['influence'] = constraint.influence
elif constraint.type in ('COPY_SCALE'):
out_constraint['target'] = constraint.target.name
out_constraint['use_x'] = constraint.use_x
out_constraint['use_y'] = constraint.use_y
out_constraint['use_z'] = constraint.use_z
out_constraint['use_offset'] = constraint.use_offset
out_constraint['influence'] = constraint.influence
elif constraint.type == 'COPY_TRANSFORMS':
out_constraint['target'] = constraint.target.name
out_constraint['influence'] = constraint.influence
elif constraint.type == 'CHILD_OF':
out_constraint['target'] = constraint.target.name
out_constraint['influence'] = constraint.influence
elif constraint.type in ('LIMIT_LOCATION', 'LIMIT_ROTATION', 'LIMIT_SCALE'):
if constraint.type == 'LIMIT_ROTATION':
out_constraint['use_limit_x'] = constraint.use_limit_x
out_constraint['use_limit_y'] = constraint.use_limit_y
out_constraint['use_limit_z'] = constraint.use_limit_z
else:
out_constraint['use_min_x'] = constraint.use_min_x
out_constraint['use_min_y'] = constraint.use_min_y
out_constraint['use_min_z'] = constraint.use_min_z
out_constraint['use_max_x'] = constraint.use_max_x
out_constraint['use_max_y'] = constraint.use_max_y
out_constraint['use_max_z'] = constraint.use_max_z
out_constraint['min_x'] = constraint.min_x
out_constraint['min_y'] = constraint.min_y
out_constraint['min_z'] = constraint.min_z
out_constraint['max_x'] = constraint.max_x
out_constraint['max_y'] = constraint.max_y
out_constraint['max_z'] = constraint.max_z
out_constraint['influence'] = constraint.influence
o['constraints'].append(out_constraint)
@ -3632,6 +3790,35 @@ class LeenkxExporter:
for out_trait in self.output['traits']:
LeenkxExporter.import_traits.append(out_trait['class_name'])
def export_scene_properties(self) -> None:
bscene = self.scene
if len(bscene.lnx_propertylist) > 0:
if 'properties' not in self.output:
self.output['properties'] = []
for proplist_item in bscene.lnx_propertylist:
if proplist_item.type_prop == 'array':
array_type = proplist_item.array_item_type
collection_value = getattr(proplist_item, 'array_prop')
property_name = array_type + '_prop'
if array_type == 'vector':
value = [[v for v in getattr(item, property_name)] for item in collection_value]
else:
value = [getattr(item, property_name) for item in collection_value]
elif proplist_item.type_prop == 'vector':
value = [v for v in getattr(proplist_item, 'vector_prop')]
else:
value = getattr(proplist_item, proplist_item.type_prop + '_prop')
out_property = {
'name': proplist_item.name_prop,
'value': value
}
self.output['properties'].append(out_property)
@staticmethod
def export_canvas_themes():
path_themes = os.path.join(lnx.utils.get_fp(), 'Bundled', 'canvas')
@ -3657,16 +3844,21 @@ class LeenkxExporter:
# ClothModifier
if modifier.type == 'CLOTH':
bend = modifier.settings.bending_stiffness
friction = bobject.lnx_soft_body_friction
damping = bobject.lnx_soft_body_damping
soft_type = 0
# SoftBodyModifier
elif modifier.type == 'SOFT_BODY':
bend = (modifier.settings.bend + 1.0) * 10
bend = modifier.settings.bend
friction = modifier.settings.friction
damping = modifier.settings.goal_friction
soft_type = 1
else:
# Wrong modifier type
return
out_trait['parameters'] = [str(soft_type), str(bend), str(modifier.settings.mass), str(bobject.lnx_soft_body_margin)]
out_trait['parameters'] = [str(soft_type), str(bend), str(modifier.settings.mass), str(bobject.lnx_soft_body_margin), str(friction), str(damping),
str(bobject.lnx_soft_body_linear_stiffness), str(bobject.lnx_soft_body_angular_stiffness), str(bobject.lnx_soft_body_pressure)]
o['traits'].append(out_trait)
if soft_type == 0:

View File

@ -214,7 +214,7 @@ def export_mesh_data(self, export_mesh: bpy.types.Mesh, bobject: bpy.types.Objec
# Scale for packed coords
maxdim = max(bobject.data.lnx_aabb[0], max(bobject.data.lnx_aabb[1], bobject.data.lnx_aabb[2]))
if maxdim > 2:
o['scale_pos'] = maxdim / 2
o['scale_pos'] = maxdim / 1 if bpy.app.version >= (4, 1, 0) else 2
else:
o['scale_pos'] = 1.0
if has_armature: # Allow up to 2x bigger bounds for skinned mesh

View File

@ -228,7 +228,7 @@ context_screen = None
@persistent
def on_save_pre(context):
# Ensure that files are saved with the correct version number
# (e.g. startup files with an "Arm" world may have old version numbers)
# (e.g. startup files with an "Lnx" world may have old version numbers)
wrd = bpy.data.worlds['Lnx']
wrd.lnx_version = props.lnx_version
wrd.lnx_commit = props.lnx_commit

View File

@ -29,8 +29,9 @@ def register():
if not lnx_start:
kmw = keyconfig.keymaps.new(name='Window', space_type='EMPTY', region_type="WINDOW")
kmw.keymap_items.new(props_ui.LeenkxPlayButton.bl_idname, type='F5', value='PRESS')
kmw.keymap_items.new('tlm.build_lightmaps', type='F6', value='PRESS')
kmw.keymap_items.new('tlm.clean_lightmaps', type='F7', value='PRESS')
kmw.keymap_items.new(props_ui.LeenkxCleanProjectButton.bl_idname, type='F6', value='PRESS')
#kmw.keymap_items.new('tlm.build_lightmaps', type='F6', value='PRESS')
#kmw.keymap_items.new('tlm.clean_lightmaps', type='F7', value='PRESS')
kmn = keyconfig.keymaps.new(name='Node Editor', space_type='NODE_EDITOR')
kmn.keymap_items.new('lnx.add_call_group_node', 'G', 'PRESS', shift=True)
kmn.keymap_items.new('lnx.add_group_tree_from_selected', 'G', 'PRESS', ctrl=True)
@ -44,8 +45,9 @@ def register():
def unregister():
kmw = bpy.context.window_manager.keyconfigs.user.keymaps.get('Window')
kmw.keymap_items.remove(kmw.keymap_items[props_ui.LeenkxPlayButton.bl_idname])
kmw.keymap_items.remove(kmw.keymap_items['tlm.build_lightmaps'])
kmw.keymap_items.remove(kmw.keymap_items['tlm.clean_lightmaps'])
kmw.keymap_items.remove(kmw.keymap_items[props_ui.LeenkxCleanProjectButton.bl_idname])
#kmw.keymap_items.remove(kmw.keymap_items['tlm.build_lightmaps'])
#kmw.keymap_items.remove(kmw.keymap_items['tlm.clean_lightmaps'])
kmn = bpy.context.window_manager.keyconfigs.user.keymaps.get('Node Editor')
kmn.keymap_items.remove(kmn.keymap_items['lnx.add_call_group_node'])
kmn.keymap_items.remove(kmn.keymap_items['lnx.add_group_tree_from_selected'])

View File

@ -1,3 +1,5 @@
import os
import bpy
from typing import List, Dict, Optional, Any
import lnx.utils
@ -11,6 +13,14 @@ def parse_context(
vert: Optional[List[str]] = None,
frag: Optional[List[str]] = None,
):
if c["name"] == "compositor_pass":
wrd = bpy.data.worlds['Lnx']
rpdat = wrd.lnx_rplist[wrd.lnx_rplist_index]
if rpdat.lnx_custom_compositor:
custom_path = lnx.utils.get_fp().replace("\\", "/") + "/Compositor"
c["vertex_shader"] = custom_path + "/compositor_pass.vert.glsl"
c["fragment_shader"] = custom_path + "/compositor_pass.frag.glsl"
con = {
"name": c["name"],
"constants": [],
@ -233,6 +243,8 @@ def parse_shader(
const = {"type": ctype, "name": cid}
con["constants"].append(const)
#print(f"file: {c.get('vertex_shader', 'Unknown')} | Constant: {cid} ({ctype})")
check_link(c, defs, cid, const)

View File

@ -43,6 +43,7 @@ def init_categories():
lnx_nodes.add_category('Light', icon='LIGHT', section="data")
lnx_nodes.add_category('World', icon='WORLD', section="data")
lnx_nodes.add_category('Object', icon='OBJECT_DATA', section="data")
lnx_nodes.add_category('Curve', icon='IPO_EASE_IN_OUT', section="data")
lnx_nodes.add_category('Scene', icon='SCENE_DATA', section="data")
lnx_nodes.add_category('Trait', icon='NODETREE', section="data")
lnx_nodes.add_category('Network', icon='WORLD', section="data")

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class AnimationNode(LnxLogicTreeNode):
"""Returns the actions list of the given object."""
bl_idname = 'LNAnimationNode'
bl_label = 'Get Actions'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxNodeSocketArray', 'Actions', is_var=False)
self.add_output('LnxIntSocket', 'Length')

View File

@ -0,0 +1,18 @@
from lnx.logicnode.lnx_nodes import *
class AsyncArrayLoopNode(LnxLogicTreeNode):
"""Loops through each item of the given array."""
bl_idname = 'LNAsyncArrayLoopNode'
bl_label = 'Async Array Loop'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxIntSocket', 'Async Items')
self.add_output('LnxNodeSocketAction', 'Loop')
self.add_output('LnxDynamicSocket', 'Value')
self.add_output('LnxIntSocket', 'Index')
self.add_output('LnxNodeSocketAction', 'Done')

View File

@ -0,0 +1,15 @@
from lnx.logicnode.lnx_nodes import *
class SetCameraProjectionNode(LnxLogicTreeNode):
"""Sets the projection of the given camera."""
bl_idname = 'LNSetCameraProjectionNode'
bl_label = 'Set Camera Projection'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Camera')
self.add_input('LnxIntSocket', 'Width')
self.add_input('LnxIntSocket', 'Height')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -13,9 +13,9 @@ class SetCameraStartEndNode(LnxLogicTreeNode):
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Start':
self.add_input('LnxFloatSocket', 'Start')
if self.property0 == 'End':
elif self.property0 == 'End':
self.add_input('LnxFloatSocket', 'End')
if self.property0 == 'Start&End':
elif self.property0 == 'Start&End':
self.add_input('LnxFloatSocket', 'Start')
self.add_input('LnxFloatSocket', 'End')

View File

@ -0,0 +1,60 @@
from lnx.logicnode.lnx_nodes import *
class SetScreenCamerasNode(LnxLogicTreeNode):
"""Renders the scene from the view of specified cameras.
@input Start: Evaluate the inputs and start drawing the camera render targets.
@input Stop: Stops the rendering and drawing of the camera render targets.
@input Camera: The camera from which to render.
@input X/Y: Position where the camera's render target is drawn, in pixels from the top left corner.
@input Width/Height: Size of the camera's render target in pixels.
@output On Start: Activated after the `Start` input has been activated.
@output On Stop: Activated after the `Stop` input has been activated.
"""
bl_idname = 'LNSetScreenCamerasNode'
bl_label = 'Set Screen Cameras'
lnx_version = 1
min_inputs = 7
num_choices: IntProperty(default=0, min=0)
def __init__(self, *args, **kwargs):
super(SetScreenCamerasNode, self).__init__(*args, **kwargs)
array_nodes[str(id(self))] = self
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Start')
self.add_input('LnxNodeSocketAction', 'Stop')
self.add_sockets()
self.add_output('LnxNodeSocketAction', 'On Start')
self.add_output('LnxNodeSocketAction', 'On Stop')
def add_sockets(self):
self.num_choices += 1
self.add_input('LnxNodeSocketObject', 'Camera ' + str(self.num_choices))
self.add_input('LnxIntSocket', 'X')
self.add_input('LnxIntSocket', 'Y')
self.add_input('LnxIntSocket', 'Width')
self.add_input('LnxIntSocket', 'Height')
def remove_sockets(self):
if self.num_choices > 1:
for _ in range(5):
self.inputs.remove(self.inputs.values()[-1])
self.num_choices -= 1
def draw_buttons(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_call_func', text='Add Camera', icon='PLUS', emboss=True)
op.node_index = str(id(self))
op.callback_name = 'add_sockets'
column = row.column(align=True)
op = column.operator('lnx.node_call_func', text='', icon='X', emboss=True)
op.node_index = str(id(self))
op.callback_name = 'remove_sockets'
if len(self.inputs) == self.min_inputs:
column.enabled = False

View File

@ -0,0 +1,17 @@
from lnx.logicnode.lnx_nodes import *
class AlongCurveNode(LnxLogicTreeNode):
"""Sets an object along a curve."""
bl_idname = 'LNAlongCurveNode'
bl_label = 'Along Curve'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxIntSocket', 'Spline Index')
self.add_input('LnxStringSocket', 'Forward Axis', default_value = 'X')
self.add_input('LnxFloatSocket', 'Position')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,18 @@
from lnx.logicnode.lnx_nodes import *
class DeformCurveNode(LnxLogicTreeNode):
"""Sets an object to deform using a curve."""
bl_idname = 'LNDeformCurveNode'
bl_label = 'Deform Curve'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxIntSocket', 'Spline Index')
self.add_input('LnxStringSocket', 'Forward Axis', default_value = 'X')
self.add_input('LnxFloatSocket', 'Start')
self.add_input('LnxFloatSocket', 'End', default_value = 1.0)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,22 @@
from lnx.logicnode.lnx_nodes import *
class FollowCurveNode(LnxLogicTreeNode):
"""Sets an object to follow a curve."""
bl_idname = 'LNFollowCurveNode'
bl_label = 'Follow Curve'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxIntSocket', 'Spline Index')
self.add_input('LnxStringSocket', 'Forward Axis', default_value = 'X')
self.add_input('LnxFloatSocket', 'Speed')
self.add_input('LnxBoolSocket', 'Forward', default_value = True)
self.add_input('LnxBoolSocket', 'Cyclic')
self.add_input('LnxFloatSocket', 'Start')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketAction', 'Cycle')
self.add_output('LnxFloatSocket', 'Progress')

View File

@ -0,0 +1,18 @@
from lnx.logicnode.lnx_nodes import *
class GetCurveDataNode(LnxLogicTreeNode):
"""Gets curve data."""
bl_idname = 'LNGetCurveDataNode'
bl_label = 'Get Curve Data'
lnx_section = 'get'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_output('LnxIntSocket', 'Splines Length')
self.add_output('LnxIntSocket', 'Equidistant Samples')
self.add_output('LnxBoolSocket', 'Draw')
self.add_output('LnxFloatSocket', 'Strength')
self.add_output('LnxColorSocket', 'Color')
self.add_output('LnxNodeSocketObject', 'Curve Mesh')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class GetCurveSplineNode(LnxLogicTreeNode):
"""Gets curve spline."""
bl_idname = 'LNGetCurveSplineNode'
bl_label = 'Get Curve Spline'
lnx_section = 'get'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxIntSocket', 'Spline Index')
self.add_output('LnxNodeSocketArray', 'Points')
self.add_output('LnxBoolSocket', 'Closed')
self.add_output('LnxIntSocket', 'Resolution')

View File

@ -0,0 +1,38 @@
from lnx.logicnode.lnx_nodes import *
class SetCurveDataNode(LnxLogicTreeNode):
"""Sets curve data."""
bl_idname = 'LNSetCurveDataNode'
bl_label = 'Set Curve Data'
lnx_section = 'set'
lnx_version = 1
def update_sockets(self, context):
while len(self.inputs) > 2:
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Equidistant Samples':
self.add_input('LnxIntSocket', 'Equidistant Samples')
elif self.property0 == 'Strength':
self.add_input('LnxFloatSocket', 'Strength')
elif self.property0 == 'Color':
self.add_input('LnxColorSocket', 'Color')
else:
self.add_input('LnxIntSocket', 'Resolution', default_value = 12)
property0: HaxeEnumProperty(
'property0',
items = [('Equidistant Samples', 'Equidistant Samples', 'Equidistant Samples'),
('Resolution', 'Resolution', 'Resolution'),
('Strength', 'Strength', 'Strength'),
('Color', 'Color', 'Color')],
name='', default='Equidistant Samples', update=update_sockets)
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxIntSocket', 'Equidistant Samples')
self.add_output('LnxNodeSocketAction', 'Out')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,52 @@
from lnx.logicnode.lnx_nodes import *
class SetCurveMeshNode(LnxLogicTreeNode):
"""Sets curve mesh."""
bl_idname = 'LNSetCurveMeshNode'
bl_label = 'Set Curve Mesh'
lnx_section = 'set'
lnx_version = 1
def update_sockets(self, context):
while len(self.inputs) > 2:
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Extrude':
self.add_input('LnxFloatSocket', 'Width', default_value = 0.1)
self.add_input('LnxFloatSocket', 'Thickness')
self.add_input('LnxFloatSocket', 'Start')
self.add_input('LnxFloatSocket', 'End', default_value = 1.0)
self.add_input('LnxBoolSocket', 'Fill Caps')
elif self.property0 == 'Bevel':
self.add_input('LnxFloatSocket', 'Depth', default_value = 0.1)
self.add_input('LnxIntSocket', 'Resolution')
self.add_input('LnxFloatSocket', 'Start')
self.add_input('LnxFloatSocket', 'End', default_value = 1.0)
self.add_input('LnxBoolSocket', 'Fill Caps')
else:
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Forward Axis', default_value = 'X')
self.add_input('LnxIntSocket', 'Repetitions', default_value = 1)
self.add_input('LnxFloatSocket', 'Start')
self.add_input('LnxFloatSocket', 'End', default_value = 1.0)
property0: HaxeEnumProperty(
'property0',
items = [('Extrude', 'Extrude', 'Extrude'),
('Bevel', 'Bevel', 'Bevel'),
('Deform', 'Deform', 'Deform')],
name='', default='Extrude', update=update_sockets)
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxFloatSocket', 'Width', default_value = 0.1)
self.add_input('LnxFloatSocket', 'Thickness')
self.add_input('LnxFloatSocket', 'Start')
self.add_input('LnxFloatSocket', 'End', default_value = 1.0)
self.add_input('LnxBoolSocket', 'Fill Caps')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketObject', 'Curve Mesh')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class SetCurveShapeKeyNode(LnxLogicTreeNode):
"""Sets shape key value of the curve"""
bl_idname = 'LNSetCurveShapeKeyNode'
bl_label = 'Set Curve Shape Key'
lnx_section = 'set'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxStringSocket', 'Shape Key')
self.add_input('LnxFloatSocket', 'Value')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,17 @@
from lnx.logicnode.lnx_nodes import *
class SetCurveSplineNode(LnxLogicTreeNode):
"""Sets curve spline."""
bl_idname = 'LNSetCurveSplineNode'
bl_label = 'Set Curve Spline'
lnx_section = 'set'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Curve')
self.add_input('LnxIntSocket', 'Spline Index')
self.add_input('LnxBoolSocket', 'Closed')
self.add_input('LnxIntSocket', 'Resolution', default_value = 12)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,3 @@
from lnx.logicnode.lnx_nodes import add_node_section
add_node_section(name='default', category='curve')

View File

@ -16,53 +16,22 @@ class DrawCameraNode(LnxLogicTreeNode):
bl_idname = 'LNDrawCameraNode'
bl_label = 'Draw Camera'
lnx_section = 'draw'
lnx_version = 2
min_inputs = 7
lnx_version = 4
num_choices: IntProperty(default=0, min=0)
def __init__(self, *args, **kwargs):
super(DrawCameraNode, self).__init__(*args, **kwargs)
array_nodes[str(id(self))] = self
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Start')
self.add_input('LnxNodeSocketAction', 'Stop')
self.add_sockets()
self.add_output('LnxNodeSocketAction', 'On Start')
self.add_output('LnxNodeSocketAction', 'On Stop')
def add_sockets(self):
self.num_choices += 1
self.add_input('LnxNodeSocketObject', 'Camera ' + str(self.num_choices))
self.add_input('LnxIntSocket', 'X')
self.add_input('LnxIntSocket', 'Y')
self.add_input('LnxIntSocket', 'Width')
self.add_input('LnxIntSocket', 'Height')
self.add_input('LnxBoolSocket', 'Paused')
def remove_sockets(self):
if self.num_choices > 1:
for _ in range(5):
self.inputs.remove(self.inputs.values()[-1])
self.num_choices -= 1
def draw_buttons(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_call_func', text='Add Camera', icon='PLUS', emboss=True)
op.node_index = str(id(self))
op.callback_name = 'add_sockets'
column = row.column(align=True)
op = column.operator('lnx.node_call_func', text='', icon='X', emboss=True)
op.node_index = str(id(self))
op.callback_name = 'remove_sockets'
if len(self.inputs) == self.min_inputs:
column.enabled = False
self.add_output('LnxNodeSocketAction', 'On Start')
self.add_output('LnxNodeSocketAction', 'On Stop')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -0,0 +1,49 @@
from lnx.logicnode.lnx_nodes import *
class DrawGifNode(LnxLogicTreeNode):
"""Draws an image.
@input Draw: Activate to draw the image on this frame. The input must
@input In:
be (indirectly) called from an `On Render2D` node.
@input Image: The filename of the image.
@input Color: The color that the image's pixels are multiplied with.
@input Left/Center/Right: Horizontal anchor point of the image.
0 = Left, 1 = Center, 2 = Right
@input Top/Middle/Bottom: Vertical anchor point of the image.
0 = Top, 1 = Middle, 2 = Bottom
@input X/Y: Position of the anchor point in pixels.
@input Width/Height: Size of the image in pixels.
@input Angle: Rotation angle in radians. Image will be rotated cloclwiswe
at the anchor point.
@output Out: Activated after the image has been drawn.
@see [`kha.graphics2.Graphics.drawImage()`](http://kha.tech/api/kha/graphics2/Graphics.html#drawImage).
"""
bl_idname = 'LNDrawGifNode'
bl_label = 'Draw Gif'
lnx_section = 'draw'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Draw')
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'Image File')
self.add_input('LnxColorSocket', 'Color', default_value=[1.0, 1.0, 1.0, 1.0])
self.add_input('LnxIntSocket', '0/1/2 = Left/Center/Right', default_value=0)
self.add_input('LnxIntSocket', '0/1/2 = Top/Middle/Bottom', default_value=0)
self.add_input('LnxFloatSocket', 'X')
self.add_input('LnxFloatSocket', 'Y')
self.add_input('LnxFloatSocket', 'Width')
self.add_input('LnxFloatSocket', 'Height')
self.add_input('LnxFloatSocket', 'Angle')
self.add_input('LnxIntSocket', 'Start Index')
self.add_input('LnxIntSocket', 'End Index', default_value=-1)
self.add_input('LnxFloatSocket', 'Frame Duration', default_value=1.0)
self.add_input('LnxBoolSocket', 'Loop', default_value=True)
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxIntSocket', 'Total Frames')
self.add_output('LnxIntSocket', 'Index')

View File

@ -50,5 +50,6 @@ class DrawImageRenderNode(LnxLogicTreeNode):
self.add_input('LnxFloatSocket', 'sHeight')
self.add_input('LnxFloatSocket', 'Angle')
self.add_input('LnxBoolSocket', 'Render2D')
self.add_input('LnxBoolSocket', 'Continuous')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -27,6 +27,20 @@ class DrawStringNode(LnxLogicTreeNode):
lnx_section = 'draw'
lnx_version = 3
property1: HaxeEnumProperty(
'property1',
items = [('TextLeft', 'Hor. Align. Left', 'Hor. Align. Left'),
('TextCenter', 'Hor. Align. Center', 'Hor. Align. Center'),
('TextRight', 'Hor. Align. Right', 'Hor. Align. Right'),],
name='', default='TextLeft')
property2: HaxeEnumProperty(
'property2',
items = [('TextTop', 'Ver. Align. Top', 'Ver. Align. Top'),
('TextMiddle', 'Ver. Align. Middle', 'Ver. Align. Middle'),
('TextBottom', 'Ver. Align. Bottom', 'Ver. Align. Bottom'),],
name='', default='TextTop')
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Draw')
self.add_input('LnxStringSocket', 'String')
@ -41,6 +55,10 @@ class DrawStringNode(LnxLogicTreeNode):
self.add_output('LnxFloatSocket', 'Width')
self.add_output('LnxFloatSocket', 'Height')
def draw_buttons(self, context, layout):
layout.prop(self, 'property1')
layout.prop(self, 'property2')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1, 2):
raise LookupError()

View File

@ -0,0 +1,17 @@
from lnx.logicnode.lnx_nodes import *
class ReorderRender2DNode(LnxLogicTreeNode):
bl_idname = 'LNReorderRender2DNode'
bl_label = 'Reorder Render2D'
lnx_section = 'draw'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Render2D')
self.add_input('LnxIntSocket', 'Index')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxIntSocket', 'Index')

View File

@ -8,7 +8,18 @@ class OnRender2DNode(LnxLogicTreeNode):
"""
bl_idname = 'LNOnRender2DNode'
bl_label = 'On Render2D'
lnx_version = 1
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxIntSocket', 'Index')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxDynamicSocket', 'Render2D')
self.add_output('LnxIntSocket', 'Index', default_value = -1)
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -4,6 +4,7 @@ class GetGamepadStartedNode(LnxLogicTreeNode):
"""."""
bl_idname = 'LNGetGamepadStartedNode'
bl_label = 'Get Gamepad Started'
lnx_section = 'gamepad'
lnx_version = 1
def lnx_init(self, context):

View File

@ -0,0 +1,45 @@
from lnx.logicnode.lnx_nodes import *
class GetImageColorNode(LnxLogicTreeNode):
"""Gets Color of the pixel in X,Y position of: input Image, Render, Render2D and Render+Render2D.
@input X: pixel position regarding width.
@input Y: pixel position regarding height.
@output Color: Color of the pixel in X,Y position.
WARNING: Calling getPixels() on a renderTarget with non-standard non-POT dimensions
can cause a system crash. Ensure renderTarget resolution is a power of two
(e.g., 256x256) or a common standard resolution (e.g., 1920x1080).
"""
bl_idname = 'LNGetImageColorNode'
bl_label = 'Get Image Color'
lnx_section = 'image'
lnx_version = 1
def remove_extra_inputs(self, context):
while len(self.inputs) > 0:
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Image':
self.add_input('LnxStringSocket', 'Image')
self.add_input('LnxIntSocket', 'X')
self.add_input('LnxIntSocket', 'Y')
property0: HaxeEnumProperty(
'property0',
items = [('Image', 'Image', 'Image'),
('Render2D', 'Render2D', 'Render2D'),
('Render', 'Render', 'Render'),
('Render&Render2D', 'Render&Render2D', 'Render&Render2D')],
name='', default='Image', update=remove_extra_inputs)
def lnx_init(self, context):
self.add_input('LnxStringSocket', 'Image')
self.add_input('LnxIntSocket', 'X')
self.add_input('LnxIntSocket', 'Y')
self.add_output('LnxColorSocket', 'Color')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,30 @@
from lnx.logicnode.lnx_nodes import *
class GetKeyboardNode(LnxLogicTreeNode):
bl_idname = 'LNGetKeyboardNode'
bl_label = 'Get Keyboard'
lnx_section = 'keyboard'
lnx_version = 1
def update(self):
self.label = f'{self.bl_label}: {self.property0}'
def upd(self, context):
self.label = f'{self.bl_label}: {self.property0}'
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The keyboard button starts to be pressed'),
('down', 'Down', 'The keyboard button is pressed'),
('released', 'Released', 'The keyboard button stops being pressed')],
name='', default='started', update=upd)
def lnx_init(self, context):
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxStringSocket', 'Key')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
def draw_label(self) -> str:
return f'{self.bl_label}: {self.property0}'

View File

@ -4,6 +4,7 @@ class GetKeyboardStartedNode(LnxLogicTreeNode):
"""."""
bl_idname = 'LNGetKeyboardStartedNode'
bl_label = 'Get Keyboard Started'
lnx_section = 'keyboard'
lnx_version = 1
def lnx_init(self, context):

View File

@ -5,6 +5,7 @@ class GetMouseStartedNode(LnxLogicTreeNode):
"""."""
bl_idname = 'LNGetMouseStartedNode'
bl_label = 'Get Mouse Started'
lnx_section = 'mouse'
lnx_version = 2
property0: HaxeBoolProperty(

View File

@ -19,7 +19,7 @@ class KeyboardNode(LnxLogicTreeNode):
items = [('started', 'Started', 'The keyboard button starts to be pressed'),
('down', 'Down', 'The keyboard button is pressed'),
('released', 'Released', 'The keyboard button stops being pressed')],
name='', default='down', update=upd)
name='', default='started', update=upd)
property1: HaxeEnumProperty(
'property1',

View File

@ -26,7 +26,7 @@ class MouseNode(LnxLogicTreeNode):
('down', 'Down', 'The mouse button is pressed'),
('released', 'Released', 'The mouse button stops being pressed'),
('moved', 'Moved', 'Moved')],
name='', default='down', update=upd)
name='', default='started', update=upd)
property1: HaxeEnumProperty(
'property1',
items = [('left', 'Left', 'Left mouse button'),

View File

@ -0,0 +1,26 @@
from lnx.logicnode.lnx_nodes import *
class SetVirtualButtonNode(LnxLogicTreeNode):
bl_idname = 'LNSetVirtualButtonNode'
bl_label = 'Set Virtual Button'
lnx_section = 'virtual'
lnx_version = 1
property0: bpy.props.EnumProperty(
items=[('Keyboard', 'Keyboard', 'Keyboard'),
('Mouse', 'Mouse', 'Mouse'),
('Gamepad', 'Gamepad', 'Gamepad')],
name='Input',
default='Keyboard'
)
property1: bpy.props.StringProperty(name='Virtual Button', default='')
property2: bpy.props.StringProperty(name='Physical Button', default='')
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_output('LnxNodeSocketAction', 'Out')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
layout.prop(self, 'property1')
layout.prop(self, 'property2')

View File

@ -13,7 +13,7 @@ class SurfaceNode(LnxLogicTreeNode):
('down', 'Down', 'The screen surface is touched'),
('released', 'Released', 'The screen surface stops being touched'),
('moved', 'Moved', 'Moved')],
name='', default='down')
name='', default='started')
def lnx_init(self, context):
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class GetAreaLightDataNode(LnxLogicTreeNode):
"""Gets the data of the given area light."""
bl_idname = 'LNGetAreaLightDataNode'
bl_label = 'Get Area Light Data'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Light')
self.add_output('LnxFloatSocket', 'Size X')
self.add_output('LnxFloatSocket', 'Size Y')

View File

@ -0,0 +1,15 @@
from lnx.logicnode.lnx_nodes import *
class GetLightDataNode(LnxLogicTreeNode):
"""Get lights data info."""
bl_idname = 'LNGetLightDataNode'
bl_label = 'Get Light Data'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Light')
self.add_output('LnxIntSocket', 'Type')
self.add_output('LnxFloatSocket', 'Strength')
self.add_output('LnxColorSocket', 'Color')
self.add_output('LnxBoolSocket', 'Shadow')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class GetSpotLightDataNode(LnxLogicTreeNode):
"""Gets the data of the given spot light."""
bl_idname = 'LNGetSpotLightDataNode'
bl_label = 'Get Spot Light Data'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Light')
self.add_output('LnxFloatSocket', 'Size')
self.add_output('LnxFloatSocket', 'Blend')

View File

@ -286,7 +286,7 @@ class LnxRotationSocket(LnxCustomSocket):
default_value_unit: EnumProperty(
items=[('Deg', 'Degrees', 'Degrees'),
('Rad', 'Radians', 'Radians')],
name='', default='Rad',
name='', default='Deg',
update=on_unit_update)
default_value_order: EnumProperty(
items=[('XYZ','XYZ','XYZ'),

View File

@ -0,0 +1,42 @@
from lnx.logicnode.lnx_nodes import *
class AsyncLoopNode(LnxLogicTreeNode):
"""Resembles a for-loop (`for (i in from...to)`) that is executed at
once when this node is activated.
@seeNode While
@seeNode Loop Break
@input From: The value to start the loop from (inclusive)
@input To: The value to end the loop at (exclusive)
@output Loop: Active at every iteration of the loop
@output Index: The index for the current iteration
@output Done: Activated once when the looping is done
"""
bl_idname = 'LNAsyncLoopNode'
bl_label = 'Async Loop'
bl_description = 'Resembles a for-loop that is executed at once when this node is activated'
lnx_section = 'flow'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxIntSocket', 'From')
self.add_input('LnxIntSocket', 'To')
self.add_input('LnxIntSocket', 'Async Items')
self.add_output('LnxNodeSocketAction', 'Loop')
self.add_output('LnxIntSocket', 'Index')
self.add_output('LnxNodeSocketAction', 'Done')
def draw_label(self) -> str:
inp_from = self.inputs['From']
inp_to = self.inputs['To']
if inp_from.is_linked and inp_to.is_linked:
return self.bl_label
val_from = 'x' if inp_from.is_linked else inp_from.default_value_raw
val_to = 'y' if inp_to.is_linked else inp_to.default_value_raw
return f'{self.bl_label}: {val_from}...{val_to}'

View File

@ -16,7 +16,7 @@ class CallFunctionNode(LnxLogicTreeNode):
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxDynamicSocket', 'Trait/Any')
self.add_input('LnxDynamicSocket', 'Trait')
self.add_input('LnxStringSocket', 'Function')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -22,6 +22,8 @@ class SetCameraRenderFilterNode(LnxLogicTreeNode):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Camera')
self.add_input('LnxBoolSocket', 'Children', default_value=True)
self.add_input('LnxBoolSocket', 'Recursive', default_value=False)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,31 @@
from lnx.logicnode.lnx_nodes import *
class GetMaterialImageParamNode(LnxLogicTreeNode):
"""Set an image value material parameter to the specified object.
@seeNode Get Scene Root
@input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally.
@input Per Object:
- `Enabled`: Set material parameter specific to this object. Global parameter will be ignored.
- `Disabled`: Set parameter globally, including this object.
@input Material: Material whose parameter to be set.
@input Node: Name of the parameter.
@output Image: Name of the image.
"""
bl_idname = 'LNGetMaterialImageParamNode'
bl_label = 'Get Material Image Param'
lnx_section = 'params'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxBoolSocket', 'Per Object')
self.add_input('LnxDynamicSocket', 'Material')
self.add_input('LnxStringSocket', 'Node')
self.add_output('LnxStringSocket', 'Image')

View File

@ -0,0 +1,31 @@
from lnx.logicnode.lnx_nodes import *
class GetMaterialRgbParamNode(LnxLogicTreeNode):
"""Get a color or vector value material parameter to the specified object.
@seeNode Get Scene Root
@input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally.
@input Per Object:
- `Enabled`: Set material parameter specific to this object. Global parameter will be ignored.
- `Disabled`: Set parameter globally, including this object.
@input Material: Material whose parameter to be set.
@input Node: Name of the parameter.
@output Color: Color or vector input.
"""
bl_idname = 'LNGetMaterialRgbParamNode'
bl_label = 'Get Material RGB Param'
lnx_section = 'params'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxBoolSocket', 'Per Object')
self.add_input('LnxDynamicSocket', 'Material')
self.add_input('LnxStringSocket', 'Node')
self.add_output('LnxColorSocket', 'Color')

View File

@ -0,0 +1,31 @@
from lnx.logicnode.lnx_nodes import *
class GetMaterialValueParamNode(LnxLogicTreeNode):
"""Get a float value material parameter to the specified object.
@seeNode Get Scene Root
@input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally.
@input Per Object:
- `Enabled`: Set material parameter specific to this object. Global parameter will be ignored.
- `Disabled`: Set parameter globally, including this object.
@input Material: Material whose parameter to be set.
@input Node: Name of the parameter.
@output Float: float value.
"""
bl_idname = 'LNGetMaterialValueParamNode'
bl_label = 'Get Material Value Param'
lnx_section = 'params'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxBoolSocket', 'Per Object')
self.add_input('LnxDynamicSocket', 'Material')
self.add_input('LnxStringSocket', 'Node')
self.add_output('LnxFloatSocket', 'Float')

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class SetMaterialsNode(LnxLogicTreeNode):
"""TO DO."""
bl_idname = 'LNSetMaterialsNode'
bl_label = 'Set Object Materials'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketArray', 'Materials')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class ColorsMixNode(LnxLogicTreeNode):
"""
@see https://github.com/rvanwijnen/spectral.js 2023 Ronald van Wijnen.
"""
bl_idname = 'LNColorsMixNode'
bl_label = 'Colors Mix'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Colors')
self.add_input('LnxNodeSocketArray', 'Factors')
self.add_output('LnxColorSocket', 'Color')

View File

@ -38,7 +38,10 @@ class MathNode(LnxLogicTreeNode):
'Modulo': 2,
'Less Than': 2,
'Greater Than': 2,
'Ping-Pong': 2
'Ping-Pong': 2,
'Hyperbolic Sine': 1,
'Hyperbolic Cosine': 1,
'Hyperbolic Tangent': 1
}.get(operation_name, 0)
def get_enum(self):
@ -97,7 +100,10 @@ class MathNode(LnxLogicTreeNode):
('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')],
('Ping-Pong', 'Ping-Pong', 'The output value is moved between 0.0 and the Scale based on the input value'),
('Hyperbolic Sine', 'Hyperbolic Sine', 'Hyperbolic Sine'),
('Hyperbolic Cosine', 'Hyperbolic Cosine', 'Hyperbolic Cosine'),
('Hyperbolic Tangent', 'Hyperbolic Tangent', 'Hyperbolic Tangent')],
name='', default='Add', set=set_enum, get=get_enum)
property1: HaxeBoolProperty('property1', name='Clamp', default=False)

View File

@ -1,22 +0,0 @@
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')

View File

@ -0,0 +1,54 @@
from lnx.logicnode.lnx_nodes import *
class PoissonDiskNode(LnxLogicTreeNode):
"""
Generates an array of points with a poisson distribution.
SampleCube (Sample Rectangle):
topLeft: Minimum bounding corner position.
lowerRight: Maximum bounding corner position.
minimumDistance: Minimum required spatial distance between points.
pointsPerIteration: Candidate sample attempts per active point before rejection.
SampleSphere (Sample Circle):
center: Sphere center position vector.
radius: Bounding sphere radius.
minimumDistance: Minimum required spatial distance between points.
pointsPerIteration: Candidate sample attempts per active point before rejection.
"""
bl_idname = 'LNPoissonDiskNode'
bl_label = 'Poisson Disk Sampling'
lnx_section = 'generator'
lnx_version = 1
def remove_extra_inputs(self, context):
while len(self.inputs) > 3:
self.inputs.remove(self.inputs[-1])
if self.property0 in ('Sample Cube', 'Sample Rectangle'):
self.add_input('LnxVectorSocket', 'Top Left')
self.add_input('LnxVectorSocket', 'Lower Right')
else:
self.add_input('LnxVectorSocket', 'Center')
self.add_input('LnxFloatSocket', 'Radius')
property0: HaxeEnumProperty(
'property0',
items = [('Sample Cube', 'Sample Cube', 'Sample Cube'),
('Sample Sphere', 'Sample Sphere', 'Sample Sphere'),
('Sample Rectangle', 'Sample Rectangle', 'Sample Rectangle'),
('Sample Circle', 'Sample Circle', 'Sample Circle')],
name='', default='Sample Cube', update=remove_extra_inputs)
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxIntSocket', 'Points Per Iteration')
self.add_input('LnxFloatSocket', 'Minimum Distance')
self.add_input('LnxVectorSocket', 'Top Left')
self.add_input('LnxVectorSocket', 'Lower Right')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketArray', 'Points')
self.add_output('LnxIntSocket', 'Length')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,71 @@
from lnx.logicnode.lnx_nodes import *
class ProceduralNoiseNode(LnxLogicTreeNode):
"""
Generates different kind of noises values.
repeat: Periodicity interval for seamless tiling. -1 disables repeating.
Perlin:
x: Spatial X coordinate.
y: Spatial Y coordinate.
z: Spatial Z coordinate.
OctavePerlin:
x, y, z: Spatial coordinates.
octaves: Number of noise layers combined.
persistence: Amplitude multiplier per octave (decay factor).
frequency: Base spatial scaling factor.
DiamondSquare:
width: Total grid width.
height: Total grid height.
featureSize: Initial step size for grid subdivision.
scale: Random offset amplitude scale factor.
randFunc: Random number generator function returning a Float.
"""
bl_idname = 'LNProceduralNoiseNode'
bl_label = 'Procedural Noise'
lnx_section = 'generator'
lnx_version = 1
def remove_extra_inputs(self, context):
while len(self.inputs) > 1:
self.inputs.remove(self.inputs[-1])
if self.property0 in ('Perlin', 'Octave Perlin'):
self.add_input('LnxFloatSocket', 'X')
self.add_input('LnxFloatSocket', 'Y')
self.add_input('LnxFloatSocket', 'Z')
self.add_input('LnxIntSocket', 'Repeat', default_value = -1)
if self.property0 == 'Octave Perlin':
self.add_input('LnxIntSocket', 'Octaves', default_value = 4)
self.add_input('LnxFloatSocket', 'Persistence', default_value = 0.5)
self.add_input('LnxFloatSocket', 'Frequency', default_value = 1.0)
if self.property0 == 'Diamond Square':
self.add_input('LnxIntSocket', 'X')
self.add_input('LnxIntSocket', 'Y')
self.add_input('LnxIntSocket', 'Width', default_value = 3)
self.add_input('LnxIntSocket', 'Height', default_value = 3)
self.add_input('LnxIntSocket', 'Feature Size', default_value = 2)
self.add_input('LnxFloatSocket', 'Scale', default_value = 1.0)
self.add_input('LnxFloatSocket', 'Offset', default_value = 0.5)
property0: HaxeEnumProperty(
'property0',
items = [('Perlin', 'Perlin', 'Perlin 3D'),
('Octave Perlin', 'Octave Perlin', 'Octave Perlin 3D'),
('Diamond Square', 'Diamond Square', 'Diamond Square 2D')],
name='', default='Perlin', update=remove_extra_inputs)
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxFloatSocket', 'X')
self.add_input('LnxFloatSocket', 'Y')
self.add_input('LnxFloatSocket', 'Z')
self.add_input('LnxIntSocket', 'Repeat', default_value = -1)
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxFloatSocket', 'Value')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -45,7 +45,13 @@ class TweenFloatNode(LnxLogicTreeNode):
('CircInOut', 'CircInOut', 'CircInOut'),
('BackIn', 'BackIn', 'BackIn'),
('BackOut', 'BackOut', 'BackOut'),
('BackInOut', 'BackInOut', 'BackInOut')],
('BackInOut', 'BackInOut', 'BackInOut'),
('BounceIn', 'BounceIn', 'BounceIn'),
('BounceOut', 'BounceOut', 'BounceOut'),
('BounceInOut', 'BounceInOut', 'BounceInOut'),
('ElasticIn', 'ElasticIn', 'ElasticIn'),
('ElasticOut', 'ElasticOut', 'ElasticOut'),
('ElasticInOut', 'ElasticInOut', 'ElasticInOut')],
name='', default='Linear')
def lnx_init(self, context):

View File

@ -45,7 +45,13 @@ class TweenFloatNode(LnxLogicTreeNode):
('CircInOut', 'CircInOut', 'CircInOut'),
('BackIn', 'BackIn', 'BackIn'),
('BackOut', 'BackOut', 'BackOut'),
('BackInOut', 'BackInOut', 'BackInOut')],
('BackInOut', 'BackInOut', 'BackInOut'),
('BounceIn', 'BounceIn', 'BounceIn'),
('BounceOut', 'BounceOut', 'BounceOut'),
('BounceInOut', 'BounceInOut', 'BounceInOut'),
('ElasticIn', 'ElasticIn', 'ElasticIn'),
('ElasticOut', 'ElasticOut', 'ElasticOut'),
('ElasticInOut', 'ElasticInOut', 'ElasticInOut')],
name='', default='Linear')
def lnx_init(self, context):

View File

@ -45,7 +45,13 @@ class TweenTransformNode(LnxLogicTreeNode):
('CircInOut', 'CircInOut', 'CircInOut'),
('BackIn', 'BackIn', 'BackIn'),
('BackOut', 'BackOut', 'BackOut'),
('BackInOut', 'BackInOut', 'BackInOut')],
('BackInOut', 'BackInOut', 'BackInOut'),
('BounceIn', 'BounceIn', 'BounceIn'),
('BounceOut', 'BounceOut', 'BounceOut'),
('BounceInOut', 'BounceInOut', 'BounceInOut'),
('ElasticIn', 'ElasticIn', 'ElasticIn'),
('ElasticOut', 'ElasticOut', 'ElasticOut'),
('ElasticInOut', 'ElasticInOut', 'ElasticInOut')],
name='', default='Linear')
def lnx_init(self, context):

View File

@ -45,7 +45,13 @@ class TweenVectorNode(LnxLogicTreeNode):
('CircInOut', 'CircInOut', 'CircInOut'),
('BackIn', 'BackIn', 'BackIn'),
('BackOut', 'BackOut', 'BackOut'),
('BackInOut', 'BackInOut', 'BackInOut')],
('BackInOut', 'BackInOut', 'BackInOut'),
('BounceIn', 'BounceIn', 'BounceIn'),
('BounceOut', 'BounceOut', 'BounceOut'),
('BounceInOut', 'BounceInOut', 'BounceInOut'),
('ElasticIn', 'ElasticIn', 'ElasticIn'),
('ElasticOut', 'ElasticOut', 'ElasticOut'),
('ElasticInOut', 'ElasticInOut', 'ElasticInOut')],
name='', default='Linear')
def lnx_init(self, context):

View File

@ -6,7 +6,7 @@ class WorldToScreenSpaceNode(LnxLogicTreeNode):
bl_idname = 'LNWorldToScreenSpaceNode'
bl_label = 'World to Screen Space'
lnx_section = 'matrix'
lnx_version = 2
lnx_version = 3
def remove_extra_inputs(self, context):
while len(self.inputs) > 1:
@ -24,12 +24,14 @@ class WorldToScreenSpaceNode(LnxLogicTreeNode):
self.add_input('LnxVectorSocket', 'World')
self.add_output('LnxVectorSocket', 'Screen')
self.add_output('LnxIntSocket', 'X')
self.add_output('LnxIntSocket', 'Y')
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):
if self.lnx_version not in (0, 1, 2):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -5,3 +5,4 @@ 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')
add_node_section(name='generator', category='Math')

View File

@ -4,8 +4,17 @@ class TimeNode(LnxLogicTreeNode):
"""Returns the application execution time and the delta time."""
bl_idname = 'LNTimeNode'
bl_label = 'Get Application Time'
lnx_version = 1
lnx_section = 'time'
lnx_version = 2
def lnx_init(self, context):
self.add_output('LnxFloatSocket', 'Time')
self.add_output('LnxFloatSocket', 'Delta')
self.add_output('LnxFloatSocket', 'RealTime')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -1,7 +1,7 @@
from lnx.logicnode.lnx_nodes import *
class GetDebugConsoleSettings(LnxLogicTreeNode):
class GetDebugConsoleSettingsNode(LnxLogicTreeNode):
"""Return properties of the debug console.
@output Enabled: Whether the debug console is enabled.
@ -17,6 +17,7 @@ class GetDebugConsoleSettings(LnxLogicTreeNode):
"""
bl_idname = 'LNGetDebugConsoleSettings'
bl_label = 'Get Debug Console Settings'
lnx_section = 'debug'
lnx_version = 2
def lnx_init(self, context):

View File

@ -1,9 +1,10 @@
from lnx.logicnode.lnx_nodes import *
class SetDebugConsoleSettings(LnxLogicTreeNode):
class SetDebugConsoleSettingsNode(LnxLogicTreeNode):
"""Sets the debug console settings."""
bl_idname = 'LNSetDebugConsoleSettings'
bl_label = 'Set Debug Console Settings'
lnx_section = 'debug'
lnx_version = 1
property0: HaxeEnumProperty(

View File

@ -4,6 +4,7 @@ class SetTimeScaleNode(LnxLogicTreeNode):
"""Sets the global time scale."""
bl_idname = 'LNSetTimeScaleNode'
bl_label = 'Set Time Scale'
lnx_section = 'time'
lnx_version = 1
def lnx_init(self, context):

View File

@ -5,6 +5,7 @@ class SleepNode(LnxLogicTreeNode):
through the incoming signal."""
bl_idname = 'LNSleepNode'
bl_label = 'Sleep'
lnx_section = 'time'
lnx_version = 1
def lnx_init(self, context):

View File

@ -24,6 +24,7 @@ class TimerNode(LnxLogicTreeNode):
"""
bl_idname = 'LNTimerNode'
bl_label = 'Timer'
lnx_section = 'time'
lnx_version = 1
def lnx_init(self, context):

View File

@ -0,0 +1,11 @@
from lnx.logicnode.lnx_nodes import *
class GetAssetsNode(LnxLogicTreeNode):
""""""
bl_idname = 'LNGetAssetsNode'
bl_label = 'Get Assets'
lnx_section = 'Assets'
lnx_version = 1
def lnx_init(self, context):
self.add_output('LnxNodeSocketArray', 'Assets')

View File

@ -0,0 +1,43 @@
from lnx.logicnode.lnx_nodes import *
class WriteGifNode(LnxLogicTreeNode):
"""Writes the given image to the given file. If the image
already exists, the existing content of the image is overwritten.
Aspect ratio must match display resolution ratio.
Render2D flag to include render draws.
@input Image File: the name of the image
@input Camera: the render target image of the camera to write to the image file.
@input Width: width of the image file.
@input Height: heigth of the image file.
@input sX: sub position of first x pixel of the sub image (0 for start).
@input sY: sub position of first y pixel of the sub image (0 for start).
@input sWidth: width of the sub image.
@input sHeight: height of the sub image.
@input Render2D: include Render 2D draws.
@seeNode Read File
"""
bl_idname = 'LNWriteGifNode'
bl_label = 'Write Gif'
lnx_section = 'file'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Start')
self.add_input('LnxNodeSocketAction', 'Stop')
self.add_input('LnxStringSocket', 'Image File')
self.add_input('LnxNodeSocketObject', 'Camera')
self.add_input('LnxIntSocket', 'Width')
self.add_input('LnxIntSocket', 'Height')
self.add_input('LnxIntSocket', 'sX')
self.add_input('LnxIntSocket', 'sY')
self.add_input('LnxIntSocket', 'sWidth')
self.add_input('LnxIntSocket', 'sHeight')
self.add_input('LnxBoolSocket', 'Render2D')
self.add_input('LnxFloatSocket', 'Frame duration')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class AroundNavigableLocationNode(LnxLogicTreeNode):
"""A random around a navigable location in the navmesh."""
bl_idname = 'LNAroundNavigableLocationNode'
bl_label = 'Around NavMesh Location'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxStringSocket', 'NavMeshId', default_value = 'NavMesh')
self.add_input('LnxVectorSocket', 'Position')
self.add_input('LnxFloatSocket', 'Radius')
self.add_output('LnxVectorSocket', 'Location')

View File

@ -4,17 +4,26 @@ class GoToLocationNode(LnxLogicTreeNode):
"""Makes a NavCrowd agent go to location.
@input In: Start navigation.
@input NavMeshId: The ID of the NavMesh to use.
@input Object: The object to navigate. Object must have `NavCrowd` trait applied.
@input Location: Closest point on the navmesh to navigate to.
@input Max Speed: Maximum speed of the crowd agent.
@input Max Accelaration: Maximum acceleration of the agent.
@input Turn Speed: Turn rate in range (0, 1).
"""
bl_idname = 'LNCrowdGoToLocationNode'
bl_label = 'Crowd Go to Location'
lnx_section = 'crowd'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'NavMeshId', default_value = 'NavMesh')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxVectorSocket', 'Location')
self.add_input('LnxFloatSocket', 'Max Speed', 5.0)
self.add_input('LnxFloatSocket', 'Max Accelaration', 100)
self.add_input('LnxFloatSocket', 'Turn Speed', 0.1)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,21 @@
from lnx.logicnode.lnx_nodes import *
class CrowdSetLocationNode(LnxLogicTreeNode):
"""Makes a NavCrowd agent go to location.
@input In: Start teleport.
@input Object: The object to navigate. Object must have `NavCrowd` trait applied.
@input Location: Closest point on the navmesh to navigate to.
"""
bl_idname = 'LNCrowdSetLocationNode'
bl_label = 'Crowd Set Location'
lnx_section = 'crowd'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'NavMeshId', default_value = 'NavMesh')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxVectorSocket', 'Location')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -4,10 +4,13 @@ class GetAgentDataNode(LnxLogicTreeNode):
"""Gets the speed and turn duration of the agent"""
bl_idname = 'LNGetAgentDataNode'
bl_label = 'Get Agent Data'
lnx_version = 1
lnx_section = 'agent'
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxStringSocket', 'NavMesh ID')
self.add_output('LnxFloatSocket', 'Speed')
self.add_output('LnxFloatSocket', 'Turn Duration')
self.add_output('LnxFloatSocket', 'Turn Duration')
self.add_output('LnxNodeSocketArray', 'Path')

View File

@ -0,0 +1,21 @@
from lnx.logicnode.lnx_nodes import *
class GetCrowdDataNode(LnxLogicTreeNode):
"""Gets the speed and position of the crowd"""
bl_idname = 'LNGetCrowdDataNode'
bl_label = 'Get Crowd Data'
lnx_section = 'crowd'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxStringSocket', 'NavMeshId')
self.add_output('LnxVectorSocket', 'Speed')
self.add_output('LnxFloatSocket', 'Max Speed')
self.add_output('LnxFloatSocket', 'Max Acceleration')
self.add_output('LnxFloatSocket', 'Turn Speed')
self.add_output('LnxVectorSocket', 'Location')
self.add_output('LnxVectorSocket', 'Target Location')
self.add_output('LnxIntSocket', 'Id')
self.add_output('LnxNodeSocketArray', 'Path')

View File

@ -4,37 +4,28 @@ class GoToLocationNode(LnxLogicTreeNode):
"""Makes a NavMesh agent go to location.
@input In: Start navigation.
@input NavMeshId: The ID of the NavMesh to use.
@input Object: The object to navigate. Object must have `NavAgent` trait applied.
@input Location: Closest point on the navmesh to navigate to.
@input Speed: Rate of movement.
@input Turn Duration: Rate of turn.
@input Height Offset: Height of the object from the navmesh.
@input Use Raycast: Use physics ray cast to get more precise z positioning.
@input Ray Cast Depth: Depth of ray cast from the object origin.
@input Ray Cast Mask: Ray cast mask for collision detection.
@output Out: Executed immidiately after start of the navigation.
@output Tick Position: Executed at every step of navigation translation.
@output Tick Rotation: Executed at every step of navigation rotation.
"""
bl_idname = 'LNGoToLocationNode'
bl_label = 'Go to Location'
bl_label = 'Agent Go to Location'
lnx_section = 'agent'
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'NavMeshId', default_value = 'NavMesh')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxVectorSocket', 'Location')
self.add_input('LnxFloatSocket', 'Speed', 5.0)
self.add_input('LnxFloatSocket', 'Turn Duration', 0.4)
self.add_input('LnxFloatSocket', 'Height Offset', 0.0)
self.add_input('LnxBoolSocket','Use Raycast')
self.add_input('LnxFloatSocket', 'Ray Cast Depth', -5.0)
self.add_input('LnxIntSocket', 'Ray Cast Mask', 1)
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketAction', 'Tick Position')
self.add_output('LnxNodeSocketAction', 'Tick Rotation')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):

View File

@ -1,10 +1,12 @@
from lnx.logicnode.lnx_nodes import *
class NavigableLocationNode(LnxLogicTreeNode):
"""TO DO."""
"""A random navigable location in the navmesh."""
bl_idname = 'LNNavigableLocationNode'
bl_label = 'Navigable Location'
lnx_version = 1
bl_label = 'Random NavMesh Location'
lnx_version = 2
def lnx_init(self, context):
self.add_output('LnxDynamicSocket', 'Location')
self.add_input('LnxNodeSocketObject', 'NavMesh')
self.add_output('LnxVectorSocket', 'Location')

View File

@ -1,13 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class PickLocationNode(LnxLogicTreeNode):
"""Pick a location coordinates in the given NavMesh."""
"""Pick a location on the given NavMesh using screen coordinates."""
bl_idname = 'LNPickLocationNode'
bl_label = 'Pick NavMesh Location'
lnx_version = 1
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'NavMesh')
self.add_input('LnxVectorSocket', 'Screen Coords')
self.add_input('LnxIntSocket', 'X')
self.add_input('LnxIntSocket', 'Y')
self.add_output('LnxVectorSocket', 'Location')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class ReconstructNavMeshNode(LnxLogicTreeNode):
"""Reconstruct a given NavMesh."""
bl_idname = 'LNReconstructNavMeshNode'
bl_label = 'Reconstruct NavMesh'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxStringSocket', 'NavMeshId', default_value = 'NavMesh')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -4,6 +4,7 @@ class StopAgentNode(LnxLogicTreeNode):
"""Stops the given NavMesh agent."""
bl_idname = 'LNStopAgentNode'
bl_label = 'Stop Agent'
lnx_section = 'agent'
lnx_version = 1
def lnx_init(self, context):

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class StopCrowdNode(LnxLogicTreeNode):
"""Stops the given NavMesh agent."""
bl_idname = 'LNStopCrowdNode'
bl_label = 'Stop Crowd'
lnx_section = 'crowd'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,39 @@
from lnx.logicnode.lnx_nodes import *
class ConvexBreakNode(LnxLogicTreeNode):
"""Breaks a convex object."""
bl_idname = 'LNConvexBreakNode'
bl_label = 'Convex Break'
lnx_version = 1
def update_sockets(self, context):
while len(self.inputs) > 2:
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Impact':
self.add_input('LnxVectorSocket', 'Impact Point')
self.add_input('LnxVectorSocket', 'Impact Normal')
else:
self.add_input('LnxBoolSocket', 'Random Plane')
self.add_input('LnxVectorSocket', 'Plane')
self.add_input('LnxFloatSocket', 'Scale UV')
self.add_input('LnxBoolSocket', 'Flat Shading', default_value = True)
property0: HaxeEnumProperty(
'property0',
items = [('Impact', 'Subdivide by Impact', 'Subdivide by Impact'),
('Plane', 'Subdivide by Plane', 'Subdivide by Plane')],
name='', default='Impact', update=update_sockets)
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketAction', 'True')
self.add_output('LnxNodeSocketAction', 'False')
self.add_output('LnxNodeSocketArray', 'Array')
self.update_sockets(context)
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class GetObjectCameraDataNode(LnxLogicTreeNode):
"""Returns the camera data of the given object."""
bl_idname = 'LNGetObjectCameraDataNode'
bl_label = 'Get Object Camera Data'
lnx_section = 'props'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxFloatSocket', 'Camera Distance')
self.add_output('LnxFloatSocket', 'Screen Size')

View File

@ -1,26 +1,42 @@
from lnx.logicnode.lnx_nodes import *
class GetChildNode(LnxLogicTreeNode):
"""Returns the child of the given object by the child object's name."""
"""Returns the child of the given object by name or by a specific type."""
bl_idname = 'LNGetChildNode'
bl_label = 'Get Object Child'
lnx_section = 'relations'
lnx_version = 1
def sw_update(self, context):
self.inputs[1].hide = (self.property0 == 'By Type')
property0: HaxeEnumProperty(
'property0',
items = [('By Name', 'By Name', 'By Name'),
('Contains', 'Contains', 'Contains'),
('Starts With', 'Starts With', 'Starts With'),
('Ends With', 'Ends With', 'Ends With'),
],
name='', default='By Name')
('By Type', 'By Type', 'By Type')],
name='Method', default='By Name', update=sw_update)
property1: HaxeEnumProperty(
'property1',
items = [('MeshObject', 'Mesh', 'MeshObject'),
('CameraObject', 'Camera', 'CameraObject'),
('LightObject', 'Light', 'LightObject'),
('SpeakerObject', 'Speaker', 'SpeakerObject'),
('DecalObject', 'Decal', 'DecalObject'),
('ProbeObject', 'Probe', 'ProbeObject'),
('CurveObject', 'Curve', 'CurveObject')],
name='Type', default='MeshObject')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Parent')
self.add_input('LnxStringSocket', 'Child Name')
self.add_output('LnxNodeSocketObject', 'Child')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
if self.property0 == 'By Type':
layout.prop(self, 'property1')

View File

@ -11,3 +11,4 @@ class GetChildrenNode(LnxLogicTreeNode):
self.add_input('LnxNodeSocketObject', 'Parent')
self.add_output('LnxNodeSocketArray', 'Children')
self.add_output('LnxIntSocket', 'Length')

View File

@ -16,10 +16,16 @@ class GetObjectGeomNode(LnxLogicTreeNode):
"""
bl_idname = 'LNGetObjectGeomNode'
bl_label = 'Get Object Geometry Node'
bl_label = 'Get Object Geometry'
lnx_section = 'relations'
lnx_version = 1
property0: HaxeEnumProperty(
'property0',
items = [('Local', 'Local', 'Local'),
('Global', 'Global', 'Global')],
name='', default='Local', update='')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
@ -28,5 +34,8 @@ class GetObjectGeomNode(LnxLogicTreeNode):
self.add_output('LnxNodeSocketArray', 'Vertices Indices')
self.add_output('LnxNodeSocketArray', 'Vertices Material Indices')
self.add_output('LnxNodeSocketArray', 'Vertices Face Indices')
self.add_output('LnxDynamicSocket', 'Vertex Groups Maps')
self.add_output('LnxNodeSocketArray', 'Vertex Groups Names')
self.add_output('LnxDynamicSocket', 'Vertex Groups Map')
self.add_output('LnxNodeSocketArray', 'Vertex Groups Names')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class GetObjectInstancedNode(LnxLogicTreeNode):
"""Gets the object instanced from an array of LOC, ROT and SCL."""
bl_idname = 'LNGetObjectInstancedNode'
bl_label = 'Get Object Instanced'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxBoolSocket', 'Instanced', default_value=True)
self.add_output('LnxIntSocket', 'Type')
self.add_output('LnxIntSocket', 'Count')
self.add_output('LnxIntSocket', 'Stride')
self.add_output('LnxNodeSocketArray', 'Array Instanced')

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class SetObjectShapeKeyNode(LnxLogicTreeNode):
"""Gets shape key value of the object"""
bl_idname = 'LNGetObjectShapeKeyNode'
bl_label = 'Get Object Shape Key'
lnx_section = 'props'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Shape Key')
self.add_output('LnxFloatSocket', 'Value')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class SampleObjectPointsNode(LnxLogicTreeNode):
"""
Samples points and surface normals across a mesh object using a low-discrepancy distribution.
"""
bl_idname = 'LNSampleObjectPointsNode'
bl_label = 'Sample Object Points'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxIntSocket', 'Sample')
self.add_output('LnxNodeSocketArray', 'Point/Normal')

View File

@ -0,0 +1,27 @@
from lnx.logicnode.lnx_nodes import *
class SetObjectInstancedNode(LnxLogicTreeNode):
"""Sets the object instanced from an array of LOC, ROT and SCL."""
bl_idname = 'LNSetObjectInstancedNode'
bl_label = 'Set Object Instanced'
lnx_version = 1
property0: HaxeEnumProperty(
'property0',
items = [('1', 'Loc', 'Instances use their unique position (ipos)'),
('2', 'Loc + Rot', 'Instances use their unique position and rotation (ipos and irot)'),
('3', 'Loc + Scale', 'Instances use their unique position and scale (ipos and iscl)'),
('4', 'Loc + Rot + Scale', 'Instances use their unique position, rotation and scale (ipos, irot, iscl)')],
name='', default='1', update='')
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketArray', 'Array Instanced')
self.add_input('LnxBoolSocket', 'Include original', default_value=False)
self.add_input('LnxBoolSocket', 'Dynamic Usage', default_value=False)
self.add_output('LnxNodeSocketAction', 'Out')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -18,7 +18,7 @@ class SetParentNode(LnxLogicTreeNode):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Parent')
self.add_input('LnxBoolSocket', 'Keep Transform', default_value = True)
self.add_input('LnxBoolSocket', 'Parent Inverse')
self.add_input('LnxBoolSocket', 'Parent Inverse', default_value = True)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -19,7 +19,7 @@ class SetVisibleNode(LnxLogicTreeNode):
property0: HaxeEnumProperty(
'property0',
items = [('object', 'Object', 'All object componenets visibility'),
items = [('object', 'Object', 'All object components visibility'),
('mesh', 'Mesh', 'Mesh visibility only'),
('shadow', 'Shadow', 'Shadow visibility only'),
],

View File

@ -0,0 +1,17 @@
from lnx.logicnode.lnx_nodes import *
class CurveGuideNode(LnxLogicTreeNode):
"""Sets the curves guide of the given particle source."""
bl_idname = 'LNCurveGuideNode'
bl_label = 'Curve Guide'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxIntSocket', 'Slot')
self.add_input('LnxNodeSocketArray', 'Curve Array')
self.add_input('LnxFloatSocket', 'Strengh', default_value = 1.0)
self.add_input('LnxFloatSocket', 'Speed', default_value = 1.0)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -3,7 +3,7 @@ from lnx.logicnode.lnx_nodes import *
class GetParticleNode(LnxLogicTreeNode):
"""Returns the Particle Systems of an object."""
bl_idname = 'LNGetParticleNode'
bl_label = 'Get Particle'
bl_label = 'Get Particle Systems'
lnx_version = 1
def lnx_init(self, context):

View File

@ -32,3 +32,4 @@ class GetParticleDataNode(LnxLogicTreeNode):
self.outputs.new('LnxFloatSocket', 'Lap')
self.outputs.new('LnxFloatSocket', 'Lap Time')
self.outputs.new('LnxIntSocket', 'Count')
self.outputs.new('LnxNodeSocketObject', 'Particle')

View File

@ -15,7 +15,7 @@ class AddRigidBodyNode(LnxLogicTreeNode):
@input Animated: Rigid body follows animation and will affect other active non-animated rigid bodies.
@input Trigger: Rigid body behaves as a trigger and detects collision. However, rigd body does not contribute to or receive collissions.
@input Trigger: Rigid body behaves as a trigger and detects collision. However, rigid body does not contribute to or receive collissions.
@input Friction: Surface friction of the rigid body. Minimum value = 0, Preferred max value = 1.
@ -77,7 +77,8 @@ class AddRigidBodyNode(LnxLogicTreeNode):
('Cone', 'Cone', 'Cone'),
('Cylinder', 'Cylinder', 'Cylinder'),
('Convex Hull', 'Convex Hull', 'Convex Hull'),
('Mesh', 'Mesh', 'Mesh')],
('Mesh', 'Mesh', 'Mesh'),
('Compound Parent', 'Compound Parent', 'Compound Parent')],
name='Shape', default='Box')
def lnx_init(self, context):
@ -114,7 +115,7 @@ class AddRigidBodyNode(LnxLogicTreeNode):
self.add_input('LnxFloatSocket', 'Linear Damping', 0.04)
self.add_input('LnxFloatSocket', 'Angular Damping', 0.1)
self.add_input('LnxFloatSocket', 'Angular Friction', 0.1)
self.add_input('LnxBoolSocket', 'Use Deacivation')
self.add_input('LnxBoolSocket', 'Use Deactivation')
self.add_input('LnxFloatSocket', 'Linear Velocity Threshold', 0.4)
self.add_input('LnxFloatSocket', 'Angular Velocity Threshold', 0.5)
self.add_input('LnxIntSocket', 'Collision Group', 1)

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class AddPhysicsHookNode(LnxLogicTreeNode):
bl_idname = 'LNAddPhysicsHookNode'
bl_label = 'Add Physics Hook'
lnx_section = 'add'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Hook')
self.add_input('LnxNodeSocketArray', 'Vertices')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,23 @@
from lnx.logicnode.lnx_nodes import *
class AddSoftBodyNode(LnxLogicTreeNode):
bl_idname = 'LNAddSoftBodyNode'
bl_label = 'Add Soft Body'
lnx_section = 'softbody'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
# 0: Cloth, 1: Volume
self.add_input('LnxIntSocket', 'Shape', default_value=0)
self.add_input('LnxFloatSocket', 'Bend', default_value=0.5)
self.add_input('LnxFloatSocket', 'Mass', default_value=1.0)
self.add_input('LnxFloatSocket', 'Margin', default_value=0.04)
self.add_input('LnxFloatSocket', 'Friction', default_value=0.5)
self.add_input('LnxFloatSocket', 'Damping', default_value=0.01)
self.add_input('LnxFloatSocket', 'Linear Stiffness', default_value=0.9)
self.add_input('LnxFloatSocket', 'Angular Stiffness', default_value=0.9)
self.add_input('LnxFloatSocket', 'Pressure', default_value=0.0)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -8,19 +8,21 @@ class GetRigidBodyDataNode(LnxLogicTreeNode):
lnx_version = 1
def lnx_init(self, context):
self.inputs.new('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'RB')
self.outputs.new('LnxBoolSocket', 'Is RB')
self.outputs.new('LnxIntSocket', 'Collision Group')
self.outputs.new('LnxIntSocket', 'Collision Mask')
self.outputs.new('LnxBoolSocket', 'Is Animated')
self.outputs.new('LnxBoolSocket', 'Is Static')
self.outputs.new('LnxFloatSocket', 'Angular Damping')
self.outputs.new('LnxFloatSocket', 'Linear Damping')
self.outputs.new('LnxFloatSocket', 'Friction')
self.outputs.new('LnxFloatSocket', 'Mass')
#self.outputs.new('LnxStringSocket', 'Collision Shape')
#self.outputs.new('LnxIntSocket', 'Activation State')
#self.outputs.new('LnxBoolSocket', 'Is Gravity Enabled')
#self.outputs.new(LnxVectorSocket', Angular Factor')
#self.outputs.new('LnxVectorSocket', Linear Factor')
self.add_output('LnxBoolSocket', 'Is RB')
self.add_output('LnxIntSocket', 'Collision Group')
self.add_output('LnxIntSocket', 'Collision Mask')
self.add_output('LnxBoolSocket', 'Is Animated')
self.add_output('LnxBoolSocket', 'Is Static')
self.add_output('LnxFloatSocket', 'Linear Damping')
self.add_output('LnxFloatSocket', 'Angular Damping')
self.add_output('LnxFloatSocket', 'Friction')
self.add_output('LnxFloatSocket', 'Angular Friction')
self.add_output('LnxFloatSocket', 'Mass')
self.add_output('LnxBoolSocket', 'Is Trigger')
self.add_output('LnxFloatSocket', 'Bounciness')
self.add_output('LnxBoolSocket', 'Gravity Enabled')
self.add_output('LnxVectorSocket', 'Gravity')
self.add_output('LnxVectorSocket', 'Linear Factor')
self.add_output('LnxVectorSocket', 'Angular Factor')

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class SetRigidBodyDynamicsNode(LnxLogicTreeNode):
bl_idname = 'LNSetRigidBodyDynamicsNode'
bl_label = 'Set RB Dynamics'
lnx_section = 'props'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxBoolSocket', 'Is Static', default_value=False)
self.add_input('LnxFloatSocket', 'Mass', default_value=1.0)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -3,11 +3,11 @@ from lnx.logicnode.lnx_nodes import *
class GetPointVelocityNode(LnxLogicTreeNode):
"""Returns the world velocity of the given point along the rigid body."""
bl_idname = 'LNGetPointVelocityNode'
bl_label = 'Get RB Point Velocity'
bl_label = 'Get RB Velocity at Location'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'RB')
self.add_input('LnxVectorSocket', 'Point')
self.add_input('LnxVectorSocket', 'Location')
self.add_output('LnxVectorSocket', 'Velocity')

View File

@ -0,0 +1,22 @@
from lnx.logicnode.lnx_nodes import *
class GetSoftBodyDataNode(LnxLogicTreeNode):
bl_idname = 'LNGetSoftBodyDataNode'
bl_label = 'Get SB Data'
lnx_section = 'softbody'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'SB')
self.add_output('LnxBoolSocket', 'Has SB')
self.add_output('LnxFloatSocket', 'Mass')
self.add_output('LnxFloatSocket', 'Bend')
self.add_output('LnxIntSocket', 'Shape')
self.add_output('LnxFloatSocket', 'Margin')
self.add_output('LnxVectorSocket', 'World Center')
self.add_output('LnxFloatSocket', 'Friction')
self.add_output('LnxFloatSocket', 'Damping')
self.add_output('LnxFloatSocket', 'Pressure')
self.add_output('LnxFloatSocket', 'Linear Stiffness')
self.add_output('LnxFloatSocket', 'Angular Stiffness')

View File

@ -19,6 +19,8 @@ class OnContactArrayNode(LnxLogicTreeNode):
self.add_input('LnxNodeSocketArray', 'RBs')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketArray', 'RBs')
self.add_output('LnxNodeSocketObject', 'RB')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class RemovePhysicsConstraintNode(LnxLogicTreeNode):
bl_idname = 'LNRemovePhysicsConstraintNode'
bl_label = 'Remove Physics Constraint'
lnx_section = 'props'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Pivot Object')
self.add_output('LnxNodeSocketAction', 'Out')

Some files were not shown because too many files have changed in this diff Show More