Add support for smooth follow to the effect (no UI updates)
This commit is contained in:
parent
5623fe1126
commit
ea01dd79bd
|
|
@ -73,7 +73,6 @@ if [[ ! -f "$BASH_PROFILE" ]] || ! grep -Fq "$QT_PLUGIN_EXPORT" "$BASH_PROFILE"
|
|||
|
||||
# Added by Breezy Desktop installer: QT plugin path setup
|
||||
$QT_PLUGIN_EXPORT
|
||||
export QT_DEBUG_PLUGINS=1
|
||||
EOF
|
||||
fi
|
||||
|
||||
|
|
@ -85,7 +84,6 @@ if [[ ! -f "$PLASMA_ENV_SCRIPT" ]]; then
|
|||
|
||||
# Added by Breezy Desktop installer: QT plugin path setup
|
||||
$QT_PLUGIN_EXPORT
|
||||
export QT_DEBUG_PLUGINS=1
|
||||
EOF
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -487,6 +487,14 @@ bool BreezyDesktopEffect::mirrorPhysicalDisplays() const {
|
|||
return m_mirrorPhysicalDisplays;
|
||||
}
|
||||
|
||||
QList<QQuaternion> BreezyDesktopEffect::smoothFollowOrigin() const {
|
||||
return m_smoothFollowOrigin;
|
||||
}
|
||||
|
||||
bool BreezyDesktopEffect::smoothFollowEnabled() const {
|
||||
return m_smoothFollowEnabled;
|
||||
}
|
||||
|
||||
bool BreezyDesktopEffect::checkParityByte(const char* data) {
|
||||
const uint8_t parityByte = static_cast<uint8_t>(data[DataView::IMU_PARITY_BYTE[DataView::OFFSET_INDEX]]);
|
||||
uint8_t parity = 0;
|
||||
|
|
@ -627,6 +635,33 @@ void BreezyDesktopEffect::updateImuRotation() {
|
|||
m_imuTimeElapsedMs = static_cast<quint32>(imuData[imuDataOffset + 0] - imuData[imuDataOffset + 1]);
|
||||
|
||||
m_imuTimestamp = imuDateMs;
|
||||
|
||||
float originData[4 * DataView::IMU_QUAT_ENTRIES]; // 4 quaternion-sized rows
|
||||
memcpy(originData, data + DataView::SMOOTH_FOLLOW_ORIGIN_DATA[DataView::OFFSET_INDEX], sizeof(originData));
|
||||
|
||||
// convert NWU to EUS by passing root.rotation values: -y, z, -x
|
||||
QQuaternion sfQuatT0(originData[3], -originData[1], originData[2], -originData[0]);
|
||||
|
||||
int originDataOffset = DataView::IMU_QUAT_ENTRIES;
|
||||
QQuaternion sfQuatT1(originData[originDataOffset + 3], -originData[originDataOffset + 1], originData[originDataOffset + 2], -originData[originDataOffset + 0]);
|
||||
|
||||
originDataOffset += DataView::IMU_QUAT_ENTRIES;
|
||||
|
||||
// skip the 3rd quaternion
|
||||
originDataOffset += DataView::IMU_QUAT_ENTRIES;
|
||||
|
||||
// set smoothFollowOrigin to the last two rotations, leave out the elapsed time
|
||||
m_smoothFollowOrigin.clear();
|
||||
m_smoothFollowOrigin.append(sfQuatT0);
|
||||
m_smoothFollowOrigin.append(sfQuatT1);
|
||||
|
||||
uint8_t smoothFollowEnabled = false;
|
||||
memcpy(&smoothFollowEnabled, data + DataView::SMOOTH_FOLLOW_ENABLED[DataView::OFFSET_INDEX], sizeof(smoothFollowEnabled));
|
||||
bool nextSmoothFollowEnabled = (smoothFollowEnabled != 0);
|
||||
if (m_smoothFollowEnabled != nextSmoothFollowEnabled) {
|
||||
m_smoothFollowEnabled = nextSmoothFollowEnabled;
|
||||
Q_EMIT smoothFollowEnabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString BreezyDesktopEffect::cursorImageSource() const
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ namespace KWin
|
|||
Q_PROPERTY(int displayWrappingScheme READ displayWrappingScheme NOTIFY displayWrappingSchemeChanged)
|
||||
Q_PROPERTY(qreal diagonalFOV READ diagonalFOV NOTIFY devicePropertiesChanged)
|
||||
Q_PROPERTY(qreal lensDistanceRatio READ lensDistanceRatio NOTIFY devicePropertiesChanged)
|
||||
Q_PROPERTY(bool sbsEnabled READ sbsEnabled NOTIFY devicePropertiesChanged)
|
||||
Q_PROPERTY(bool sbsEnabled READ sbsEnabled NOTIFY sbsEnabledChanged)
|
||||
Q_PROPERTY(bool smoothFollowEnabled READ smoothFollowEnabled NOTIFY smoothFollowEnabledChanged)
|
||||
Q_PROPERTY(QList<QQuaternion> smoothFollowOrigin READ smoothFollowOrigin)
|
||||
Q_PROPERTY(bool customBannerEnabled READ customBannerEnabled NOTIFY devicePropertiesChanged)
|
||||
Q_PROPERTY(int antialiasingQuality READ antialiasingQuality NOTIFY antialiasingQualityChanged)
|
||||
Q_PROPERTY(bool removeVirtualDisplaysOnDisable READ removeVirtualDisplaysOnDisable NOTIFY removeVirtualDisplaysOnDisableChanged)
|
||||
|
|
@ -79,6 +81,8 @@ namespace KWin
|
|||
qreal diagonalFOV() const;
|
||||
qreal lensDistanceRatio() const;
|
||||
bool sbsEnabled() const;
|
||||
bool smoothFollowEnabled() const;
|
||||
QList<QQuaternion> smoothFollowOrigin() const;
|
||||
bool customBannerEnabled() const;
|
||||
int antialiasingQuality() const;
|
||||
bool removeVirtualDisplaysOnDisable() const;
|
||||
|
|
@ -110,12 +114,14 @@ namespace KWin
|
|||
void enabledStateChanged();
|
||||
void zoomOnFocusChanged();
|
||||
void imuResetStateChanged();
|
||||
void cursorImageSourceChanged();
|
||||
void cursorPosChanged();
|
||||
void sbsEnabledChanged();
|
||||
void smoothFollowEnabledChanged();
|
||||
void devicePropertiesChanged();
|
||||
void antialiasingQualityChanged();
|
||||
void removeVirtualDisplaysOnDisableChanged();
|
||||
void mirrorPhysicalDisplaysChanged();
|
||||
void cursorImageSourceChanged();
|
||||
void cursorPosChanged();
|
||||
|
||||
protected:
|
||||
QVariantMap initialProperties(Output *screen) override;
|
||||
|
|
@ -142,6 +148,8 @@ namespace KWin
|
|||
qreal m_diagonalFOV;
|
||||
qreal m_lensDistanceRatio;
|
||||
bool m_sbsEnabled;
|
||||
bool m_smoothFollowEnabled;
|
||||
QList<QQuaternion> m_smoothFollowOrigin;
|
||||
bool m_customBannerEnabled;
|
||||
QFileSystemWatcher *m_shmFileWatcher = nullptr;
|
||||
QFileSystemWatcher *m_shmDirectoryWatcher = nullptr;
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ Node {
|
|||
id: breezyDesktop
|
||||
|
||||
property var viewportResolution: effect.displayResolution
|
||||
property bool smoothFollowEnabled: effect.smoothFollowEnabled
|
||||
required property var screens
|
||||
required property var fovDetails
|
||||
required property var monitorPlacements
|
||||
property int focusedMonitorIndex: -1
|
||||
property var smoothFollowFocusedDisplay
|
||||
|
||||
Displays {
|
||||
id: displays
|
||||
|
|
@ -22,6 +24,124 @@ Node {
|
|||
return breezyDesktopDisplays.objectAt(index);
|
||||
}
|
||||
|
||||
function updateFocus(smoothFollowEnabledChanged = false) {
|
||||
const rotations = smoothFollowEnabled ? effect.smoothFollowOrigin : effect.imuRotations;
|
||||
if (rotations && rotations.length > 0) {
|
||||
let focusedIndex = -1;
|
||||
|
||||
if (effect.zoomOnFocusEnabled || smoothFollowEnabled) {
|
||||
focusedIndex = displays.findFocusedMonitor(
|
||||
displays.eusToNwuQuat(rotations[0]),
|
||||
breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook),
|
||||
breezyDesktop.focusedMonitorIndex,
|
||||
smoothFollowEnabled,
|
||||
breezyDesktop.fovDetails,
|
||||
breezyDesktop.screens.map(screen => screen.geometry)
|
||||
);
|
||||
}
|
||||
|
||||
let focusedDisplay;
|
||||
let unfocusedDisplay;
|
||||
let startSmoothFollowFocusAnimation = false;
|
||||
if (smoothFollowEnabledChanged) {
|
||||
let targetDisplay;
|
||||
let targetProgress;
|
||||
if (focusedIndex !== -1) {
|
||||
focusedDisplay = breezyDesktop.displayAtIndex(focusedIndex);
|
||||
targetDisplay = focusedDisplay;
|
||||
targetProgress = 1.0;
|
||||
startSmoothFollowFocusAnimation = true;
|
||||
} else if (breezyDesktop.focusedMonitorIndex !== -1) {
|
||||
unfocusedDisplay = breezyDesktop.displayAtIndex(breezyDesktop.focusedMonitorIndex);
|
||||
targetDisplay = unfocusedDisplay;
|
||||
targetProgress = 0.0;
|
||||
}
|
||||
smoothFollowTransitionAnimation.stop();
|
||||
smoothFollowTransitionAnimation.target = targetDisplay;
|
||||
smoothFollowTransitionAnimation.from = targetDisplay.smoothFollowTransitionProgress;
|
||||
smoothFollowTransitionAnimation.to = targetProgress;
|
||||
smoothFollowTransitionAnimation.start();
|
||||
}
|
||||
|
||||
if (focusedIndex !== breezyDesktop.focusedMonitorIndex) {
|
||||
const unfocusedIndex = breezyDesktop.focusedMonitorIndex;
|
||||
if (!focusedDisplay) focusedDisplay = focusedIndex !== -1 ? breezyDesktop.displayAtIndex(focusedIndex) : null;
|
||||
const allDisplaysDistanceBinding = Qt.binding(function() { return effect.allDisplaysDistance; });
|
||||
const focusedDisplayDistanceBinding = Qt.binding(function() { return effect.focusedDisplayDistance; });
|
||||
if (focusedDisplay === null) {
|
||||
if (!unfocusedDisplay) unfocusedDisplay = breezyDesktop.displayAtIndex(unfocusedIndex);
|
||||
zoomOutAnimation.target = unfocusedDisplay;
|
||||
zoomOutAnimation.target.targetDistance = effect.allDisplaysDistance;
|
||||
zoomOutAnimation.start();
|
||||
} else {
|
||||
if (unfocusedIndex === -1) {
|
||||
zoomInAnimation.target = focusedDisplay;
|
||||
focusedDisplay.targetDistance = effect.focusedDisplayDistance;
|
||||
zoomInAnimation.start();
|
||||
} else {
|
||||
zoomInSeqAnimation.target = focusedDisplay;
|
||||
focusedDisplay.targetDistance = effect.focusedDisplayDistance;
|
||||
|
||||
if (!unfocusedDisplay) unfocusedDisplay = breezyDesktop.displayAtIndex(unfocusedIndex);
|
||||
zoomOutSeqAnimation.target = unfocusedDisplay;
|
||||
zoomOutSeqAnimation.target.targetDistance = effect.allDisplaysDistance;
|
||||
|
||||
zoomOnFocusSequence.start();
|
||||
}
|
||||
}
|
||||
breezyDesktop.focusedMonitorIndex = focusedIndex;
|
||||
}
|
||||
|
||||
if (startSmoothFollowFocusAnimation) smoothFollowFocusedAnimation.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// monitorPlacement assumed to be present
|
||||
function displayEusVector(display) {
|
||||
const displayNwu =
|
||||
display.monitorPlacement.centerNoRotate
|
||||
.times(display.monitorDistance / effect.allDisplaysDistance);
|
||||
|
||||
return displays.nwuToEusVector(displayNwu);
|
||||
}
|
||||
|
||||
function displayRotationVector(display, eusVector) {
|
||||
return display.rotationMatrix.times(eusVector);
|
||||
}
|
||||
|
||||
// smoothFollowOrigin is the rotation away from the original placement of the displays
|
||||
// imuRotations is the smooth follow rotation relative to the camera (very near an identity quat)
|
||||
// subtract the latter from the former to get the complete rotation
|
||||
function smoothFollowQuat() {
|
||||
return effect.smoothFollowOrigin[0].times(effect.imuRotations[0].conjugated());
|
||||
}
|
||||
|
||||
function displaySmoothFollowVector(display, eusVector) {
|
||||
return smoothFollowQuat().times(eusVector);
|
||||
}
|
||||
|
||||
// don't call this from the delegate to avoid binding the position property to the effect properties
|
||||
// used for smooth follow
|
||||
function displayPosition(display, smoothFollowRotation) {
|
||||
const displayEus = displayEusVector(display);
|
||||
|
||||
// short circuit to avoid slerping if not needed
|
||||
if (display.smoothFollowTransitionProgress === 0.0) {
|
||||
return displayRotationVector(display, displayEus);
|
||||
}
|
||||
if (display.smoothFollowTransitionProgress === 1.0) {
|
||||
return displaySmoothFollowVector(display, displayEus, smoothFollowRotation);
|
||||
}
|
||||
|
||||
const finalPosition = displays.slerpVector(
|
||||
displayRotationVector(display, displayEus),
|
||||
displaySmoothFollowVector(display, displayEus, smoothFollowRotation),
|
||||
display.smoothFollowTransitionProgress
|
||||
);
|
||||
|
||||
return finalPosition
|
||||
}
|
||||
|
||||
Repeater3D {
|
||||
id: breezyDesktopDisplays
|
||||
model: breezyDesktop.screens.length
|
||||
|
|
@ -29,6 +149,7 @@ Node {
|
|||
screen: breezyDesktop.screens[index]
|
||||
monitorPlacement: breezyDesktop.monitorPlacements[index]
|
||||
|
||||
property real smoothFollowTransitionProgress: 0.0
|
||||
property real monitorDistance: effect.allDisplaysDistance
|
||||
property real targetDistance: effect.allDisplaysDistance
|
||||
property real screenRotationY: displays.radianToDegree(monitorPlacement?.rotationAngleRadians.y ?? 0)
|
||||
|
|
@ -53,64 +174,63 @@ Node {
|
|||
position: {
|
||||
if (!monitorPlacement) return Qt.vector3d(0, 0, 0);
|
||||
|
||||
const displayNwu =
|
||||
monitorPlacement.centerNoRotate
|
||||
.times(monitorDistance / effect.allDisplaysDistance);
|
||||
|
||||
|
||||
return rotationMatrix.times(displays.nwuToEusVector(displayNwu));
|
||||
return displayRotationVector(this, displayEusVector(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// smoothFollowEnabled gets cleared before the IMU begins slerping back to the origin so we can't just
|
||||
// switch off smooth follow logic based on this flag. Instead, we have to rely on
|
||||
// smoothFollowTransitionProgress to determine how much of the IMU positions to apply.
|
||||
onSmoothFollowEnabledChanged: {
|
||||
updateFocus(true);
|
||||
}
|
||||
|
||||
FrameAnimation {
|
||||
id: smoothFollowFocusedAnimation
|
||||
running: false
|
||||
onTriggered: {
|
||||
if (!breezyDesktop.smoothFollowFocusedDisplay && breezyDesktop.focusedMonitorIndex !== -1) {
|
||||
breezyDesktop.smoothFollowFocusedDisplay = breezyDesktopDisplays.objectAt(breezyDesktop.focusedMonitorIndex)
|
||||
}
|
||||
|
||||
let continueRunning = false;
|
||||
const focusedDisplay = breezyDesktop.smoothFollowFocusedDisplay;
|
||||
if (focusedDisplay) {
|
||||
const smoothFollowRotation = smoothFollowQuat();
|
||||
focusedDisplay.position = displayPosition(focusedDisplay, smoothFollowRotation);
|
||||
continueRunning = focusedDisplay.smoothFollowTransitionProgress > 0.0;
|
||||
|
||||
if (continueRunning) {
|
||||
focusedDisplay.eulerRotation = Qt.vector3d(0, 0, 0);
|
||||
focusedDisplay.rotation = smoothFollowRotation;
|
||||
} else {
|
||||
focusedDisplay.eulerRotation.x = focusedDisplay.screenRotationX;
|
||||
focusedDisplay.eulerRotation.y = focusedDisplay.screenRotationY;
|
||||
focusedDisplay.eulerRotation.z = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!continueRunning) {
|
||||
smoothFollowFocusedAnimation.stop();
|
||||
breezyDesktop.smoothFollowFocusedDisplay = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: smoothFollowTransitionAnimation
|
||||
duration: 150
|
||||
property: "smoothFollowTransitionProgress"
|
||||
running: false
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 500 // 500ms - 2x per second to avoid running this check too frequently
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: {
|
||||
if (effect.imuRotations && effect.imuRotations.length > 0) {
|
||||
let focusedIndex = -1;
|
||||
|
||||
if (effect.zoomOnFocusEnabled) {
|
||||
focusedIndex = displays.findFocusedMonitor(
|
||||
displays.eusToNwuQuat(effect.imuRotations[0]),
|
||||
breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook),
|
||||
breezyDesktop.focusedMonitorIndex,
|
||||
false, // TODO smooth follow
|
||||
breezyDesktop.fovDetails,
|
||||
breezyDesktop.screens.map(screen => screen.geometry)
|
||||
);
|
||||
}
|
||||
|
||||
if (focusedIndex !== breezyDesktop.focusedMonitorIndex) {
|
||||
const unfocusedIndex = breezyDesktop.focusedMonitorIndex;
|
||||
const focusedDisplay = focusedIndex !== -1 ? breezyDesktop.displayAtIndex(focusedIndex) : null;
|
||||
const allDisplaysDistanceBinding = Qt.binding(function() { return effect.allDisplaysDistance; });
|
||||
const focusedDisplayDistanceBinding = Qt.binding(function() { return effect.focusedDisplayDistance; });
|
||||
if (focusedDisplay === null) {
|
||||
const unfocusedDisplay = breezyDesktop.displayAtIndex(unfocusedIndex);
|
||||
zoomOutAnimation.target = unfocusedDisplay;
|
||||
zoomOutAnimation.target.targetDistance = effect.allDisplaysDistance;
|
||||
zoomOutAnimation.start();
|
||||
} else {
|
||||
if (unfocusedIndex === -1) {
|
||||
zoomInAnimation.target = focusedDisplay;
|
||||
focusedDisplay.targetDistance = effect.focusedDisplayDistance;
|
||||
zoomInAnimation.start();
|
||||
} else {
|
||||
zoomInSeqAnimation.target = focusedDisplay;
|
||||
focusedDisplay.targetDistance = effect.focusedDisplayDistance;
|
||||
|
||||
const unfocusedDisplay = breezyDesktop.displayAtIndex(unfocusedIndex);
|
||||
zoomOutSeqAnimation.target = unfocusedDisplay;
|
||||
zoomOutSeqAnimation.target.targetDistance = effect.allDisplaysDistance;
|
||||
|
||||
zoomOnFocusSequence.start();
|
||||
}
|
||||
}
|
||||
breezyDesktop.focusedMonitorIndex = focusedIndex;
|
||||
}
|
||||
}
|
||||
updateFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,11 +240,14 @@ Node {
|
|||
zoomOutAnimation.stop();
|
||||
zoomInAnimation.stop();
|
||||
zoomOnFocusSequence.stop();
|
||||
smoothFollowTransitionAnimation.stop();
|
||||
smoothFollowFocusedAnimation.stop();
|
||||
|
||||
zoomOutAnimation.target = null;
|
||||
zoomInAnimation.target = null;
|
||||
zoomOutSeqAnimation.target = null;
|
||||
zoomInSeqAnimation.target = null;
|
||||
smoothFollowTransitionAnimation.target = null;
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
|
|
|
|||
|
|
@ -12,19 +12,24 @@ Item {
|
|||
property real lensDistanceRatio: effect.lensDistanceRatio
|
||||
property bool sbsEnabled: effect.sbsEnabled
|
||||
property bool customBannerEnabled: effect.customBannerEnabled
|
||||
property bool smoothFollowEnabled: effect.smoothFollowEnabled
|
||||
|
||||
// if true, then smoothFollowEnabled just cleared and the IMU data is slerping back,
|
||||
// continue to use the origin data for the duration of the Timer
|
||||
property bool smoothFollowDisabling: false
|
||||
|
||||
Displays {
|
||||
id: displays
|
||||
}
|
||||
|
||||
function updateCamera() {
|
||||
function updateCamera(rotations) {
|
||||
camera.eulerRotation = applyLookAhead(
|
||||
effect.imuRotations[0],
|
||||
effect.imuRotations[1],
|
||||
rotations[0],
|
||||
rotations[1],
|
||||
effect.imuTimeElapsedMs,
|
||||
lookAheadMS(effect.imuTimestamp, effect.lookAheadConfig, effect.lookAheadOverride)
|
||||
);
|
||||
camera.position = effect.imuRotations[0].times(Qt.vector3d(0, 0, -fovDetails.lensDistancePixels));
|
||||
camera.position = rotations[0].times(Qt.vector3d(0, 0, -fovDetails.lensDistancePixels));
|
||||
}
|
||||
|
||||
// how far to look ahead is how old the IMU data is plus a constant that is either the default for this device or an override
|
||||
|
|
@ -71,9 +76,25 @@ Item {
|
|||
FrameAnimation {
|
||||
running: true
|
||||
onTriggered: {
|
||||
if (effect.imuRotations && effect.imuRotations.length > 0) {
|
||||
updateCamera();
|
||||
const rotations = (effect.smoothFollowEnabled || smoothFollowDisabling) ? effect.smoothFollowOrigin : effect.imuRotations;
|
||||
if (rotations && rotations.length > 0) {
|
||||
updateCamera(rotations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: smoothFollowDisablingTimer
|
||||
interval: 750
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
cameraController.smoothFollowDisabling = false;
|
||||
}
|
||||
}
|
||||
|
||||
onSmoothFollowEnabledChanged: {
|
||||
smoothFollowDisablingTimer.stop();
|
||||
smoothFollowDisabling = !smoothFollowEnabled;
|
||||
if (smoothFollowDisabling) smoothFollowDisablingTimer.start();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,6 +336,8 @@ QtObject {
|
|||
}
|
||||
|
||||
function findFocusedMonitor(quaternion, monitorVectors, currentFocusedIndex, smoothFollowEnabled, fovDetails, monitorsDetails) {
|
||||
if (currentFocusedIndex !== -1 && smoothFollowEnabled) return currentFocusedIndex;
|
||||
|
||||
var lookVector = Qt.vector3d(1.0, 0.0, 0.0); // NWU vector pointing to the center of the screen
|
||||
var rotatedLookVector = quaternion.times(lookVector);
|
||||
|
||||
|
|
@ -369,8 +371,7 @@ QtObject {
|
|||
westConversionFns.angleToLength
|
||||
) * effect.focusedDisplayDistance / effect.allDisplaysDistance;
|
||||
|
||||
if (smoothFollowEnabled || focusedDistance < unfocusThreshold)
|
||||
return currentFocusedIndex;
|
||||
if (focusedDistance < unfocusThreshold) return currentFocusedIndex;
|
||||
}
|
||||
|
||||
var closestIndex = -1;
|
||||
|
|
@ -402,4 +403,15 @@ QtObject {
|
|||
// Unfocus all displays
|
||||
return -1;
|
||||
}
|
||||
|
||||
function slerpVector(from, to, progress) {
|
||||
const inverseProgress = 1.0 - progress;
|
||||
const finalVector = Qt.vector3d(
|
||||
from.x * inverseProgress + to.x * progress,
|
||||
from.y * inverseProgress + to.y * progress,
|
||||
from.z * inverseProgress + to.z * progress
|
||||
);
|
||||
|
||||
return finalVector;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue