sjkillen / 3d-mouse-plus-blender

A Blender Addon for transforming objects with a 3DConnexion mouse
GNU General Public License v3.0
4 stars 0 forks source link

Spacemouse enterprise compatibility #7

Open sjkillen opened 2 years ago

sjkillen commented 2 years ago

Getting the plugin to work with the enterprise mouse which has different buttons than the tested model

almuradyh commented 2 years ago

I have the same keybinding Screenshot 2022-01-28 073225 maybe different its a different key cod and Blender keeps freezing and does not respond to anything except the closing key ❌ I even stopped the 3Dconnexion serves and got the same problem

sjkillen commented 2 years ago

On windows, the addon will directly access your 3D mouse so I think your keyboard shortcuts set in your 3D connexion app will be ignored. Could you try rebinding the NDOFTransform to a different button on your 3D mouse (In blender settings), it might register as a button that is not button 1, 2, menu, or fit... Changing this should allow you to trigger the NDOFTransform operator using your 3D mouse. That's odd, if you wait a few seconds does Windows say that Blender is not responding?

almuradyh commented 2 years ago

if I stop 3Dconnexion serves nothing registered and if I start 3Dconnexion serves the 3Dconnexion app is used so I tried to key bind it to a keyboard shortcut and blender freeze again I think it's not recognizing my 3dmouse

sjkillen commented 2 years ago

You shouldn't have to close the 3Dconnexion app. You can keep it running as usual, but the addon interacts directly with the USB device. So when you try to rebind the keyboard shortcut in Blender to a button Blender freezes before a different key is detected? That's very odd. There might be additional error logging outputted to the console. Could you try launching blender from the command line and try changing the keybinding inside Blender again?

You can do this by opening cmd.exe then typing C:\Users\YourUsername\...\Blender/blender.exe the additional logging will be output in this cmd window.

almuradyh commented 2 years ago

I got this at the end line of cmd

ndof: unknown 3Dconnexion product c654

sjkillen commented 2 years ago

Thanks, that's just a message outputted by Blender (I get the same message for c652 when I use Blender without the addon installed) I think that message just means that Blender recognizes the device. Do you get any other errors after blender freezes?

almuradyh commented 2 years ago

I got this after closing blender❌

Exception in module unregister(): 'C:\Users\**\AppData\Roaming\Blender Foundation\Blender\3.0\scripts\addons\3d-mouse-plus\init.py' Traceback (most recent call last): File "C:\Program Files (x86)\Steam\steamapps\common\Blender\3.0\scripts\modules\addon_utils.py", line 439, in disable mod.unregister() File "C:\Users**\AppData\Roaming\Blender Foundation\Blender\3.0\scripts\addons\3d-mouse-plus__init__.py", line 37, in unregister auto_load.unregister() File "C:\Users**\AppData\Roaming\Blender Foundation\Blender\3.0\scripts\addons\3d-mouse-plus\auto_load.py", line 48, in unregister module.unregister() File "C:\Users**\AppData\Roaming\Blender Foundation\Blender\3.0\scripts\addons\3d-mouse-plus\tool.py", line 274, in unregister spnav_listener.kill() File "C:\Users**\AppData\Roaming\Blender Foundation\Blender\3.0\scripts\addons\3d-mouse-plus\listener.py", line 118, in kill self.connection.close() AttributeError: 'NoneType' object has no attribute 'close' Exception ignored in: <function Pool.del at 0x00000201FC029F70> Traceback (most recent call last): File "C:\Program Files (x86)\Steam\steamapps\common\Blender\3.0\python\lib\multiprocessing\pool.py", line 268, in del__ File "C:\Program Files (x86)\Steam\steamapps\common\Blender\3.0\python\lib\multiprocessing\queues.py", line 375, in put File "C:\Program Files (x86)\Steam\steamapps\common\Blender\3.0\python\lib\multiprocessing\connection.py", line 205, in send_bytes File "C:\Program Files (x86)\Steam\steamapps\common\Blender\3.0\python\lib\multiprocessing\connection.py", line 285, in _send_bytes AttributeError: 'NoneType' object has no attribute 'WriteFile'

