]> git.mxchange.org Git - flightgear.git/blob - src/Main/viewer.cxx
Make spoilers and speed-brake position accessible
[flightgear.git] / src / Main / viewer.cxx
1 // viewer.cxx -- class for managing a viewer in the flightgear world.
2 //
3 // Written by Curtis Olson, started August 1997.
4 //                          overhaul started October 2000.
5 //   partially rewritten by Jim Wilson jim@kelcomaine.com using interface
6 //                          by David Megginson March 2002
7 //
8 // Copyright (C) 1997 - 2000  Curtis L. Olson  - curt@flightgear.org
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 // General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 //
24 // $Id$
25
26
27 #include <simgear/compiler.h>
28
29 #include "fg_props.hxx"
30
31 #ifdef HAVE_CONFIG_H
32 #  include <config.h>
33 #endif
34
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/constants.h>
37 #include <simgear/math/point3d.hxx>
38 #include <simgear/math/polar3d.hxx>
39 #include <simgear/math/sg_geodesy.hxx>
40
41 #include <Scenery/scenery.hxx>
42
43 #include <simgear/math/vector.hxx>
44 #include <Main/globals.hxx>
45 #include <Model/acmodel.hxx>
46 #include <Model/model.hxx>
47
48 #include "viewer.hxx"
49
50 \f
51 //////////////////////////////////////////////////////////////////
52 // Norman's Optimized matrix rotators!                          //
53 //////////////////////////////////////////////////////////////////
54
55
56 // Since these are pure rotation matrices we can save some bookwork
57 // by considering them to be 3x3 until the very end -- NHV
58 static void MakeVIEW_OFFSET( sgMat4 dst,
59                       const float angle1, const sgVec3 axis1,
60                       const float angle2, const sgVec3 axis2 )
61 {
62     // make rotmatrix1 from angle and axis
63     float s = (float) sin ( angle1 ) ;
64     float c = (float) cos ( angle1 ) ;
65     float t = SG_ONE - c ;
66
67     sgMat3 mat1;
68     float tmp = t * axis1[0];
69     mat1[0][0] = tmp * axis1[0] + c ;
70     mat1[0][1] = tmp * axis1[1] + s * axis1[2] ;
71     mat1[0][2] = tmp * axis1[2] - s * axis1[1] ;
72
73     tmp = t * axis1[1];
74     mat1[1][0] = tmp * axis1[0] - s * axis1[2] ;
75     mat1[1][1] = tmp * axis1[1] + c ;
76     mat1[1][2] = tmp * axis1[2] + s * axis1[0] ;
77
78     tmp = t * axis1[2];
79     mat1[2][0] = tmp * axis1[0] + s * axis1[1] ;
80     mat1[2][1] = tmp * axis1[1] - s * axis1[0] ;
81     mat1[2][2] = tmp * axis1[2] + c ;
82
83     // make rotmatrix2 from angle and axis
84     s = (float) sin ( angle2 ) ;
85     c = (float) cos ( angle2 ) ;
86     t = SG_ONE - c ;
87
88     sgMat3 mat2;
89     tmp = t * axis2[0];
90     mat2[0][0] = tmp * axis2[0] + c ;
91     mat2[0][1] = tmp * axis2[1] + s * axis2[2] ;
92     mat2[0][2] = tmp * axis2[2] - s * axis2[1] ;
93
94     tmp = t * axis2[1];
95     mat2[1][0] = tmp * axis2[0] - s * axis2[2] ;
96     mat2[1][1] = tmp * axis2[1] + c ;
97     mat2[1][2] = tmp * axis2[2] + s * axis2[0] ;
98
99     tmp = t * axis2[2];
100     mat2[2][0] = tmp * axis2[0] + s * axis2[1] ;
101     mat2[2][1] = tmp * axis2[1] - s * axis2[0] ;
102     mat2[2][2] = tmp * axis2[2] + c ;
103
104     // multiply matrices
105     for ( int j = 0 ; j < 3 ; j++ ) {
106         dst[0][j] = mat2[0][0] * mat1[0][j] +
107                     mat2[0][1] * mat1[1][j] +
108                     mat2[0][2] * mat1[2][j];
109
110         dst[1][j] = mat2[1][0] * mat1[0][j] +
111                     mat2[1][1] * mat1[1][j] +
112                     mat2[1][2] * mat1[2][j];
113
114         dst[2][j] = mat2[2][0] * mat1[0][j] +
115                     mat2[2][1] * mat1[1][j] +
116                     mat2[2][2] * mat1[2][j];
117     }
118     // fill in 4x4 matrix elements
119     dst[0][3] = SG_ZERO; 
120     dst[1][3] = SG_ZERO; 
121     dst[2][3] = SG_ZERO;
122     dst[3][0] = SG_ZERO;
123     dst[3][1] = SG_ZERO;
124     dst[3][2] = SG_ZERO;
125     dst[3][3] = SG_ONE;
126 }
127
128
129 ////////////////////////////////////////////////////////////////////////
130 // Implementation of FGViewer.
131 ////////////////////////////////////////////////////////////////////////
132
133 // Constructor...
134 FGViewer::FGViewer( fgViewType Type, bool from_model, int from_model_index, 
135                     bool at_model, int at_model_index,
136                     double x_offset_m, double y_offset_m, double z_offset_m, 
137                     double heading_offset_deg, double pitch_offset_deg,
138                     double roll_offset_deg, double fov_deg,
139                     double target_x_offset_m, double target_y_offset_m,
140                     double target_z_offset_m, double near_m ):
141     _dirty(true),
142     _lon_deg(0),
143     _lat_deg(0),
144     _alt_ft(0),
145     _target_lon_deg(0),
146     _target_lat_deg(0),
147     _target_alt_ft(0),
148     _roll_deg(0),
149     _pitch_deg(0),
150     _heading_deg(0),
151     _scaling_type(FG_SCALING_MAX)
152 {
153     sgdZeroVec3(_absolute_view_pos);
154     _type = Type;
155     _from_model = from_model;
156     _from_model_index = from_model_index;
157     _at_model = at_model;
158     _at_model_index = at_model_index;
159     _x_offset_m = x_offset_m;
160     _y_offset_m = y_offset_m;
161     _z_offset_m = z_offset_m;
162     _heading_offset_deg = heading_offset_deg;
163     _pitch_offset_deg = pitch_offset_deg;
164     _roll_offset_deg = roll_offset_deg;
165     _goal_heading_offset_deg = heading_offset_deg;
166     _goal_pitch_offset_deg = pitch_offset_deg;
167     _goal_roll_offset_deg = roll_offset_deg;
168     if (fov_deg > 0) {
169       _fov_deg = fov_deg;
170     } else {
171       _fov_deg = 55;
172     }
173     _target_x_offset_m = target_x_offset_m;
174     _target_y_offset_m = target_y_offset_m;
175     _target_z_offset_m = target_z_offset_m;
176     _ground_level_nearplane_m = near_m;
177     // a reasonable guess for init, so that the math doesn't blow up
178 }
179
180
181 // Destructor
182 FGViewer::~FGViewer( void ) {
183 }
184
185 void
186 FGViewer::init ()
187 {
188   if ( _from_model )
189     _location = (FGLocation *) globals->get_aircraft_model()->get3DModel()->getFGLocation();
190   else
191     _location = (FGLocation *) new FGLocation;
192
193   if ( _type == FG_LOOKAT ) {
194     if ( _at_model )
195       _target_location = (FGLocation *) globals->get_aircraft_model()->get3DModel()->getFGLocation();
196     else
197       _target_location = (FGLocation *) new FGLocation;
198   }
199 }
200
201 void
202 FGViewer::bind ()
203 {
204 }
205
206 void
207 FGViewer::unbind ()
208 {
209 }
210
211 void
212 FGViewer::setType ( int type )
213 {
214   if (type == 0)
215     _type = FG_LOOKFROM;
216   if (type == 1)
217     _type = FG_LOOKAT;
218 }
219
220 void
221 FGViewer::setLongitude_deg (double lon_deg)
222 {
223   _dirty = true;
224   _lon_deg = lon_deg;
225 }
226
227 void
228 FGViewer::setLatitude_deg (double lat_deg)
229 {
230   _dirty = true;
231   _lat_deg = lat_deg;
232 }
233
234 void
235 FGViewer::setAltitude_ft (double alt_ft)
236 {
237   _dirty = true;
238   _alt_ft = alt_ft;
239 }
240
241 void
242 FGViewer::setPosition (double lon_deg, double lat_deg, double alt_ft)
243 {
244   _dirty = true;
245   _lon_deg = lon_deg;
246   _lat_deg = lat_deg;
247   _alt_ft = alt_ft;
248 }
249
250 void
251 FGViewer::setTargetLongitude_deg (double lon_deg)
252 {
253   _dirty = true;
254   _target_lon_deg = lon_deg;
255 }
256
257 void
258 FGViewer::setTargetLatitude_deg (double lat_deg)
259 {
260   _dirty = true;
261   _target_lat_deg = lat_deg;
262 }
263
264 void
265 FGViewer::setTargetAltitude_ft (double alt_ft)
266 {
267   _dirty = true;
268   _target_alt_ft = alt_ft;
269 }
270
271 void
272 FGViewer::setTargetPosition (double lon_deg, double lat_deg, double alt_ft)
273 {
274   _dirty = true;
275   _target_lon_deg = lon_deg;
276   _target_lat_deg = lat_deg;
277   _target_alt_ft = alt_ft;
278 }
279
280 void
281 FGViewer::setRoll_deg (double roll_deg)
282 {
283   _dirty = true;
284   _roll_deg = roll_deg;
285 }
286
287 void
288 FGViewer::setPitch_deg (double pitch_deg)
289 {
290   _dirty = true;
291   _pitch_deg = pitch_deg;
292 }
293
294 void
295 FGViewer::setHeading_deg (double heading_deg)
296 {
297   _dirty = true;
298   _heading_deg = heading_deg;
299 }
300
301 void
302 FGViewer::setOrientation (double roll_deg, double pitch_deg, double heading_deg)
303 {
304   _dirty = true;
305   _roll_deg = roll_deg;
306   _pitch_deg = pitch_deg;
307   _heading_deg = heading_deg;
308 }
309
310 void
311 FGViewer::setTargetRoll_deg (double target_roll_deg)
312 {
313   _dirty = true;
314   _target_roll_deg = target_roll_deg;
315 }
316
317 void
318 FGViewer::setTargetPitch_deg (double target_pitch_deg)
319 {
320   _dirty = true;
321   _target_pitch_deg = target_pitch_deg;
322 }
323
324 void
325 FGViewer::setTargetHeading_deg (double target_heading_deg)
326 {
327   _dirty = true;
328   _target_heading_deg = target_heading_deg;
329 }
330
331 void
332 FGViewer::setTargetOrientation (double target_roll_deg, double target_pitch_deg, double target_heading_deg)
333 {
334   _dirty = true;
335   _target_roll_deg = target_roll_deg;
336   _target_pitch_deg = target_pitch_deg;
337   _target_heading_deg = target_heading_deg;
338 }
339
340 void
341 FGViewer::setXOffset_m (double x_offset_m)
342 {
343   _dirty = true;
344   _x_offset_m = x_offset_m;
345 }
346
347 void
348 FGViewer::setYOffset_m (double y_offset_m)
349 {
350   _dirty = true;
351   _y_offset_m = y_offset_m;
352 }
353
354 void
355 FGViewer::setZOffset_m (double z_offset_m)
356 {
357   _dirty = true;
358   _z_offset_m = z_offset_m;
359 }
360
361 void
362 FGViewer::setTargetXOffset_m (double target_x_offset_m)
363 {
364   _dirty = true;
365   _target_x_offset_m = target_x_offset_m;
366 }
367
368 void
369 FGViewer::setTargetYOffset_m (double target_y_offset_m)
370 {
371   _dirty = true;
372   _target_y_offset_m = target_y_offset_m;
373 }
374
375 void
376 FGViewer::setTargetZOffset_m (double target_z_offset_m)
377 {
378   _dirty = true;
379   _target_z_offset_m = target_z_offset_m;
380 }
381
382 void
383 FGViewer::setPositionOffsets (double x_offset_m, double y_offset_m, double z_offset_m)
384 {
385   _dirty = true;
386   _x_offset_m = x_offset_m;
387   _y_offset_m = y_offset_m;
388   _z_offset_m = z_offset_m;
389 }
390
391 void
392 FGViewer::setRollOffset_deg (double roll_offset_deg)
393 {
394   _dirty = true;
395   _roll_offset_deg = roll_offset_deg;
396 }
397
398 void
399 FGViewer::setPitchOffset_deg (double pitch_offset_deg)
400 {
401   _dirty = true;
402   _pitch_offset_deg = pitch_offset_deg;
403 }
404
405 void
406 FGViewer::setHeadingOffset_deg (double heading_offset_deg)
407 {
408   _dirty = true;
409   _heading_offset_deg = heading_offset_deg;
410 }
411
412 void
413 FGViewer::setGoalRollOffset_deg (double goal_roll_offset_deg)
414 {
415   _dirty = true;
416   _goal_roll_offset_deg = goal_roll_offset_deg;
417 }
418
419 void
420 FGViewer::setGoalPitchOffset_deg (double goal_pitch_offset_deg)
421 {
422   _dirty = true;
423   _goal_pitch_offset_deg = goal_pitch_offset_deg;
424   if ( _goal_pitch_offset_deg < -90 ) {
425     _goal_pitch_offset_deg = -90.0;
426   }
427   if ( _goal_pitch_offset_deg > 90.0 ) {
428     _goal_pitch_offset_deg = 90.0;
429   }
430
431 }
432
433 void
434 FGViewer::setGoalHeadingOffset_deg (double goal_heading_offset_deg)
435 {
436   _dirty = true;
437   _goal_heading_offset_deg = goal_heading_offset_deg;
438   while ( _goal_heading_offset_deg < 0.0 ) {
439     _goal_heading_offset_deg += 360;
440   }
441   while ( _goal_heading_offset_deg > 360 ) {
442     _goal_heading_offset_deg -= 360;
443   }
444 }
445
446 void
447 FGViewer::setOrientationOffsets (double roll_offset_deg, double pitch_offset_deg, double heading_offset_deg)
448 {
449   _dirty = true;
450   _roll_offset_deg = roll_offset_deg;
451   _pitch_offset_deg = pitch_offset_deg;
452   _heading_offset_deg = heading_offset_deg;
453 }
454
455 double *
456 FGViewer::get_absolute_view_pos () 
457 {
458   if (_dirty)
459     recalc();
460   return _absolute_view_pos;
461 }
462
463 float *
464 FGViewer::getRelativeViewPos () 
465 {
466   if (_dirty)
467     recalc();
468   return _relative_view_pos;
469 }
470
471 float *
472 FGViewer::getZeroElevViewPos () 
473 {
474   if (_dirty)
475     recalc();
476   return _zero_elev_view_pos;
477 }
478
479 void
480 FGViewer::updateFromModelLocation (FGLocation * location)
481 {
482   sgCopyMat4(LOCAL, location->getCachedTransformMatrix());
483 }
484
485 void
486 FGViewer::updateAtModelLocation (FGLocation * location)
487 {
488   sgCopyMat4(ATLOCAL, location->getCachedTransformMatrix());
489 }
490
491 void
492 FGViewer::recalcOurOwnLocation (FGLocation * location, double lon_deg, double lat_deg, double alt_ft, 
493                         double roll_deg, double pitch_deg, double heading_deg)
494 {
495   // update from our own data...
496   location->setPosition( lon_deg, lat_deg, alt_ft );
497   location->setOrientation( roll_deg, pitch_deg, heading_deg );
498   sgCopyMat4(LOCAL, location->getTransformMatrix());
499 }
500
501 // recalc() is done every time one of the setters is called (making the 
502 // cached data "dirty") on the next "get".  It calculates all the outputs 
503 // for viewer.
504 void
505 FGViewer::recalc ()
506 {
507   if (_type == FG_LOOKFROM) {
508     recalcLookFrom();
509   } else {
510     recalcLookAt();
511   }
512
513   set_clean();
514 }
515
516 // recalculate for LookFrom view type...
517 void
518 FGViewer::recalcLookFrom ()
519 {
520
521   sgVec3 right, forward;
522   sgVec3 eye_pos;
523   sgVec3 position_offset; // eye position offsets (xyz)
524
525   // LOOKFROM mode...
526
527   // Update location data...
528   if ( _from_model ) {
529     // update or data from model location
530     updateFromModelLocation(_location);
531   } else {
532     // update from our own data...
533     recalcOurOwnLocation( _location, _lon_deg, _lat_deg, _alt_ft, 
534           _roll_deg, _pitch_deg, _heading_deg );
535   }
536
537   // copy data from location class to local items...
538   copyLocationData();
539
540   // make sg vectors view up, right and forward vectors from LOCAL
541   sgSetVec3( _view_up, LOCAL[2][0], LOCAL[2][1], LOCAL[2][2] );
542   sgSetVec3( right, LOCAL[1][0], LOCAL[1][1], LOCAL[1][2] );
543   sgSetVec3( forward, -LOCAL[0][0], -LOCAL[0][1], -LOCAL[0][2] );
544
545
546   // Note that when in "lookfrom" view the "view up" vector is always applied
547   // to the viewer.  View up is based on verticle of the aircraft itself. (see
548   // "LOCAL" matrix above)
549
550   // Orientation Offsets matrix
551   MakeVIEW_OFFSET( VIEW_OFFSET,
552     _heading_offset_deg  * SG_DEGREES_TO_RADIANS, _view_up,
553     _pitch_offset_deg  * SG_DEGREES_TO_RADIANS, right );
554
555   // Make the VIEW matrix.
556   sgSetVec4(VIEW[0], right[0], right[1], right[2],SG_ZERO);
557   sgSetVec4(VIEW[1], forward[0], forward[1], forward[2],SG_ZERO);
558   sgSetVec4(VIEW[2], _view_up[0], _view_up[1], _view_up[2],SG_ZERO);
559   sgSetVec4(VIEW[3], SG_ZERO, SG_ZERO, SG_ZERO,SG_ONE);
560
561   // rotate model or local matrix to get a matrix to apply Eye Position Offsets
562   sgMat4 VIEW_UP; // L0 forward L1 right L2 up
563   sgCopyVec4(VIEW_UP[0], LOCAL[1]); 
564   sgCopyVec4(VIEW_UP[1], LOCAL[2]);
565   sgCopyVec4(VIEW_UP[2], LOCAL[0]);
566   sgZeroVec4(VIEW_UP[3]);
567
568   // Eye Position Offsets to vector
569   sgSetVec3( position_offset, _x_offset_m, _y_offset_m, _z_offset_m );
570   sgXformVec3( position_offset, position_offset, VIEW_UP);
571
572   // add the offsets including rotations to the translation vector
573   sgAddVec3( _view_pos, position_offset );
574
575   // multiply the OFFSETS (for heading and pitch) into the VIEW
576   sgPostMultMat4(VIEW, VIEW_OFFSET);
577
578   // add the position data to the matrix
579   sgSetVec4(VIEW[3], _view_pos[0], _view_pos[1], _view_pos[2],SG_ONE);
580
581 }
582
583 void
584 FGViewer::recalcLookAt ()
585 {
586
587   sgVec3 right;
588   sgVec3 eye_pos, at_pos;
589   sgVec3 position_offset; // eye position offsets (xyz)
590   sgVec3 target_position_offset; // target position offsets (xyz)
591
592   // The position vectors originate from the view point or target location
593   // depending on the type of view.
594
595   // LOOKAT mode...
596
597   // Update location data for target...
598   if ( _at_model ) {
599     // update or data from model location
600     updateAtModelLocation(_target_location);
601   } else {
602     // if not model then calculate our own target position...
603     recalcOurOwnLocation( _target_location, _target_lon_deg, _target_lat_deg, _target_alt_ft, 
604           _target_roll_deg, _target_pitch_deg, _target_heading_deg );
605   }
606   // calculate the "at" target object positon relative to eye or view's tile center...
607   sgdVec3 dVec3;
608   sgdSetVec3(dVec3,  _location->get_tile_center()[0], _location->get_tile_center()[1], _location->get_tile_center()[2]);
609   sgdSubVec3(dVec3, _target_location->get_absolute_view_pos(), dVec3 );
610   sgSetVec3(at_pos, dVec3[0], dVec3[1], dVec3[2]);
611
612   // Update location data for eye...
613   if ( _from_model ) {
614     // update or data from model location
615     updateFromModelLocation(_location);
616   } else {
617     // update from our own data, just the rotation here...
618     recalcOurOwnLocation( _location, _lon_deg, _lat_deg, _alt_ft, 
619           _roll_deg, _pitch_deg, _heading_deg );
620   }
621   // save the eye positon...
622   sgCopyVec3(eye_pos,  _location->get_view_pos());
623
624   // copy data from location class to local items...
625   copyLocationData();
626
627   // make sg vectors view up, right and forward vectors from LOCAL
628   sgSetVec3( _view_up, LOCAL[2][0], LOCAL[2][1], LOCAL[2][2] );
629   sgSetVec3( right, LOCAL[1][0], LOCAL[1][1], LOCAL[1][2] );
630
631   // rotate model or local matrix to get a matrix to apply Eye Position Offsets
632   sgMat4 VIEW_UP; // L0 forward L1 right L2 up
633   sgCopyVec4(VIEW_UP[0], LOCAL[1]); 
634   sgCopyVec4(VIEW_UP[1], LOCAL[2]);
635   sgCopyVec4(VIEW_UP[2], LOCAL[0]);
636   sgZeroVec4(VIEW_UP[3]);
637
638   // get Orientation Offsets matrix
639   MakeVIEW_OFFSET( VIEW_OFFSET,
640     (_heading_offset_deg - 180) * SG_DEGREES_TO_RADIANS, _view_up,
641     _pitch_offset_deg * SG_DEGREES_TO_RADIANS, right );
642
643   // add in the position offsets
644   sgSetVec3( position_offset, _y_offset_m, _x_offset_m, _z_offset_m );
645   sgXformVec3( position_offset, position_offset, VIEW_UP);
646
647   // apply the Orientation offsets
648   sgXformVec3( position_offset, position_offset, VIEW_OFFSET );
649
650   // add the Position offsets from object to the eye position
651   sgAddVec3( eye_pos, eye_pos, position_offset );
652
653   // add target offsets to at_position...
654   sgSetVec3(target_position_offset, _target_z_offset_m,  _target_x_offset_m, _target_y_offset_m );
655   sgXformVec3(target_position_offset, target_position_offset, ATLOCAL);
656   sgAddVec3( at_pos, at_pos, target_position_offset);
657
658   // Make the VIEW matrix for a "LOOKAT".
659   sgMakeLookAtMat4( VIEW, eye_pos, at_pos, _view_up );
660
661 }
662
663 // copy results from location class to viewer...
664 // FIXME: some of these should be changed to reference directly to FGLocation...
665 void
666 FGViewer::copyLocationData()
667 {
668   // Get our friendly vectors from the eye location...
669   sgCopyVec3(_zero_elev_view_pos,  _location->get_zero_elev());
670   sgCopyVec3(_relative_view_pos, _location->get_view_pos());
671   sgdCopyVec3(_absolute_view_pos, _location->get_absolute_view_pos());
672   sgCopyMat4(UP, _location->getCachedUpMatrix());
673   sgCopyVec3(_world_up, _location->get_world_up());
674   // these are the vectors that the sun and moon code like to get...
675   sgCopyVec3(_surface_east, _location->get_surface_east());
676   sgCopyVec3(_surface_south, _location->get_surface_south());
677
678   // Update viewer's postion data for the eye location...
679   _lon_deg = _location->getLongitude_deg();
680   _lat_deg = _location->getLatitude_deg();
681   _alt_ft = _location->getAltitudeASL_ft();
682   _roll_deg = _location->getRoll_deg();
683   _pitch_deg = _location->getPitch_deg();
684   _heading_deg = _location->getHeading_deg();
685
686   // Update viewer's postion data for the target (at object) location
687   if (_type == FG_LOOKAT) {
688     _target_lon_deg = _target_location->getLongitude_deg();
689     _target_lat_deg = _target_location->getLatitude_deg();
690     _target_alt_ft = _target_location->getAltitudeASL_ft();
691     _target_roll_deg = _target_location->getRoll_deg();
692     _target_pitch_deg = _target_location->getPitch_deg();
693     _target_heading_deg = _target_location->getHeading_deg();
694   }
695
696   // copy coordinates to outputs for viewer...
697   sgCopyVec3(_zero_elev, _zero_elev_view_pos);
698   sgCopyVec3(_view_pos, _relative_view_pos);
699 }
700
701 double
702 FGViewer::get_h_fov()
703 {
704     switch (_scaling_type) {
705     case FG_SCALING_WIDTH:  // h_fov == fov
706         return _fov_deg;
707     case FG_SCALING_MAX:
708         if (_aspect_ratio < 1.0) {
709             // h_fov == fov
710             return _fov_deg;
711         } else {
712             // v_fov == fov
713             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) / _aspect_ratio) *
714                 SG_RADIANS_TO_DEGREES * 2;
715         }
716     default:
717         assert(false);
718     }
719     return 0.0;
720 }
721
722
723
724 double
725 FGViewer::get_v_fov()
726 {
727     switch (_scaling_type) {
728     case FG_SCALING_WIDTH:  // h_fov == fov
729         return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
730             SG_RADIANS_TO_DEGREES * 2;
731     case FG_SCALING_MAX:
732         if (_aspect_ratio < 1.0) {
733             // h_fov == fov
734             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
735                 SG_RADIANS_TO_DEGREES * 2;
736         } else {
737             // v_fov == fov
738             return _fov_deg;
739         }
740     default:
741         assert(false);
742     }
743     return 0.0;
744 }
745
746 void
747 FGViewer::update (double dt)
748 {
749   int i;
750   int dt_ms = int(dt * 1000);
751   for ( i = 0; i < dt_ms; i++ ) {
752     if ( fabs( _goal_heading_offset_deg - _heading_offset_deg) < 1 ) {
753       setHeadingOffset_deg( _goal_heading_offset_deg );
754       break;
755     } else {
756       // move current_view.headingoffset towards
757       // current_view.goal_view_offset
758       if ( _goal_heading_offset_deg > _heading_offset_deg )
759         {
760           if ( _goal_heading_offset_deg - _heading_offset_deg < 180 ){
761             incHeadingOffset_deg( 0.5 );
762           } else {
763             incHeadingOffset_deg( -0.5 );
764           }
765         } else {
766           if ( _heading_offset_deg - _goal_heading_offset_deg < 180 ){
767             incHeadingOffset_deg( -0.5 );
768           } else {
769             incHeadingOffset_deg( 0.5 );
770           }
771         }
772       if ( _heading_offset_deg > 360 ) {
773         incHeadingOffset_deg( -360 );
774       } else if ( _heading_offset_deg < 0 ) {
775         incHeadingOffset_deg( 360 );
776       }
777     }
778   }
779
780   for ( i = 0; i < dt_ms; i++ ) {
781     if ( fabs( _goal_pitch_offset_deg - _pitch_offset_deg ) < 1 ) {
782       setPitchOffset_deg( _goal_pitch_offset_deg );
783       break;
784     } else {
785       // move current_view.pitch_offset_deg towards
786       // current_view.goal_pitch_offset
787       if ( _goal_pitch_offset_deg > _pitch_offset_deg )
788         {
789           incPitchOffset_deg( 1.0 );
790         } else {
791             incPitchOffset_deg( -1.0 );
792         }
793       if ( _pitch_offset_deg > 90 ) {
794         setPitchOffset_deg(90);
795       } else if ( _pitch_offset_deg < -90 ) {
796         setPitchOffset_deg( -90 );
797       }
798     }
799   }
800 }