106 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			106 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | from lnx.logicnode.lnx_nodes import * | ||
|  | from lnx.logicnode.lnx_sockets import LnxRotationSocket as Rotation | ||
|  | 
 | ||
|  | class RotateObjectNode(LnxLogicTreeNode): | ||
|  |     """Rotates the given object.""" | ||
|  |     bl_idname = 'LNRotateObjectNode' | ||
|  |     bl_label = 'Rotate Object' | ||
|  |     lnx_section = 'rotation' | ||
|  |     lnx_version = 2 | ||
|  | 
 | ||
|  |     def lnx_init(self, context): | ||
|  |         self.add_input('LnxNodeSocketAction', 'In') | ||
|  |         self.add_input('LnxNodeSocketObject', 'Object') | ||
|  |         self.add_input('LnxRotationSocket', 'Rotation') | ||
|  | 
 | ||
|  |         self.add_output('LnxNodeSocketAction', 'Out') | ||
|  | 
 | ||
|  |     def draw_buttons(self, context, layout): | ||
|  |         layout.prop(self, 'property0_proxy') | ||
|  | 
 | ||
|  |          | ||
|  |     # this property swaperoo is kinda janky-looking, but listen out: | ||
|  |     # - when you reload an old file, the properties of loaded nodes can be mangled if the node class drops the property or the specific value within the property. | ||
|  |     # -> to fix this, 'property0' needs to contain the old values so that node replacement can be done decently. | ||
|  |     # - "but", I hear you ask, "why not make property0 a simple blender property, and create a property0v2 HaxeProperty to be bound to the haxe-time property0?" | ||
|  |     # -> well, at the time of writing, a HaxeProperty's prop_name is only used for livepatching, not at initial setup, so a freshly-compiled game would get completely borked properties. | ||
|  |     # solution: have a property0 HaxeProperty contain every possible value, and have a property0_proxy Property be in the UI. | ||
|  | 
 | ||
|  |     # NOTE FOR FUTURE MAINTAINERS: the value of the proxy property does **not** matter, only the value of property0 does. When eventually editing this class, you can safely drop the values in the proxy property, and *only* the proxy property. | ||
|  | 
 | ||
|  |     def on_proxyproperty_update(self, context=None): | ||
|  |         self.property0 = self.property0_proxy | ||
|  | 
 | ||
|  |     property0_proxy: EnumProperty( | ||
|  |         items = [('Local', 'Local F.O.R.', 'Frame of reference oriented with the object'), | ||
|  |                  ('Global', 'Global/Parent F.O.R.', | ||
|  |                   'Frame of reference oriented with the object\'s parent or the world')], | ||
|  |         name='', default='Local', | ||
|  |         update = on_proxyproperty_update | ||
|  |     ) | ||
|  |     property0: HaxeEnumProperty( | ||
|  |         'property0', | ||
|  |         items=[('Euler Angles', 'NODE REPLACEMENT ONLY', ''), | ||
|  |                ('Angle Axies (Radians)', 'NODE REPLACEMENT ONLY', ''),   | ||
|  |                ('Angle Axies (Degrees)', 'NODE REPLACEMENT ONLY', ''), | ||
|  |                ('Quaternion', 'NODE REPLACEMENT ONLY', ''), | ||
|  |                ('Local', 'Local F.O.R.', 'Frame of reference oriented with the object'), | ||
|  |                  ('Global', 'Global/Parent F.O.R.', | ||
|  |                   'Frame of reference oriented with the object\'s parent or the world') | ||
|  |                ], | ||
|  |         name='', default='Local') | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  |     def get_replacement_node(self, node_tree: bpy.types.NodeTree): | ||
|  |         if self.lnx_version not in (0, 1): | ||
|  |             raise LookupError() | ||
|  | 
 | ||
|  |          | ||
|  |         # transition from version 1 to version 2: make rotations their own sockets | ||
|  |         # this transition is a mess, I know. | ||
|  | 
 | ||
|  |         newself = self.id_data.nodes.new('LNRotateObjectNode') | ||
|  |         inputnode = self.id_data.nodes.new('LNRotationNode') | ||
|  |         self.id_data.links.new(inputnode.outputs[0], newself.inputs[2]) | ||
|  |         newself.inputs[1].default_value_raw = self.inputs[1].default_value_raw | ||
|  |         inputnode.inputs[0].default_value = self.inputs[2].default_value | ||
|  |         inputnode.inputs[1].default_value = self.inputs[3].default_value | ||
|  | 
 | ||
|  |         if len(self.inputs[0].links) >0: | ||
|  |             self.id_data.links.new(self.inputs[0].links[0].from_socket, newself.inputs[0]) | ||
|  |         if len(self.inputs[1].links) >0: | ||
|  |             self.id_data.links.new(self.inputs[1].links[0].from_socket, newself.inputs[1]) | ||
|  |         if len(self.inputs[2].links) >0: | ||
|  |             self.id_data.links.new(self.inputs[2].links[0].from_socket, inputnode.inputs[0]) | ||
|  |         if len(self.inputs[3].links) >0: | ||
|  |             self.id_data.links.new(self.inputs[3].links[0].from_socket, inputnode.inputs[1]) | ||
|  | 
 | ||
|  |         # first, convert the default value | ||
|  |         if self.property0 == 'Quaternion': | ||
|  |             inputnode.property0 = 'Quaternion' | ||
|  |         elif self.property0 == 'Euler Angles': | ||
|  |             inputnode.property0 = 'EulerAngles' | ||
|  |             inputnode.property1 = 'Rad' | ||
|  |             inputnode.property2 = 'XZY'  # legacy order | ||
|  |         else:  # starts with "Angle Axies" | ||
|  |             inputnode.property0 = 'AxisAngle' | ||
|  |             if 'Degrees' in self.property0: | ||
|  |                 inputnode.property1 = 'Deg' | ||
|  |             else: | ||
|  |                 inputnode.property1 = 'Rad' | ||
|  |         quat = Rotation.convert_to_quaternion( | ||
|  |             self.inputs[2].default_value, | ||
|  |             self.inputs[3].default_value, | ||
|  |             inputnode.property0, | ||
|  |             inputnode.property1, | ||
|  |             inputnode.property2 | ||
|  |         ) | ||
|  |         newself.inputs[2].default_value_raw = quat | ||
|  |         return [newself, inputnode] |