Blender quit Error: Not freed memory blocks: 16, total unfreed memory 0.001598 MB

sjkillen commented 2 years ago

Ah thanks, this helps a lot. It seems the close function is failing because the connect function failed. Looking deeper, it seems like the Spacemouse enterprise isn't supported by the library I'm using on Windows github.com/johnhw/pyspacenavigator I'll shop around for another library that supports Enterprise and let you know when I have something that will work

techdragon commented 2 years ago

@sjkillen is there a reason you aren't using the actual 3DConnexion SDK ? You can dynamically load the SDK library in python and use it like the spnav.py does with the windows library. You might need separate platform specific listeners to translate though since 3DConnexion don't make any guarantees about having consistent API naming between macOS/Linux/windows ... so while it will probably be 95% logically the same between each ctypes wrapper, it might need a bunch of differently named enums/pointers/etc in each platform specific wrapper.

sjkillen commented 2 years ago

@techdragon Hmm, When I poked around for Windows Python libraries I didn't really find anything else so I don't think anyone's created/published Python bindings for the official SDK. Maybe that'll give me an excuse to play around with bindgen, I'll give it a look, thanks. The current implementation already splits the listeners between Linux and Windows, but the library I'm using for Windows is fairly limited.

techdragon commented 2 years ago

@sjkillen There aren't any official python bindings, but I generated some when I took a stab at building this myself... but learning blender's internal APIs just to fix weird peripheral behaviour, before I even start using blender, sort of feels like putting the cart before the horse. I was able to generate a basic wrapper for the macOS SDK using https://github.com/ctypesgen/ctypesgen. Using the official SDK might also be a good way to take care of #10

I did re-order my result a bit so if you use ctypesgen it won't look exactly like this. But it generated basically everything here, I've just renamed & reordered it, and fixed a few of the c string constants it made mistakes with (it had trouble with 1 struct for no obvious reason, but it was as simple as copying the correct 4 char strings over the incorrect 1 char strings ctypesgen produced)

r"""Wrapper for ConnexionClient.h

Generated with:
/Users/techdragon/.local/bin/ctypesgen --no-embed-preamble -l 3DconnexionClient /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h -o ./connexion_client.pxd

Do not modify this file.
"""

__docformat__ = "restructuredtext"

# Begin preamble for Python

from .ctypes_preamble import *
from .ctypes_preamble import _variadic_function

# End preamble

_libs = {}
_libdirs = []

# Begin loader

from .ctypes_loader import *

# End loader

add_library_search_dirs([])

# Begin libraries
_libs["3DconnexionClient"] = load_library("3DconnexionClient")

# 1 libraries
# End libraries

# No modules

# Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint8_t.h: 31
uint8_t = c_ubyte
# Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint16_t.h: 31
uint16_t = c_ushort
# Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint32_t.h: 31
uint32_t = c_uint
# Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/_types/_uint64_t.h: 31
uint64_t = c_ulonglong

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 26
kConnexionClientWildcard = 0x2A2A2A2A
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 32
kConnexionClientManual = 0x2B2B2B2B

# Operating Mode C Enum
# # /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 38
# enum_anon_2 = c_int
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 38
kConnexionClientModeTakeOver: c_int = c_int(1)
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 38
kConnexionClientModePlugin: c_int = c_int(2)

# Client Command C Enum
# # /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 48
# enum_anon_3 = c_int
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 48
kConnexionCmdNone: c_int = c_int(0)
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 48
kConnexionCmdHandleRawData: c_int = c_int(1)
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 48
kConnexionCmdHandleButtons: c_int = c_int(2)
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 48
kConnexionCmdHandleAxis: c_int = c_int(3)
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 48
kConnexionCmdAppSpecific: c_int = c_int(10)

# ==============================================================================
# Messages
# \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

# The following messages are forwarded to user space clients:

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 62 -> 64
kConnexionMsgDeviceState = '3dSR'
kConnexionMsgPrefsChanged = '3dPC'
kConnexionMsgCalibrateDevice = '3dSC'

