]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/HUD/HUD_runway.cxx
74240e6270c842b5ba340d8619e3dbb04045d154
[flightgear.git] / src / Instrumentation / HUD / HUD_runway.cxx
1 // HUD_runway.cxx -- An instrument that renders a virtual runway on the HUD
2 //
3 // Written by Aaron Wilson & Phillip Merritt, Nov 2004.
4 //
5 // Copyright (C) 2004 Aaron Wilson, Aaron.I.Wilson@nasa.gov
6 // Copyright (C) 2004 Phillip Merritt, Phillip.M.Merritt@nasa.gov
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21
22 #include <simgear/compiler.h>
23 #include <simgear/math/sg_geodesy.hxx>
24 #include <simgear/math/polar3d.hxx>
25 #include SG_GLU_H
26
27 #include <Main/globals.hxx>
28 #include <Scenery/scenery.hxx>
29 #include <Aircraft/aircraft.hxx>
30 #include <Environment/environment.hxx>
31 #include <Environment/environment_mgr.hxx>
32 #include <Main/viewer.hxx>
33 #include <Main/viewmgr.hxx>
34 #include <ATC/ATCutils.hxx>
35
36 #include "HUD.hxx"
37
38
39 HUD::Runway::Runway(HUD *hud, const SGPropertyNode *node, float x, float y) :
40     Item(hud, node, x, y),
41     _agl(fgGetNode("/position/altitude-agl-ft", true)),
42     _arrow_scale(node->getDoubleValue("arrow-scale", 1.0)),
43     _arrow_radius(node->getDoubleValue("arrow-radius")),
44     _line_scale(node->getDoubleValue("line-scale", 1.0)),
45     _scale_dist(node->getDoubleValue("scale-dist-nm")),
46     _default_pitch(fgGetDouble("/sim/view[0]/config/pitch-pitch-deg", 0.0)),
47     _default_heading(fgGetDouble("/sim/view[0]/config/pitch-heading-deg", 0.0)),
48     _cockpit_view(globals->get_viewmgr()->get_view(0)),
49     _stipple_out(node->getIntValue("outer_stipple", 0xFFFF)),
50     _stipple_center(node->getIntValue("center-stipple", 0xFFFF)),
51     _draw_arrow(_arrow_scale > 0 ? true : false),
52     _draw_arrow_always(_arrow_scale > 0 ? node->getBoolValue("arrow-always") : false)
53 {
54     _view[0] = 0;
55     _view[1] = 0;
56     _view[2] = 640;
57     _view[3] = 480;
58
59     _center.x = _view[2] / 2;
60     _center.y = _view[3] / 2;
61
62     _location.left = _center.x - (get_width() / 2) + get_x();
63     _location.right = _center.x + (get_width() / 2) + get_x();
64     _location.bottom = _center.y - (get_height() / 2) + get_y();
65     _location.top = _center.y + (get_height() / 2) + get_y();
66 }
67
68
69 void HUD::Runway::draw()
70 {
71     if (!get_active_runway(_runway))
72         return;
73
74     glPushAttrib(GL_LINE_STIPPLE | GL_LINE_STIPPLE_PATTERN | GL_LINE_WIDTH);
75     float modelView[4][4], projMat[4][4];
76     bool anyLines;
77     //Get the current view
78     FGViewer* curr_view = globals->get_viewmgr()->get_current_view();
79     int curr_view_id = globals->get_viewmgr()->get_current();
80     double gpo = curr_view->getGoalPitchOffset_deg();
81     double gho = curr_view->getGoalHeadingOffset_deg();
82     double po = curr_view->getPitchOffset_deg();
83     double ho = curr_view->getHeadingOffset_deg();
84
85     double yaw = -(_cockpit_view->getHeadingOffset_deg() - _default_heading) * SG_DEGREES_TO_RADIANS;
86     double pitch = (_cockpit_view->getPitchOffset_deg() - _default_pitch) * SG_DEGREES_TO_RADIANS;
87     //double roll = fgGetDouble("/sim/view[0]/config/roll-offset-deg",0.0) //TODO: adjust for default roll offset
88     double sPitch = sin(pitch), cPitch = cos(pitch),
89            sYaw = sin(yaw), cYaw = cos(yaw);
90
91     //Assuming that the "Cockpit View" is always at position zero!!!
92     if (curr_view_id != 0) {
93         globals->get_viewmgr()->set_view(0);
94         globals->get_viewmgr()->copyToCurrent();
95     }
96     //Set the camera to the cockpit view to get the view of the runway from the cockpit
97     ssgSetCamera((sgVec4 *)_cockpit_view->get_VIEW());
98     get_rwy_points(_points3d);
99     //Get the current project matrix
100     ssgGetProjectionMatrix(projMat);
101 //    const sgVec4 *viewMat = globals->get_current_view()->get_VIEW();
102     //Get the current model view matrix (cockpit view)
103     ssgGetModelviewMatrix(modelView);
104     //Create a rotation matrix to correct for any offsets (other than default offsets) to the model view matrix
105     sgMat4 xy; //rotation about the Rxy, negate the sin's on Ry
106     xy[0][0] = cYaw;         xy[1][0] = 0.0f;   xy[2][0] = -sYaw;        xy[3][0] = 0.0f;
107     xy[0][1] = sPitch*-sYaw; xy[1][1] = cPitch; xy[2][1] = -sPitch*cYaw; xy[3][1] = 0.0f;
108     xy[0][2] = cPitch*sYaw;  xy[1][2] = sPitch; xy[2][2] = cPitch*cYaw;  xy[3][2] = 0.0f;
109     xy[0][3] = 0.0f;         xy[1][3] = 0.0f;   xy[2][3] = 0.0f;         xy[3][3] = 1.0f;
110     //Re-center the model view
111     sgPostMultMat4(modelView,xy);
112     //copy float matrices to double
113     for (int i = 0; i < 4; i++) {
114         for (int j = 0; j < 4; j++) {
115             int idx = (i * 4) + j;
116             _mm[idx] = (double)modelView[i][j];
117             _pm[idx] = (double)projMat[i][j];
118         }
119     }
120
121     //Calculate the 2D points via gluProject
122     int result = GL_TRUE;
123     for (int i = 0; i < 6; i++) {
124         result = gluProject(_points3d[i][0], _points3d[i][1], _points3d[i][2], _mm,
125                 _pm, _view, &_points2d[i][0], &_points2d[i][1], &_points2d[i][2]);
126     }
127     //set the line width based on our distance from the runway
128     setLineWidth();
129     //Draw the runway lines on the HUD
130     glEnable(GL_LINE_STIPPLE);
131     glLineStipple(1, _stipple_out);
132     anyLines =
133             drawLine(_points3d[0], _points3d[1], _points2d[0], _points2d[1]) | //draw top
134             drawLine(_points3d[2], _points3d[1], _points2d[2], _points2d[1]) | //draw right
135             drawLine(_points3d[2], _points3d[3], _points2d[2], _points2d[3]) | //draw bottom
136             drawLine(_points3d[3], _points3d[0], _points2d[3], _points2d[0]);  //draw left
137
138     glLineStipple(1, _stipple_center);
139     anyLines |= drawLine(_points3d[5], _points3d[4], _points2d[5], _points2d[4]); //draw center
140
141     //Check to see if arrow needs drawn
142     if ((!anyLines && _draw_arrow) || _draw_arrow_always) {
143         drawArrow(); //draw indication arrow
144     }
145
146     //Restore the current view and any offsets
147     if (curr_view_id != 0) {
148         globals->get_viewmgr()->set_view(curr_view_id);
149         globals->get_viewmgr()->copyToCurrent();
150         curr_view->setHeadingOffset_deg(ho);
151         curr_view->setPitchOffset_deg(po);
152         curr_view->setGoalHeadingOffset_deg(gho);
153         curr_view->setGoalPitchOffset_deg(gpo);
154     }
155     //Set the camera back to the current view
156     ssgSetCamera((sgVec4 *)curr_view);
157     glPopAttrib();
158 }
159
160
161 bool HUD::Runway::get_active_runway(FGRunway& runway)
162 {
163     FGEnvironment stationweather =
164             ((FGEnvironmentMgr *)globals->get_subsystem("environment"))->getEnvironment();
165     double hdg = stationweather.get_wind_from_heading_deg();
166     return globals->get_runways()->search(fgGetString("/sim/presets/airport-id"), int(hdg), &runway);
167 }
168
169
170 void HUD::Runway::get_rwy_points(sgdVec3 *_points3d)
171 {
172     static Point3D center = globals->get_scenery()->get_center();
173
174     //Get the current tile center
175     Point3D currentCenter = globals->get_scenery()->get_center();
176     Point3D tileCenter = currentCenter;
177     if (center != currentCenter) //if changing tiles
178         tileCenter = center; //use last center
179
180     double alt = current_aircraft.fdm_state->get_Runway_altitude() * SG_FEET_TO_METER;
181     double length = (_runway._length / 2.0) * SG_FEET_TO_METER;
182     double width = (_runway._width / 2.0) * SG_FEET_TO_METER;
183     double frontLat, frontLon, backLat, backLon,az, tempLat, tempLon;
184
185     geo_direct_wgs_84(alt, _runway._lat, _runway._lon, _runway._heading, length, &backLat, &backLon, &az);
186     sgGeodToCart(backLat * SG_DEGREES_TO_RADIANS, backLon * SG_DEGREES_TO_RADIANS, alt, _points3d[4]);
187
188     geo_direct_wgs_84(alt, _runway._lat, _runway._lon, _runway._heading + 180, length, &frontLat, &frontLon, &az);
189     sgGeodToCart(frontLat * SG_DEGREES_TO_RADIANS, frontLon * SG_DEGREES_TO_RADIANS, alt, _points3d[5]);
190
191     geo_direct_wgs_84(alt, backLat, backLon, _runway._heading + 90, width, &tempLat, &tempLon, &az);
192     sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[0]);
193
194     geo_direct_wgs_84(alt, backLat, backLon, _runway._heading - 90, width, &tempLat, &tempLon, &az);
195     sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[1]);
196
197     geo_direct_wgs_84(alt, frontLat, frontLon, _runway._heading - 90, width, &tempLat, &tempLon, &az);
198     sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[2]);
199
200     geo_direct_wgs_84(alt, frontLat, frontLon, _runway._heading + 90, width, &tempLat, &tempLon, &az);
201     sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[3]);
202
203     for (int i = 0; i < 6; i++) {
204         _points3d[i][0] -= tileCenter.x();
205         _points3d[i][1] -= tileCenter.y();
206         _points3d[i][2] -= tileCenter.z();
207     }
208     center = currentCenter;
209 }
210
211
212 bool HUD::Runway::drawLine(const sgdVec3& a1, const sgdVec3& a2, const sgdVec3& point1, const sgdVec3& point2)
213 {
214     sgdVec3 p1, p2;
215     sgdCopyVec3(p1, point1);
216     sgdCopyVec3(p2, point2);
217     bool p1Inside = (p1[0] >= _location.left && p1[0] <= _location.right
218             && p1[1] >= _location.bottom && p1[1] <= _location.top);
219     bool p1Insight = (p1[2] >= 0.0 && p1[2] < 1.0);
220     bool p1Valid = p1Insight && p1Inside;
221     bool p2Inside = (p2[0] >= _location.left && p2[0] <= _location.right
222             && p2[1] >= _location.bottom && p2[1] <= _location.top);
223     bool p2Insight = (p2[2] >= 0.0 && p2[2] < 1.0);
224     bool p2Valid = p2Insight && p2Inside;
225
226     if (p1Valid && p2Valid) { //Both project points are valid, draw the line
227         glBegin(GL_LINES);
228         glVertex2d(p1[0],p1[1]);
229         glVertex2d(p2[0],p2[1]);
230         glEnd();
231
232     } else if (p1Valid) { //p1 is valid and p2 is not, calculate a new valid point
233         sgdVec3 vec = {a2[0] - a1[0], a2[1] - a1[1], a2[2] - a1[2]};
234         //create the unit vector
235         sgdScaleVec3(vec, 1.0 / sgdLengthVec3(vec));
236         sgdVec3 newPt;
237         sgdCopyVec3(newPt, a1);
238         sgdAddVec3(newPt, vec);
239         if (gluProject(newPt[0], newPt[1], newPt[2], _mm, _pm, _view, &p2[0], &p2[1], &p2[2])
240                 && (p2[2] > 0 && p2[2] < 1.0)) {
241             boundPoint(p1, p2);
242             glBegin(GL_LINES);
243             glVertex2d(p1[0], p1[1]);
244             glVertex2d(p2[0], p2[1]);
245             glEnd();
246         }
247
248     } else if (p2Valid) { //p2 is valid and p1 is not, calculate a new valid point
249         sgdVec3 vec = {a1[0] - a2[0], a1[1] - a2[1], a1[2] - a2[2]};
250         //create the unit vector
251         sgdScaleVec3(vec, 1.0 / sgdLengthVec3(vec));
252         sgdVec3 newPt;
253         sgdCopyVec3(newPt, a2);
254         sgdAddVec3(newPt, vec);
255         if (gluProject(newPt[0], newPt[1], newPt[2], _mm, _pm, _view, &p1[0], &p1[1], &p1[2])
256                 && (p1[2] > 0 && p1[2] < 1.0)) {
257             boundPoint(p2, p1);
258             glBegin(GL_LINES);
259             glVertex2d(p2[0], p2[1]);
260             glVertex2d(p1[0], p1[1]);
261             glEnd();
262         }
263
264     } else if (p1Insight && p2Insight) { //both points are insight, but not inside
265         bool v = boundOutsidePoints(p1, p2);
266         if (v) {
267             glBegin(GL_LINES);
268                 glVertex2d(p1[0], p1[1]);
269                 glVertex2d(p2[0], p2[1]);
270             glEnd();
271         }
272         return v;
273     }
274     //else both points are not insight, don't draw anything
275     return (p1Valid && p2Valid);
276 }
277
278
279 void HUD::Runway::boundPoint(const sgdVec3& v, sgdVec3& m)
280 {
281     double y = v[1];
282     if (m[1] < v[1])
283         y = _location.bottom;
284     else if (m[1] > v[1])
285         y = _location.top;
286
287     if (m[0] == v[0]) {
288         m[1] = y;
289         return;  //prevent divide by zero
290     }
291
292     double slope = (m[1] - v[1]) / (m[0] - v[0]);
293     m[0] = (y - v[1]) / slope + v[0];
294     m[1] = y;
295
296     if (m[0] < _location.left) {
297         m[0] = _location.left;
298         m[1] = slope * (_location.left - v[0]) + v[1];
299
300     } else if (m[0] > _location.right) {
301         m[0] = _location.right;
302         m[1] = slope * (_location.right - v[0]) + v[1];
303     }
304 }
305
306
307 bool HUD::Runway::boundOutsidePoints(sgdVec3& v, sgdVec3& m)
308 {
309     bool pointsInvalid = (v[1] > _location.top && m[1] > _location.top) ||
310                          (v[1] < _location.bottom && m[1] < _location.bottom) ||
311                          (v[0] > _location.right && m[0] > _location.right) ||
312                          (v[0] < _location.left && m[0] < _location.left);
313     if (pointsInvalid)
314         return false;
315
316     if (m[0] == v[0]) {//x's are equal, vertical line
317         if (m[1] > v[1]) {
318             m[1] = _location.top;
319             v[1] = _location.bottom;
320         } else {
321             v[1] = _location.top;
322             m[1] = _location.bottom;
323         }
324         return true;
325     }
326
327     if (m[1] == v[1]) { //y's are equal, horizontal line
328         if (m[0] > v[0]) {
329             m[0] = _location.right;
330             v[0] = _location.left;
331         } else {
332             v[0] = _location.right;
333             m[0] = _location.left;
334         }
335         return true;
336     }
337
338     double slope = (m[1] - v[1]) / (m[0] - v[0]);
339     double b = v[1] - (slope * v[0]);
340     double y1 = slope * _location.left + b;
341     double y2 = slope * _location.right + b;
342     double x1 = (_location.bottom - b) / slope;
343     double x2 = (_location.top - b) / slope;
344     int counter = 0;
345
346     if  (y1 >= _location.bottom && y1 <= _location.top) {
347         v[0] = _location.left;
348         v[1] = y1;
349         counter++;
350     }
351
352     if (y2 >= _location.bottom && y2 <= _location.top) {
353         if (counter > 0) {
354             m[0] = _location.right;
355             m[1] = y2;
356         } else {
357             v[0] = _location.right;
358             v[1] = y2;
359         }
360         counter++;
361     }
362
363     if (x1 >= _location.left && x1 <= _location.right) {
364         if (counter > 0) {
365             m[0] = x1;
366             m[1] = _location.bottom;
367         } else {
368             v[0] = x1;
369             v[1] = _location.bottom;
370         }
371         counter++;
372     }
373
374     if (x2 >= _location.left && x2 <= _location.right) {
375         m[0] = x1;
376         m[1] = _location.bottom;
377         counter++;
378     }
379     return (counter == 2);
380 }
381
382
383 void HUD::Runway::drawArrow()
384 {
385     Point3D ac(0.0), rwy(0.0);
386     ac.setlat(current_aircraft.fdm_state->get_Latitude_deg());
387     ac.setlon(current_aircraft.fdm_state->get_Longitude_deg());
388     rwy.setlat(_runway._lat);
389     rwy.setlon(_runway._lon);
390     float theta = GetHeadingFromTo(ac, rwy);
391     theta -= fgGetDouble("/orientation/heading-deg");
392     theta = -theta;
393     glMatrixMode(GL_MODELVIEW);
394     glPushMatrix();
395     glTranslated((_location.right + _location.left) / 2.0,(_location.top + _location.bottom) / 2.0, 0.0);
396     glRotated(theta, 0.0, 0.0, 1.0);
397     glTranslated(0.0, _arrow_radius, 0.0);
398     glScaled(_arrow_scale, _arrow_scale, 0.0);
399
400     glBegin(GL_TRIANGLES);
401     glVertex2d(-5.0, 12.5);
402     glVertex2d(0.0, 25.0);
403     glVertex2d(5.0, 12.5);
404     glEnd();
405
406     glBegin(GL_QUADS);
407     glVertex2d(-2.5, 0.0);
408     glVertex2d(-2.5, 12.5);
409     glVertex2d(2.5, 12.5);
410     glVertex2d(2.5, 0.0);
411     glEnd();
412     glPopMatrix();
413 }
414
415
416 void HUD::Runway::setLineWidth()
417 {
418     //Calculate the distance from the runway, A
419     double course, distance;
420     calc_gc_course_dist(Point3D(_runway._lon * SGD_DEGREES_TO_RADIANS,
421             _runway._lat * SGD_DEGREES_TO_RADIANS, 0.0),
422             Point3D(current_aircraft.fdm_state->get_Longitude(),
423             current_aircraft.fdm_state->get_Latitude(), 0.0 ),
424             &course, &distance);
425     distance *= SG_METER_TO_NM;
426     //Get altitude above runway, B
427     double alt_nm = _agl->getDoubleValue();
428
429     if (_hud->getUnits() == FEET)
430         alt_nm *= SG_FEET_TO_METER;
431
432     alt_nm *= SG_METER_TO_NM;
433
434     //Calculate distance away from runway, C = v(A²+B²)
435     distance = sqrt(alt_nm * alt_nm + distance*distance);
436     if (distance < _scale_dist)
437         glLineWidth(1.0 + ((_line_scale - 1) * ((_scale_dist - distance) / _scale_dist)));
438     else
439         glLineWidth(1.0);
440
441 }
442
443