GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/OSGManipulator/keyboard-manipulator.cpp Lines: 0 219 0.0 %
Date: 2023-03-14 11:04:37 Branches: 0 240 0.0 %

Line Branch Exec Source
1
//
2
//  KeyboardManipulator
3
//  gepetto-viewer
4
//
5
//  Alternative CameraManipulator for OSG, use keyboard and mouse
6
//  KeyBinding are inspired by the classic system in games
7
//
8
//  Created by Pierre Fernbach in january 2016
9
//
10
#include <gepetto/viewer/OSGManipulator/keyboard-manipulator.h>
11
#include <gepetto/viewer/config-osg.h>
12
#include <stdio.h>
13
#include <stdlib.h>
14
15
#include <iostream>
16
17
/**
18
 * CameraManipulator which use keyboard for moving the camera, same deplacement
19
 * as most of the FPS games :
20
 *
21
 * independant of keyboard layout (use the active keyboard layout when the
22
 * osg::viewer constructor is called) , only use physical position of the key
23
 * keybinding for azerty (qwerty) users :
24
 * z/s (w/s) : move forward/backward
25
 * q/d (a/d) : move left/right
26
 * a/e (a/e) : rotation (roll) left/right
27
 * space/c : move up/down
28
 * rotation yaw and pitch with the mouse (keep left button pressed)
29
 *
30
 */
31
using namespace osg;
32
using namespace osgGA;
33
34
/// Constructor.
35
KeyboardManipulator::KeyboardManipulator(int flags) : inherited(flags) {
36
  speed_ = startSpeed_;
37
  speedRoll_ = 0.;
38
  speedX_ = 0.;
39
  speedY_ = 0.;
40
  speedZ_ = 0.;
41
  ctrl_ = false;
42
  shift_ = false;
43
  rightClic_ = false;
44
  keyLayout_ = LAYOUT_unknown;
45
  localUp_ = getUpVector(getCoordinateFrame(_eye));
46
  noRoll_ = true;
47
  setAllowThrow(false);  // stop all mouse motions when mouse is released
48
  // display_=XOpenDisplay(0);
49
  initKeyboard();
50
}
51
52
/// Constructor with reference to the graphic window, needed for hidding mouse
53
/// cursor
54
KeyboardManipulator::KeyboardManipulator(osgViewer::GraphicsWindow* window,
55
                                         int flags)
56
    : inherited(flags),
57
      gWindow_(window) /*,camera_(viewer->getCamera())*/
58
{
59
  speed_ = startSpeed_;
60
  speedRoll_ = 0.;
61
  speedX_ = 0.;
62
  speedY_ = 0.;
63
  speedZ_ = 0.;
64
  ctrl_ = false;
65
  shift_ = false;
66
  rightClic_ = false;
67
  keyLayout_ = LAYOUT_unknown;
68
  localUp_ = getUpVector(getCoordinateFrame(_eye));
69
  noRoll_ = true;
70
  setAllowThrow(false);  // stop all mouse motions when mouse is released
71
                         /* osgViewer::Viewer::Windows windows;
72
                          window->getWindows(windows);
73
                          gWindow_=windows.front();*/
74
75
  initKeyboard();
76
}
77
78
/// Copy Constructor.
79
KeyboardManipulator::KeyboardManipulator(const KeyboardManipulator& fpm,
80
                                         const CopyOp& copyOp)