# Control messages for the driver sent via the ConnexionControl API:

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 68 -> 75
kConnexionCtlSetLEDState = '3dsl'
kConnexionCtlGetDeviceID = '3did'
kConnexionCtlCalibrate = '3dca'
kConnexionCtlUncalibrate = '3dde'
kConnexionCtlOpenPrefPane = '3dop'
kConnexionCtlSetSwitches = '3dss'
kConnexionCtlActivateClient = '3dac'
kConnexionCtlDeactivateClient = '3ddc'

# Client capability mask constants
# (this mask defines which buttons and controls should be sent to clients, the others are handled by the driver)

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 79 -> 99
kConnexionMaskButton1 = 0x0001
kConnexionMaskButton2 = 0x0002
kConnexionMaskButton3 = 0x0004
kConnexionMaskButton4 = 0x0008
kConnexionMaskButton5 = 0x0010
kConnexionMaskButton6 = 0x0020
kConnexionMaskButton7 = 0x0040
kConnexionMaskButton8 = 0x0080
kConnexionMaskAxis1 = 0x0100
kConnexionMaskAxis2 = 0x0200
kConnexionMaskAxis3 = 0x0400
kConnexionMaskAxis4 = 0x0800
kConnexionMaskAxis5 = 0x1000
kConnexionMaskAxis6 = 0x2000
kConnexionMaskButtons = 0x00FF
kConnexionMaskAxisTrans = 0x0700
kConnexionMaskAxisRot = 0x3800
kConnexionMaskAxis = 0x3F00
kConnexionMaskAll = 0x3FFF

# Added in version 10:0 to support all 32 buttons on the SpacePilot Pro,
# use with the new SetConnexionClientButtonMask API

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 103 -> 130
kConnexionMaskButton9 = 0x00000100
kConnexionMaskButton10 = 0x00000200
kConnexionMaskButton11 = 0x00000400
kConnexionMaskButton12 = 0x00000800
kConnexionMaskButton13 = 0x00001000
kConnexionMaskButton14 = 0x00002000
kConnexionMaskButton15 = 0x00004000
kConnexionMaskButton16 = 0x00008000
kConnexionMaskButton17 = 0x00010000
kConnexionMaskButton18 = 0x00020000
kConnexionMaskButton19 = 0x00040000
kConnexionMaskButton20 = 0x00080000
kConnexionMaskButton21 = 0x00100000
kConnexionMaskButton22 = 0x00200000
kConnexionMaskButton23 = 0x00400000
kConnexionMaskButton24 = 0x00800000
kConnexionMaskButton25 = 0x01000000
kConnexionMaskButton26 = 0x02000000
kConnexionMaskButton27 = 0x04000000
kConnexionMaskButton28 = 0x08000000
kConnexionMaskButton29 = 0x10000000
kConnexionMaskButton30 = 0x20000000
kConnexionMaskButton31 = 0x40000000
kConnexionMaskButton32 = 0x80000000
kConnexionMaskAllButtons = 0xFFFFFFFF

# Masks for client-controlled feature switches

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 134 -> 157
kConnexionSwitchDominant = 0x0002
kConnexionSwitchEnableAxis1 = 0x0004
kConnexionSwitchEnableAxis2 = 0x0008
kConnexionSwitchEnableAxis3 = 0x0010
kConnexionSwitchEnableAxis4 = 0x0020
kConnexionSwitchEnableAxis5 = 0x0040
kConnexionSwitchEnableAxis6 = 0x0080
kConnexionSwitchEnableTrans = 0x001C
kConnexionSwitchEnableRot = 0x00E0
kConnexionSwitchEnableAll = 0x00FC
kConnexionSwitchZoomOnY = 0x0001  # no longer applies, no effect on new driver
kConnexionSwitchReverseAxis1 = 0x0100  # no longer applies, no effect on new driver
kConnexionSwitchReverseAxis2 = 0x0200  # no longer applies, no effect on new driver
kConnexionSwitchReverseAxis3 = 0x0400  # no longer applies, no effect on new driver
kConnexionSwitchReverseAxis4 = 0x0800  # no longer applies, no effect on new driver
kConnexionSwitchReverseAxis5 = 0x1000  # no longer applies, no effect on new driver
kConnexionSwitchReverseAxis6 = 0x2000  # no longer applies, no effect on new driver
kConnexionSwitchReverseTrans = 0x0700  # no longer applies, no effect on new driver
kConnexionSwitchReverseRot = 0x3800  # no longer applies, no effect on new driver
kConnexionSwitchReverseAll = 0x3F00  # no longer applies, no effect on new driver
kConnexionSwitchesDisabled = 0x80000000  # use driver defaults instead of client-controlled switches

