forked from LeenkxTeam/LNXSDK
207 lines
6.6 KiB
Haxe
207 lines
6.6 KiB
Haxe
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)
|
|
public var property5: String; // Use local space (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 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
|
|
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 WORLD position (important for child objects)
|
|
targetLoc = new Vec4(targetObject.transform.worldx(), targetObject.transform.worldy(), targetObject.transform.worldz());
|
|
}
|
|
|
|
// 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");
|
|
}
|
|
|
|
// 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;
|
|
|
|
// 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 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(currentLocalRot);
|
|
invCurrent.x = -invCurrent.x;
|
|
invCurrent.y = -invCurrent.y;
|
|
invCurrent.z = -invCurrent.z;
|
|
diffQuat.multquats(targetRotation, 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 local rotation
|
|
var newLocalRot = new Quat();
|
|
newLocalRot.multquats(partialRot, currentLocalRot);
|
|
|
|
// Apply the new local rotation
|
|
objectToUse.transform.rot.setFrom(newLocalRot);
|
|
} else {
|
|
// No damping, apply instant rotation
|
|
objectToUse.transform.rot.setFrom(targetRotation);
|
|
}
|
|
|
|
objectToUse.transform.buildMatrix();
|
|
|
|
runOutput(0);
|
|
}
|
|
|
|
// No output sockets needed - this node only performs actions
|
|
}
|