1. added local rotation so that if the source object is child then it would still align to the target object. 2. removed rotation output socket since is not really needed.

This commit is contained in:
2025-06-26 03:05:11 +02:00
parent 0423a735fc
commit f5c9e70d1a
2 changed files with 47 additions and 24 deletions

View File

@ -12,6 +12,7 @@ class SetLookAtRotationNode extends LogicNode {
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)
public var property5: String; // Use local space (true/false)
// Store the calculated rotation for output
var calculatedRotation: Quat = null;
@ -51,8 +52,8 @@ class SetLookAtRotationNode extends LogicNode {
return;
}
// Get source object's position
objectLoc = objectToUse.transform.loc;
// Get source object's WORLD position (important for child objects)
objectLoc = new Vec4(objectToUse.transform.worldx(), objectToUse.transform.worldy(), objectToUse.transform.worldz());
}
// Determine if we're using a vector or an object as target
@ -74,8 +75,8 @@ class SetLookAtRotationNode extends LogicNode {
return;
}
// Get target object's position
targetLoc = targetObject.transform.loc;
// Get target object's WORLD position (important for child objects)
targetLoc = new Vec4(targetObject.transform.worldx(), targetObject.transform.worldy(), targetObject.transform.worldz());
}
// Calculate direction to target
@ -122,6 +123,28 @@ class SetLookAtRotationNode extends LogicNode {
calculatedRotation.fromEulerOrdered(eulerAngles, "XYZ");
}
// Convert world rotation to local rotation if local space is enabled and object has a parent
var targetRotation = new Quat();
if (property5 == "true" && objectToUse.parent != null) {
// Get parent's world rotation
var parentWorldLoc = new Vec4();
var parentWorldRot = new Quat();
var parentWorldScale = new Vec4();
objectToUse.parent.transform.world.decompose(parentWorldLoc, parentWorldRot, parentWorldScale);
// Convert world rotation to local space by removing parent's rotation influence
// local_rotation = inverse(parent_world_rotation) * world_rotation
var invParentRot = new Quat().setFrom(parentWorldRot);
invParentRot.x = -invParentRot.x;
invParentRot.y = -invParentRot.y;
invParentRot.z = -invParentRot.z;
targetRotation.multquats(invParentRot, calculatedRotation);
} else {
// No local space conversion needed, use world rotation directly
targetRotation.setFrom(calculatedRotation);
}
// Apply rotation with damping
var dampingValue: Float = 0.0;
@ -141,17 +164,17 @@ class SetLookAtRotationNode extends LogicNode {
// 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);
// Get current local rotation as quaternion
var currentLocalRot = 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);
var invCurrent = new Quat().setFrom(currentLocalRot);
invCurrent.x = -invCurrent.x;
invCurrent.y = -invCurrent.y;
invCurrent.z = -invCurrent.z;
diffQuat.multquats(calculatedRotation, invCurrent);
diffQuat.multquats(targetRotation, invCurrent);
// Convert to axis-angle representation
var axis = new Vec4();
@ -163,15 +186,15 @@ class SetLookAtRotationNode extends LogicNode {
// 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 this partial rotation to current local rotation
var newLocalRot = new Quat();
newLocalRot.multquats(partialRot, currentLocalRot);
// Apply the new rotation
objectToUse.transform.rot.setFrom(newRot);
// Apply the new local rotation
objectToUse.transform.rot.setFrom(newLocalRot);
} else {
// No damping, apply instant rotation
objectToUse.transform.rot.setFrom(calculatedRotation);
objectToUse.transform.rot.setFrom(targetRotation);
}
objectToUse.transform.buildMatrix();
@ -179,12 +202,5 @@ class SetLookAtRotationNode extends LogicNode {
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;
}
// No output sockets needed - this node only performs actions
}

View File

@ -29,6 +29,8 @@ class SetLookAtRotationNode(LnxLogicTreeNode):
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)',
@ -74,6 +76,12 @@ class SetLookAtRotationNode(LnxLogicTreeNode):
('false', 'False', 'False')],
name='Disable Rotation on Aligning Axis', default='false')
property5: HaxeEnumProperty(
'property5',
items = [('true', 'True', 'True'),
('false', 'False', 'False')],
name='Use Local Space', default='false')
def lnx_init(self, context):
# Add inputs in standard order
self.inputs.new('LnxNodeSocketAction', 'In')
@ -90,8 +98,6 @@ class SetLookAtRotationNode(LnxLogicTreeNode):
# 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
@ -114,6 +120,7 @@ class SetLookAtRotationNode(LnxLogicTreeNode):
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'
self.property5 = 'true' # Always use local space functionality
# Store current object references before changing sockets
self.save_object_references()