GCC Code Coverage Report


Directory: ./
File: src/OSGManipulator/keyboard-manipulator.cpp
Date: 2024-12-20 15:53:58
Exec Total Coverage
Lines: 0 219 0.0%
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 }
526