# ==============================================================================
# Device state record
# \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

# Structure type and current version:

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 164 -> 165
kConnexionDeviceStateType = 0x4D53
kConnexionDeviceStateVers = 0x6D33

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 185
class ConnexionDeviceState(Structure):
    _pack_ = 2
    __slots__ = [
        'version',
        'client',
        'command',
        'param',
        'value',
        'time',
        'report',
        'buttons8',
        'axis',
        'address',
        'buttons',
    ]
    _fields_ = [
        ('version', uint16_t),
        ('client', uint16_t),
        ('command', uint16_t),
        ('param', c_int16),
        ('value', c_int32),
        ('time', uint64_t),
        ('report', uint8_t * int(8)),
        ('buttons8', uint16_t),
        ('axis', c_int16 * int(6)),
        ('address', uint16_t),
        ('buttons', uint32_t),
    ]

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 185
ConnexionDeviceStatePtr = POINTER(ConnexionDeviceState)

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 234
class ConnexionDevicePrefs(Structure):
    _pack_ = 2
    __slots__ = [
        'type',
        'version',
        'deviceID',
        'reserved1',
        'appSignature',
        'reserved2',
        'appName',
        'mainSpeed',
        'zoomOnY',
        'dominant',
        'reserved3',
        'mapV',
        'mapH',
        'enabled',
        'reversed',
        'speed',
        'sensitivity',
        'scale',
        'gamma',
        'intersect',
    ]
    _fields_ = [
        ('type', uint16_t),
        ('version', uint16_t),
        ('deviceID', uint16_t),
        ('reserved1', uint16_t),
        ('appSignature', uint32_t),
        ('reserved2', uint32_t),
        ('appName', uint8_t * int(64)),
        ('mainSpeed', uint8_t),
        ('zoomOnY', uint8_t),
        ('dominant', uint8_t),
        ('reserved3', uint8_t),
        ('mapV', c_int8 * int(6)),
        ('mapH', c_int8 * int(6)),
        ('enabled', uint8_t * int(6)),
        ('reversed', uint8_t * int(6)),
        ('speed', uint8_t * int(6)),
        ('sensitivity', uint8_t * int(6)),
        ('scale', c_int32 * int(6)),
        ('gamma', uint32_t),
        ('intersect', uint32_t),
    ]

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 234
ConnexionDevicePrefsPtr: POINTER = POINTER(ConnexionDevicePrefs)

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 21
ConnexionAddedHandlerProc: CFUNCTYPE = CFUNCTYPE(UNCHECKED(None), c_uint)
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 22
ConnexionRemovedHandlerProc: CFUNCTYPE = CFUNCTYPE(UNCHECKED(None), c_uint)
# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 23
ConnexionMessageHandlerProc: CFUNCTYPE = CFUNCTYPE(UNCHECKED(None), c_uint, c_uint, POINTER(None))

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 32
if _libs["3DconnexionClient"].has("SetConnexionHandlers", "cdecl"):
    SetConnexionHandlers = _libs["3DconnexionClient"].get("SetConnexionHandlers", "cdecl")
    SetConnexionHandlers.argtypes = [ConnexionMessageHandlerProc, ConnexionAddedHandlerProc, ConnexionRemovedHandlerProc, c_bool]
    SetConnexionHandlers.restype = c_int16

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 33
if _libs["3DconnexionClient"].has("CleanupConnexionHandlers", "cdecl"):
    CleanupConnexionHandlers = _libs["3DconnexionClient"].get("CleanupConnexionHandlers", "cdecl")
    CleanupConnexionHandlers.argtypes = []
    CleanupConnexionHandlers.restype = None

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 37
if _libs["3DconnexionClient"].has("InstallConnexionHandlers", "cdecl"):
    InstallConnexionHandlers = _libs["3DconnexionClient"].get("InstallConnexionHandlers", "cdecl")
    InstallConnexionHandlers.argtypes = [ConnexionMessageHandlerProc, ConnexionAddedHandlerProc, ConnexionRemovedHandlerProc]
    InstallConnexionHandlers.restype = c_int16

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 44
if _libs["3DconnexionClient"].has("RegisterConnexionClient", "cdecl"):
    RegisterConnexionClient = _libs["3DconnexionClient"].get("RegisterConnexionClient", "cdecl")
    RegisterConnexionClient.argtypes = [uint32_t, POINTER(uint8_t), uint16_t, uint32_t]
    RegisterConnexionClient.restype = uint16_t

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 45
if _libs["3DconnexionClient"].has("SetConnexionClientMask", "cdecl"):
    SetConnexionClientMask = _libs["3DconnexionClient"].get("SetConnexionClientMask", "cdecl")
    SetConnexionClientMask.argtypes = [uint16_t, uint32_t]
    SetConnexionClientMask.restype = None

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 46
if _libs["3DconnexionClient"].has("SetConnexionClientButtonMask", "cdecl"):
    SetConnexionClientButtonMask = _libs["3DconnexionClient"].get("SetConnexionClientButtonMask", "cdecl")
    SetConnexionClientButtonMask.argtypes = [uint16_t, uint32_t]
    SetConnexionClientButtonMask.restype = None

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 47
if _libs["3DconnexionClient"].has("UnregisterConnexionClient", "cdecl"):
    UnregisterConnexionClient = _libs["3DconnexionClient"].get("UnregisterConnexionClient", "cdecl")
    UnregisterConnexionClient.argtypes = [uint16_t]
    UnregisterConnexionClient.restype = None

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 55
if _libs["3DconnexionClient"].has("ConnexionControl", "cdecl"):
    ConnexionControl = _libs["3DconnexionClient"].get("ConnexionControl", "cdecl")
    ConnexionControl.argtypes = [uint32_t, c_int32, POINTER(c_int32)]
    ConnexionControl.restype = c_int16

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 56
if _libs["3DconnexionClient"].has("ConnexionClientControl", "cdecl"):
    ConnexionClientControl = _libs["3DconnexionClient"].get("ConnexionClientControl", "cdecl")
    ConnexionClientControl.argtypes = [uint16_t, uint32_t, c_int32, POINTER(c_int32)]
    ConnexionClientControl.restype = c_int16

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 61
if _libs["3DconnexionClient"].has("ConnexionGetCurrentDevicePrefs", "cdecl"):
    ConnexionGetCurrentDevicePrefs = _libs["3DconnexionClient"].get("ConnexionGetCurrentDevicePrefs", "cdecl")
    ConnexionGetCurrentDevicePrefs.argtypes = [uint32_t, POINTER(ConnexionDevicePrefs)]
    ConnexionGetCurrentDevicePrefs.restype = c_int16

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClientAPI.h: 66
if _libs["3DconnexionClient"].has("ConnexionSetButtonLabels", "cdecl"):
    ConnexionSetButtonLabels = _libs["3DconnexionClient"].get("ConnexionSetButtonLabels", "cdecl")
    ConnexionSetButtonLabels.argtypes = [POINTER(uint8_t), uint16_t]
    ConnexionSetButtonLabels.restype = c_int16

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 189
kConnexionDeviceStateSize = sizeof(ConnexionDeviceState)

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 197
kDevID_AnyDevice = 0x7FFF

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 204
kConnexionDevicePrefsType = 0x4D50

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 205
kConnexionDevicePrefsVers = 0x7031

# /Library/Frameworks/3DconnexionClient.framework/Versions/A/Headers/ConnexionClient.h: 238
kConnexionDevicePrefsSize = sizeof(ConnexionDevicePrefs)

# No inserted files

# No prefix-stripping
sjkillen commented 2 years ago

@techdragon Oh yeah, Blender does have a platform-agnostic API for 3D mice. Ideally, this tool should use those. But that would involve changing its NDOF events to also include data about rotations and translations at the bare minimum. This looks promising, thanks! I'll have to give it a shot. Seems like an option to "use the official SDK" would be good.