merge upstream
This commit is contained in:
		@ -399,17 +399,33 @@ class Quat {
 | 
				
			|||||||
		@return	This quaternion.
 | 
							@return	This quaternion.
 | 
				
			||||||
	**/
 | 
						**/
 | 
				
			||||||
	public inline function fromEulerOrdered(e: Vec4, order: String): Quat {
 | 
						public inline function fromEulerOrdered(e: Vec4, order: String): Quat {
 | 
				
			||||||
		var c1 = Math.cos(e.x / 2);
 | 
							
 | 
				
			||||||
		var c2 = Math.cos(e.y / 2);
 | 
							var mappedAngles = new Vec4();
 | 
				
			||||||
		var c3 = Math.cos(e.z / 2);
 | 
							switch (order) {
 | 
				
			||||||
		var s1 = Math.sin(e.x / 2);
 | 
								case "XYZ":
 | 
				
			||||||
		var s2 = Math.sin(e.y / 2);
 | 
									mappedAngles.set(e.x, e.y, e.z);
 | 
				
			||||||
		var s3 = Math.sin(e.z / 2);
 | 
								case "XZY":
 | 
				
			||||||
 | 
									mappedAngles.set(e.x, e.z, e.y);
 | 
				
			||||||
 | 
								case "YXZ": 
 | 
				
			||||||
 | 
									mappedAngles.set(e.y, e.x, e.z);
 | 
				
			||||||
 | 
								case "YZX": 
 | 
				
			||||||
 | 
									mappedAngles.set(e.y, e.z, e.x);
 | 
				
			||||||
 | 
								case "ZXY": 
 | 
				
			||||||
 | 
									mappedAngles.set(e.z, e.x, e.y);
 | 
				
			||||||
 | 
								case "ZYX": 
 | 
				
			||||||
 | 
									mappedAngles.set(e.z, e.y, e.x);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							var c1 = Math.cos(mappedAngles.x / 2);
 | 
				
			||||||
 | 
							var c2 = Math.cos(mappedAngles.y / 2);
 | 
				
			||||||
 | 
							var c3 = Math.cos(mappedAngles.z / 2);
 | 
				
			||||||
 | 
							var s1 = Math.sin(mappedAngles.x / 2);
 | 
				
			||||||
 | 
							var s2 = Math.sin(mappedAngles.y / 2);
 | 
				
			||||||
 | 
							var s3 = Math.sin(mappedAngles.z / 2);
 | 
				
			||||||
		var qx = new Quat(s1, 0, 0, c1);
 | 
							var qx = new Quat(s1, 0, 0, c1);
 | 
				
			||||||
		var qy = new Quat(0, s2, 0, c2);
 | 
							var qy = new Quat(0, s2, 0, c2);
 | 
				
			||||||
		var qz = new Quat(0, 0, s3, c3);
 | 
							var qz = new Quat(0, 0, s3, c3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Original multiplication sequence (implements reverse of 'order')
 | 
				
			||||||
		if (order.charAt(2) == 'X')
 | 
							if (order.charAt(2) == 'X')
 | 
				
			||||||
			this.setFrom(qx);
 | 
								this.setFrom(qx);
 | 
				
			||||||
		else if (order.charAt(2) == 'Y')
 | 
							else if (order.charAt(2) == 'Y')
 | 
				
			||||||
@ -429,7 +445,7 @@ class Quat {
 | 
				
			|||||||
		else
 | 
							else
 | 
				
			||||||
			this.mult(qz);
 | 
								this.mult(qz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// TO DO quick fix doesnt make sense.
 | 
							// TO DO quick fix somethings wrong..
 | 
				
			||||||
		this.x = -this.x;
 | 
							this.x = -this.x;
 | 
				
			||||||
		this.y = -this.y;
 | 
							this.y = -this.y;
 | 
				
			||||||
		this.z = -this.z;
 | 
							this.z = -this.z;
 | 
				
			||||||
 | 
				
			|||||||
@ -37,6 +37,7 @@ class RotationNode extends LogicNode {
 | 
				
			|||||||
				value.y = vect.y;
 | 
									value.y = vect.y;
 | 
				
			||||||
				value.z = vect.z;
 | 
									value.z = vect.z;
 | 
				
			||||||
				value.w = inputs[1].get();
 | 
									value.w = inputs[1].get();
 | 
				
			||||||
 | 
									value.normalize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case "AxisAngle":
 | 
								case "AxisAngle":
 | 
				
			||||||
				var vec: Vec4 = inputs[0].get();
 | 
									var vec: Vec4 = inputs[0].get();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
from math import pi, cos, sin, sqrt
 | 
					from math import radians, pi, cos, sin, sqrt
 | 
				
			||||||
from typing import Type
 | 
					from typing import Type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import bpy
 | 
					import bpy
 | 
				
			||||||
@ -158,70 +158,90 @@ class LnxRotationSocket(LnxCustomSocket):
 | 
				
			|||||||
        self.do_update_raw(context)
 | 
					        self.do_update_raw(context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def convert_to_quaternion(part1,part2,param1,param2,param3):
 | 
					    def convert_to_quaternion(vec3_val, scalar_val, mode, unit, order):
 | 
				
			||||||
        """converts a representation of rotation into a quaternion.
 | 
					        '''Converts Euler or Axis-Angle representation to a Quaternion Vector'''
 | 
				
			||||||
        ``part1`` is a vector, ``part2`` is a scalar or None,
 | 
					
 | 
				
			||||||
        ``param1`` is in ('Quaternion', 'EulerAngles', 'AxisAngle'),
 | 
					        if mode == 'Quaternion':
 | 
				
			||||||
        ``param2`` is in ('Rad','Deg') for both EulerAngles and AxisAngle,
 | 
					            qx, qy, qz = vec3_val[0], vec3_val[1], vec3_val[2]
 | 
				
			||||||
        ``param3`` is a len-3 string like "XYZ", for EulerAngles """
 | 
					            qw = scalar_val 
 | 
				
			||||||
        if param1=='Quaternion':
 | 
					
 | 
				
			||||||
            qx, qy, qz = part1[0], part1[1], part1[2]
 | 
					            ql = sqrt(qx**2 + qy**2 + qz**2 + qw**2)
 | 
				
			||||||
            qw = part2
 | 
					            if abs(ql) < 1E-5:
 | 
				
			||||||
            # need to normalize the quaternion for a rotation (having it be 0 is not an option)
 | 
					                qx, qy, qz, qw = 0.0, 0.0, 0.0, 1.0
 | 
				
			||||||
            ql = sqrt(qx**2+qy**2+qz**2+qw**2)
 | 
					 | 
				
			||||||
            if abs(ql)<1E-5:
 | 
					 | 
				
			||||||
                qx, qy, qz, qw = 0.0,0.0,0.0,1.0
 | 
					 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                qx /= ql
 | 
					                qx /= ql
 | 
				
			||||||
                qy /= ql
 | 
					                qy /= ql
 | 
				
			||||||
                qz /= ql
 | 
					                qz /= ql
 | 
				
			||||||
                qw /= ql
 | 
					                qw /= ql
 | 
				
			||||||
            return mathutils.Vector((qx,qy,qz,qw))
 | 
					            return mathutils.Vector((qx, qy, qz, qw))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif param1 == 'AxisAngle':
 | 
					        elif mode == 'EulerAngles':
 | 
				
			||||||
            if param2 == 'Deg':
 | 
					            x, y, z = vec3_val.to_tuple()
 | 
				
			||||||
                angle = part2 * pi/180
 | 
					
 | 
				
			||||||
            else:
 | 
					            if unit == 'Deg':
 | 
				
			||||||
                angle = part2
 | 
					                x, y, z = radians(x), radians(y), radians(z)
 | 
				
			||||||
            cang, sang = cos(angle/2), sin(angle/2)
 | 
					
 | 
				
			||||||
            x,y,z = part1[0], part1[1], part1[2]
 | 
					            angles_ordered = [0.0, 0.0, 0.0]
 | 
				
			||||||
            veclen = sqrt(x**2+y**2+z**2)
 | 
					            for i, axis in enumerate(order):
 | 
				
			||||||
            if veclen<1E-5:
 | 
					                if axis == 'X':
 | 
				
			||||||
                return mathutils.Vector((0.0,0.0,0.0,1.0))
 | 
					                    angles_ordered[i] = x
 | 
				
			||||||
            else:
 | 
					                elif axis == 'Y':
 | 
				
			||||||
                return mathutils.Vector((
 | 
					                    angles_ordered[i] = y
 | 
				
			||||||
                    x/veclen * sang,
 | 
					                elif axis == 'Z':
 | 
				
			||||||
                    y/veclen * sang,
 | 
					                    angles_ordered[i] = z
 | 
				
			||||||
                    z/veclen * sang,
 | 
					            eul = mathutils.Euler(angles_ordered, order)
 | 
				
			||||||
                    cang
 | 
					            quat = eul.to_quaternion()
 | 
				
			||||||
                ))
 | 
					 | 
				
			||||||
        else:  # param1 == 'EulerAngles'
 | 
					 | 
				
			||||||
            x,y,z = part1[0], part1[1], part1[2]
 | 
					 | 
				
			||||||
            if param2 == 'Deg':
 | 
					 | 
				
			||||||
                x *= pi/180
 | 
					 | 
				
			||||||
                y *= pi/180
 | 
					 | 
				
			||||||
                z *= pi/180
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
            euler = mathutils.Euler((x, y, z), param3)
 | 
					 | 
				
			||||||
            quat = euler.to_quaternion()
 | 
					 | 
				
			||||||
            return mathutils.Vector((quat.x, quat.y, quat.z, quat.w))
 | 
					            return mathutils.Vector((quat.x, quat.y, quat.z, quat.w))
 | 
				
			||||||
            
 | 
					
 | 
				
			||||||
 | 
					        elif mode == 'AxisAngle':
 | 
				
			||||||
 | 
					            axis = vec3_val.normalized().to_tuple()
 | 
				
			||||||
 | 
					            angle = scalar_val
 | 
				
			||||||
 | 
					            if unit == 'Deg':
 | 
				
			||||||
 | 
					                angle = radians(angle)
 | 
				
			||||||
 | 
					            quat = mathutils.Quaternion(axis, angle)
 | 
				
			||||||
 | 
					            return mathutils.Vector((quat.x, quat.y, quat.z, quat.w))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print(f"Warning: Invalid mode '{mode}' in convert_to_quaternion")
 | 
				
			||||||
 | 
					        return mathutils.Vector((0.0, 0.0, 0.0, 1.0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def do_update_raw(self, context):
 | 
					    def do_update_raw(self, context):
 | 
				
			||||||
        part1 = mathutils.Vector((
 | 
					        if self.default_value_mode == 'Quaternion':
 | 
				
			||||||
            self.default_value_s0,
 | 
					            # Directly construct the quaternion vector from s0, s1, s2, s3 (x, y, z, w)
 | 
				
			||||||
            self.default_value_s1,
 | 
					            vec3_val = mathutils.Vector((
 | 
				
			||||||
            self.default_value_s2, 1
 | 
					                self.default_value_s0, # X component or Euler X or Axis X
 | 
				
			||||||
        ))
 | 
					                self.default_value_s1, # Y component or Euler Y or Axis Y
 | 
				
			||||||
        part2 = self.default_value_s3
 | 
					                self.default_value_s2  # Z component or Euler Z or Axis Z
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					            scalar_val = self.default_value_s3 # W component or Axis Angle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.default_value_raw = self.convert_to_quaternion(
 | 
					            # Always call the unified conversion function
 | 
				
			||||||
            part1,
 | 
					            # The result will be in (x, y, z, w) order
 | 
				
			||||||
            self.default_value_s3,
 | 
					            self.default_value_raw = self.convert_to_quaternion(
 | 
				
			||||||
            self.default_value_mode,
 | 
					                vec3_val,
 | 
				
			||||||
            self.default_value_unit,
 | 
					                scalar_val,
 | 
				
			||||||
            self.default_value_order
 | 
					                self.default_value_mode,
 | 
				
			||||||
        )
 | 
					                self.default_value_unit,
 | 
				
			||||||
 | 
					                self.default_value_order
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # Handle EulerAngles and AxisAngle using the conversion helper
 | 
				
			||||||
 | 
					            vec3_val = mathutils.Vector((
 | 
				
			||||||
 | 
					                self.default_value_s0,
 | 
				
			||||||
 | 
					                self.default_value_s1,
 | 
				
			||||||
 | 
					                self.default_value_s2
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					            # s3 is used for AxisAngle angle, irrelevant for Euler
 | 
				
			||||||
 | 
					            scalar_val = self.default_value_s3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.default_value_raw = self.convert_to_quaternion(
 | 
				
			||||||
 | 
					                vec3_val,           # Vector part (Euler angles X,Y,Z or Axis X,Y,Z)
 | 
				
			||||||
 | 
					                scalar_val,         # Scalar part (Axis angle)
 | 
				
			||||||
 | 
					                self.default_value_mode, # Mode ('EulerAngles' or 'AxisAngle')
 | 
				
			||||||
 | 
					                self.default_value_unit, # Unit ('Rad' or 'Deg')
 | 
				
			||||||
 | 
					                self.default_value_order # Order ('XYZ', 'ZYX', etc. - used only for Euler)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def draw(self, context, layout, node, text):
 | 
					    def draw(self, context, layout, node, text):
 | 
				
			||||||
@ -275,9 +295,11 @@ class LnxRotationSocket(LnxCustomSocket):
 | 
				
			|||||||
               ('YZX','YZX','YZX'),
 | 
					               ('YZX','YZX','YZX'),
 | 
				
			||||||
               ('ZXY','ZXY','ZXY'),
 | 
					               ('ZXY','ZXY','ZXY'),
 | 
				
			||||||
               ('ZYX','ZYX','ZYX')],
 | 
					               ('ZYX','ZYX','ZYX')],
 | 
				
			||||||
        name='', default='XYZ'
 | 
					        name='', default='XYZ',
 | 
				
			||||||
 | 
					        update=do_update_raw
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    default_value_s0: FloatProperty(update=do_update_raw)
 | 
					    default_value_s0: FloatProperty(update=do_update_raw)
 | 
				
			||||||
    default_value_s1: FloatProperty(update=do_update_raw)
 | 
					    default_value_s1: FloatProperty(update=do_update_raw)
 | 
				
			||||||
    default_value_s2: FloatProperty(update=do_update_raw)
 | 
					    default_value_s2: FloatProperty(update=do_update_raw)
 | 
				
			||||||
 | 
				
			|||||||
@ -344,6 +344,8 @@ class LNX_PT_NodeDevelopment(bpy.types.Panel):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            layout.separator()
 | 
					            layout.separator()
 | 
				
			||||||
            layout.operator('lnx.node_replace_all')
 | 
					            layout.operator('lnx.node_replace_all')
 | 
				
			||||||
 | 
					            layout.operator('lnx.recalculate_rotations')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def _draw_row(col: bpy.types.UILayout, text: str, val: Any):
 | 
					    def _draw_row(col: bpy.types.UILayout, text: str, val: Any):
 | 
				
			||||||
@ -366,6 +368,40 @@ class LNX_OT_ReplaceNodesOperator(bpy.types.Operator):
 | 
				
			|||||||
    def poll(cls, context):
 | 
					    def poll(cls, context):
 | 
				
			||||||
        return context.space_data is not None and context.space_data.type == 'NODE_EDITOR'
 | 
					        return context.space_data is not None and context.space_data.type == 'NODE_EDITOR'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LNX_OT_RecalculateRotations(bpy.types.Operator):
 | 
				
			||||||
 | 
					    """Recalculates internal rotation values for all rotation sockets in the tree"""
 | 
				
			||||||
 | 
					    bl_idname = "lnx.recalculate_rotations"
 | 
				
			||||||
 | 
					    bl_label = "Recalculate Rotations"
 | 
				
			||||||
 | 
					    bl_description = "Forces recalculation of internal quaternion values for all LnxRotationSockets in the active tree using their current settings. Useful for fixing old files."
 | 
				
			||||||
 | 
					    bl_options = {'REGISTER', 'UNDO'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def poll(cls, context):
 | 
				
			||||||
 | 
					        return (context.space_data is not None and
 | 
				
			||||||
 | 
					                context.space_data.type == 'NODE_EDITOR' and
 | 
				
			||||||
 | 
					                context.space_data.tree_type == 'LnxLogicTreeType' and
 | 
				
			||||||
 | 
					                context.space_data.edit_tree is not None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self, context):
 | 
				
			||||||
 | 
					        tree = context.space_data.edit_tree
 | 
				
			||||||
 | 
					        if not tree:
 | 
				
			||||||
 | 
					            self.report({'WARNING'}, "No active Logic Node tree found")
 | 
				
			||||||
 | 
					            return {'CANCELLED'}
 | 
				
			||||||
 | 
					        recalculated_count = 0
 | 
				
			||||||
 | 
					        for node in tree.nodes:
 | 
				
			||||||
 | 
					            for socket in list(node.inputs) + list(node.outputs):
 | 
				
			||||||
 | 
					                if hasattr(socket, 'do_update_raw') and callable(socket.do_update_raw):
 | 
				
			||||||
 | 
					                    try:
 | 
				
			||||||
 | 
					                        socket.do_update_raw(context)
 | 
				
			||||||
 | 
					                        recalculated_count += 1
 | 
				
			||||||
 | 
					                    except Exception as e:
 | 
				
			||||||
 | 
					                        print(f"Error recalculating socket '{socket.name}' on node '{node.name}': {e}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.report({'INFO'}, f"Recalculated {recalculated_count} rotation sockets in tree '{tree.name}'")
 | 
				
			||||||
 | 
					        tree.update_tag()
 | 
				
			||||||
 | 
					        return {'FINISHED'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LNX_UL_InterfaceSockets(bpy.types.UIList):
 | 
					class LNX_UL_InterfaceSockets(bpy.types.UIList):
 | 
				
			||||||
    """UI List of input and output sockets"""
 | 
					    """UI List of input and output sockets"""
 | 
				
			||||||
    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
 | 
					    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
 | 
				
			||||||
@ -421,6 +457,7 @@ __REG_CLASSES = (
 | 
				
			|||||||
    LnxOpenNodePythonSource,
 | 
					    LnxOpenNodePythonSource,
 | 
				
			||||||
    LnxOpenNodeWikiEntry,
 | 
					    LnxOpenNodeWikiEntry,
 | 
				
			||||||
    LNX_OT_ReplaceNodesOperator,
 | 
					    LNX_OT_ReplaceNodesOperator,
 | 
				
			||||||
 | 
					    LNX_OT_RecalculateRotations,
 | 
				
			||||||
    LNX_MT_NodeAddOverride,
 | 
					    LNX_MT_NodeAddOverride,
 | 
				
			||||||
    LNX_OT_AddNodeOverride,
 | 
					    LNX_OT_AddNodeOverride,
 | 
				
			||||||
    LNX_UL_InterfaceSockets,
 | 
					    LNX_UL_InterfaceSockets,
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user