1 | #include <float.h> |
---|
2 | #include <QMouseEvent> |
---|
3 | |
---|
4 | #include "gui/QGLViewerWidget.h" |
---|
5 | |
---|
6 | #define INERTIA_MAX 5.0f |
---|
7 | #define INERTIA_THRESHOLD 0.2f |
---|
8 | |
---|
9 | using namespace gmtl; |
---|
10 | |
---|
11 | static void dom2vp(const QDomElement& parent, gmtl::Vec3f& center, gmtl::Quatf& rot, float& distance) |
---|
12 | { |
---|
13 | if (parent.isNull()) |
---|
14 | return; |
---|
15 | |
---|
16 | center[0] = parent.attribute("centerx", "0").toFloat(); |
---|
17 | center[1] = parent.attribute("centery", "0").toFloat(); |
---|
18 | center[2] = parent.attribute("centerz", "0").toFloat(); |
---|
19 | rot[0] = parent.attribute("rotx", "0").toFloat(); |
---|
20 | rot[1] = parent.attribute("roty", "0").toFloat(); |
---|
21 | rot[2] = parent.attribute("rotz", "0").toFloat(); |
---|
22 | rot[3] = parent.attribute("rotw", "1").toFloat(); |
---|
23 | distance = parent.attribute("distance", "1").toFloat(); |
---|
24 | } |
---|
25 | |
---|
26 | static QDomElement vp2dom(QDomDocument& doc, const gmtl::Vec3f& center, const gmtl::Quatf& rot, float distance) |
---|
27 | { |
---|
28 | QDomElement cm = doc.createElement("Camera"); |
---|
29 | cm.setAttribute("centerx", QString::number(center[0])); |
---|
30 | cm.setAttribute("centery", QString::number(center[1])); |
---|
31 | cm.setAttribute("centerz", QString::number(center[2])); |
---|
32 | cm.setAttribute("rotx", QString::number(rot[0])); |
---|
33 | cm.setAttribute("roty", QString::number(rot[1])); |
---|
34 | cm.setAttribute("rotz", QString::number(rot[2])); |
---|
35 | cm.setAttribute("rotw", QString::number(rot[3])); |
---|
36 | cm.setAttribute("distance", QString::number(distance)); |
---|
37 | return cm; |
---|
38 | } |
---|
39 | |
---|
40 | QGLViewerWidget::QGLViewerWidget(QWidget* pParent, const QGLWidget* pShareWidget, Qt::WindowFlags f) : QGLWidget(pParent, pShareWidget, f) |
---|
41 | { |
---|
42 | m_bPerspective = true; |
---|
43 | m_fovy = 45.0f; |
---|
44 | m_znear = 0.01f; |
---|
45 | m_zfar = 100.0f; |
---|
46 | |
---|
47 | m_bInertia = true; |
---|
48 | m_animX = 0.0f; |
---|
49 | m_animY = 0.0f; |
---|
50 | m_bAnimating = false; |
---|
51 | |
---|
52 | m_clearColor = QColor(64, 64, 64); |
---|
53 | m_sceneSize = 10.0f; |
---|
54 | m_bGridVisible = false; |
---|
55 | m_bAxisVisible = false; |
---|
56 | m_gridSize = 10.0f; |
---|
57 | m_gridSubdiv = 10; |
---|
58 | |
---|
59 | viewAll(); |
---|
60 | |
---|
61 | m_timer.setInterval(30); |
---|
62 | connect(&m_timer, SIGNAL(timeout()), this, SLOT(timeout())); |
---|
63 | m_timer.start(); |
---|
64 | |
---|
65 | setFocusPolicy(Qt::StrongFocus); |
---|
66 | } |
---|
67 | |
---|
68 | void QGLViewerWidget::setView(const gmtl::Vec3f& center, const gmtl::Quatf& rot, float distance) |
---|
69 | { |
---|
70 | m_center = center; |
---|
71 | m_rot = rot; |
---|
72 | m_distance = distance; |
---|
73 | m_matrix = makeTrans<Matrix44f>(m_center) * makeRot<Matrix44f>(m_rot) * makeTrans<Matrix44f>(Vec3f(0.0f, 0.0f, m_distance)); |
---|
74 | invert(m_matrix); |
---|
75 | |
---|
76 | emit cameraChanged(center, rot, distance); |
---|
77 | } |
---|
78 | |
---|
79 | void QGLViewerWidget::init() |
---|
80 | { |
---|
81 | } |
---|
82 | |
---|
83 | void QGLViewerWidget::draw() |
---|
84 | { |
---|
85 | } |
---|
86 | |
---|
87 | void QGLViewerWidget::idle() |
---|
88 | { |
---|
89 | } |
---|
90 | |
---|
91 | void QGLViewerWidget::stop() |
---|
92 | { |
---|
93 | m_pivotCenter = m_center; |
---|
94 | m_pivotRot = m_rot; |
---|
95 | m_pivotDistance = m_distance; |
---|
96 | m_pivotOrthoScale = m_orthoScale; |
---|
97 | m_animX = 0.0f; |
---|
98 | m_animY = 0.0f; |
---|
99 | m_bAnimating = false; |
---|
100 | } |
---|
101 | |
---|
102 | void QGLViewerWidget::centerView() |
---|
103 | { |
---|
104 | stop(); |
---|
105 | m_center = Vec3f(0.0f, 0.0f, 0.0f); |
---|
106 | m_rot = Quatf(); |
---|
107 | m_distance = 0.0f; |
---|
108 | m_orthoScale = 0.25f; |
---|
109 | setView(m_center, m_rot, m_distance); |
---|
110 | updateGL(); |
---|
111 | } |
---|
112 | |
---|
113 | void QGLViewerWidget::viewAll() |
---|
114 | { |
---|
115 | stop(); |
---|
116 | m_center = Vec3f(0.0f, 0.0f, 0.0f); |
---|
117 | if (m_bPerspective) |
---|
118 | m_distance = m_sceneSize * 0.5f; |
---|
119 | else |
---|
120 | m_distance = 0.0f; |
---|
121 | m_orthoScale = 0.25f; |
---|
122 | setView(m_center, m_rot, m_distance); |
---|
123 | updateGL(); |
---|
124 | } |
---|
125 | |
---|
126 | void QGLViewerWidget::drawGrid() |
---|
127 | { |
---|
128 | if (m_gridSubdiv == 0) |
---|
129 | return; |
---|
130 | |
---|
131 | glPushAttrib(GL_ALL_ATTRIB_BITS); |
---|
132 | glDisable(GL_TEXTURE_2D); |
---|
133 | glDisable(GL_LIGHTING); |
---|
134 | glDisable(GL_BLEND); |
---|
135 | glEnable(GL_DEPTH_TEST); |
---|
136 | glLineWidth(1.0f); |
---|
137 | glColor4f(0.5f, 0.5f, 0.5f, 1.0f); |
---|
138 | glBegin(GL_LINES); |
---|
139 | for (int x=0; x<m_gridSubdiv+1; ++x) |
---|
140 | { |
---|
141 | glVertex3f(-m_gridSize/2.0f+m_gridSize/m_gridSubdiv*x, 0.0f, -m_gridSize/2.0f); |
---|
142 | glVertex3f(-m_gridSize/2.0f+m_gridSize/m_gridSubdiv*x, 0.0f, m_gridSize/2.0f); |
---|
143 | } |
---|
144 | for (int y=0; y<m_gridSubdiv+1; ++y) |
---|
145 | { |
---|
146 | glVertex3f(-m_gridSize/2.0f, 0.0f, -m_gridSize/2.0f+m_gridSize/m_gridSubdiv*y); |
---|
147 | glVertex3f( m_gridSize/2.0f, 0.0f, -m_gridSize/2.0f+m_gridSize/m_gridSubdiv*y); |
---|
148 | } |
---|
149 | glEnd(); |
---|
150 | glPopAttrib(); |
---|
151 | } |
---|
152 | |
---|
153 | void QGLViewerWidget::drawAxis() |
---|
154 | { |
---|
155 | glPushAttrib(GL_ALL_ATTRIB_BITS); |
---|
156 | glDisable(GL_TEXTURE_2D); |
---|
157 | glDisable(GL_LIGHTING); |
---|
158 | glDisable(GL_BLEND); |
---|
159 | glEnable(GL_DEPTH_TEST); |
---|
160 | glLineWidth(3.0f); |
---|
161 | glColor4f(1.0f, 0.0f, 0.0f, 1.0f); |
---|
162 | glBegin(GL_LINES); |
---|
163 | glVertex3f(0.0f, 0.0f, 0.0f); |
---|
164 | glVertex3f(m_gridSize*0.6f, 0.0f, 0.0f); |
---|
165 | glEnd(); |
---|
166 | glColor4f(0.0f, 1.0f, 0.0f, 1.0f); |
---|
167 | glBegin(GL_LINES); |
---|
168 | glVertex3f(0.0f, 0.0f, 0.0f); |
---|
169 | glVertex3f(0.0f, m_gridSize*0.6f, 0.0f); |
---|
170 | glEnd(); |
---|
171 | glColor4f(0.0f, 0.0f, 1.0f, 1.0f); |
---|
172 | glBegin(GL_LINES); |
---|
173 | glVertex3f(0.0f, 0.0f, 0.0f); |
---|
174 | glVertex3f(0.0f, 0.0f, m_gridSize*0.6f); |
---|
175 | glEnd(); |
---|
176 | glPopAttrib(); |
---|
177 | } |
---|
178 | |
---|
179 | void QGLViewerWidget::initializeGL() |
---|
180 | { |
---|
181 | init(); |
---|
182 | } |
---|
183 | |
---|
184 | void QGLViewerWidget::paintGL() |
---|
185 | { |
---|
186 | glViewport(0, 0, width(), height()); |
---|
187 | |
---|
188 | glClearColor((float)m_clearColor.red() / 255.0f, |
---|
189 | (float)m_clearColor.green() / 255.0f, |
---|
190 | (float)m_clearColor.blue() / 255.0f, |
---|
191 | 1.0f); |
---|
192 | |
---|
193 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); |
---|
194 | |
---|
195 | glMatrixMode(GL_PROJECTION); |
---|
196 | glLoadIdentity(); |
---|
197 | |
---|
198 | float aspect = 1.0f; |
---|
199 | if (height() > 0) |
---|
200 | aspect = (float)width()/height(); |
---|
201 | if (m_bPerspective) |
---|
202 | gluPerspective(m_fovy, aspect, m_znear, m_zfar); |
---|
203 | else |
---|
204 | { |
---|
205 | float size = m_sceneSize * m_orthoScale; |
---|
206 | glOrtho(-size*aspect, size*aspect, -size, size, -m_sceneSize*4.0f, m_sceneSize*4.0f); |
---|
207 | } |
---|
208 | |
---|
209 | glMatrixMode(GL_MODELVIEW); |
---|
210 | glLoadMatrixf(m_matrix.getData()); |
---|
211 | |
---|
212 | glEnable(GL_LIGHTING); |
---|
213 | glEnable(GL_LIGHT0); |
---|
214 | Vec3f pos = Vec3f(1.0f, 1.0f, 1.0f); |
---|
215 | if (!_isnan(m_rot[0])) |
---|
216 | pos = m_rot * pos; |
---|
217 | GLfloat lposition[] = { pos[0], pos[1], pos[2], 0.0f }; |
---|
218 | GLfloat lambient[] = { 0.1f, 0.1f, 0.1f, 1.0f }; |
---|
219 | GLfloat ldiffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; |
---|
220 | glLightfv(GL_LIGHT0, GL_POSITION, lposition); |
---|
221 | glLightfv(GL_LIGHT0, GL_AMBIENT, lambient); |
---|
222 | glLightfv(GL_LIGHT0, GL_DIFFUSE, ldiffuse); |
---|
223 | |
---|
224 | glEnable(GL_DEPTH_TEST); |
---|
225 | glEnable(GL_COLOR_MATERIAL); |
---|
226 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
---|
227 | |
---|
228 | draw(); |
---|
229 | |
---|
230 | glDisable(GL_LIGHTING); |
---|
231 | |
---|
232 | if (m_bGridVisible) |
---|
233 | drawGrid(); |
---|
234 | |
---|
235 | if (m_bAxisVisible) |
---|
236 | drawAxis(); |
---|
237 | |
---|
238 | glFlush(); |
---|
239 | } |
---|
240 | |
---|
241 | void QGLViewerWidget::mousePressEvent(QMouseEvent* pEvent) |
---|
242 | { |
---|
243 | m_timer.stop(); |
---|
244 | |
---|
245 | stop(); |
---|
246 | |
---|
247 | m_lastMousePos = pEvent->pos(); |
---|
248 | m_lastTime = QTime::currentTime(); |
---|
249 | |
---|
250 | setMouseTracking(true); |
---|
251 | } |
---|
252 | |
---|
253 | void QGLViewerWidget::mouseMoveEvent(QMouseEvent* pEvent) |
---|
254 | { |
---|
255 | if (m_bPerspective && (pEvent->buttons() & Qt::LeftButton)) |
---|
256 | { |
---|
257 | float delta = (float)m_lastTime.msecsTo(QTime::currentTime()) / 1000.0f; |
---|
258 | float deltaX = ((float)(pEvent->x()-m_lastMousePos.x())/width()*90.0f) / 180.0f * Math::PI; |
---|
259 | float deltaY = ((float)(pEvent->y()-m_lastMousePos.y())/height()*90.0f) / 180.0f * Math::PI; |
---|
260 | if (delta != 0.0f && (fabs(deltaX / delta) > INERTIA_THRESHOLD || fabs(deltaY / delta) > INERTIA_THRESHOLD)) |
---|
261 | { |
---|
262 | m_animX = deltaX / delta; |
---|
263 | m_animY = deltaY / delta; |
---|
264 | float length = sqrt(m_animX*m_animX+m_animY*m_animY); |
---|
265 | if (length > INERTIA_MAX) |
---|
266 | { |
---|
267 | m_animX = m_animX / length * INERTIA_MAX; |
---|
268 | m_animY = m_animY / length * INERTIA_MAX; |
---|
269 | } |
---|
270 | } |
---|
271 | else |
---|
272 | { |
---|
273 | m_animX = 0.0f; |
---|
274 | m_animY = 0.0f; |
---|
275 | } |
---|
276 | m_rot = m_pivotRot * make<Quatf>(AxisAnglef(-deltaX, Vec3f(0.0f, 1.0f, 0.0f))) * |
---|
277 | make<Quatf>(AxisAnglef(-deltaY, Vec3f(1.0f, 0.0f, 0.0f))); |
---|
278 | } |
---|
279 | else if (pEvent->buttons() & Qt::MidButton || |
---|
280 | (!m_bPerspective && pEvent->buttons() & Qt::LeftButton)) |
---|
281 | { |
---|
282 | float deltaX = (float)(pEvent->x()-m_lastMousePos.x())/width(); |
---|
283 | float deltaY = (float)(pEvent->y()-m_lastMousePos.y())/height(); |
---|
284 | Vec3f panX = m_rot * Vec3f(-deltaX, 0.0f, 0.0f); |
---|
285 | Vec3f panY = m_rot * Vec3f(0.0f, deltaY, 0.0f); |
---|
286 | m_center = m_pivotCenter + panX + panY; |
---|
287 | } |
---|
288 | else if (m_bPerspective && (pEvent->buttons() & Qt::RightButton)) |
---|
289 | { |
---|
290 | if (m_distance > 0.0f) |
---|
291 | { |
---|
292 | m_distance = m_pivotDistance - m_pivotDistance * (float)(pEvent->y()-m_lastMousePos.y())/height() * 1.0f; |
---|
293 | if (m_distance < m_znear*2.0f) |
---|
294 | m_distance = m_znear*2.0f; |
---|
295 | } |
---|
296 | } |
---|
297 | else if (!m_bPerspective && (pEvent->buttons() & Qt::RightButton)) |
---|
298 | { |
---|
299 | m_orthoScale = m_pivotOrthoScale - m_pivotOrthoScale * (float)(pEvent->y()-m_lastMousePos.y())/height() * 1.0f; |
---|
300 | if (m_orthoScale < 0.01f) |
---|
301 | m_orthoScale = 0.01f; |
---|
302 | } |
---|
303 | |
---|
304 | setView(m_center, m_rot, m_distance); |
---|
305 | m_lastTime = QTime::currentTime(); |
---|
306 | |
---|
307 | updateGL(); |
---|
308 | } |
---|
309 | |
---|
310 | void QGLViewerWidget::mouseReleaseEvent(QMouseEvent* pEvent) |
---|
311 | { |
---|
312 | Q_UNUSED(pEvent); |
---|
313 | |
---|
314 | if (m_lastTime.msecsTo(QTime::currentTime()) > 50 || !m_bInertia) |
---|
315 | { |
---|
316 | m_animX = 0.0f; |
---|
317 | m_animY = 0.0f; |
---|
318 | m_bAnimating = false; |
---|
319 | } |
---|
320 | if (m_animX != 0.0f || m_animY != 0.0f) |
---|
321 | m_bAnimating = true; |
---|
322 | |
---|
323 | setMouseTracking(false); |
---|
324 | |
---|
325 | m_timer.start(); |
---|
326 | } |
---|
327 | |
---|
328 | void QGLViewerWidget::wheelEvent(QWheelEvent* pEvent) |
---|
329 | { |
---|
330 | if (m_bPerspective) |
---|
331 | { |
---|
332 | if (pEvent->delta() > 0.0f) |
---|
333 | setView(m_center, m_rot, m_distance * 1.1f); |
---|
334 | else |
---|
335 | setView(m_center, m_rot, m_distance / 1.1f); |
---|
336 | } |
---|
337 | else |
---|
338 | { |
---|
339 | if (pEvent->delta() > 0.0f) |
---|
340 | m_orthoScale *= 1.1f; |
---|
341 | else |
---|
342 | m_orthoScale /= 1.1f; |
---|
343 | if (m_orthoScale < 0.01f) |
---|
344 | m_orthoScale = 0.01f; |
---|
345 | } |
---|
346 | |
---|
347 | updateGL(); |
---|
348 | } |
---|
349 | |
---|
350 | void QGLViewerWidget::keyReleaseEvent(QKeyEvent* pEvent) |
---|
351 | { |
---|
352 | QGLWidget::keyReleaseEvent(pEvent); |
---|
353 | |
---|
354 | switch (pEvent->key()) |
---|
355 | { |
---|
356 | case Qt::Key_A: |
---|
357 | setAxisVisible(!m_bAxisVisible); |
---|
358 | updateGL(); |
---|
359 | break; |
---|
360 | case Qt::Key_G: |
---|
361 | setGridVisible(!m_bGridVisible); |
---|
362 | updateGL(); |
---|
363 | break; |
---|
364 | case Qt::Key_R: |
---|
365 | viewAll(); |
---|
366 | break; |
---|
367 | default: |
---|
368 | return; |
---|
369 | } |
---|
370 | } |
---|
371 | |
---|
372 | void QGLViewerWidget::timeout() |
---|
373 | { |
---|
374 | if (m_bAnimating) |
---|
375 | { |
---|
376 | float delta = (float)m_lastTime.msecsTo(QTime::currentTime()) / 1000.0f; |
---|
377 | Quatf deltaRot = |
---|
378 | make<Quatf>(AxisAnglef(-m_animX*delta, Vec3f(0.0f, 1.0f, 0.0f))) * |
---|
379 | make<Quatf>(AxisAnglef(-m_animY*delta, Vec3f(1.0f, 0.0f, 0.0f))); |
---|
380 | Quatf rot = m_rot * deltaRot; |
---|
381 | if (!_isnan(rot[0])) |
---|
382 | setView(m_center, rot, m_distance); |
---|
383 | m_lastTime = QTime::currentTime(); |
---|
384 | } |
---|
385 | |
---|
386 | idle(); |
---|
387 | |
---|
388 | if (m_bAnimating) |
---|
389 | updateGL(); |
---|
390 | } |
---|
391 | |
---|
392 | void QGLViewerWidget::initFromDOMElement(const QDomElement& element) |
---|
393 | { |
---|
394 | if (!element.isNull()) |
---|
395 | { |
---|
396 | QDomElement camera = element.firstChildElement("Camera"); |
---|
397 | dom2vp(camera, m_center, m_rot, m_distance); |
---|
398 | m_bPerspective = (element.attribute("perspective", "true")=="true"); |
---|
399 | m_fovy = element.attribute("fovy", QString::number(45.0f)).toFloat(); |
---|
400 | m_orthoScale = element.attribute("orthoScale", QString::number(0.25f)).toFloat(); |
---|
401 | m_znear = element.attribute("near", QString::number(0.01f)).toFloat(); |
---|
402 | m_zfar = element.attribute("far", QString::number(100.0f)).toFloat(); |
---|
403 | m_bInertia = (element.attribute("inertia", "true")=="true"); |
---|
404 | m_clearColor = QColor(element.attribute("clearColor", QColor(64, 64, 64).name())); |
---|
405 | m_bGridVisible = (element.attribute("grid", "true")=="true"); |
---|
406 | m_bAxisVisible = (element.attribute("axis", "true")=="true"); |
---|
407 | m_sceneSize = element.attribute("sceneSize", QString::number(10.0f)).toFloat(); |
---|
408 | if (!parentWidget()) |
---|
409 | { |
---|
410 | move(element.attribute("x", QString::number(x())).toInt(), element.attribute("y", QString::number(y())).toInt()); |
---|
411 | resize(element.attribute("width", QString::number(width())).toInt(), element.attribute("height", QString::number(height())).toInt()); |
---|
412 | } |
---|
413 | setView(m_center, m_rot, m_distance); |
---|
414 | stop(); |
---|
415 | } |
---|
416 | } |
---|
417 | |
---|
418 | QDomElement QGLViewerWidget::domElement(const QString& name, QDomDocument& doc) const |
---|
419 | { |
---|
420 | QDomElement de = doc.createElement(name); |
---|
421 | de.appendChild(vp2dom(doc, m_center, m_rot, m_distance)); |
---|
422 | de.setAttribute("perspective", m_bPerspective?"true":"false"); |
---|
423 | de.setAttribute("fovy", m_fovy); |
---|
424 | de.setAttribute("orthoScale", m_orthoScale); |
---|
425 | de.setAttribute("near", m_znear); |
---|
426 | de.setAttribute("far", m_zfar); |
---|
427 | de.setAttribute("inertia", m_bInertia?"true":"false"); |
---|
428 | de.setAttribute("clearColor", m_clearColor.name()); |
---|
429 | de.setAttribute("grid", m_bGridVisible?"true":"false"); |
---|
430 | de.setAttribute("axis", m_bAxisVisible?"true":"false"); |
---|
431 | de.setAttribute("sceneSize", m_sceneSize); |
---|
432 | if (!parentWidget()) |
---|
433 | { |
---|
434 | de.setAttribute("x", x()); |
---|
435 | de.setAttribute("y", y()); |
---|
436 | de.setAttribute("width", width()); |
---|
437 | de.setAttribute("height", height()); |
---|
438 | } |
---|
439 | return de; |
---|
440 | } |
---|