1 /***************************************************************************
2 * This file is part of Tecorrec. *
3 * Copyright 2008 James Hogan <james@albanarts.com> *
5 * Tecorrec is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 2 of the License, or *
8 * (at your option) any later version. *
10 * Tecorrec is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with Tecorrec. If not, write to the Free Software Foundation, *
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
21 * @file tcViewportWidget.cpp
22 * @brief OpenGL viewport widget.
25 #include "tcViewportWidget.h"
26 #include <tcObserver.h>
31 #include <QMouseEvent>
36 * Constructors + destructor
39 /// Primary constructor.
40 tcViewportWidget::tcViewportWidget(QWidget
* parent
)
42 , m_polygonMode(GL_FILL
)
43 , m_observer(new tcObserver())
45 , m_mouseInteracting(false)
47 , m_mouseIntersecting(false)
49 , m_mouseSlicing(false)
52 setMouseTracking(true);
53 setMinimumSize(400,300);
57 tcViewportWidget::~tcViewportWidget()
66 /// Set the globe object.
67 void tcViewportWidget::setGlobe(tcGlobe
* globe
)
70 tcGeo
pos( 7.03227 * M_PI
/180, 45.92093 * M_PI
/180);
71 m_observer
->setFocus(pos
, globe
->radiusAt(pos
));
78 /// Find the coordinates under the mouse.
79 tcGeo
tcViewportWidget::geoAt(float x
, float y
, bool* ok
)
81 maths::Vector
<2,double> screenXy(x
/ width(), 1.0 - y
/ height());
82 maths::Vector
<3,double> obs
= m_observer
->position();
83 maths::Vector
<3,double> ray
= m_observer
->ray(screenXy
, (double)width() / height());
84 double r
= m_globe
->meanRadius();
87 bool intersecting
= false;
88 // find intersection P of ray and globe
89 // given globe radius r, observer O, ray R, where |R|=1
92 // (O+t*R)*(O+t*R) = r*r
93 // O*O + 2*O*t*R + t*t*R*R = r*r
94 // O*O + 2*O*t*R + t*t - r*r = 0
95 // quadratic in parameter t
96 // a = 1, b = 2*RO c = O*O-r*r RO = O*R
97 // t = (-b +- sqrt(b*b - 4ac)) / 2a
98 // = (-2RO +- sqrt(4*RO*RO - 4*O*O + 4*r*r)) / 2
99 // = (-2*RO +- 2*sqrt(RO*RO - O*O + r*r)) / 2
100 // = RO += sqrt(RO*RO - O*O + r*r)
101 double discriminant
= roro
- obs
.sqr() + r
*r
;
102 if (discriminant
>= 0.0)
104 discriminant
= sqrt(discriminant
);
105 double t
= -ro
- discriminant
;
109 obs
/= r
; // should now be normalized
117 // mouse is outside of globe
118 // use closest point on outline circle of globe
120 // tangential circle: P*(P-O) = 0
123 // line: P/s = O + tR
124 // P = s(O + tR) (s>0)
125 // r*r - s(O + tR)O = 0
126 // r*r - s(OO + tRO) = 0
129 // t = (r*r/s - OO)/RO
131 // (sO + tsR)(sO + tsR - O) = 0
132 // (O + tR)(sO + tsR - O) = 0
133 // sOO + tsRO + tsOR + ttsRR - OO - tRO = 0
134 // sOO + 2tsRO + tts - OO - tRO = 0
135 // s(OO + 2tRO + tt) = OO - tRO
136 // substitute s=rr/(OO+tRO)
137 // rr/(OO+tRO)(OO + 2tRO + tt) = OO - tRO
138 // rr(OO + 2tRO + tt) = (OO - tRO)(OO+tRO)
139 // rrOO + 2rrtRO + rrtt = OO*OO - ttRO*RO
140 // tt(rr + RO*RO) + t(2rrRO) + (rrOO - OO*OO)
141 // a = rr + RO*RO, b = 2rrRO, c = rr00 - OO*OO
142 // t = (-2rrRO +- sqrt(4rrrrRO*RO - 4(rr + RO*RO)(rr00 - OO*OO))) / 2(rr + RO*RO)
143 // t = (-rrRO +- sqrt(rrrrRO*RO - (rr + RO*RO)(rr00 - OO*OO))) / (rr + RO*RO)
147 double discriminant
= rr
*rr
*roro
- (rr
+ roro
)*(rr
*oo
- oo
*oo
);
148 if (discriminant
>= 0)
150 discriminant
= sqrt(discriminant
);
151 double t
= (-rr
*ro
+ discriminant
) / (rr
+ roro
);
152 double s
= rr
/(oo
+ t
*ro
);
157 obs
/= r
; // should now be normalized
170 return tcGeo(obs
, true);
179 * General rendering slots
182 /// Change the polygon mode to normal.
183 void tcViewportWidget::setPolygonModeNormal()
185 m_polygonMode
= GL_FILL
;
189 /// Change the polygon mode to wireframe.
190 void tcViewportWidget::setPolygonModeWireframe()
192 m_polygonMode
= GL_LINE
;
196 /// Remove colour coding.
197 void tcViewportWidget::setColourCodingNone()
199 m_globe
->setColourCoding(tcGlobe::NoColourCoding
);
203 /// Set colour coding to elevation sample alignment.
204 void tcViewportWidget::setColourCodingElevationSampleAlignment()
206 m_globe
->setColourCoding(tcGlobe::ElevationSampleAlignment
);
211 * Elevation modification slots
214 /// Go to flat elevation mode.
215 void tcViewportWidget::setElevationFlat()
219 m_globe
->setElevationMode(tcGlobe::NoElevation
);
220 m_observer
->setFocusAltitude(m_globe
->radiusAt(m_observer
->focus()));
225 /// Go to raw SRTM elevation mode.
226 void tcViewportWidget::setElevationRaw()
230 m_globe
->setElevationMode(tcGlobe::RawElevation
);
231 m_observer
->setFocusAltitude(m_globe
->radiusAt(m_observer
->focus()));
236 /// Go to corrected elevation mode.
237 void tcViewportWidget::setElevationCorrected()
241 m_globe
->setElevationMode(tcGlobe::CorrectedElevation
);
242 m_observer
->setFocusAltitude(m_globe
->radiusAt(m_observer
->focus()));
247 /// Set the level of correction applied to the elevation data.
248 void tcViewportWidget::setElevationCorrection(int correction
)
252 m_globe
->setElevationCorrection((float)correction
/100);
253 m_observer
->setFocusAltitude(m_globe
->radiusAt(m_observer
->focus()));
258 /// Set the elevation data set name.
259 void tcViewportWidget::setElevationDataSet(const QString
& name
)
263 m_globe
->setElevationDataSet(name
);
264 m_observer
->setFocusAltitude(m_globe
->radiusAt(m_observer
->focus()));
273 void tcViewportWidget::initializeGL()
275 glClearColor(0.0, 0.3, 0.7, 0.0);
278 void tcViewportWidget::resizeGL(int w
, int h
)
280 glViewport(0, 0, (GLint
)w
, (GLint
)h
);
282 double aspect
= (double)w
/ h
;
283 m_observer
->setupProjection(aspect
);
286 void tcViewportWidget::paintGL()
288 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
289 glEnable(GL_DEPTH_TEST
);
290 glEnable(GL_CULL_FACE
);
293 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
295 glPolygonMode(GL_FRONT_AND_BACK
, m_polygonMode
);
297 m_observer
->setupModelView();
301 m_globe
->render(m_observer
, m_slice
);
305 m_globe
->render(m_observer
);
308 float meanRadius
= m_globe
->meanRadius();
311 double spotlightExtent
= m_observer
->range()*0.4;
312 // Draw a spotlight for the mouse
313 if (m_mouseIntersecting
)
315 maths::Vector
<3,double> vec
= m_mouseCoord
;
316 float altitude
= m_globe
->altitudeAt(m_mouseCoord
);
319 glColor4f(1.0f
, 1.0f
, 0.0f
, 1.0f
);
320 glVertex3(vec
*meanRadius
);
321 glVertex3(vec
*(meanRadius
+ altitude
));
324 glColor4f(1.0f
, 0.0f
, 1.0f
, 1.0f
);
325 glVertex3(vec
*(meanRadius
+ altitude
));
326 glColor4f(1.0f
, 1.0f
, 1.0f
, 0.0f
);
327 glVertex3(vec
*(meanRadius
+ altitude
+ spotlightExtent
));
331 // Draw a spotlight for the focus
333 maths::Vector
<3,double> vec
= m_observer
->focus();
334 float altitude
= m_globe
->altitudeAt(m_observer
->focus());
337 glColor4f(1.0f
, 1.0f
, 0.0f
, 1.0f
);
338 glVertex3(vec
*meanRadius
);
339 glVertex3(vec
*(meanRadius
+ altitude
));
342 glColor4f(0.0f
, 1.0f
, 1.0f
, 1.0f
);
343 glVertex3(vec
*(meanRadius
+ altitude
));
344 glColor4f(1.0f
, 1.0f
, 1.0f
, 0.0f
);
345 glVertex3(vec
*(meanRadius
+ altitude
+ spotlightExtent
));
351 // Draw a fence around the selected region
352 for (int i
= 0; i
< 2; ++i
)
356 float colour
[4] = {1.0f
, 0.5f
, 1.0f
, 1.0f
};
357 if (0 == i
&& m_mouseSlicing
&& m_mouseIntersecting
)
359 coord
= m_mouseCoord
;
360 ending
= m_mouseStartCoord
;
362 else if (1 == i
&& m_sliced
)
374 tcGeo delta
= ending
- coord
;
375 int nlon
= 2 + fabs(delta
.lon())*10*meanRadius
/ m_observer
->range();
376 int nlat
= 2 + fabs(delta
.lat())*10*meanRadius
/ m_observer
->range();
377 double dlon
= delta
.lon() / nlon
;
378 double dlat
= delta
.lat() / nlat
;
380 glDisable(GL_CULL_FACE
);
381 glBegin(GL_TRIANGLE_STRIP
);
382 for (int i
= 0; i
<= nlon
*2 + nlat
*2; ++i
)
384 float radius
= m_globe
->radiusAt(coord
);
385 maths::Vector
<3,double> coordVec
= coord
;
387 glVertex3(coordVec
*radius
);
388 glColor4f(1.0f
, 1.0f
, 1.0f
, 0.0f
);
389 glVertex3(coordVec
*(radius
+ m_observer
->range()*0.1));
393 coord
.setLon(coord
.lon() + dlon
);
395 else if (i
< nlon
+ nlat
)
397 coord
.setLat(coord
.lat() + dlat
);
399 else if (i
< nlon
+ nlat
+ nlon
)
401 coord
.setLon(coord
.lon() - dlon
);
405 coord
.setLat(coord
.lat() - dlat
);
409 glEnable(GL_CULL_FACE
);
418 void tcViewportWidget::mouseMoveEvent(QMouseEvent
* event
)
420 if (m_mouseInteracting
)
422 QPointF dpos
= event
->posF() - m_mousePos
;
423 m_mousePos
= event
->posF();
425 // Pan the scene, adjust the observer focus
426 m_observer
->moveFocusRelative(dpos
.x() / height(), dpos
.y() / width());
427 m_observer
->setFocusAltitude(m_globe
->radiusAt(m_observer
->focus()));
430 QString mouseDescription
;
432 m_mouseCoord
= geoAt(event
->posF().x(), event
->posF().y(), &ok
);
433 m_mouseIntersecting
= ok
;
436 mouseDescription
= m_mouseCoord
.describe() + " " + tr("%1 m","metres")
437 .arg(m_globe
->altitudeAt(m_mouseCoord
));
438 emit
mouseGeoChanged(m_mouseCoord
);
442 mouseDescription
= tr("--");
444 emit
mouseGeoTextChanged(tr("Focus: %1 %2, Mouse: %3")
445 .arg(m_observer
->focus().describe())
446 .arg(tr("%1 m","metres")
447 .arg(m_globe
->altitudeAt(m_observer
->focus())))
448 .arg(mouseDescription
));
450 if (m_mouseInteracting
|| m_mouseSlicing
|| m_sliced
)
456 void tcViewportWidget::mousePressEvent(QMouseEvent
* event
)
458 if (event
->button() == Qt::LeftButton
)
460 m_mouseInteracting
= true;
461 m_mousePos
= event
->posF();
463 else if (event
->button() == Qt::RightButton
)
466 m_mouseStartCoord
= geoAt(event
->posF().x(), event
->posF().y(), &ok
);
467 m_mouseIntersecting
= false;
470 m_mouseSlicing
= true;
475 void tcViewportWidget::mouseReleaseEvent(QMouseEvent
* event
)
477 if (m_mouseInteracting
)
479 m_mouseInteracting
= false;
481 else if (m_mouseSlicing
)
483 m_mouseSlicing
= false;
484 m_sliced
= m_mouseIntersecting
;
485 if (m_mouseIntersecting
)
487 m_slice
[0] = m_mouseStartCoord
;
488 m_slice
[1] = m_mouseCoord
;
494 void tcViewportWidget::wheelEvent(QWheelEvent
* event
)
496 float delta
= M_PI
/180.0 * event
->delta()/8;
498 if (event
->orientation() == Qt::Vertical
)
500 if (event
->modifiers().testFlag(Qt::ControlModifier
))
502 m_observer
->adjustElevation(delta
);
506 m_observer
->adjustRange(-delta
);
511 m_observer
->adjustAzimuth(-delta
);
513 m_observer
->setFocusAltitude(m_globe
->radiusAt(m_observer
->focus()));