| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  | from lnx.logicnode.lnx_nodes import * | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BoneIKNode(LnxLogicTreeNode): | 
					
						
							|  |  |  |     """Performs inverse kinematics on the selected armature with specified bone.
 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     @input Object: Armature on which IK should be performed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @input Bone: Effector or tip bone for the inverse kinematics | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @input Goal Position: Position in world coordinates the effector bone will track to | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @input Enable Pole: Bend IK solution towards pole location | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @input Pole Position: Location of the pole in world coordinates | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @input Chain Length: Number of bones to include in the IK solver including the effector. If set to 0, all bones from effector to the root bone of the armature will be considered. | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     @input Max Iterations: Maximum allowed FABRIK iterations to solve for IK. For longer chains, more iterations are needed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @input Precision: Presition of IK to stop at. It is described as a tolerence in length. Typically 0.01 is a good value. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @input Roll Angle: Roll the bones along their local axis with specified radians. set 0 for no extra roll. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     bl_idname = 'LNBoneIKNode' | 
					
						
							|  |  |  |     bl_label = 'Bone IK' | 
					
						
							|  |  |  |     lnx_version = 3 | 
					
						
							|  |  |  |     lnx_section = 'armature' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NUM_STATIC_INS = 9 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def update_advanced(self, context): | 
					
						
							|  |  |  |         self.update_sockets(context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     property0: HaxeEnumProperty( | 
					
						
							|  |  |  |         'property0', | 
					
						
							|  |  |  |         items = [('2 Bone', '2 Bone', '2 Bone'), | 
					
						
							|  |  |  |                  ('FABRIK', 'FABRIK', 'FABRIK')], | 
					
						
							|  |  |  |         name='', default='2 Bone', update=update_advanced) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def lnx_init(self, context): | 
					
						
							|  |  |  |         self.add_input('LnxNodeSocketObject', 'Object') | 
					
						
							|  |  |  |         self.add_input('LnxNodeSocketAnimTree', 'Action') | 
					
						
							|  |  |  |         self.add_input('LnxStringSocket', 'Bone') | 
					
						
							|  |  |  |         self.add_input('LnxVectorSocket', 'Goal Position') | 
					
						
							|  |  |  |         self.add_input('LnxBoolSocket', 'Enable Pole') | 
					
						
							|  |  |  |         self.add_input('LnxVectorSocket', 'Pole Position') | 
					
						
							|  |  |  |         self.add_input('LnxFloatSocket', 'Roll Angle') | 
					
						
							| 
									
										
										
										
											2025-03-24 16:25:00 +00:00
										 |  |  |         self.add_input('LnxFactorSocket', 'Influence', default_value = 1.0) | 
					
						
							| 
									
										
										
										
											2025-01-22 16:18:30 +01:00
										 |  |  |         self.add_input('LnxIntSocket', 'Bone Group', default_value = 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.add_output('LnxNodeSocketAnimTree', 'Result') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.update_sockets(context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def update_sockets(self, context): | 
					
						
							|  |  |  |         remove_list = [] | 
					
						
							|  |  |  |         for i in range(BoneIKNode.NUM_STATIC_INS, len(self.inputs)): | 
					
						
							|  |  |  |             remove_list.append(self.inputs[i]) | 
					
						
							|  |  |  |         for i in remove_list: | 
					
						
							|  |  |  |             self.inputs.remove(i) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if self.property0 == 'FABRIK': | 
					
						
							|  |  |  |             self.add_input('LnxIntSocket', 'Chain Length') | 
					
						
							|  |  |  |             self.add_input('LnxIntSocket', 'Max Iterations', 10) | 
					
						
							|  |  |  |             self.add_input('LnxFloatSocket', 'Precision', 0.01) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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, 2): | 
					
						
							|  |  |  |             raise LookupError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return NodeReplacement( | 
					
						
							|  |  |  |             'LNBoneIKNode', self.lnx_version, 'LNBoneIKNode', 3, | 
					
						
							|  |  |  |             in_socket_mapping={}, out_socket_mapping={} | 
					
						
							|  |  |  |         ) |