81
    : inherited(fpm, copyOp) {
82
  initKeyboard();
83
}
84
85
// pressing a key
86
bool KeyboardManipulator::handleKeyDown(const GUIEventAdapter& ea,
87
                                        GUIActionAdapter& us) {
88
  // keycode_ = XKeysymToKeycode(display_,ea.getUnmodifiedKey());
89
90
  int keySym = ea.getUnmodifiedKey();
91
  if (keySym == 0) {  // issue with getUnmodifiedKey() and hpp-gui (always
92
                      // return 0 because not initialised in osgQT)
93
    keySym = ea.getKey();
94
    if (keySym < 96)  // ctrl mask
95
      keySym += 96;
96
  }
97
  if (keyLayout_ == LAYOUT_azerty) {  // adapt to azerty keyboard
98
    switch (keySym) {
99
      case osgGA::GUIEventAdapter::KEY_Z:
100
        keySym = osgGA::key_forward;
101
        break;
102
      case osgGA::GUIEventAdapter::KEY_A:
103
        keySym = osgGA::key_roll_left;
104
        break;
105
      case osgGA::GUIEventAdapter::KEY_Q:
106
        keySym = osgGA::key_left;
107
        break;
108
    }
109
  }
110
111
  switch (keySym) {
112
    case osgGA::key_forward:
113
    case osgGA::GUIEventAdapter::KEY_Up:
114
      // move forward
115
      if (speedX_ <= 0) {
116
        speedX_ = 1.;
117
        return true;
118
      } else
119
        return false;
120
      break;
121
    case osgGA::key_backward:
122
    case osgGA::GUIEventAdapter::KEY_Down:
123
      // move backward
124
      if (speedX_ >= 0) {
125
        speedX_ = -1.;
126
        return true;
127
      } else
128
        return false;
129
      break;
130
    case osgGA::key_left:
131
    case osgGA::GUIEventAdapter::KEY_Left:
132
      // move left
133
      if (speedY_ >= 0) {
134
        speedY_ = -1.;
135
        return true;
136
      } else
137
        return false;
138
      break;
139
    case osgGA::key_right:
140
    case osgGA::GUIEventAdapter::KEY_Right:
141
      // move right
142
      if (speedY_ <= 0) {
143
        speedY_ = 1.;
144
        return true;
145
      } else
146
        return false;
147
      break;
148
    case osgGA::key_up:  // spacebar
149
      // move up
150
      if (speedZ_ <= 0) {
151
        speedZ_ = 1.;
152
        return true;
153
      } else
154
        return false;
155
      break;
156
    case osgGA::key_down:
157
      // move down
158
      if (speedZ_ >= 0) {
159
        speedZ_ = -1.;
160
        return true;
161
      } else
162
        return false;
163
      break;
164
    case osgGA::key_roll_left:
165
      // roll rotation left
166
      noRoll_ = false;
167
      if (speedRoll_ >= 0) {
168
        speedRoll_ = -1.;
169
        return true;
170
      } else
171
        return false;
172
      break;
173
    case osgGA::key_roll_right:
174
      // roll rotation right
175
      noRoll_ = false;
176
      if (speedRoll_ <= 0) {
177
        speedRoll_ = 1.;
178
        return true;
179
      } else
180
        return false;
181
      break;
182
    case osgGA::GUIEventAdapter::KEY_R:
183
      flushMouseEventStack();
184
      _thrown = false;
185
      localUp_ = getUpVector(getCoordinateFrame(_eye));
186
      home(ea, us);
187
      return true;
188
      break;
189
    case osgGA::GUIEventAdapter::KEY_Plus:
190
    case 65451:  // '+'  numpad
191
      speed_ *= 1.2;
192
      return false;
193
      break;
194
    case osgGA::GUIEventAdapter::KEY_Minus:
195
    case 65453:  // '-'  numpad
196
      speed_ *= 0.8;
197
      return false;
198
      break;
199
    case osgGA::GUIEventAdapter::KEY_Asterisk:
200
    case 65450:              // '*'  numpad
201
      speed_ = startSpeed_;  // reset speed
202
      return false;
203
      break;
204
    case osgGA::GUIEventAdapter::KEY_Control_L:
205
    case osgGA::GUIEventAdapter::KEY_Control_R:
206
      ctrl_ = true;
207
      break;
208
    case osgGA::GUIEventAdapter::KEY_Shift_L:
209
    case osgGA::GUIEventAdapter::KEY_Shift_R:
210
      shift_ = true;
211
      break;
212
  }
213
214
  return false;
215
}
216
217
/// Releasing the key
218
bool KeyboardManipulator::handleKeyUp(const GUIEventAdapter& ea,
219
                                      GUIActionAdapter& /*us*/) {
220
  int keySym = ea.getUnmodifiedKey();
221
  if (keySym == 0) {  // issue with getUnmodifiedKey() and hpp-gui (always
222
                      // return 0 because not initialised in osgQT)
223
    keySym = ea.getKey();
224
    if (keySym < 96)  // ctrl mask
225
      keySym += 96;
226
  }
227
228
  if (keyLayout_ == LAYOUT_azerty) {  // adapt to azerty keyboard
229
    switch (keySym) {
230
      case osgGA::GUIEventAdapter::KEY_Z:
231
        keySym = osgGA::key_forward;
232
        break;
233
      case osgGA::GUIEventAdapter::KEY_A:
234
        keySym = osgGA::key_roll_left;
235
        break;
236
      case osgGA::GUIEventAdapter::KEY_Q:
237
        keySym = osgGA::key_left;
238
        break;
239
    }
240
  }
241
242
  switch (keySym) {
243
    case osgGA::key_forward:
244
    case osgGA::key_backward:
245
    case osgGA::GUIEventAdapter::KEY_Up:
246
    case osgGA::GUIEventAdapter::KEY_Down:
247
      speedX_ = 0.;
248
      return false;
249
      break;
250
    case osgGA::key_right:
251
    case osgGA::key_left:
252
    case osgGA::GUIEventAdapter::KEY_Right:
253
    case osgGA::GUIEventAdapter::KEY_Left:
254
      speedY_ = 0.;
255
      return false;
256
      break;
257
    case osgGA::key_up:  // spacebar
258
    case osgGA::key_down:
259
      speedZ_ = 0.;
260
      return false;
261
      break;
262
    case osgGA::key_roll_left:
263
    case osgGA::key_roll_right:
264
      speedRoll_ = 0.;
265
      return false;
266
      break;
267
  }
268
  switch (ea.getKey()) {
269
    case '2':
270
      // getUsage();
271
      break;
272
    case osgGA::GUIEventAdapter::KEY_Control_L:
273
    case osgGA::GUIEventAdapter::KEY_Control_R:
274
      ctrl_ = false;
275
      break;
276
    case osgGA::GUIEventAdapter::KEY_Shift_L:
277
    case osgGA::GUIEventAdapter::KEY_Shift_R:
278
      shift_ = false;
279
      break;
280
  }
281
  return false;
282
}
283
284
void KeyboardManipulator::rotateRoll(
285
    const double roll /*,const osg::Vec3d& localUp */) {
286
  // bool verticalAxisFixed = (localUp != Vec3d( 0.,0.,0. ));
287
288
  // fix current rotation
289
290
  // rotations
291
  rotateRoll_.makeRotate(roll, _rotation * Vec3d(0., 0., -1.));
292
  _rotation = _rotation * rotateRoll_;
293
}
294
295
// free rotation (remove localUp constraint from parent class)
296
bool KeyboardManipulator::performMovementLeftMouseButton(
297
    const double /*eventTimeDelta*/, const double dx, const double dy) {
298
  rotateYawPitch(_rotation, dx, dy, localUp_);
299
300
  return true;
301
}
302
303
// called at each refresh, need to check the speed and move camera accordingly
304
bool KeyboardManipulator::handleFrame(const GUIEventAdapter& ea,
305
                                      GUIActionAdapter& us) {
306
  double current_frame_time = ea.getTime();
307
308
  _delta_frame_time = current_frame_time - _last_frame_time;
309
  _last_frame_time = current_frame_time;
310
311
  double dist =
312
      speed_ * _delta_frame_time;  // distance according to refresh rate
313
314
  if (ctrl_) dist = dist / 10.;
315
316
  rotateRoll_.makeRotate(dist * speedRoll_,
317
                         _rotation * Vec3d(0., 0., -1.));  // apply rotation
318
  _rotation = _rotation * rotateRoll_;
319
320
  _eye += _rotation * Vec3d(dist * speedY_, dist * speedZ_, -dist * speedX_);
321
322
  return inherited::handleFrame(ea, us);
323
}
324
325
// method overrided for hidding the mouse cursor when the view move :
326
bool KeyboardManipulator::handleMousePush(const osgGA::GUIEventAdapter& ea,
327
                                          osgGA::GUIActionAdapter& us) {
328
  gWindow_->useCursor(false);
329
  if (!noRoll_) localUp_ = _rotation * Vec3d(0., 1., 0.);
330
331
  return inherited::handleMousePush(ea, us);
332
}
333
334
bool KeyboardManipulator::handleMouseRelease(const osgGA::GUIEventAdapter& ea,
335
                                             osgGA::GUIActionAdapter& us) {
336
  gWindow_->useCursor(true);
337
  noRoll_ = true;
338
  return inherited::handleMouseRelease(ea, us);
339
}
340
341
/*bool KeyboardManipulator::handleMouseWheel( const GUIEventAdapter& ea,
342
GUIActionAdapter& us ){ osgGA::GUIEventAdapter::ScrollingMotion sm =
343
ea.getScrollingMotion();
344
345
  // handle centering
346
  if( _flags & SET_CENTER_ON_WHEEL_FORWARD_MOVEMENT )
347
  {
348
      if( ((sm == GUIEventAdapter::SCROLL_DOWN) && (_wheelMovement > 0.)) ||
349
          ((sm == GUIEventAdapter::SCROLL_UP)   && (_wheelMovement < 0.)) )
350
      {
351
          // stop thrown animation
352
          _thrown = false;
353
          if( getAnimationTime() <= 0. )
354
              // center by mouse intersection (no animation)
355
              setCenterByMousePointerIntersection( ea, us );
356
          else {
357
              // start new animation only if there is no animation in progress
358
              if( !isAnimating() )
359
                  startAnimationByMousePointerIntersection( ea, us );
360
          }
361
      }
362
  }
363
364
  switch( sm )
365
  {
366
      // mouse scroll up event
367
      case GUIEventAdapter::SCROLL_UP: // increase clipping
368
      {
369
        camera_->getProjectionMatrixAsPerspective(fovy_,ratio_,zNear_,zFar_); //
370
initialise value with current setting if(ctrl_){// increase far distance view
371
            zFar_ +=1;
372
          }
373
        else{ // reduce close distance view
374
            zNear_ -= 1;
375
            if (zNear_<0.1)
376
              zNear_=0.1;
377
          }
378
        std::cout<<"zNear = "<<zNear_<<"  zFar = "<<zFar_<<std::endl;
379
        camera_->setProjectionMatrixAsPerspective(fovy_,ratio_,zNear_,zFar_);
380
        return false;
381
      }
382
      // mouse scroll down event
383
      case GUIEventAdapter::SCROLL_DOWN:  // reduce clipping
384
      {
385
        camera_->getProjectionMatrixAsPerspective(fovy_,ratio_,zNear_,zFar_); //
386
initialise value with current setting if(ctrl_){// reduce far distance view
387
            zFar_ -=1;
388
            if(zFar_ <= zNear_)
389
              zFar_ = zNear_ +0.1;
390
          }
391
        else{ // increase close distance view
392
            zNear_ += 1;
393
          }
394
        std::cout<<"zNear = "<<zNear_<<"  zFar = "<<zFar_<<std::endl;
395
        camera_->setProjectionMatrixAsPerspective(fovy_,ratio_,zNear_,zFar_);
396
        return false;
397
      }
398
      // unhandled mouse scrolling motion
399
      default:
400
          return false;
401
  }
402
}*/
403
404
// if rightClic is activated, do the same as performLeftMouvement
405
/*bool KeyboardManipulator::performMovement(){
406
  if(rightClic_){
407
    // return if less then two events have been added
408
    if( _ga_t0.get() == NULL || _ga_t1.get() == NULL )
409
        return false;
410
411
    // get delta time
412
    double eventTimeDelta = _ga_t0->getTime() - _ga_t1->getTime();
413
    if( eventTimeDelta < 0. )
414
    {
415
        OSG_WARN << "Manipulator warning: eventTimeDelta = " << eventTimeDelta
416
<< std::endl; eventTimeDelta = 0.;
417
    }
418
419
    // get deltaX and deltaY
420
    float dx = _ga_t0->getXnormalized() - _ga_t1->getXnormalized();
421
    float dy = _ga_t0->getYnormalized() - _ga_t1->getYnormalized();
422
423
    // return if there is no movement.
424
    if( dx == 0. && dy == 0. )
425
        return false;
426
427
    performMovementLeftMouseButton(eventTimeDelta,dx,dy);
428
  }
429
430
  return inherited::performMovement();
431
}*/
432
433
// check if the current keyboard is azerty or qwerty and adapt the keybinding
434
bool KeyboardManipulator::initKeyboard() {
435
  char buf[128];
436
  FILE* fp;
437
  // send system command and get the output
438
  if ((fp = popen("LANG=C LC_ALL=C setxkbmap -print", "r")) == NULL) {
439
    std::cout << "Error sending terminal command !" << std::endl;
440
    return false;
441
  }
442
443
  fgets(buf, 128, fp);
444
  fgets(
445
      buf, 128,
446
      fp);  // the second line of the output contain either "azerty" or "qwerty"
447
  std::string output(buf);
448
449
  if (output.find("azerty") != std::string::npos) {
450
    keyLayout_ = LAYOUT_azerty;
451
    // std::cout<<"azerty keyboard detected"<<std::endl;
452
  } else if (output.find("qwerty") != std::string::npos) {
453
    keyLayout_ = LAYOUT_qwerty;
454
    // std::cout<<"qwerty keyboard detected"<<std::endl;
455
  } else
456
    std::cout << "Unknow keyboard layout" << std::endl;
457
458
  if (pclose(fp)) {
459
    std::cout << "Command not found or exited with error status" << std::endl;
460
    return false;
461
  }
462
463
  return true;
464
}
465
466
void KeyboardManipulator::getUsage(osg::ApplicationUsage& usage) const {
467
  if (keyLayout_ == LAYOUT_azerty) {
468
    usage.addKeyboardMouseBinding(
469
        " -1. Keyboard : ",
470
        "Move with keyboard arrow or zqsd and the mouse for rotation");
471
    usage.addKeyboardMouseBinding(" -2. Arrow keys  ", "Planar translations");
472
    usage.addKeyboardMouseBinding(" -3. z/s ",
473
                                  "Forward / backward translation");
474
    usage.addKeyboardMouseBinding(" -4. q/d ", "Left / right translation");
475
    usage.addKeyboardMouseBinding(" -5. Space/v ", "Up / down translation");
476
    usage.addKeyboardMouseBinding(" -6. a/e ", "Roll rotation");
477
478
  } else {
479
    usage.addKeyboardMouseBinding(
480
        " -1. Keyboard manipulator : ",
481
        "move with keyboard arrow or wasd and the mouse for rotation");
482
    usage.addKeyboardMouseBinding(" -2. arrow keys  ", "Planar translations");
483
    usage.addKeyboardMouseBinding(" -3. w/s ",
484
                                  "Forward / backward translation");
485
    usage.addKeyboardMouseBinding(" -4. a/d ", "Left / right translation");
486
    usage.addKeyboardMouseBinding(" -5. Space/v ", "Up / down translation");
487
    usage.addKeyboardMouseBinding(" -6. q/e ", "Roll rotation");
488
  }
489
  usage.addKeyboardMouseBinding(" -7. Mouse ",
490
                                "Left button : Yaw / pitch rotation");
491
  usage.addKeyboardMouseBinding(" -8. ", "-------- ");
492
  usage.addKeyboardMouseBinding(" -9. r", "Reset the viewing position to home");
493
  usage.addKeyboardMouseBinding("-10. hold Ctrl", "Slow movement mode");
494
  usage.addKeyboardMouseBinding("-11. + / - ", "Change movement speed");
495
  usage.addKeyboardMouseBinding("-12. *", "Reset movement speed");
496
  usage.addKeyboardMouseBinding("-13. ---------", "----------------");
497
498
  /*std::cout<<"###################################################"<<std::endl;
499
  std::cout<<"#              Usage of FPSManipulator :          #"<<std::endl;
500
  std::cout<<"# Translation with the keyboard                   #"<<std::endl;
501
  if(keyLayout_ != LAYOUT_qwerty){
502
    std::cout<<"# Planar translation with arrow key or zqsd       #"<<std::endl;
503
    std::cout<<"# z/s  : forward/backward                         #"<<std::endl;
504
    std::cout<<"# q/d  : left/right                               #"<<std::endl;
505
    std::cout<<"# a/e  : roll rotation                            #"<<std::endl;
506
  }  else {
507
    std::cout<<"# Planar translation with arrow key or wasd       #"<<std::endl;
508
    std::cout<<"# w/s : forward/backward                          #"<<std::endl;
509
    std::cout<<"# a/d : left/right                                #"<<std::endl;
510
    std::cout<<"# q/e : roll rotation                             #"<<std::endl;
511
  }
512
  std::cout<<"# space/c : up/down                               #"<<std::endl;
513
  std::cout<<"# Mouse (left button) : yaw/pitch rotation        #"<<std::endl;
514
  std::cout<<"#            -------------------------            #"<<std::endl;
515
  std::cout<<"# r : Reset the view                              #"<<std::endl;
516
  std::cout<<"# hold Ctrl : Slow mouvements                     #"<<std::endl;
517
  std::cout<<"# + / - : change mouvement speed (keyboard only)  #"<<std::endl;
518
  std::cout<<"# * : reset mouvement speed (keyboard only)       #"<<std::endl;
519
  std::cout<<"# h : display this message                        #"<<std::endl;
520
  std::cout<<"# 1 : switch back to trackball manipulator        #"<<std::endl;
521
  std::cout<<"###################################################"<<std::endl;
522
523
524
*/
525
}