add new Set Look At Rotation Node for making logic nodes great again
This commit is contained in:
		
							
								
								
									
										190
									
								
								leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								leenkx/Sources/leenkx/logicnode/SetLookAtRotationNode.hx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,190 @@ | ||||
| package leenkx.logicnode; | ||||
|  | ||||
| import iron.math.Vec4; | ||||
| import iron.math.Quat; | ||||
| import iron.math.Mat4; | ||||
| import iron.object.Object; | ||||
|  | ||||
| class SetLookAtRotationNode extends LogicNode { | ||||
|  | ||||
| 	public var property0: String; // Axis to align | ||||
| 	public var property1: String; // Use vector for target (true/false) | ||||
| 	public var property2: String; // Use vector for source (true/false) | ||||
| 	public var property3: String; // Damping value (backward compatibility, now input socket) | ||||
| 	public var property4: String; // Disable rotation on aligning axis (true/false) | ||||
| 	 | ||||
| 	// Store the calculated rotation for output | ||||
| 	var calculatedRotation: Quat = null; | ||||
| 	// Store the previous rotation for smooth interpolation | ||||
| 	var previousRotation: Quat = null; | ||||
|  | ||||
| 	public function new(tree: LogicTree) { | ||||
| 		super(tree); | ||||
| 		previousRotation = new Quat(); | ||||
| 	} | ||||
|  | ||||
| 	override function run(from: Int): Void { | ||||
| 		// Determine if we're using a vector or an object as source | ||||
| 		var useSourceVector: Bool = property2 == "true"; | ||||
| 		var objectToUse: Object = null; | ||||
| 		var objectLoc: Vec4 = null; | ||||
|  | ||||
| 		if (useSourceVector) { | ||||
| 			// Use tree.object as the object to rotate | ||||
| 			objectToUse = tree.object; | ||||
| 			if (objectToUse == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			// Get the source location directly | ||||
| 			objectLoc = inputs[1].get(); | ||||
| 			if (objectLoc == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Get the source object (or fallback to tree.object) | ||||
| 			objectToUse = (inputs.length > 1 && inputs[1] != null) ? inputs[1].get() : tree.object; | ||||
| 			if (objectToUse == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			// Get source object's position | ||||
| 			objectLoc = objectToUse.transform.loc; | ||||
| 		} | ||||
|  | ||||
| 		// Determine if we're using a vector or an object as target | ||||
| 		var useTargetVector: Bool = property1 == "true"; | ||||
| 		var targetLoc: Vec4 = null; | ||||
|  | ||||
| 		if (useTargetVector) { | ||||
| 			// Get the target location directly | ||||
| 			targetLoc = inputs[2].get(); | ||||
| 			if (targetLoc == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Get the target object | ||||
| 			var targetObject: Object = inputs[2].get(); | ||||
| 			if (targetObject == null) { | ||||
| 				runOutput(0); | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			// Get target object's position | ||||
| 			targetLoc = targetObject.transform.loc; | ||||
| 		} | ||||
| 		 | ||||
| 		// Calculate direction to target | ||||
| 		var direction = new Vec4( | ||||
| 			targetLoc.x - objectLoc.x, | ||||
| 			targetLoc.y - objectLoc.y, | ||||
| 			targetLoc.z - objectLoc.z | ||||
| 		); | ||||
| 		direction.normalize(); | ||||
| 		 | ||||
| 		// Calculate target rotation based on selected axis | ||||
| 		calculatedRotation = new Quat(); | ||||
| 		switch (property0) { | ||||
| 			case "X": | ||||
| 				calculatedRotation.fromTo(new Vec4(1, 0, 0), direction); | ||||
| 			case "-X": | ||||
| 				calculatedRotation.fromTo(new Vec4(-1, 0, 0), direction); | ||||
| 			case "Y": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, 1, 0), direction); | ||||
| 			case "-Y": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, -1, 0), direction); | ||||
| 			case "Z": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, 0, 1), direction); | ||||
| 			case "-Z": | ||||
| 				calculatedRotation.fromTo(new Vec4(0, 0, -1), direction); | ||||
| 		} | ||||
| 		 | ||||
| 		// If disable rotation on aligning axis is enabled, constrain the target rotation | ||||
| 		if (property4 == "true") { | ||||
| 			// Apply constraint to the target rotation BEFORE damping to avoid jiggling | ||||
| 			var eulerAngles = calculatedRotation.toEulerOrdered("XYZ"); | ||||
| 			 | ||||
| 			// Set the rotation around the selected axis to 0 | ||||
| 			switch (property0) { | ||||
| 				case "X", "-X": | ||||
| 					eulerAngles.x = 0.0; | ||||
| 				case "Y", "-Y": | ||||
| 					eulerAngles.y = 0.0; | ||||
| 				case "Z", "-Z": | ||||
| 					eulerAngles.z = 0.0; | ||||
| 			} | ||||
| 			 | ||||
| 			// Convert back to quaternion | ||||
| 			calculatedRotation.fromEulerOrdered(eulerAngles, "XYZ"); | ||||
| 		} | ||||
| 		 | ||||
| 		// Apply rotation with damping | ||||
| 		var dampingValue: Float = 0.0; | ||||
| 		 | ||||
| 		// Try to get damping from input socket first (index 3), fallback to property | ||||
| 		if (inputs.length > 3 && inputs[3] != null) { | ||||
| 			var dampingInput: Dynamic = inputs[3].get(); | ||||
| 			if (dampingInput != null) { | ||||
| 				dampingValue = dampingInput; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Fallback to property for backward compatibility | ||||
| 			dampingValue = Std.parseFloat(property3); | ||||
| 		} | ||||
| 		 | ||||
| 		if (dampingValue > 0.0) { | ||||
| 			// Create a fixed interpolation rate that never reaches exactly 1.0 | ||||
| 			// Higher damping = slower rotation (smaller step) | ||||
| 			var step = Math.max(0.001, (1.0 - dampingValue) * 0.2); // 0.001 to 0.2 range | ||||
| 			 | ||||
| 			// Get current rotation as quaternion | ||||
| 			var currentRot = new Quat().setFrom(objectToUse.transform.rot); | ||||
| 			 | ||||
| 			// Calculate the difference between current and target rotation | ||||
| 			var diffQuat = new Quat(); | ||||
| 			// q1 * inverse(q2) gives the rotation from q2 to q1 | ||||
| 			var invCurrent = new Quat().setFrom(currentRot); | ||||
| 			invCurrent.x = -invCurrent.x; | ||||
| 			invCurrent.y = -invCurrent.y; | ||||
| 			invCurrent.z = -invCurrent.z; | ||||
| 			diffQuat.multquats(calculatedRotation, invCurrent); | ||||
| 			 | ||||
| 			// Convert to axis-angle representation | ||||
| 			var axis = new Vec4(); | ||||
| 			var angle = diffQuat.toAxisAngle(axis); | ||||
| 			 | ||||
| 			// Apply only a portion of this rotation (step) | ||||
| 			var partialAngle = angle * step; | ||||
| 			 | ||||
| 			// Create partial rotation quaternion | ||||
| 			var partialRot = new Quat().fromAxisAngle(axis, partialAngle); | ||||
| 			 | ||||
| 			// Apply this partial rotation to current | ||||
| 			var newRot = new Quat(); | ||||
| 			newRot.multquats(partialRot, currentRot); | ||||
| 			 | ||||
| 			// Apply the new rotation | ||||
| 			objectToUse.transform.rot.setFrom(newRot); | ||||
| 		} else { | ||||
| 			// No damping, apply instant rotation | ||||
| 			objectToUse.transform.rot.setFrom(calculatedRotation); | ||||
| 		} | ||||
| 		 | ||||
| 		objectToUse.transform.buildMatrix(); | ||||
| 		 | ||||
| 		runOutput(0); | ||||
| 	} | ||||
|  | ||||
| 	// Getter method for output sockets | ||||
| 	override function get(from: Int): Dynamic { | ||||
| 		// Output index 1 is the rotation socket (global rotation) | ||||
| 		if (from == 1) { | ||||
| 			return calculatedRotation; | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,367 @@ | ||||
| from lnx.logicnode.lnx_nodes import * | ||||
| import bpy | ||||
|  | ||||
| class SetLookAtRotationNode(LnxLogicTreeNode): | ||||
|     """Returns a rotation that makes an object look at a target object or location""" | ||||
|     bl_idname = 'LNSetLookAtRotationNode' | ||||
|     bl_label = 'Set Look At Rotation' | ||||
|     lnx_section = 'rotation' | ||||
|     lnx_version = 1 | ||||
|  | ||||
|     use_vector: bpy.props.BoolProperty( | ||||
|         name='Use Vector for Target Location', | ||||
|         description='Use a vector location instead of a target object', | ||||
|         default=False, | ||||
|         update=lambda self, context: self.update_sockets(context) | ||||
|     ) | ||||
|  | ||||
|     use_source_vector: bpy.props.BoolProperty( | ||||
|         name='Use Vector for Source', | ||||
|         description='Use a vector location instead of a source object', | ||||
|         default=False, | ||||
|         update=lambda self, context: self.update_sockets(context) | ||||
|     ) | ||||
|  | ||||
|     disable_rotation_on_align_axis: bpy.props.BoolProperty( | ||||
|         name='Disable Rotation on Aligning Axis', | ||||
|         description='Zero out the rotation on the aligning axis after look at is applied', | ||||
|         default=False, | ||||
|         update=lambda self, context: self.update_sockets(context) | ||||
|     ) | ||||
|  | ||||
|     damping: bpy.props.FloatProperty( | ||||
|         name='Damping', | ||||
|         description='Amount of damping for rotation (0.0 = instant, 1.0 = no movement)', | ||||
|         default=0.0, | ||||
|         min=0.0, | ||||
|         max=1.0, | ||||
|         step=10, | ||||
|         precision=2, | ||||
|         update=lambda self, context: self.update_damping_socket(context) | ||||
|     ) | ||||
|  | ||||
|     # Store object references as custom properties | ||||
|     source_object_name: bpy.props.StringProperty(default="") | ||||
|     target_object_name: bpy.props.StringProperty(default="") | ||||
|  | ||||
|     property0: HaxeEnumProperty( | ||||
|         'property0', | ||||
|         items = [('X', ' X', 'X'), | ||||
|                  ('-X', '-X', '-X'), | ||||
|                  ('Y', ' Y', 'Y'), | ||||
|                  ('-Y', '-Y', '-Y'), | ||||
|                  ('Z', ' Z', 'Z'), | ||||
|                  ('-Z', '-Z', '-Z')], | ||||
|         name='With', default='Z') | ||||
|  | ||||
|     property1: HaxeEnumProperty( | ||||
|         'property1', | ||||
|         items = [('true', 'True', 'True'), | ||||
|                  ('false', 'False', 'False')], | ||||
|         name='Use Vector for Target Location', default='false') | ||||
|  | ||||
|     property2: HaxeEnumProperty( | ||||
|         'property2', | ||||
|         items = [('true', 'True', 'True'), | ||||
|                  ('false', 'False', 'False')], | ||||
|         name='Use Vector for Source', default='false') | ||||
|  | ||||
|     property3: bpy.props.StringProperty(name='Damping', default='0.0') | ||||
|  | ||||
|     property4: HaxeEnumProperty( | ||||
|         'property4', | ||||
|         items = [('true', 'True', 'True'), | ||||
|                  ('false', 'False', 'False')], | ||||
|         name='Disable Rotation on Aligning Axis', default='false') | ||||
|  | ||||
|     def lnx_init(self, context): | ||||
|         # Add inputs in standard order | ||||
|         self.inputs.new('LnxNodeSocketAction', 'In') | ||||
|  | ||||
|         # Add the initial source input | ||||
|         self.inputs.new('LnxNodeSocketObject', 'Source Object') | ||||
|  | ||||
|         # Add the initial target input | ||||
|         self.inputs.new('LnxNodeSocketObject', 'Target Object') | ||||
|          | ||||
|         # Add damping input socket with default value | ||||
|         damping_socket = self.inputs.new('LnxFloatSocket', 'Damping') | ||||
|         damping_socket.default_value_raw = 0.0 | ||||
|  | ||||
|         # Add outputs | ||||
|         self.add_output('LnxNodeSocketAction', 'Out') | ||||
|         # Add rotation output socket | ||||
|         self.add_output('LnxRotationSocket', 'Rotation') | ||||
|  | ||||
|     def draw_buttons(self, context, layout): | ||||
|         # 1. Axis Selector | ||||
|         layout.prop(self, 'property0') | ||||
|  | ||||
|         # 2. 'Use Vector for Source' checkbox | ||||
|         layout.prop(self, 'use_source_vector') | ||||
|  | ||||
|         # 3. 'Use Vector for Target Location' checkbox | ||||
|         layout.prop(self, 'use_vector') | ||||
|          | ||||
|         # 4. 'Disable Rotation on Aligning Axis' checkbox | ||||
|         layout.prop(self, 'disable_rotation_on_align_axis') | ||||
|  | ||||
|         # Note: Damping is now handled by the input socket | ||||
|  | ||||
|     def update_sockets(self, context): | ||||
|         # Update the Haxe properties to match the Python properties | ||||
|         self.property1 = 'true' if self.use_vector else 'false' | ||||
|         self.property2 = 'true' if self.use_source_vector else 'false' | ||||
|         self.property3 = str(self.damping) # Keep for backward compatibility | ||||
|         self.property4 = 'true' if self.disable_rotation_on_align_axis else 'false' | ||||
|  | ||||
|         # Store current object references before changing sockets | ||||
|         self.save_object_references() | ||||
|  | ||||
|         # Helper to find a socket by name | ||||
|         def find_input_socket(name): | ||||
|             for i, s in enumerate(self.inputs): | ||||
|                 if s.name == name: | ||||
|                     return i, s | ||||
|             return -1, None | ||||
|          | ||||
|         # Ensure we have the 'In' socket at index 0 | ||||
|         in_idx, in_socket = find_input_socket('In') | ||||
|         if in_idx == -1: | ||||
|             # If 'In' socket is missing, add it at index 0 | ||||
|             self.inputs.new('LnxNodeSocketAction', 'In') | ||||
|              | ||||
|         # Fixed indices for our sockets | ||||
|         SOURCE_INDEX = 1 | ||||
|         TARGET_INDEX = 2 | ||||
|         DAMPING_INDEX = 3 | ||||
|              | ||||
|         # Get current socket information | ||||
|         source_names = ['Source Object', 'Source Location'] | ||||
|         target_names = ['Target Object', 'Target Location'] | ||||
|          | ||||
|         # Find current source and target sockets | ||||
|         source_idx = -1 | ||||
|         source_socket = None | ||||
|         for name in source_names: | ||||
|             idx, socket = find_input_socket(name) | ||||
|             if idx != -1: | ||||
|                 source_idx = idx | ||||
|                 source_socket = socket | ||||
|                 break | ||||
|                  | ||||
|         target_idx = -1 | ||||
|         target_socket = None | ||||
|         for name in target_names: | ||||
|             idx, socket = find_input_socket(name) | ||||
|             if idx != -1: | ||||
|                 target_idx = idx | ||||
|                 target_socket = socket | ||||
|                 break | ||||
|          | ||||
|         # Expected types based on current settings | ||||
|         expected_source_name = 'Source Location' if self.use_source_vector else 'Source Object' | ||||
|         expected_source_type = 'LnxVectorSocket' if self.use_source_vector else 'LnxNodeSocketObject' | ||||
|          | ||||
|         expected_target_name = 'Target Location' if self.use_vector else 'Target Object' | ||||
|         expected_target_type = 'LnxVectorSocket' if self.use_vector else 'LnxNodeSocketObject' | ||||
|          | ||||
|         # Ensure we have exactly 4 sockets (In, Source, Target, Damping) in the correct order | ||||
|         while len(self.inputs) > 4: | ||||
|             # Remove any extra sockets | ||||
|             self.inputs.remove(self.inputs[-1]) | ||||
|              | ||||
|         # Make sure we have exactly 4 sockets | ||||
|         while len(self.inputs) < 4: | ||||
|             if len(self.inputs) == 0: | ||||
|                 self.inputs.new('LnxNodeSocketAction', 'In') | ||||
|             elif len(self.inputs) == 1: | ||||
|                 self.inputs.new(expected_source_type, expected_source_name) | ||||
|             elif len(self.inputs) == 2: | ||||
|                 self.inputs.new(expected_target_type, expected_target_name) | ||||
|             elif len(self.inputs) == 3: | ||||
|                 damping_socket = self.inputs.new('LnxFloatSocket', 'Damping') | ||||
|                 damping_socket.default_value_raw = self.damping | ||||
|          | ||||
|         # Now update the source socket if needed | ||||
|         if source_socket and source_socket.name != expected_source_name: | ||||
|             # Store links before removing | ||||
|             links = [] | ||||
|             for link in source_socket.links: | ||||
|                 links.append(link.from_socket) | ||||
|              | ||||
|             # Get the index where this socket should be | ||||
|             correct_idx = SOURCE_INDEX | ||||
|              | ||||
|             # Create the new socket at the correct position | ||||
|             self.inputs.remove(source_socket) | ||||
|             new_socket = self.inputs.new(expected_source_type, expected_source_name) | ||||
|              | ||||
|             # Move the new socket to the correct position | ||||
|             if not new_socket.is_linked:  # Only move if not linked | ||||
|                 # Move the socket to the correct index | ||||
|                 if correct_idx < len(self.inputs) - 1: | ||||
|                     self.inputs.move(len(self.inputs) - 1, correct_idx) | ||||
|              | ||||
|             # Restore links | ||||
|             if links and hasattr(context, 'space_data') and context.space_data and hasattr(context.space_data, 'edit_tree'): | ||||
|                 for link_socket in links: | ||||
|                     context.space_data.edit_tree.links.new(link_socket, new_socket) | ||||
|          | ||||
|         # Update the target socket if needed | ||||
|         if target_socket and target_socket.name != expected_target_name: | ||||
|             # Store links before removing | ||||
|             links = [] | ||||
|             for link in target_socket.links: | ||||
|                 links.append(link.from_socket) | ||||
|              | ||||
|             # Get the index where this socket should be | ||||
|             correct_idx = TARGET_INDEX | ||||
|              | ||||
|             # Create the new socket at the correct position | ||||
|             self.inputs.remove(target_socket) | ||||
|             new_socket = self.inputs.new(expected_target_type, expected_target_name) | ||||
|              | ||||
|             # Move the new socket to the correct position | ||||
|             if not new_socket.is_linked:  # Only move if not linked | ||||
|                 # Move the socket to the correct index | ||||
|                 if correct_idx < len(self.inputs) - 1: | ||||
|                     self.inputs.move(len(self.inputs) - 1, correct_idx) | ||||
|              | ||||
|             # Restore links | ||||
|             if links and hasattr(context, 'space_data') and context.space_data and hasattr(context.space_data, 'edit_tree'): | ||||
|                 for link_socket in links: | ||||
|                     context.space_data.edit_tree.links.new(link_socket, new_socket) | ||||
|                      | ||||
|         # Make a final check to ensure the sockets are in the correct order | ||||
|         # This is a safety measure to ensure consistent UI | ||||
|         in_idx, in_socket = find_input_socket('In') | ||||
|         source_idx = -1 | ||||
|         for name in source_names: | ||||
|             idx, _ = find_input_socket(name) | ||||
|             if idx != -1: | ||||
|                 source_idx = idx | ||||
|                 break | ||||
|                  | ||||
|         target_idx = -1 | ||||
|         for name in target_names: | ||||
|             idx, _ = find_input_socket(name) | ||||
|             if idx != -1: | ||||
|                 target_idx = idx | ||||
|                 break | ||||
|                  | ||||
|         damping_idx, damping_socket = find_input_socket('Damping') | ||||
|          | ||||
|         # If the order is wrong, fix it by recreating the sockets in the correct order | ||||
|         if not (in_idx == 0 and source_idx == 1 and target_idx == 2 and damping_idx == 3): | ||||
|             # Store all links | ||||
|             all_links = {} | ||||
|              | ||||
|             for i, socket in enumerate(self.inputs): | ||||
|                 # Store links | ||||
|                 links = [] | ||||
|                 for link in socket.links: | ||||
|                     links.append(link.from_socket) | ||||
|                 all_links[socket.name] = links | ||||
|              | ||||
|             # Clear all inputs | ||||
|             while len(self.inputs) > 0: | ||||
|                 self.inputs.remove(self.inputs[0]) | ||||
|              | ||||
|             # Recreate in the correct order | ||||
|             in_socket = self.inputs.new('LnxNodeSocketAction', 'In') | ||||
|             source_socket = self.inputs.new(expected_source_type, expected_source_name) | ||||
|             target_socket = self.inputs.new(expected_target_type, expected_target_name) | ||||
|             damping_socket = self.inputs.new('LnxFloatSocket', 'Damping') | ||||
|             damping_socket.default_value_raw = self.damping | ||||
|              | ||||
|             # Restore links | ||||
|             if hasattr(context, 'space_data') and context.space_data and hasattr(context.space_data, 'edit_tree'): | ||||
|                 # Restore In links | ||||
|                 if 'In' in all_links: | ||||
|                     for link_socket in all_links['In']: | ||||
|                         context.space_data.edit_tree.links.new(link_socket, in_socket) | ||||
|                  | ||||
|                 # Restore Source links | ||||
|                 source_links = [] | ||||
|                 for name in source_names: | ||||
|                     if name in all_links: | ||||
|                         source_links.extend(all_links[name]) | ||||
|                 for link_socket in source_links: | ||||
|                     context.space_data.edit_tree.links.new(link_socket, source_socket) | ||||
|                  | ||||
|                 # Restore Target links | ||||
|                 target_links = [] | ||||
|                 for name in target_names: | ||||
|                     if name in all_links: | ||||
|                         target_links.extend(all_links[name]) | ||||
|                 for link_socket in target_links: | ||||
|                     context.space_data.edit_tree.links.new(link_socket, target_socket) | ||||
|                      | ||||
|                 # Restore Damping links | ||||
|                 if 'Damping' in all_links: | ||||
|                     for link_socket in all_links['Damping']: | ||||
|                         context.space_data.edit_tree.links.new(link_socket, damping_socket) | ||||
|          | ||||
|         # Restore object references after socket changes | ||||
|         self.restore_object_references() | ||||
|  | ||||
|     def update_damping_socket(self, context): | ||||
|         """Update the damping socket default value when the slider changes""" | ||||
|         for socket in self.inputs: | ||||
|             if socket.name == 'Damping' and hasattr(socket, 'default_value_raw'): | ||||
|                 socket.default_value_raw = self.damping | ||||
|                 break | ||||
|  | ||||
|     def save_object_references(self): | ||||
|         """Save object references to custom properties""" | ||||
|         # Find source and target object sockets | ||||
|         for socket in self.inputs: | ||||
|             if socket.name == 'Source Object' and socket.is_linked: | ||||
|                 for link in socket.links: | ||||
|                     if hasattr(link.from_node, 'item') and link.from_node.item: | ||||
|                         self.source_object_name = link.from_node.item.name | ||||
|              | ||||
|             if socket.name == 'Target Object' and socket.is_linked: | ||||
|                 for link in socket.links: | ||||
|                     if hasattr(link.from_node, 'item') and link.from_node.item: | ||||
|                         self.target_object_name = link.from_node.item.name | ||||
|  | ||||
|     def restore_object_references(self): | ||||
|         """Restore object references from custom properties""" | ||||
|         # Only restore if we're not using vector inputs | ||||
|         if not self.use_source_vector: | ||||
|             # Find source object socket | ||||
|             for socket in self.inputs: | ||||
|                 if socket.name == 'Source Object' and not socket.is_linked: | ||||
|                     # Try to find the object in the scene | ||||
|                     if self.source_object_name and self.source_object_name in bpy.context.scene.objects: | ||||
|                         # Find the appropriate object node in the node tree | ||||
|                         if hasattr(self, 'id_data') and hasattr(self.id_data, 'nodes'): | ||||
|                             for node in self.id_data.nodes: | ||||
|                                 if (node.bl_idname == 'LNObjectNode' and  | ||||
|                                     hasattr(node, 'item') and  | ||||
|                                     node.item and  | ||||
|                                     node.item.name == self.source_object_name): | ||||
|                                     # Create a link between the nodes | ||||
|                                     if hasattr(self.id_data, 'links'): | ||||
|                                         self.id_data.links.new(node.outputs[0], socket) | ||||
|                                     break | ||||
|          | ||||
|         if not self.use_vector: | ||||
|             # Find target object socket | ||||
|             for socket in self.inputs: | ||||
|                 if socket.name == 'Target Object' and not socket.is_linked: | ||||
|                     # Try to find the object in the scene | ||||
|                     if self.target_object_name and self.target_object_name in bpy.context.scene.objects: | ||||
|                         # Find the appropriate object node in the node tree | ||||
|                         if hasattr(self, 'id_data') and hasattr(self.id_data, 'nodes'): | ||||
|                             for node in self.id_data.nodes: | ||||
|                                 if (node.bl_idname == 'LNObjectNode' and  | ||||
|                                     hasattr(node, 'item') and  | ||||
|                                     node.item and  | ||||
|                                     node.item.name == self.target_object_name): | ||||
|                                     # Create a link between the nodes | ||||
|                                     if hasattr(self.id_data, 'links'): | ||||
|                                         self.id_data.links.new(node.outputs[0], socket) | ||||
|                                     break | ||||
		Reference in New Issue
	
	Block a user