Line |
Branch |
Exec |
Source |
1 |
|
|
// Copyright (c) 2015-2018, LAAS-CNRS |
2 |
|
|
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr) |
3 |
|
|
// |
4 |
|
|
// This file is part of gepetto-viewer. |
5 |
|
|
// gepetto-viewer is free software: you can redistribute it |
6 |
|
|
// and/or modify it under the terms of the GNU Lesser General Public |
7 |
|
|
// License as published by the Free Software Foundation, either version |
8 |
|
|
// 3 of the License, or (at your option) any later version. |
9 |
|
|
// |
10 |
|
|
// gepetto-viewer is distributed in the hope that it will be |
11 |
|
|
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
12 |
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 |
|
|
// General Lesser Public License for more details. You should have |
14 |
|
|
// received a copy of the GNU Lesser General Public License along with |
15 |
|
|
// gepetto-viewer. If not, see <http://www.gnu.org/licenses/>. |
16 |
|
|
|
17 |
|
|
#include "gepetto/gui/pick-handler.hh" |
18 |
|
|
|
19 |
|
|
#include <gepetto/viewer/node.h> |
20 |
|
|
|
21 |
|
|
#include <QApplication> |
22 |
|
|
#include <QDebug> |
23 |
|
|
#include <QStatusBar> |
24 |
|
|
#include <gepetto/gui/bodytreewidget.hh> |
25 |
|
|
#include <gepetto/gui/mainwindow.hh> |
26 |
|
|
#include <gepetto/gui/osgwidget.hh> |
27 |
|
|
#include <gepetto/gui/selection-event.hh> |
28 |
|
|
#include <gepetto/gui/selection-handler.hh> |
29 |
|
|
#include <gepetto/gui/tree-item.hh> |
30 |
|
|
#include <gepetto/gui/windows-manager.hh> |
31 |
|
|
#include <iostream> |
32 |
|
|
#include <osg/io_utils> |
33 |
|
|
#include <osgGA/KeySwitchMatrixManipulator> |
34 |
|
|
#include <osgGA/TrackballManipulator> |
35 |
|
|
#include <osgUtil/IntersectionVisitor> |
36 |
|
|
#include <osgUtil/LineSegmentIntersector> |
37 |
|
|
#include <osgViewer/Viewer> |
38 |
|
|
|
39 |
|
|
#include "point-intersector.hh" |
40 |
|
|
|
41 |
|
|
namespace gepetto { |
42 |
|
|
namespace gui { |
43 |
|
✗ |
PickHandler::PickHandler(OSGWidget* parent, WindowsManagerPtr_t wsm) |
44 |
|
|
: osgGA::GUIEventHandler(), |
45 |
|
✗ |
wsm_(wsm), |
46 |
|
✗ |
parent_(parent), |
47 |
|
✗ |
pushed_(false), |
48 |
|
✗ |
lastX_(0), |
49 |
|
✗ |
lastY_(0), |
50 |
|
✗ |
lineIntersector_(new osgUtil::LineSegmentIntersector( |
51 |
|
✗ |
osgUtil::Intersector::WINDOW, 0., 0.)), |
52 |
|
✗ |
pointIntersector_( |
53 |
|
✗ |
new PointIntersector(osgUtil::Intersector::WINDOW, 0., 0.)) { |
54 |
|
✗ |
lineIntersector_->setIntersectionLimit(osgUtil::Intersector::LIMIT_NEAREST); |
55 |
|
✗ |
pointIntersector_->setPickBias(0.01f); |
56 |
|
✗ |
pointIntersector_->setIntersectionLimit(osgUtil::Intersector::LIMIT_NEAREST); |
57 |
|
|
} |
58 |
|
|
|
59 |
|
✗ |
PickHandler::~PickHandler() {} |
60 |
|
|
|
61 |
|
✗ |
bool PickHandler::handle(const osgGA::GUIEventAdapter& ea, |
62 |
|
|
osgGA::GUIActionAdapter& aa) { |
63 |
|
✗ |
switch (ea.getEventType()) { |
64 |
|
✗ |
case osgGA::GUIEventAdapter::PUSH: |
65 |
|
|
case osgGA::GUIEventAdapter::RELEASE: |
66 |
|
✗ |
if (ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON) { |
67 |
|
✗ |
if (pushed_ && ea.getEventType() == osgGA::GUIEventAdapter::RELEASE) { |
68 |
|
✗ |
pushed_ = false; |
69 |
|
✗ |
if ((int)floor(lastX_ - ea.getX() + 0.5) == 0 && |
70 |
|
✗ |
(int)floor(lastY_ - ea.getY() + 0.5) == 0) { |
71 |
|
✗ |
selectionNodeUnderCursor(aa, ea.getX(), ea.getY(), |
72 |
|
|
ea.getModKeyMask()); |
73 |
|
✗ |
return true; |
74 |
|
|
} |
75 |
|
|
} |
76 |
|
✗ |
if (ea.getEventType() == osgGA::GUIEventAdapter::PUSH) { |
77 |
|
✗ |
lastX_ = ea.getX(); |
78 |
|
✗ |
lastY_ = ea.getY(); |
79 |
|
✗ |
pushed_ = true; |
80 |
|
|
} |
81 |
|
|
} |
82 |
|
✗ |
return false; |
83 |
|
|
break; |
84 |
|
✗ |
case osgGA::GUIEventAdapter::KEYUP: |
85 |
|
✗ |
switch (ea.getKey()) { |
86 |
|
✗ |
case 'z': |
87 |
|
✗ |
setCameraToSelected(aa, false); |
88 |
|
✗ |
return true; |
89 |
|
✗ |
case 'Z': |
90 |
|
✗ |
setCameraToSelected(aa, true); |
91 |
|
✗ |
return true; |
92 |
|
✗ |
case 'f': |
93 |
|
✗ |
centerViewToMouse(aa, ea.getX(), ea.getY()); |
94 |
|
✗ |
return true; |
95 |
|
✗ |
default: |
96 |
|
✗ |
break; |
97 |
|
|
} |
98 |
|
✗ |
break; |
99 |
|
✗ |
default: |
100 |
|
✗ |
break; |
101 |
|
|
} |
102 |
|
✗ |
return false; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
✗ |
void PickHandler::getUsage(osg::ApplicationUsage& usage) const { |
106 |
|
✗ |
usage.addKeyboardMouseBinding("Right click", "Select node"); |
107 |
|
✗ |
usage.addKeyboardMouseBinding('z', "Move camera on selected node"); |
108 |
|
✗ |
usage.addKeyboardMouseBinding('Z', "Move and zoom on selected node"); |
109 |
|
✗ |
usage.addKeyboardMouseBinding('f', "Center view to mouse"); |
110 |
|
|
} |
111 |
|
|
|
112 |
|
✗ |
void PickHandler::computeLineIntersection(osgGA::GUIActionAdapter& aa, |
113 |
|
|
const float& x, const float& y) { |
114 |
|
✗ |
lineIntersector_->reset(); |
115 |
|
✗ |
lineIntersector_->setStart(osg::Vec3d(x, y, 0.)); |
116 |
|
✗ |
lineIntersector_->setEnd(osg::Vec3d(x, y, 1.)); |
117 |
|
|
|
118 |
|
✗ |
osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa); |
119 |
|
✗ |
if (viewer) { |
120 |
|
|
// There is no need to lock the windows manager mutex |
121 |
|
|
// as this is treated in the event loop of OSG, and not Qt. |
122 |
|
|
// On the contrary, locking here creates a deadlock as the lock is |
123 |
|
|
// already acquired by OSGWidget::paintEvent. |
124 |
|
|
// wsm_->lock().lock(); |
125 |
|
|
// osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = |
126 |
|
|
// new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x, y); |
127 |
|
|
// intersector->setIntersectionLimit( osgUtil::Intersector::LIMIT_NEAREST ); |
128 |
|
|
|
129 |
|
✗ |
osgUtil::IntersectionVisitor iv(lineIntersector_); |
130 |
|
✗ |
iv.setTraversalMask(viewer::IntersectionBit); |
131 |
|
|
|
132 |
|
✗ |
osg::Camera* camera = viewer->getCamera(); |
133 |
|
✗ |
camera->accept(iv); |
134 |
|
|
} |
135 |
|
|
} |
136 |
|
|
|
137 |
|
✗ |
PickHandler::LineSegmentIntersector PickHandler::computeLineOrPointIntersection( |
138 |
|
|
osgGA::GUIActionAdapter& aa, const float& x, const float& y) { |
139 |
|
✗ |
osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa); |
140 |
|
✗ |
if (!viewer) return LineSegmentIntersector(); |
141 |
|
✗ |
osg::Camera* camera = viewer->getCamera(); |
142 |
|
|
|
143 |
|
✗ |
lineIntersector_->reset(); |
144 |
|
✗ |
lineIntersector_->setStart(osg::Vec3d(x, y, 0.)); |
145 |
|
✗ |
lineIntersector_->setEnd(osg::Vec3d(x, y, 1.)); |
146 |
|
|
{ |
147 |
|
✗ |
osgUtil::IntersectionVisitor iv(lineIntersector_); |
148 |
|
✗ |
iv.setTraversalMask(viewer::IntersectionBit); |
149 |
|
✗ |
camera->accept(iv); |
150 |
|
|
} |
151 |
|
✗ |
if (lineIntersector_->containsIntersections()) return lineIntersector_; |
152 |
|
|
|
153 |
|
✗ |
pointIntersector_->reset(); |
154 |
|
✗ |
pointIntersector_->setStart(osg::Vec3d(x, y, 0.)); |
155 |
|
✗ |
pointIntersector_->setEnd(osg::Vec3d(x, y, 1.)); |
156 |
|
|
{ |
157 |
|
✗ |
osgUtil::IntersectionVisitor iv(pointIntersector_); |
158 |
|
✗ |
iv.setTraversalMask(viewer::IntersectionBit); |
159 |
|
|
// iv.setTraversalMask(viewer::IntersectionBit | viewer::EditionBit); |
160 |
|
✗ |
camera->accept(iv); |
161 |
|
|
} |
162 |
|
✗ |
if (pointIntersector_->containsIntersections()) return pointIntersector_; |
163 |
|
✗ |
return LineSegmentIntersector(); |
164 |
|
|
} |
165 |
|
|
|
166 |
|
✗ |
void PickHandler::selectionNodeUnderCursor(osgGA::GUIActionAdapter& aa, |
167 |
|
|
const float& x, const float& y, |
168 |
|
|
int modKeyMask) { |
169 |
|
✗ |
LineSegmentIntersector li = computeLineOrPointIntersection(aa, x, y); |
170 |
|
✗ |
BodyTreeWidget* bt = MainWindow::instance()->bodyTree(); |
171 |
|
|
|
172 |
|
✗ |
if (!li || !li->containsIntersections()) { |
173 |
|
✗ |
bt->emitBodySelected(new SelectionEvent(SelectionEvent::FromOsgWindow, |
174 |
|
✗ |
QApplication::keyboardModifiers())); |
175 |
|
✗ |
return; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
// Only one intersection. Otherwise, one has to loop on elements of |
179 |
|
|
// intersector->getIntersections(); |
180 |
|
|
const osgUtil::LineSegmentIntersector::Intersection& intersection = |
181 |
|
✗ |
li->getFirstIntersection(); |
182 |
|
✗ |
for (int i = (int)intersection.nodePath.size() - 1; i >= 0; --i) { |
183 |
|
✗ |
if (!(intersection.nodePath[i]->getNodeMask() & viewer::NodeBit)) continue; |
184 |
|
✗ |
NodePtr_t n = wsm_->getNode(intersection.nodePath[i]->getName()); |
185 |
|
✗ |
if (n) { |
186 |
|
|
SelectionEvent* event = new SelectionEvent( |
187 |
|
✗ |
SelectionEvent::FromOsgWindow, n, mapper_.getQtModKey(modKeyMask)); |
188 |
|
✗ |
event->setupIntersection(intersection); |
189 |
|
✗ |
bt->emitBodySelected(event); |
190 |
|
|
|
191 |
|
✗ |
QStatusBar* statusBar = MainWindow::instance()->statusBar(); |
192 |
|
✗ |
statusBar->clearMessage(); |
193 |
|
✗ |
if (osg::dynamic_pointer_cast<PointIntersector>(li)) |
194 |
|
✗ |
statusBar->showMessage(QString::fromStdString(n->getID()) + |
195 |
|
✗ |
QString(" - Vectex index: ") + |
196 |
|
✗ |
QString::number(intersection.primitiveIndex)); |
197 |
|
|
else |
198 |
|
✗ |
statusBar->showMessage(QString::fromStdString(n->getID())); |
199 |
|
✗ |
return; |
200 |
|
|
} |
201 |
|
|
} |
202 |
|
|
} |
203 |
|
|
|
204 |
|
✗ |
void PickHandler::centerViewToMouse(osgGA::GUIActionAdapter& aa, const float& x, |
205 |
|
|
const float& y) { |
206 |
|
✗ |
osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa); |
207 |
|
✗ |
if (!viewer) return; |
208 |
|
|
|
209 |
|
✗ |
computeLineIntersection(aa, x, y); |
210 |
|
✗ |
if (!lineIntersector_->containsIntersections()) return; |
211 |
|
|
|
212 |
|
|
// Only one intersection. Otherwise, one has to loop on elements of |
213 |
|
|
// intersector->getIntersections(); |
214 |
|
|
const osgUtil::LineSegmentIntersector::Intersection& intersection = |
215 |
|
✗ |
lineIntersector_->getFirstIntersection(); |
216 |
|
|
|
217 |
|
✗ |
osg::Vec3f P(intersection.getWorldIntersectPoint()); |
218 |
|
|
|
219 |
|
✗ |
osgGA::TrackballManipulator* tbm = dynamic_cast<osgGA::TrackballManipulator*>( |
220 |
|
✗ |
viewer->getCameraManipulator()); |
221 |
|
✗ |
if (!tbm) { |
222 |
|
|
osgGA::KeySwitchMatrixManipulator* ksm = |
223 |
|
✗ |
dynamic_cast<osgGA::KeySwitchMatrixManipulator*>( |
224 |
|
✗ |
viewer->getCameraManipulator()); |
225 |
|
✗ |
if (ksm) { |
226 |
|
✗ |
tbm = dynamic_cast<osgGA::TrackballManipulator*>( |
227 |
|
✗ |
ksm->getCurrentMatrixManipulator()); |
228 |
|
|
} |
229 |
|
|
} |
230 |
|
✗ |
if (tbm) { |
231 |
|
✗ |
tbm->setCenter(P); |
232 |
|
|
} else { |
233 |
|
✗ |
osg::Vec3f eye, center, up; |
234 |
|
✗ |
viewer->getCameraManipulator()->getInverseMatrix().getLookAt(eye, center, |
235 |
|
|
up); |
236 |
|
|
|
237 |
|
✗ |
osg::Vec3f u(center - eye); |
238 |
|
✗ |
u.normalize(); |
239 |
|
✗ |
osg::Vec3f v(up ^ u); |
240 |
|
✗ |
osg::Vec3f t(v * (v * (P - eye))); |
241 |
|
✗ |
eye += t; |
242 |
|
✗ |
center += t; |
243 |
|
✗ |
viewer->getCameraManipulator()->setByInverseMatrix( |
244 |
|
✗ |
osg::Matrix::lookAt(eye, center, up)); |
245 |
|
|
} |
246 |
|
|
} |
247 |
|
|
|
248 |
|
✗ |
void PickHandler::setCameraToSelected(osgGA::GUIActionAdapter& aa, bool zoom) { |
249 |
|
✗ |
MainWindow* main = MainWindow::instance(); |
250 |
|
✗ |
NodePtr_t last = main->osg()->getNode( |
251 |
|
✗ |
main->selectionHandler()->mode()->currentBody().toStdString()); |
252 |
|
✗ |
if (!last) return; |
253 |
|
✗ |
osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa); |
254 |
|
✗ |
if (!viewer) return; |
255 |
|
|
|
256 |
|
✗ |
const osg::BoundingSphere& bs = last->asGroup()->getBound(); |
257 |
|
✗ |
osg::Vec3f eye, center, up; |
258 |
|
✗ |
viewer->getCameraManipulator()->getInverseMatrix().getLookAt(eye, center, up); |
259 |
|
|
|
260 |
|
✗ |
osgGA::TrackballManipulator* tbm = dynamic_cast<osgGA::TrackballManipulator*>( |
261 |
|
✗ |
viewer->getCameraManipulator()); |
262 |
|
✗ |
if (!tbm) { |
263 |
|
|
osgGA::KeySwitchMatrixManipulator* ksm = |
264 |
|
✗ |
dynamic_cast<osgGA::KeySwitchMatrixManipulator*>( |
265 |
|
✗ |
viewer->getCameraManipulator()); |
266 |
|
✗ |
if (ksm) { |
267 |
|
✗ |
tbm = dynamic_cast<osgGA::TrackballManipulator*>( |
268 |
|
✗ |
ksm->getCurrentMatrixManipulator()); |
269 |
|
|
} |
270 |
|
|
} |
271 |
|
✗ |
if (tbm) { |
272 |
|
✗ |
tbm->setCenter(bs.center()); |
273 |
|
✗ |
if (zoom) tbm->setDistance(3 * bs.radius()); |
274 |
|
|
} else { |
275 |
|
✗ |
if (zoom) { |
276 |
|
✗ |
osg::Vec3f tmp = center - eye; |
277 |
|
✗ |
tmp.normalize(); |
278 |
|
✗ |
eye = bs.center() - tmp * 3 * bs.radius(); |
279 |
|
|
} else { |
280 |
|
✗ |
eye += bs.center() - center; |
281 |
|
|
} |
282 |
|
✗ |
viewer->getCameraManipulator()->setByInverseMatrix( |
283 |
|
✗ |
osg::Matrixd::lookAt(eye, bs.center(), up)); |
284 |
|
|
} |
285 |
|
|
} |
286 |
|
|
} // namespace gui |
287 |
|
|
} // namespace gepetto |
288 |
|
|
|