diff --git a/Editor/ViewportController.cs b/Editor/ViewportController.cs old mode 100644 new mode 100755 index a346ec0..5b28b43 --- a/Editor/ViewportController.cs +++ b/Editor/ViewportController.cs @@ -134,9 +134,17 @@ static void Fly(SceneView sceneView, Vector3 translationInversion, Vector3 rotat { SyncRigWithScene(); + var rotation = SpaceNavigatorHID.current.Rotation.ReadValue(); + + // Check to see if we need to swap rotation Y and Z for fly mode + if(Settings.FlySwapRotationAxesYAndZ) + { + rotation = new Vector3(rotation.x, rotation.z, rotation.y); + } + // Apply inversion of axes for fly/grabmove mode. Vector3 translation = Vector3.Scale(SpaceNavigatorHID.current.Translation.ReadValue(), translationInversion); - Vector3 rotation = Vector3.Scale(SpaceNavigatorHID.current.Rotation.ReadValue(), rotationInversion); + rotation = Vector3.Scale(rotation, rotationInversion); // Apply sensitivity translation *= Settings.TransSens[Settings.CurrentGear]; diff --git a/Runtime/Settings/Settings.cs b/Runtime/Settings/Settings.cs old mode 100644 new mode 100755 index 3940c47..4794f27 --- a/Runtime/Settings/Settings.cs +++ b/Runtime/Settings/Settings.cs @@ -64,6 +64,8 @@ public static class Settings { public static bool RuntimeEditorNav = true; public static bool RuntimeEditorNavSuspendOnGameViewFocus; + public static bool FlySwapRotationAxesYAndZ; + // Inversion public static Vector3 FlyInvertTranslation, FlyInvertRotation; public static Vector3 OrbitInvertTranslation, OrbitInvertRotation; @@ -238,6 +240,7 @@ public static void OnGUI() { RuntimeEditorNav = GUILayout.Toggle(RuntimeEditorNav, "Runtime Editor Navigation"); EditorGUI.BeginDisabledGroup(!RuntimeEditorNav); RuntimeEditorNavSuspendOnGameViewFocus = GUILayout.Toggle(RuntimeEditorNavSuspendOnGameViewFocus, "Suspend on GameView focus"); + FlySwapRotationAxesYAndZ = GUILayout.Toggle(FlySwapRotationAxesYAndZ, "Swap Y and Z rotation axes in Fly mode"); EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); @@ -425,6 +428,7 @@ public static void Write() { // Runtime Editor Navigation PlayerPrefs.SetInt("RuntimeEditorNav", RuntimeEditorNav ? 1 : 0); PlayerPrefs.SetInt("RuntimeEditorNavWithFocussedGameView", RuntimeEditorNavSuspendOnGameViewFocus ? 1 : 0); + PlayerPrefs.SetInt("FlySwapRotationAxesYAndZ", FlySwapRotationAxesYAndZ ? 1 : 0); // Axis Inversions WriteAxisInversions(FlyInvertTranslation, FlyInvertRotation, "Fly"); WriteAxisInversions(OrbitInvertTranslation, OrbitInvertRotation, "Orbit"); @@ -469,6 +473,7 @@ public static void Read() { // Runtime Editor Navigation RuntimeEditorNav = PlayerPrefs.GetInt("RuntimeEditorNav", 1) == 1; RuntimeEditorNavSuspendOnGameViewFocus = PlayerPrefs.GetInt("RuntimeEditorNavWithFocussedGameView", 1) == 1; + FlySwapRotationAxesYAndZ = PlayerPrefs.GetInt("FlySwapRotationAxesYAndZ", 1) == 1; // Axis Inversions ReadAxisInversions(ref FlyInvertTranslation, ref FlyInvertRotation, "Fly"); ReadAxisInversions(ref OrbitInvertTranslation, ref OrbitInvertRotation, "Orbit"); diff --git a/Runtime/SpaceMouseWirelessHID.cs b/Runtime/SpaceMouseWirelessHID.cs new file mode 100755 index 0000000..c6805ff --- /dev/null +++ b/Runtime/SpaceMouseWirelessHID.cs @@ -0,0 +1,101 @@ +using UnityEditor; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; +using UnityEngine.InputSystem.Layouts; +using UnityEngine.InputSystem.Utilities; + +namespace SpaceNavigatorDriver +{ + struct SpaceMouseWirelessHIDState : IInputStateTypeInfo + { + public FourCC format => new FourCC('H', 'I', 'D'); + + public struct ReportFormat1 + { + public Vector3 translation; + public Vector3 rotation; + } + + public struct ReportFormat3 + { + public byte buttons; + } + + // 1st report + [InputControl(name = "translation", format = "VC3S", layout = "Vector3", displayName = "Translation")] + [InputControl(name = "translation/x", offset = 0, format = "SHRT", parameters = "scale=true, scaleFactor=10")] + [InputControl(name = "translation/y", offset = 4, format = "SHRT", parameters = "scale=true, scaleFactor=-10")] + [InputControl(name = "translation/z", offset = 2, format = "SHRT", parameters = "scale=true, scaleFactor=-10")] + [InputControl(name = "rotation", format = "VC3S", layout = "Vector3", displayName = "Rotation")] + [InputControl(name = "rotation/x", offset = 6, format = "SHRT", parameters = "scale=true, scaleFactor=-80")] + [InputControl(name = "rotation/y", offset = 8, format = "SHRT", parameters = "scale=true, scaleFactor=80")] + [InputControl(name = "rotation/z", offset = 10, format = "SHRT", parameters = "scale=true, scaleFactor=80")] + public ReportFormat1 report1; + + // 3rd report + [InputControl(name = "button1", bit = 0, format = "BIT", layout = "Button", displayName = "Button 1")] + [InputControl(name = "button2", bit = 1, format = "BIT", layout = "Button", displayName = "Button 2")] + public ReportFormat3 report3; + } + +#if UNITY_EDITOR + [InitializeOnLoad] // Make sure static constructor is called during startup. +#endif + [InputControlLayout(stateType = typeof(SpaceMouseWirelessHIDState))] + public class SpaceMouseWirelessHID : SpaceNavigatorHID + { + static SpaceMouseWirelessHID() + { + // Register a layout with product ID, so this layout will have a higher score than SpaceNavigatorHID + InputSystem.RegisterLayout( + matches: new InputDeviceMatcher() + .WithInterface("HID") + .WithManufacturer("3Dconnexion.*") + .WithCapability("productId", 0xC62E)); + DebugLog("SpaceMouseWirelessHID : Register layout for SpaceMouse Wireless productId:0xC62E"); + } + + // When one of our custom devices is removed, we want to make sure that if + // it is the '.current' device, we null out '.current'. + public override unsafe void OnStateEvent(InputEventPtr eventPtr) + { + // Refuse delta events. + if (eventPtr.IsA()) + return; + + var stateEventPtr = StateEvent.From(eventPtr); + if (stateEventPtr->stateFormat != new FourCC('H', 'I', 'D')) + return; + + var reportPtr = (byte*) stateEventPtr->state; + var reportId = *reportPtr; + var reportStatePtr = (reportPtr + 1); // or wherever the actual report starts. + + // We have two options here. We can either use InputState.Change with a DeltaStateEvent that we set up + // from the event we have received (and simply update either report1 or report2 only) or we can merge + // our current state with the state we have just received. The latter is simpler so we do that here. + + var newState = default(SpaceMouseWirelessHIDState); + // Can opt to only copy the state that we won't override. We don't bother here. + UnsafeUtility.MemCpy(&newState, (byte*) currentStatePtr + stateBlock.byteOffset, sizeof(SpaceMouseWirelessHIDState)); + + switch (reportId) + { + case 1: + UnsafeUtility.MemCpy(&newState.report1, reportStatePtr, sizeof(SpaceMouseWirelessHIDState.ReportFormat1)); + DebugLog("SpaceMouseWirelessHID : Copied report1"); + break; + case 3: + UnsafeUtility.MemCpy(&newState.report3, reportStatePtr, sizeof(SpaceMouseWirelessHIDState.ReportFormat3)); + DebugLog("SpaceMouseWirelessHID : Copied report3"); + break; + } + + // Apply the state change. Don't simply MemCpy over currentStatePtr as that will lead to various + // malfunctions. The system needs to do the memcpy itself. + InputState.Change(this, newState, eventPtr: eventPtr); + } + } +} \ No newline at end of file diff --git a/Runtime/SpaceMouseWirelessHID.cs.meta b/Runtime/SpaceMouseWirelessHID.cs.meta new file mode 100755 index 0000000..e7afe50 --- /dev/null +++ b/Runtime/SpaceMouseWirelessHID.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ac96e4caab42df45aed55e788458d79 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: