forked from LeenkxTeam/LNXSDK
made default resolution adaptive sensiticity because makes more sense, removed other things since no difference and dunno
This commit is contained in:
@ -7,44 +7,24 @@ import kha.System;
|
|||||||
import kha.FastFloat;
|
import kha.FastFloat;
|
||||||
|
|
||||||
class MouseLookNode extends LogicNode {
|
class MouseLookNode extends LogicNode {
|
||||||
// Note: This implementation works in degrees internally and converts to radians only when applying rotations
|
public var property0: String;
|
||||||
// Sub-pixel interpolation is always enabled for optimal precision
|
public var property1: Bool;
|
||||||
// Features: Resolution-adaptive scaling and precise low-sensitivity support
|
public var property2: Bool;
|
||||||
|
public var property3: Bool;
|
||||||
public var property0: String; // Front axis
|
public var property4: Bool;
|
||||||
public var property1: Bool; // Center Mouse
|
public var property5: Bool;
|
||||||
public var property2: Bool; // Invert X
|
public var property7: Bool;
|
||||||
public var property3: Bool; // Invert Y
|
|
||||||
public var property4: Bool; // Cap Left/Right
|
|
||||||
public var property5: Bool; // Cap Up/Down
|
|
||||||
|
|
||||||
// New strategy toggles
|
|
||||||
public var property6: Bool; // Resolution-Adaptive Scaling
|
|
||||||
|
|
||||||
// Smoothing variables
|
|
||||||
var smoothX: FastFloat = 0.0;
|
|
||||||
var smoothY: FastFloat = 0.0;
|
|
||||||
|
|
||||||
// Capping limits (in degrees)
|
|
||||||
var maxHorizontal: FastFloat = 180.0; // 180 degrees
|
|
||||||
var maxVertical: FastFloat = 90.0; // 90 degrees
|
|
||||||
|
|
||||||
// Current accumulated rotations for capping
|
|
||||||
var currentHorizontal: FastFloat = 0.0;
|
|
||||||
var currentVertical: FastFloat = 0.0;
|
|
||||||
|
|
||||||
// Sub-pixel interpolation accumulators
|
|
||||||
var accumulatedHorizontalRotation: FastFloat = 0.0;
|
|
||||||
var accumulatedVerticalRotation: FastFloat = 0.0;
|
|
||||||
var minimumRotationThreshold: FastFloat = 0.01; // degrees (was 0.0001 radians)
|
|
||||||
|
|
||||||
// Frame rate independence removed - not applicable to mouse input
|
|
||||||
|
|
||||||
// Resolution adaptive scaling
|
|
||||||
var baseResolutionWidth: FastFloat = 1920.0;
|
|
||||||
var baseResolutionHeight: FastFloat = 1080.0;
|
|
||||||
|
|
||||||
|
var smoothX: Float = 0.0;
|
||||||
|
var smoothY: Float = 0.0;
|
||||||
|
var maxHorizontal: Float = Math.PI;
|
||||||
|
var maxVertical: Float = Math.PI / 2;
|
||||||
|
var currentHorizontal: Float = 0.0;
|
||||||
|
var currentVertical: Float = 0.0;
|
||||||
|
var baseResolutionWidth: Float = 1920.0;
|
||||||
|
|
||||||
|
static inline var BASE_SCALE: Float = 1500.0;
|
||||||
|
static var RADIAN_SCALING_FACTOR: Float = Math.PI * 50.0 / 180.0;
|
||||||
|
|
||||||
public function new(tree: LogicTree) {
|
public function new(tree: LogicTree) {
|
||||||
super(tree);
|
super(tree);
|
||||||
@ -63,114 +43,64 @@ class MouseLookNode extends LogicNode {
|
|||||||
|
|
||||||
var mouse = Input.getMouse();
|
var mouse = Input.getMouse();
|
||||||
|
|
||||||
// Handle mouse centering/locking
|
|
||||||
if (property1) {
|
if (property1) {
|
||||||
if (mouse.started() && !mouse.locked) {
|
if (mouse.started() && !mouse.locked) {
|
||||||
mouse.lock();
|
mouse.lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only process if mouse is active
|
|
||||||
if (!mouse.locked && !mouse.down()) {
|
if (!mouse.locked && !mouse.down()) {
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mouse movement deltas
|
var deltaX: Float = mouse.movementX;
|
||||||
var deltaX: FastFloat = mouse.movementX;
|
var deltaY: Float = mouse.movementY;
|
||||||
var deltaY: FastFloat = mouse.movementY;
|
|
||||||
|
|
||||||
// Note: Sensitivity will be applied later to preserve precision for small movements
|
|
||||||
|
|
||||||
// Apply inversion
|
|
||||||
if (property2) deltaX = -deltaX;
|
if (property2) deltaX = -deltaX;
|
||||||
if (property3) deltaY = -deltaY;
|
if (property3) deltaY = -deltaY;
|
||||||
|
|
||||||
// Strategy 1: Resolution-Adaptive Scaling
|
// Always apply resolution-adaptive scaling
|
||||||
var resolutionMultiplier: FastFloat = 1.0;
|
var resolutionMultiplier: Float = System.windowWidth() / baseResolutionWidth;
|
||||||
if (property6) {
|
|
||||||
var currentWidth = System.windowWidth();
|
|
||||||
var currentHeight = System.windowHeight();
|
|
||||||
resolutionMultiplier = (currentWidth / baseResolutionWidth) * (currentHeight / baseResolutionHeight);
|
|
||||||
resolutionMultiplier = Math.sqrt(resolutionMultiplier); // Take square root to avoid over-scaling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frame Rate Independence disabled for mouse input - mouse deltas are inherently frame-rate independent
|
|
||||||
|
|
||||||
// Apply smoothing
|
|
||||||
if (smoothing > 0.0) {
|
if (smoothing > 0.0) {
|
||||||
var smoothFactor = 1.0 - Math.min(smoothing, 0.99); // Prevent complete smoothing
|
var smoothingFactor: Float = Math.min(smoothing, 0.99);
|
||||||
smoothX = smoothX * smoothing + deltaX * smoothFactor;
|
smoothX = smoothX * smoothingFactor + deltaX * (1.0 - smoothingFactor);
|
||||||
smoothY = smoothY * smoothing + deltaY * smoothFactor;
|
smoothY = smoothY * smoothingFactor + deltaY * (1.0 - smoothingFactor);
|
||||||
deltaX = smoothX;
|
deltaX = smoothX;
|
||||||
deltaY = smoothY;
|
deltaY = smoothY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine rotation axes based on front axis setting
|
|
||||||
var horizontalAxis = new Vec4();
|
var horizontalAxis = new Vec4();
|
||||||
var verticalAxis = new Vec4();
|
var verticalAxis = new Vec4();
|
||||||
|
|
||||||
switch (property0) {
|
switch (property0) {
|
||||||
case "X": // X is front
|
case "X":
|
||||||
horizontalAxis.set(0, 0, 1); // Z axis for horizontal (yaw)
|
horizontalAxis.set(0, 0, 1);
|
||||||
verticalAxis.set(0, 1, 0); // Y axis for vertical (pitch)
|
verticalAxis.set(0, 1, 0);
|
||||||
case "Y": // Y is front (default)
|
case "Y":
|
||||||
#if lnx_yaxisup
|
#if lnx_yaxisup
|
||||||
horizontalAxis.set(0, 0, 1); // Z axis for horizontal (yaw)
|
horizontalAxis.set(0, 0, 1);
|
||||||
verticalAxis.set(1, 0, 0); // X axis for vertical (pitch)
|
verticalAxis.set(1, 0, 0);
|
||||||
#else
|
#else
|
||||||
horizontalAxis.set(0, 0, 1); // Z axis for horizontal (yaw)
|
horizontalAxis.set(0, 0, 1);
|
||||||
verticalAxis.set(1, 0, 0); // X axis for vertical (pitch)
|
verticalAxis.set(1, 0, 0);
|
||||||
#end
|
#end
|
||||||
case "Z": // Z is front
|
case "Z":
|
||||||
horizontalAxis.set(0, 1, 0); // Y axis for horizontal (yaw)
|
horizontalAxis.set(0, 1, 0);
|
||||||
verticalAxis.set(1, 0, 0); // X axis for vertical (pitch)
|
verticalAxis.set(1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base scaling
|
// Always apply resolution-adaptive scaling
|
||||||
var baseScale: FastFloat = 1500.0;
|
var finalScale: Float = BASE_SCALE * resolutionMultiplier;
|
||||||
var finalScale = baseScale;
|
|
||||||
|
|
||||||
// Apply resolution scaling
|
|
||||||
if (property6) {
|
|
||||||
finalScale *= resolutionMultiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Apply sensitivity scaling after all enhancement strategies to preserve precision
|
|
||||||
deltaX *= sensitivity;
|
deltaX *= sensitivity;
|
||||||
deltaY *= sensitivity;
|
deltaY *= sensitivity;
|
||||||
|
|
||||||
// Calculate rotation amounts (in degrees)
|
var horizontalRotation: Float = (-deltaX / finalScale) * RADIAN_SCALING_FACTOR;
|
||||||
var horizontalRotation: FastFloat = (-deltaX / finalScale) * 180.0 / Math.PI;
|
var verticalRotation: Float = (-deltaY / finalScale) * RADIAN_SCALING_FACTOR;
|
||||||
var verticalRotation: FastFloat = (-deltaY / finalScale) * 180.0 / Math.PI;
|
|
||||||
|
|
||||||
// Note: Frame rate independence removed for mouse input as mouse deltas
|
if (property4) {
|
||||||
// are already frame-rate independent by nature. Mouse input represents
|
|
||||||
// instantaneous user intent, not time-based movement.
|
|
||||||
|
|
||||||
// Strategy 2: Sub-Pixel Interpolation (always enabled)
|
|
||||||
accumulatedHorizontalRotation += horizontalRotation;
|
|
||||||
accumulatedVerticalRotation += verticalRotation;
|
|
||||||
|
|
||||||
// Only apply rotation if accumulated amount exceeds threshold
|
|
||||||
if (Math.abs(accumulatedHorizontalRotation) >= minimumRotationThreshold) {
|
|
||||||
horizontalRotation = accumulatedHorizontalRotation;
|
|
||||||
accumulatedHorizontalRotation = 0.0;
|
|
||||||
} else {
|
|
||||||
horizontalRotation = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Math.abs(accumulatedVerticalRotation) >= minimumRotationThreshold) {
|
|
||||||
verticalRotation = accumulatedVerticalRotation;
|
|
||||||
accumulatedVerticalRotation = 0.0;
|
|
||||||
} else {
|
|
||||||
verticalRotation = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply capping constraints
|
|
||||||
if (property4) { // Cap Left/Right
|
|
||||||
currentHorizontal += horizontalRotation;
|
currentHorizontal += horizontalRotation;
|
||||||
if (currentHorizontal > maxHorizontal) {
|
if (currentHorizontal > maxHorizontal) {
|
||||||
horizontalRotation -= (currentHorizontal - maxHorizontal);
|
horizontalRotation -= (currentHorizontal - maxHorizontal);
|
||||||
@ -181,7 +111,7 @@ class MouseLookNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property5) { // Cap Up/Down
|
if (property5) {
|
||||||
currentVertical += verticalRotation;
|
currentVertical += verticalRotation;
|
||||||
if (currentVertical > maxVertical) {
|
if (currentVertical > maxVertical) {
|
||||||
verticalRotation -= (currentVertical - maxVertical);
|
verticalRotation -= (currentVertical - maxVertical);
|
||||||
@ -192,40 +122,37 @@ class MouseLookNode extends LogicNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply horizontal rotation to body (yaw)
|
if (horizontalRotation != 0.0) {
|
||||||
if (Math.abs(horizontalRotation) > 0.01) { // 0.01 degrees threshold
|
bodyObject.transform.rotate(horizontalAxis, horizontalRotation);
|
||||||
bodyObject.transform.rotate(horizontalAxis, horizontalRotation * Math.PI / 180.0); // Convert degrees to radians
|
|
||||||
|
|
||||||
// Sync physics if needed
|
|
||||||
#if lnx_physics
|
#if lnx_physics
|
||||||
var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody);
|
var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody);
|
||||||
if (rigidBody != null) rigidBody.syncTransform();
|
if (rigidBody != null) rigidBody.syncTransform();
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply vertical rotation to head (pitch) if head object is provided
|
if (headObject != null && verticalRotation != 0.0) {
|
||||||
if (headObject != null && Math.abs(verticalRotation) > 0.01) { // 0.01 degrees threshold
|
if (property7) {
|
||||||
// For head rotation, use the head's local coordinate system
|
// Local space rotation (recommended for FPS)
|
||||||
|
headObject.transform.rotate(verticalAxis, verticalRotation);
|
||||||
|
} else {
|
||||||
|
// World space rotation
|
||||||
var headVerticalAxis = headObject.transform.world.right();
|
var headVerticalAxis = headObject.transform.world.right();
|
||||||
headObject.transform.rotate(headVerticalAxis, verticalRotation * Math.PI / 180.0); // Convert degrees to radians
|
headObject.transform.rotate(headVerticalAxis, verticalRotation);
|
||||||
|
}
|
||||||
|
|
||||||
// Sync physics if needed
|
|
||||||
#if lnx_physics
|
#if lnx_physics
|
||||||
var headRigidBody = headObject.getTrait(leenkx.trait.physics.RigidBody);
|
var headRigidBody = headObject.getTrait(leenkx.trait.physics.RigidBody);
|
||||||
if (headRigidBody != null) headRigidBody.syncTransform();
|
if (headRigidBody != null) headRigidBody.syncTransform();
|
||||||
#end
|
#end
|
||||||
} else if (headObject == null) {
|
} else if (headObject == null && verticalRotation != 0.0) {
|
||||||
// If no head object, apply vertical rotation to body as well
|
bodyObject.transform.rotate(verticalAxis, verticalRotation);
|
||||||
if (Math.abs(verticalRotation) > 0.01) { // 0.01 degrees threshold
|
|
||||||
bodyObject.transform.rotate(verticalAxis, verticalRotation * Math.PI / 180.0); // Convert degrees to radians
|
|
||||||
|
|
||||||
// Sync physics if needed
|
|
||||||
#if lnx_physics
|
#if lnx_physics
|
||||||
var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody);
|
var rigidBody = bodyObject.getTrait(leenkx.trait.physics.RigidBody);
|
||||||
if (rigidBody != null) rigidBody.syncTransform();
|
if (rigidBody != null) rigidBody.syncTransform();
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
runOutput(0);
|
runOutput(0);
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,7 @@ class MouseLookNode(LnxLogicTreeNode):
|
|||||||
"""Controls object rotation based on mouse movement for FPS-style camera control.
|
"""Controls object rotation based on mouse movement for FPS-style camera control.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Sub-pixel interpolation (always enabled) for optimal precision and smooth low-sensitivity movement
|
- Built-in resolution-adaptive scaling for consistent feel across different screen resolutions
|
||||||
- Resolution-adaptive scaling for consistent feel across different screen resolutions
|
|
||||||
"""
|
"""
|
||||||
bl_idname = 'LNMouseLookNode'
|
bl_idname = 'LNMouseLookNode'
|
||||||
bl_label = 'Mouse Look'
|
bl_label = 'Mouse Look'
|
||||||
@ -56,11 +55,11 @@ class MouseLookNode(LnxLogicTreeNode):
|
|||||||
description='Limit vertical rotation',
|
description='Limit vertical rotation',
|
||||||
default=True)
|
default=True)
|
||||||
|
|
||||||
# Strategy toggles
|
# Head rotation space toggle
|
||||||
property6: HaxeBoolProperty(
|
property7: HaxeBoolProperty(
|
||||||
'property6',
|
'property7',
|
||||||
name='Resolution Adaptive',
|
name='Head Local Space',
|
||||||
description='Scale sensitivity based on screen resolution',
|
description='Enable it if the Head is child of the Body to avoid weird rotation',
|
||||||
default=False)
|
default=False)
|
||||||
|
|
||||||
|
|
||||||
@ -72,7 +71,7 @@ class MouseLookNode(LnxLogicTreeNode):
|
|||||||
self.add_input('LnxNodeSocketObject', 'Body')
|
self.add_input('LnxNodeSocketObject', 'Body')
|
||||||
self.add_input('LnxNodeSocketObject', 'Head')
|
self.add_input('LnxNodeSocketObject', 'Head')
|
||||||
self.add_input('LnxFloatSocket', 'Sensitivity', default_value=0.5)
|
self.add_input('LnxFloatSocket', 'Sensitivity', default_value=0.5)
|
||||||
self.add_input('LnxFloatSocket', 'Smoothing', default_value=0.0)
|
self.add_input('LnxFactorSocket', 'Smoothing', default_value=0.0)
|
||||||
|
|
||||||
self.add_output('LnxNodeSocketAction', 'Out')
|
self.add_output('LnxNodeSocketAction', 'Out')
|
||||||
|
|
||||||
@ -92,10 +91,6 @@ class MouseLookNode(LnxLogicTreeNode):
|
|||||||
col.prop(self, 'property4', text='Cap Left / Right')
|
col.prop(self, 'property4', text='Cap Left / Right')
|
||||||
col.prop(self, 'property5', text='Cap Up / Down')
|
col.prop(self, 'property5', text='Cap Up / Down')
|
||||||
|
|
||||||
# Separator
|
# Head behavior section
|
||||||
layout.separator()
|
|
||||||
|
|
||||||
# Enhancement strategies section
|
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.label(text="Enhancement Strategies:")
|
col.prop(self, 'property7', text='Head Local Space')
|
||||||
col.prop(self, 'property6', text='Resolution Adaptive')
|
|
Reference in New Issue
Block a user