ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Mouse3DInput.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "Mouse3DInput.h"
9 
10 // qCC_db
11 #include <CVLog.h>
12 // cloudViewer
13 #include <CVPlatform.h>
14 
15 // Qt
16 #include <QApplication>
17 
18 // system
19 #include <assert.h>
20 #include <math.h>
21 #ifdef CV_WINDOWS
22 #include <windows.h>
23 #endif
24 
25 // 3DxWare
26 #include <si.h>
27 #include <siapp.h>
28 #include <spwmacro.h>
29 #include <spwmath.h>
30 
32 static const double c_3dmouseAngularVelocity = 1.0e-6;
33 
34 // Unique instance
36 
37 #include <QAbstractNativeEventFilter>
38 class RawInputEventFilter : public QAbstractNativeEventFilter {
39 public:
40  bool nativeEventFilter(const QByteArray& eventType,
41  void* msg,
42  long* result) override {
43  if (!s_mouseInputInstance || !msg) {
44  return false;
45  }
46 
47  // Platform-specific event data
48  SiGetEventData eData;
49 #ifdef CV_WINDOWS
50  MSG* messageStruct = static_cast<MSG*>(msg);
51  SiGetEventWinInit(&eData, messageStruct->message, messageStruct->wParam,
52  messageStruct->lParam);
53 #endif
54  return s_mouseInputInstance->onSiEvent(&eData);
55  }
56 };
58 
60  : QObject(parent), m_siHandle(SI_NO_HANDLE) {
61  // Register current instance
62  assert(s_mouseInputInstance == nullptr);
63  s_mouseInputInstance = this;
64 }
65 
67  // Unregister current instance
68  if (s_mouseInputInstance == this) {
69  s_mouseInputInstance = nullptr;
70  }
71 }
72 
73 bool Mouse3DInput::connect(QWidget* mainWidget, QString appName) {
74  if (!mainWidget) {
75  assert(false);
76  return false;
77  }
78 
79  // Attempt to connect with the 3DxWare driver
80  assert(m_siHandle == SI_NO_HANDLE);
81 
82  if (SiInitialize() == SPW_DLL_LOAD_ERROR) {
83  CVLog::Warning(tr("[3D Mouse] Could not load SiAppDll dll files"));
84  return false;
85  }
86 
87  // Platform-specific device data
88  SiOpenDataEx oData;
89  SiOpenWinInitEx(&oData, (HWND)mainWidget->winId());
90  SiOpenWinAddHintBoolEnum(&oData, SI_HINT_USESV3DCMDS, SPW_TRUE);
91  // 3DxWare device handle
92  m_siHandle = SiOpenEx(qUtf16Printable(appName), SI_ANY_DEVICE, SI_NO_MASK,
93  SI_EVENT, &oData);
94 
95  if (m_siHandle == SI_NO_HANDLE) {
96  // Get and display initialization error
97  SiTerminate();
98  CVLog::Warning(tr("[3D Mouse] Could not open a 3DxWare device"));
99  return false;
100  }
101 
102  SiDevInfo info;
103  if (SiGetDeviceInfo(m_siHandle, &info) == SPW_NO_ERROR) {
104  // DGM: strangely, we get these wrong versions on real wireless
105  // devices?! if (info.majorVersion == 0 && info.minorVersion == 0)
106  //{
107  // // Not a real device
108  // SiTerminate();
109  // CVLog::Warning("[3D Mouse] Couldn't find a connected device");
110  // return false;
111  // }
112 
113  SiDeviceName name;
114  SiGetDeviceName(m_siHandle, &name);
115  CVLog::Print(tr("[3D Mouse] Device: %1 (%2 buttons) - firmware v%3.%4")
116  .arg(name.name)
117  .arg(info.numButtons)
118  .arg(info.majorVersion)
119  .arg(info.minorVersion));
120 
121  if (info.numButtons == 0 && info.majorVersion == 0 &&
122  info.minorVersion == 0) {
123  // notification only device = fake device
124  // can make CC interactions and mouse capture super slow!!!
125  CVLog::Print(
126  tr("[3D Mouse] Notification-only device will be ignored"));
127  SiClose(m_siHandle);
128  SiTerminate();
129  m_siHandle = SI_NO_HANDLE;
130  return false;
131  }
132  } else {
133  CVLog::Warning(tr("[3D Mouse] Failed to retrieve device info"));
134  }
135 
136  // To avoid drift
137  SiRezero(m_siHandle);
138 
139  // Setup event filter
140  qApp->installNativeEventFilter(&s_rawInputEventFilter);
141 
142  return true;
143 }
144 
146  if (m_siHandle != SI_NO_HANDLE) {
147  SiClose(m_siHandle);
148  SiTerminate();
149  m_siHandle = SI_NO_HANDLE;
150  qApp->removeNativeEventFilter(&s_rawInputEventFilter);
151  }
152 }
153 
154 bool Mouse3DInput::onSiEvent(void* siGetEventData) {
155  if (m_siHandle == SI_NO_HANDLE || !siGetEventData) {
156  return false;
157  }
158 
159  SiSpwEvent siEvent;
160  if (SiGetEvent(m_siHandle, 0, static_cast<SiGetEventData*>(siGetEventData),
161  &siEvent) != SI_IS_EVENT) {
162  return false;
163  }
164  switch (siEvent.type) {
165  case SI_MOTION_EVENT: {
166  const SiSpwData& eventData = siEvent.u.spwData;
167 
168  if (eventData.mData[SI_TX] != 0 || eventData.mData[SI_TY] != 0 ||
169  eventData.mData[SI_TZ] != 0 || eventData.mData[SI_RX] != 0 ||
170  eventData.mData[SI_RY] != 0 || eventData.mData[SI_RZ] != 0) {
171  std::vector<float> axes(6);
172  double ds = eventData.period *
173  c_3dmouseAngularVelocity; // period is in ms
174  // translation data
175  axes[0] = -static_cast<float>(eventData.mData[SI_TX] * ds);
176  axes[1] = static_cast<float>(eventData.mData[SI_TY] * ds);
177  axes[2] = static_cast<float>(eventData.mData[SI_TZ] * ds);
178  // rotation data
179  axes[3] = -static_cast<float>(eventData.mData[SI_RX] * ds);
180  axes[4] = static_cast<float>(eventData.mData[SI_RY] * ds);
181  axes[5] = static_cast<float>(eventData.mData[SI_RZ] * ds);
182 
183  move3d(axes);
184  }
185  } break;
186 
187  case SI_ZERO_EVENT:
188  // FIXME: too flickery!
189  Q_EMIT sigReleased();
190  break;
191 
192  case SI_BUTTON_EVENT: {
193  // CVLog::Print(QString("SI_BUTTON_EVENT"));
194  SPWuint32 buttonNumber = siEvent.u.hwButtonEvent.buttonNumber;
195  if (buttonNumber != 0) {
196  if (SiButtonPressed(&siEvent) > 0)
197  on3dmouseKeyDown(buttonNumber);
198  else if (SiButtonReleased(&siEvent) > 0)
199  on3dmouseKeyUp(buttonNumber);
200  }
201  } break;
202 
203  case SI_BUTTON_PRESS_EVENT:
204  // CVLog::Print(QString("SI_BUTTON_PRESS_EVENT"));
205  on3dmouseKeyDown(siEvent.u.hwButtonEvent.buttonNumber);
206  break;
207 
208  case SI_BUTTON_RELEASE_EVENT:
209  // CVLog::Print(QString("SI_BUTTON_RELEASE_EVENT"));
210  on3dmouseKeyUp(siEvent.u.hwButtonEvent.buttonNumber);
211  break;
212  case SI_CMD_EVENT:
213  // CVLog::Print(QString("SI_CMD_EVENT"));
214  if (siEvent.u.cmdEventData.pressed) {
215  if (siEvent.u.cmdEventData.functionNumber ==
217  SiSetUiMode(m_siHandle, SI_UI_ALL_CONTROLS);
218  else
219  on3dmouseCMDKeyDown(siEvent.u.cmdEventData.functionNumber);
220  } else {
221  on3dmouseCMDKeyUp(siEvent.u.cmdEventData.functionNumber);
222  }
223  break;
224 
225  default:
226  // CVLog::Print(QString("siEvent.type = %1").arg(siEvent.type));
227  break;
228  }
229 
230  return true;
231 }
232 
233 void Mouse3DInput::move3d(std::vector<float>& motionData) {
234  Q_EMIT sigMove3d(motionData);
235 }
236 
237 void Mouse3DInput::on3dmouseKeyDown(int virtualKeyCode) {
238  Q_EMIT sigOn3dmouseKeyDown(virtualKeyCode);
239 }
240 
241 void Mouse3DInput::on3dmouseCMDKeyDown(int virtualCMDCode) {
242  Q_EMIT sigOn3dmouseCMDKeyDown(virtualCMDCode);
243 }
244 
245 void Mouse3DInput::on3dmouseKeyUp(int virtualKeyCode) {
246  Q_EMIT sigOn3dmouseKeyUp(virtualKeyCode);
247 }
248 
249 void Mouse3DInput::on3dmouseCMDKeyUp(int virtualCMDCode) {
250  Q_EMIT sigOn3dmouseCMDKeyUp(virtualCMDCode);
251 }
252 
253 void Mouse3DInput::GetMatrix(const std::vector<float>& vec, ccGLMatrixd& mat) {
254  assert(vec.size() == 6);
255 
256  float axis[3] = {-vec[3], vec[4], -vec[5]};
257 
258  Matrix Rd;
259  SPW_ArbitraryAxisToMatrix(Rd, axis, 1.0f);
260 
261  for (unsigned i = 0; i < 3; ++i) {
262  mat.getColumn(i)[0] = Rd[0][i];
263  mat.getColumn(i)[1] = Rd[1][i];
264  mat.getColumn(i)[2] = Rd[2][i];
265  }
266 }
267 
268 void Mouse3DInput::Apply(const std::vector<float>& motionData,
269  ccGLWindow* win) {
270  assert(motionData.size() >= 6);
271 
272  // no active window?
273  if (!win) {
274  return;
275  }
276 
277  // copy input parameters
278  std::vector<float> vec = motionData;
279 
280  // view parameters
281  const ecvViewportParameters& viewParams = win->getViewportParameters();
282  bool bubbleViewMode = win->bubbleViewModeEnabled();
283 
284  // panning or zooming
285  if (!bubbleViewMode) {
286  double X = vec[0];
287  double Y = vec[1];
288  double Z = vec[2];
289 
290  // CVLog::Print(QString("Mouse translation:
291  // (%1,%2,%3)").arg(X).arg(Y).arg(Z));
292 
293  // Zoom: object moves closer/away (only for ortho. mode)
294  if (!viewParams.perspectiveView &&
295  cloudViewer::GreaterThanEpsilon(std::abs(Z))) {
296  ecvViewportParameters viewParams = win->getViewportParameters();
297  viewParams.setFocalDistance(viewParams.getFocalDistance() /
298  (1.0 - Z / 1.5));
299  win->setViewportParameters(viewParams);
300  Z = 0.0;
301  }
302 
303  // Zoom & Panning: camera moves right/left + up/down + backward/forward
304  // (only for perspective mode)
305  if (cloudViewer::GreaterThanEpsilon(std::abs(X)) ||
306  cloudViewer::GreaterThanEpsilon(std::abs(Y)) ||
307  cloudViewer::GreaterThanEpsilon(std::abs(Z))) {
308  if (viewParams.perspectiveView) {
309  double distanceToWidthRatio =
310  win->getViewportParameters()
311  .computeDistanceToWidthRatio();
312  X *= distanceToWidthRatio;
313  Y *= distanceToWidthRatio;
314  }
315 
316  double screenWidth3D = viewParams.computeWidthAtFocalDist();
317  if (viewParams.objectCenteredView) {
318  screenWidth3D = -screenWidth3D;
319  }
320  CCVector3d v(-X * screenWidth3D, Y * screenWidth3D,
321  -Z * screenWidth3D);
322  win->moveCamera(v);
323  }
324  }
325 
326  // rotation
327  if (cloudViewer::GreaterThanEpsilon(std::abs(vec[3])) ||
328  cloudViewer::GreaterThanEpsilon(std::abs(vec[4])) ||
329  cloudViewer::GreaterThanEpsilon(std::abs(vec[5]))) {
330  // CVLog::Print(QString("Mouse rotation:
331  // (%1,%2,%3)").arg(vec[3]).arg(vec[4]).arg(vec[5]));
332 
333  // get corresponding rotation matrix
334  ccGLMatrixd rotMat;
335  if (!bubbleViewMode) {
336  Mouse3DInput::GetMatrix(vec, rotMat);
337  win->rotateBaseViewMat(
338  viewParams.objectCenteredView ? rotMat : rotMat.inverse());
339  } else {
340  // Ry = horizontal
341  // Rx = vertical
342 
343  // rotation about the sensor Z axis
344  const ccGLMatrixd& viewMat = win->getViewportParameters().viewMat;
345  CCVector3d axis = viewMat.getColumnAsVec3D(2);
346  rotMat.initFromParameters(-vec[4], axis, CCVector3d(0, 0, 0));
347 
348  // rotation about the local X axis
349  ccGLMatrixd rotX;
350  rotX.initFromParameters(vec[3], CCVector3d(1, 0, 0),
351  CCVector3d(0, 0, 0));
352  rotMat = rotX * rotMat;
353  win->rotateBaseViewMat(rotMat);
354  }
355 
356  win->showPivotSymbol(true);
357  } else {
358  win->showPivotSymbol(false);
359  }
360 
361  win->redraw();
362 }
Vector3Tpl< double > CCVector3d
Double 3D Vector.
Definition: CVGeom.h:804
std::string name
static const double c_3dmouseAngularVelocity
Object angular velocity per mouse tick (in radians per ms per count)
static Mouse3DInput * s_mouseInputInstance
static RawInputEventFilter s_rawInputEventFilter
void * X
Definition: SmallVector.cpp:45
core::Tensor result
Definition: VtkUtils.cpp:76
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
3DxWare driver wrapper for 3D mouse handling
Definition: Mouse3DInput.h:43
void sigReleased()
void sigOn3dmouseCMDKeyDown(int virtualCMDCode)
virtual ~Mouse3DInput()
Destructor.
void * m_siHandle
3DxWare handle
Definition: Mouse3DInput.h:315
virtual void on3dmouseKeyUp(int virtualKeyCode)
Called when a 3D mouse key is released.
virtual void on3dmouseKeyDown(int virtualKeyCode)
Called when a 3D mouse key is pressed.
void sigMove3d(std::vector< float > &motionData)
void sigOn3dmouseKeyUp(int virtualKeyCode)
Mouse3DInput(QObject *parent)
Default constructor.
bool onSiEvent(void *siGetEventData)
Called when a new system message is available.
static void GetMatrix(const std::vector< float > &motionData, ccGLMatrixd &mat)
Converts 'rotation' part of motion data to a rotation matrix.
static void Apply(const std::vector< float > &motionData, ccGLWindow *win)
Applies motion data to a given 3D window.
void disconnectDriver()
Disconnects from the 3DxWare driver.
void sigOn3dmouseKeyDown(int virtualKeyCode)
virtual void on3dmouseCMDKeyDown(int virtualCMDCode)
Called when a 3D mouse key is pressed after translation to CMD.
bool connect(QWidget *mainWidget, QString appName)
Attempts to connect with the 3DxWare driver.
virtual void on3dmouseCMDKeyUp(int virtualCMDCode)
Called when a 3D mouse key is released after translation to CMD.
void sigOn3dmouseCMDKeyUp(int virtualCMDCode)
virtual void move3d(std::vector< float > &motionData)
Called with the processed motion data when a 3D mouse event is received.
bool nativeEventFilter(const QByteArray &eventType, void *msg, long *result) override
ccGLMatrixTpl< T > inverse() const
Returns inverse transformation.
Vector3Tpl< T > getColumnAsVec3D(unsigned index) const
Returns a copy of a given column as a CCVector3.
void initFromParameters(T alpha_rad, const Vector3Tpl< T > &axis3D, const Vector3Tpl< T > &t3D)
Inits transformation from a rotation axis, an angle and a translation.
T * getColumn(unsigned index)
Returns a pointer to a given column.
Double version of ccGLMatrixTpl.
Definition: ecvGLMatrix.h:56
Standard parameters for GL displays/viewports.
bool perspectiveView
Perspective view state.
double computeWidthAtFocalDist() const
Computes the object 'width' at the 'focal' distance.
double getFocalDistance() const
Computes the 'focal' distance.
void setFocalDistance(double distance)
Sets the 'focal' distance.
bool GreaterThanEpsilon(float x)
Test a floating point number against our epsilon (a very small number).
Definition: CVMath.h:37