GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/OSGManipulator/keyboard-manipulator.cpp Lines: 1 219 0.5 %
Date: 2020-05-14 11:23:33 Branches: 2 252 0.8 %

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

3
}