]> git.mxchange.org Git - flightgear.git/blob - src/Main/viewer.cxx
16a035f9a4c6e912c3264e066405a61f95359f6d
[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/placement.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, 
489              location->getCachedTransformMatrix());
490 }
491
492 void
493 FGViewer::recalcOurOwnLocation (FGLocation * location, double lon_deg, double lat_deg, double alt_ft, 
494                         double roll_deg, double pitch_deg, double heading_deg)
495 {
496   // update from our own data...
497   location->setPosition( lon_deg, lat_deg, alt_ft );
498   location->setOrientation( roll_deg, pitch_deg, heading_deg );
499   sgCopyMat4(LOCAL,
500              location->getTransformMatrix(globals->get_scenery()->get_center()));
501 }
502
503 // recalc() is done every time one of the setters is called (making the 
504 // cached data "dirty") on the next "get".  It calculates all the outputs 
505 // for viewer.
506 void
507 FGViewer::recalc ()
508 {
509   if (_type == FG_LOOKFROM) {
510     recalcLookFrom();
511   } else {
512     recalcLookAt();
513   }
514
515   set_clean();
516 }
517
518 // recalculate for LookFrom view type...
519 void
520 FGViewer::recalcLookFrom ()
521 {
522
523   sgVec3 right, forward;
524   // sgVec3 eye_pos;
525   sgVec3 position_offset; // eye position offsets (xyz)
526
527   // LOOKFROM mode...
528
529   // Update location data...
530   if ( _from_model ) {
531     // update or data from model location
532     updateFromModelLocation(_location);
533   } else {
534     // update from our own data...
535     recalcOurOwnLocation( _location, _lon_deg, _lat_deg, _alt_ft, 
536           _roll_deg, _pitch_deg, _heading_deg );
537   }
538
539   // copy data from location class to local items...
540   copyLocationData();
541
542   // make sg vectors view up, right and forward vectors from LOCAL
543   sgSetVec3( _view_up, LOCAL[2][0], LOCAL[2][1], LOCAL[2][2] );
544   sgSetVec3( right, LOCAL[1][0], LOCAL[1][1], LOCAL[1][2] );
545   sgSetVec3( forward, -LOCAL[0][0], -LOCAL[0][1], -LOCAL[0][2] );
546
547
548   // Note that when in "lookfrom" view the "view up" vector is always applied
549   // to the viewer.  View up is based on verticle of the aircraft itself. (see
550   // "LOCAL" matrix above)
551
552   // Orientation Offsets matrix
553   MakeVIEW_OFFSET( VIEW_OFFSET,
554     _heading_offset_deg  * SG_DEGREES_TO_RADIANS, _view_up,
555     _pitch_offset_deg  * SG_DEGREES_TO_RADIANS, right );
556
557   // Make the VIEW matrix.
558   sgSetVec4(VIEW[0], right[0], right[1], right[2],SG_ZERO);
559   sgSetVec4(VIEW[1], forward[0], forward[1], forward[2],SG_ZERO);
560   sgSetVec4(VIEW[2], _view_up[0], _view_up[1], _view_up[2],SG_ZERO);
561   sgSetVec4(VIEW[3], SG_ZERO, SG_ZERO, SG_ZERO,SG_ONE);
562
563   // rotate model or local matrix to get a matrix to apply Eye Position Offsets
564   sgMat4 VIEW_UP; // L0 forward L1 right L2 up
565   sgCopyVec4(VIEW_UP[0], LOCAL[1]); 
566   sgCopyVec4(VIEW_UP[1], LOCAL[2]);
567   sgCopyVec4(VIEW_UP[2], LOCAL[0]);
568   sgZeroVec4(VIEW_UP[3]);
569
570   // Eye Position Offsets to vector
571   sgSetVec3( position_offset, _x_offset_m, _y_offset_m, _z_offset_m );
572   sgXformVec3( position_offset, position_offset, VIEW_UP);
573
574   // add the offsets including rotations to the translation vector
575   sgAddVec3( _view_pos, position_offset );
576
577   // multiply the OFFSETS (for heading and pitch) into the VIEW
578   sgPostMultMat4(VIEW, VIEW_OFFSET);
579
580   // add the position data to the matrix
581   sgSetVec4(VIEW[3], _view_pos[0], _view_pos[1], _view_pos[2],SG_ONE);
582
583 }
584
585 void
586 FGViewer::recalcLookAt ()
587 {
588
589   sgVec3 right;
590   sgVec3 eye_pos, at_pos;
591   sgVec3 position_offset; // eye position offsets (xyz)
592   sgVec3 target_position_offset; // target position offsets (xyz)
593
594   // The position vectors originate from the view point or target location
595   // depending on the type of view.
596
597   // LOOKAT mode...
598
599   // Update location data for target...
600   if ( _at_model ) {
601     // update or data from model location
602     updateAtModelLocation(_target_location);
603   } else {
604     // if not model then calculate our own target position...
605     recalcOurOwnLocation( _target_location, _target_lon_deg, _target_lat_deg, _target_alt_ft, 
606           _target_roll_deg, _target_pitch_deg, _target_heading_deg );
607   }
608   // calculate the "at" target object positon relative to eye or view's tile center...
609   sgdVec3 dVec3;
610   sgdSetVec3(dVec3,  _location->get_tile_center()[0], _location->get_tile_center()[1], _location->get_tile_center()[2]);
611   sgdSubVec3(dVec3,
612              _target_location->get_absolute_view_pos(globals->get_scenery()->get_center()),
613              dVec3 );
614   sgSetVec3(at_pos, dVec3[0], dVec3[1], dVec3[2]);
615
616   // Update location data for eye...
617   if ( _from_model ) {
618     // update or data from model location
619     updateFromModelLocation(_location);
620   } else {
621     // update from our own data, just the rotation here...
622     recalcOurOwnLocation( _location, _lon_deg, _lat_deg, _alt_ft, 
623           _roll_deg, _pitch_deg, _heading_deg );
624   }
625   // save the eye positon...
626   sgCopyVec3(eye_pos,  _location->get_view_pos());
627
628   // copy data from location class to local items...
629   copyLocationData();
630
631   // make sg vectors view up, right and forward vectors from LOCAL
632   sgSetVec3( _view_up, LOCAL[2][0], LOCAL[2][1], LOCAL[2][2] );
633   sgSetVec3( right, LOCAL[1][0], LOCAL[1][1], LOCAL[1][2] );
634
635   // rotate model or local matrix to get a matrix to apply Eye Position Offsets
636   sgMat4 VIEW_UP; // L0 forward L1 right L2 up
637   sgCopyVec4(VIEW_UP[0], LOCAL[1]); 
638   sgCopyVec4(VIEW_UP[1], LOCAL[2]);
639   sgCopyVec4(VIEW_UP[2], LOCAL[0]);
640   sgZeroVec4(VIEW_UP[3]);
641
642   // get Orientation Offsets matrix
643   MakeVIEW_OFFSET( VIEW_OFFSET,
644     (_heading_offset_deg - 180) * SG_DEGREES_TO_RADIANS, _view_up,
645     _pitch_offset_deg * SG_DEGREES_TO_RADIANS, right );
646
647   // add in the position offsets
648   sgSetVec3( position_offset, _y_offset_m, _x_offset_m, _z_offset_m );
649   sgXformVec3( position_offset, position_offset, VIEW_UP);
650
651   // apply the Orientation offsets
652   sgXformVec3( position_offset, position_offset, VIEW_OFFSET );
653
654   // add the Position offsets from object to the eye position
655   sgAddVec3( eye_pos, eye_pos, position_offset );
656
657   // add target offsets to at_position...
658   sgSetVec3(target_position_offset, _target_z_offset_m,  _target_x_offset_m, _target_y_offset_m );
659   sgXformVec3(target_position_offset, target_position_offset, ATLOCAL);
660   sgAddVec3( at_pos, at_pos, target_position_offset);
661
662   // Make the VIEW matrix for a "LOOKAT".
663   sgMakeLookAtMat4( VIEW, eye_pos, at_pos, _view_up );
664
665 }
666
667 // copy results from location class to viewer...
668 // FIXME: some of these should be changed to reference directly to FGLocation...
669 void
670 FGViewer::copyLocationData()
671 {
672   // Get our friendly vectors from the eye location...
673   sgCopyVec3(_zero_elev_view_pos,  _location->get_zero_elev());
674   sgCopyVec3(_relative_view_pos, _location->get_view_pos());
675   sgdCopyVec3(_absolute_view_pos,
676               _location->get_absolute_view_pos(globals->get_scenery()->get_center()));
677   sgCopyMat4(UP, _location->getCachedUpMatrix());
678   sgCopyVec3(_world_up, _location->get_world_up());
679   // these are the vectors that the sun and moon code like to get...
680   sgCopyVec3(_surface_east, _location->get_surface_east());
681   sgCopyVec3(_surface_south, _location->get_surface_south());
682
683   // Update viewer's postion data for the eye location...
684   _lon_deg = _location->getLongitude_deg();
685   _lat_deg = _location->getLatitude_deg();
686   _alt_ft = _location->getAltitudeASL_ft();
687   _roll_deg = _location->getRoll_deg();
688   _pitch_deg = _location->getPitch_deg();
689   _heading_deg = _location->getHeading_deg();
690
691   // Update viewer's postion data for the target (at object) location
692   if (_type == FG_LOOKAT) {
693     _target_lon_deg = _target_location->getLongitude_deg();
694     _target_lat_deg = _target_location->getLatitude_deg();
695     _target_alt_ft = _target_location->getAltitudeASL_ft();
696     _target_roll_deg = _target_location->getRoll_deg();
697     _target_pitch_deg = _target_location->getPitch_deg();
698     _target_heading_deg = _target_location->getHeading_deg();
699   }
700
701   // copy coordinates to outputs for viewer...
702   sgCopyVec3(_zero_elev, _zero_elev_view_pos);
703   sgCopyVec3(_view_pos, _relative_view_pos);
704 }
705
706 double
707 FGViewer::get_h_fov()
708 {
709     switch (_scaling_type) {
710     case FG_SCALING_WIDTH:  // h_fov == fov
711         return _fov_deg;
712     case FG_SCALING_MAX:
713         if (_aspect_ratio < 1.0) {
714             // h_fov == fov
715             return _fov_deg;
716         } else {
717             // v_fov == fov
718             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) / _aspect_ratio) *
719                 SG_RADIANS_TO_DEGREES * 2;
720         }
721     default:
722         assert(false);
723     }
724     return 0.0;
725 }
726
727
728
729 double
730 FGViewer::get_v_fov()
731 {
732     switch (_scaling_type) {
733     case FG_SCALING_WIDTH:  // h_fov == fov
734         return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
735             SG_RADIANS_TO_DEGREES * 2;
736     case FG_SCALING_MAX:
737         if (_aspect_ratio < 1.0) {
738             // h_fov == fov
739             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
740                 SG_RADIANS_TO_DEGREES * 2;
741         } else {
742             // v_fov == fov
743             return _fov_deg;
744         }
745     default:
746         assert(false);
747     }
748     return 0.0;
749 }
750
751 void
752 FGViewer::update (double dt)
753 {
754   int i;
755   int dt_ms = int(dt * 1000);
756   for ( i = 0; i < dt_ms; i++ ) {
757     if ( fabs( _goal_heading_offset_deg - _heading_offset_deg) < 1 ) {
758       setHeadingOffset_deg( _goal_heading_offset_deg );
759       break;
760     } else {
761       // move current_view.headingoffset towards
762       // current_view.goal_view_offset
763       if ( _goal_heading_offset_deg > _heading_offset_deg )
764         {
765           if ( _goal_heading_offset_deg - _heading_offset_deg < 180 ){
766             incHeadingOffset_deg( 0.5 );
767           } else {
768             incHeadingOffset_deg( -0.5 );
769           }
770         } else {
771           if ( _heading_offset_deg - _goal_heading_offset_deg < 180 ){
772             incHeadingOffset_deg( -0.5 );
773           } else {
774             incHeadingOffset_deg( 0.5 );
775           }
776         }
777       if ( _heading_offset_deg > 360 ) {
778         incHeadingOffset_deg( -360 );
779       } else if ( _heading_offset_deg < 0 ) {
780         incHeadingOffset_deg( 360 );
781       }
782     }
783   }
784
785   for ( i = 0; i < dt_ms; i++ ) {
786     if ( fabs( _goal_pitch_offset_deg - _pitch_offset_deg ) < 1 ) {
787       setPitchOffset_deg( _goal_pitch_offset_deg );
788       break;
789     } else {
790       // move current_view.pitch_offset_deg towards
791       // current_view.goal_pitch_offset
792       if ( _goal_pitch_offset_deg > _pitch_offset_deg )
793         {
794           incPitchOffset_deg( 1.0 );
795         } else {
796             incPitchOffset_deg( -1.0 );
797         }
798       if ( _pitch_offset_deg > 90 ) {
799         setPitchOffset_deg(90);
800       } else if ( _pitch_offset_deg < -90 ) {
801         setPitchOffset_deg( -90 );
802       }
803     }
804   }
805 }