]> git.mxchange.org Git - flightgear.git/blob - src/Main/viewer.cxx
61c16914b3c859f095219aa999e9d466cc26aff3
[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 #include <simgear/scene/model/location.hxx>
41 #include <simgear/scene/model/placement.hxx>
42 #include <simgear/math/vector.hxx>
43
44 #include <Main/globals.hxx>
45 #include <Scenery/scenery.hxx>
46 #include <Model/acmodel.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, double at_model_damping,
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     _damped_roll_deg(0),
152     _damped_pitch_deg(0),
153     _scaling_type(FG_SCALING_MAX)
154 {
155     sgdZeroVec3(_absolute_view_pos);
156     _type = Type;
157     _from_model = from_model;
158     _from_model_index = from_model_index;
159     _at_model = at_model;
160     _at_model_index = at_model_index;
161     _damp = at_model_damping;
162     _x_offset_m = x_offset_m;
163     _y_offset_m = y_offset_m;
164     _z_offset_m = z_offset_m;
165     _heading_offset_deg = heading_offset_deg;
166     _pitch_offset_deg = pitch_offset_deg;
167     _roll_offset_deg = roll_offset_deg;
168     _goal_heading_offset_deg = heading_offset_deg;
169     _goal_pitch_offset_deg = pitch_offset_deg;
170     _goal_roll_offset_deg = roll_offset_deg;
171     if (fov_deg > 0) {
172       _fov_deg = fov_deg;
173     } else {
174       _fov_deg = 55;
175     }
176     _target_x_offset_m = target_x_offset_m;
177     _target_y_offset_m = target_y_offset_m;
178     _target_z_offset_m = target_z_offset_m;
179     _ground_level_nearplane_m = near_m;
180     // a reasonable guess for init, so that the math doesn't blow up
181 }
182
183
184 // Destructor
185 FGViewer::~FGViewer( void ) {
186 }
187
188 void
189 FGViewer::init ()
190 {
191   if ( _from_model )
192     _location = (SGLocation *) globals->get_aircraft_model()->get3DModel()->getSGLocation();
193   else
194     _location = (SGLocation *) new SGLocation;
195
196   if ( _type == FG_LOOKAT ) {
197     if ( _at_model )
198       _target_location = (SGLocation *) globals->get_aircraft_model()->get3DModel()->getSGLocation();
199     else
200       _target_location = (SGLocation *) new SGLocation;
201   }
202 }
203
204 void
205 FGViewer::bind ()
206 {
207 }
208
209 void
210 FGViewer::unbind ()
211 {
212 }
213
214 void
215 FGViewer::setType ( int type )
216 {
217   if (type == 0)
218     _type = FG_LOOKFROM;
219   if (type == 1)
220     _type = FG_LOOKAT;
221 }
222
223 void
224 FGViewer::setLongitude_deg (double lon_deg)
225 {
226   _dirty = true;
227   _lon_deg = lon_deg;
228 }
229
230 void
231 FGViewer::setLatitude_deg (double lat_deg)
232 {
233   _dirty = true;
234   _lat_deg = lat_deg;
235 }
236
237 void
238 FGViewer::setAltitude_ft (double alt_ft)
239 {
240   _dirty = true;
241   _alt_ft = alt_ft;
242 }
243
244 void
245 FGViewer::setPosition (double lon_deg, double lat_deg, double alt_ft)
246 {
247   _dirty = true;
248   _lon_deg = lon_deg;
249   _lat_deg = lat_deg;
250   _alt_ft = alt_ft;
251 }
252
253 void
254 FGViewer::setTargetLongitude_deg (double lon_deg)
255 {
256   _dirty = true;
257   _target_lon_deg = lon_deg;
258 }
259
260 void
261 FGViewer::setTargetLatitude_deg (double lat_deg)
262 {
263   _dirty = true;
264   _target_lat_deg = lat_deg;
265 }
266
267 void
268 FGViewer::setTargetAltitude_ft (double alt_ft)
269 {
270   _dirty = true;
271   _target_alt_ft = alt_ft;
272 }
273
274 void
275 FGViewer::setTargetPosition (double lon_deg, double lat_deg, double alt_ft)
276 {
277   _dirty = true;
278   _target_lon_deg = lon_deg;
279   _target_lat_deg = lat_deg;
280   _target_alt_ft = alt_ft;
281 }
282
283 void
284 FGViewer::setRoll_deg (double roll_deg)
285 {
286   _dirty = true;
287   _roll_deg = roll_deg;
288 }
289
290 void
291 FGViewer::setPitch_deg (double pitch_deg)
292 {
293   _dirty = true;
294   _pitch_deg = pitch_deg;
295 }
296
297 void
298 FGViewer::setHeading_deg (double heading_deg)
299 {
300   _dirty = true;
301   _heading_deg = heading_deg;
302 }
303
304 void
305 FGViewer::setOrientation (double roll_deg, double pitch_deg, double heading_deg)
306 {
307   _dirty = true;
308   _roll_deg = roll_deg;
309   _pitch_deg = pitch_deg;
310   _heading_deg = heading_deg;
311 }
312
313 void
314 FGViewer::setTargetRoll_deg (double target_roll_deg)
315 {
316   _dirty = true;
317   _target_roll_deg = target_roll_deg;
318 }
319
320 void
321 FGViewer::setTargetPitch_deg (double target_pitch_deg)
322 {
323   _dirty = true;
324   _target_pitch_deg = target_pitch_deg;
325 }
326
327 void
328 FGViewer::setTargetHeading_deg (double target_heading_deg)
329 {
330   _dirty = true;
331   _target_heading_deg = target_heading_deg;
332 }
333
334 void
335 FGViewer::setTargetOrientation (double target_roll_deg, double target_pitch_deg, double target_heading_deg)
336 {
337   _dirty = true;
338   _target_roll_deg = target_roll_deg;
339   _target_pitch_deg = target_pitch_deg;
340   _target_heading_deg = target_heading_deg;
341 }
342
343 void
344 FGViewer::setXOffset_m (double x_offset_m)
345 {
346   _dirty = true;
347   _x_offset_m = x_offset_m;
348 }
349
350 void
351 FGViewer::setYOffset_m (double y_offset_m)
352 {
353   _dirty = true;
354   _y_offset_m = y_offset_m;
355 }
356
357 void
358 FGViewer::setZOffset_m (double z_offset_m)
359 {
360   _dirty = true;
361   _z_offset_m = z_offset_m;
362 }
363
364 void
365 FGViewer::setTargetXOffset_m (double target_x_offset_m)
366 {
367   _dirty = true;
368   _target_x_offset_m = target_x_offset_m;
369 }
370
371 void
372 FGViewer::setTargetYOffset_m (double target_y_offset_m)
373 {
374   _dirty = true;
375   _target_y_offset_m = target_y_offset_m;
376 }
377
378 void
379 FGViewer::setTargetZOffset_m (double target_z_offset_m)
380 {
381   _dirty = true;
382   _target_z_offset_m = target_z_offset_m;
383 }
384
385 void
386 FGViewer::setPositionOffsets (double x_offset_m, double y_offset_m, double z_offset_m)
387 {
388   _dirty = true;
389   _x_offset_m = x_offset_m;
390   _y_offset_m = y_offset_m;
391   _z_offset_m = z_offset_m;
392 }
393
394 void
395 FGViewer::setRollOffset_deg (double roll_offset_deg)
396 {
397   _dirty = true;
398   _roll_offset_deg = roll_offset_deg;
399 }
400
401 void
402 FGViewer::setPitchOffset_deg (double pitch_offset_deg)
403 {
404   _dirty = true;
405   _pitch_offset_deg = pitch_offset_deg;
406 }
407
408 void
409 FGViewer::setHeadingOffset_deg (double heading_offset_deg)
410 {
411   _dirty = true;
412   _heading_offset_deg = heading_offset_deg;
413 }
414
415 void
416 FGViewer::setGoalRollOffset_deg (double goal_roll_offset_deg)
417 {
418   _dirty = true;
419   _goal_roll_offset_deg = goal_roll_offset_deg;
420 }
421
422 void
423 FGViewer::setGoalPitchOffset_deg (double goal_pitch_offset_deg)
424 {
425   _dirty = true;
426   _goal_pitch_offset_deg = goal_pitch_offset_deg;
427   if ( _goal_pitch_offset_deg < -90 ) {
428     _goal_pitch_offset_deg = -90.0;
429   }
430   if ( _goal_pitch_offset_deg > 90.0 ) {
431     _goal_pitch_offset_deg = 90.0;
432   }
433
434 }
435
436 void
437 FGViewer::setGoalHeadingOffset_deg (double goal_heading_offset_deg)
438 {
439   _dirty = true;
440   _goal_heading_offset_deg = goal_heading_offset_deg;
441   while ( _goal_heading_offset_deg < 0.0 ) {
442     _goal_heading_offset_deg += 360;
443   }
444   while ( _goal_heading_offset_deg > 360 ) {
445     _goal_heading_offset_deg -= 360;
446   }
447 }
448
449 void
450 FGViewer::setOrientationOffsets (double roll_offset_deg, double pitch_offset_deg, double heading_offset_deg)
451 {
452   _dirty = true;
453   _roll_offset_deg = roll_offset_deg;
454   _pitch_offset_deg = pitch_offset_deg;
455   _heading_offset_deg = heading_offset_deg;
456 }
457
458 double *
459 FGViewer::get_absolute_view_pos () 
460 {
461   if (_dirty)
462     recalc();
463   return _absolute_view_pos;
464 }
465
466 float *
467 FGViewer::getRelativeViewPos () 
468 {
469   if (_dirty)
470     recalc();
471   return _relative_view_pos;
472 }
473
474 float *
475 FGViewer::getZeroElevViewPos () 
476 {
477   if (_dirty)
478     recalc();
479   return _zero_elev_view_pos;
480 }
481
482 void
483 FGViewer::updateFromModelLocation (SGLocation * location)
484 {
485   sgCopyMat4(LOCAL, location->getCachedTransformMatrix());
486 }
487
488 void
489 FGViewer::updateAtModelLocation (SGLocation * location)
490 {
491   sgCopyMat4(ATLOCAL, 
492              location->getCachedTransformMatrix());
493 }
494
495 void
496 FGViewer::recalcOurOwnLocation (SGLocation * location, double lon_deg, double lat_deg, double alt_ft, 
497                         double roll_deg, double pitch_deg, double heading_deg)
498 {
499   // update from our own data...
500   if (_damp > 0.0) {
501     double d;
502
503     d = roll_deg - _damped_roll_deg;
504     if (d > 180.0)
505       _damped_roll_deg += 360.0;
506     else if (d < -180.0)
507       _damped_roll_deg -= 360.0;
508
509     d = pitch_deg - _damped_pitch_deg;
510     if (d > 180.0)
511       _damped_pitch_deg += 360.0;
512     else if (d < -180.0)
513       _damped_pitch_deg -= 360.0;
514
515     d = heading_deg - _damped_heading_deg;
516     if (d > 180.0)
517       _damped_heading_deg += 360.0;
518     else if (d < -180.0)
519       _damped_heading_deg -= 360.0;
520
521     static bool firstrun = true;
522     if (firstrun) {
523       _damped_heading_deg = _target_location->getHeading_deg();
524       firstrun = false;
525     }
526
527     d = 1.0 - _damp;
528     roll_deg = _damped_roll_deg = roll_deg * d + _damped_roll_deg * _damp;
529     pitch_deg = _damped_pitch_deg = pitch_deg * d + _damped_pitch_deg * _damp;
530     heading_deg = _damped_heading_deg = heading_deg * d + _damped_heading_deg * _damp;
531   }
532   location->setPosition( lon_deg, lat_deg, alt_ft );
533   location->setOrientation( roll_deg, pitch_deg, heading_deg );
534   sgCopyMat4(LOCAL,
535              location->getTransformMatrix(globals->get_scenery()->get_center()));
536 }
537
538 // recalc() is done every time one of the setters is called (making the 
539 // cached data "dirty") on the next "get".  It calculates all the outputs 
540 // for viewer.
541 void
542 FGViewer::recalc ()
543 {
544   if (_type == FG_LOOKFROM) {
545     recalcLookFrom();
546   } else {
547     recalcLookAt();
548   }
549
550   set_clean();
551 }
552
553 // recalculate for LookFrom view type...
554 void
555 FGViewer::recalcLookFrom ()
556 {
557
558   sgVec3 right, forward;
559   // sgVec3 eye_pos;
560   sgVec3 position_offset; // eye position offsets (xyz)
561
562   // LOOKFROM mode...
563
564   // Update location data...
565   if ( _from_model ) {
566     // update or data from model location
567     updateFromModelLocation(_location);
568   } else {
569     // update from our own data...
570     recalcOurOwnLocation( _location, _lon_deg, _lat_deg, _alt_ft, 
571           _roll_deg, _pitch_deg, _heading_deg );
572   }
573
574   // copy data from location class to local items...
575   copyLocationData();
576
577   // make sg vectors view up, right and forward vectors from LOCAL
578   sgSetVec3( _view_up, LOCAL[2][0], LOCAL[2][1], LOCAL[2][2] );
579   sgSetVec3( right, LOCAL[1][0], LOCAL[1][1], LOCAL[1][2] );
580   sgSetVec3( forward, -LOCAL[0][0], -LOCAL[0][1], -LOCAL[0][2] );
581
582
583   // Note that when in "lookfrom" view the "view up" vector is always applied
584   // to the viewer.  View up is based on verticle of the aircraft itself. (see
585   // "LOCAL" matrix above)
586
587   // Orientation Offsets matrix
588   MakeVIEW_OFFSET( VIEW_OFFSET,
589     _heading_offset_deg  * SG_DEGREES_TO_RADIANS, _view_up,
590     _pitch_offset_deg  * SG_DEGREES_TO_RADIANS, right );
591
592   // Make the VIEW matrix.
593   sgSetVec4(VIEW[0], right[0], right[1], right[2],SG_ZERO);
594   sgSetVec4(VIEW[1], forward[0], forward[1], forward[2],SG_ZERO);
595   sgSetVec4(VIEW[2], _view_up[0], _view_up[1], _view_up[2],SG_ZERO);
596   sgSetVec4(VIEW[3], SG_ZERO, SG_ZERO, SG_ZERO,SG_ONE);
597
598   // rotate model or local matrix to get a matrix to apply Eye Position Offsets
599   sgMat4 VIEW_UP; // L0 forward L1 right L2 up
600   sgCopyVec4(VIEW_UP[0], LOCAL[1]); 
601   sgCopyVec4(VIEW_UP[1], LOCAL[2]);
602   sgCopyVec4(VIEW_UP[2], LOCAL[0]);
603   sgZeroVec4(VIEW_UP[3]);
604
605   // Eye Position Offsets to vector
606   sgSetVec3( position_offset, _x_offset_m, _y_offset_m, _z_offset_m );
607   sgXformVec3( position_offset, position_offset, VIEW_UP);
608
609   // add the offsets including rotations to the translation vector
610   sgAddVec3( _view_pos, position_offset );
611
612   // multiply the OFFSETS (for heading and pitch) into the VIEW
613   sgPostMultMat4(VIEW, VIEW_OFFSET);
614
615   // add the position data to the matrix
616   sgSetVec4(VIEW[3], _view_pos[0], _view_pos[1], _view_pos[2],SG_ONE);
617
618 }
619
620 void
621 FGViewer::recalcLookAt ()
622 {
623
624   sgVec3 right;
625   sgVec3 eye_pos, at_pos;
626   sgVec3 position_offset; // eye position offsets (xyz)
627   sgVec3 target_position_offset; // target position offsets (xyz)
628
629   // The position vectors originate from the view point or target location
630   // depending on the type of view.
631
632   // LOOKAT mode...
633
634   // Update location data for target...
635   if ( _at_model ) {
636     // update or data from model location
637     updateAtModelLocation(_target_location);
638   } else {
639     // if not model then calculate our own target position...
640     recalcOurOwnLocation( _target_location, _target_lon_deg, _target_lat_deg, _target_alt_ft, 
641           _target_roll_deg, _target_pitch_deg, _target_heading_deg );
642   }
643   // calculate the "at" target object positon relative to eye or view's tile center...
644   sgdVec3 dVec3;
645   sgdSetVec3(dVec3,  _location->get_tile_center()[0], _location->get_tile_center()[1], _location->get_tile_center()[2]);
646   sgdSubVec3(dVec3,
647              _target_location->get_absolute_view_pos(globals->get_scenery()->get_center()),
648              dVec3 );
649   sgSetVec3(at_pos, dVec3[0], dVec3[1], dVec3[2]);
650
651   // Update location data for eye...
652   if ( _from_model ) {
653     // update or data from model location
654     updateFromModelLocation(_location);
655   } else {
656     // update from our own data, just the rotation here...
657     recalcOurOwnLocation( _location, _lon_deg, _lat_deg, _alt_ft, 
658           _roll_deg, _pitch_deg, _heading_deg );
659   }
660   // save the eye positon...
661   sgCopyVec3(eye_pos,  _location->get_view_pos());
662
663   // copy data from location class to local items...
664   copyLocationData();
665
666   // make sg vectors view up, right and forward vectors from LOCAL
667   sgSetVec3( _view_up, LOCAL[2][0], LOCAL[2][1], LOCAL[2][2] );
668   sgSetVec3( right, LOCAL[1][0], LOCAL[1][1], LOCAL[1][2] );
669
670   // rotate model or local matrix to get a matrix to apply Eye Position Offsets
671   sgMat4 VIEW_UP; // L0 forward L1 right L2 up
672   sgCopyVec4(VIEW_UP[0], LOCAL[1]); 
673   sgCopyVec4(VIEW_UP[1], LOCAL[2]);
674   sgCopyVec4(VIEW_UP[2], LOCAL[0]);
675   sgZeroVec4(VIEW_UP[3]);
676
677   // get Orientation Offsets matrix
678   MakeVIEW_OFFSET( VIEW_OFFSET,
679     (_heading_offset_deg - 180) * SG_DEGREES_TO_RADIANS, _view_up,
680     _pitch_offset_deg * SG_DEGREES_TO_RADIANS, right );
681
682   // add in the position offsets
683   sgSetVec3( position_offset, _y_offset_m, _x_offset_m, _z_offset_m );
684   sgXformVec3( position_offset, position_offset, VIEW_UP);
685
686   // apply the Orientation offsets
687   sgXformVec3( position_offset, position_offset, VIEW_OFFSET );
688
689   // add the Position offsets from object to the eye position
690   sgAddVec3( eye_pos, eye_pos, position_offset );
691
692   // add target offsets to at_position...
693   sgSetVec3(target_position_offset, _target_z_offset_m,  _target_x_offset_m,
694                                     _target_y_offset_m );
695   sgXformVec3(target_position_offset, target_position_offset, ATLOCAL);
696   sgAddVec3( at_pos, at_pos, target_position_offset);
697
698   sgAddVec3( eye_pos, eye_pos, target_position_offset);
699
700   // Make the VIEW matrix for a "LOOKAT".
701   sgMakeLookAtMat4( VIEW, eye_pos, at_pos, _view_up );
702
703 }
704
705 // copy results from location class to viewer...
706 // FIXME: some of these should be changed to reference directly to SGLocation...
707 void
708 FGViewer::copyLocationData()
709 {
710   // Get our friendly vectors from the eye location...
711   sgCopyVec3(_zero_elev_view_pos,  _location->get_zero_elev());
712   sgCopyVec3(_relative_view_pos, _location->get_view_pos());
713   sgdCopyVec3(_absolute_view_pos,
714               _location->get_absolute_view_pos(globals->get_scenery()->get_center()));
715   sgCopyMat4(UP, _location->getCachedUpMatrix());
716   sgCopyVec3(_world_up, _location->get_world_up());
717   // these are the vectors that the sun and moon code like to get...
718   sgCopyVec3(_surface_east, _location->get_surface_east());
719   sgCopyVec3(_surface_south, _location->get_surface_south());
720
721   // Update viewer's postion data for the eye location...
722   _lon_deg = _location->getLongitude_deg();
723   _lat_deg = _location->getLatitude_deg();
724   _alt_ft = _location->getAltitudeASL_ft();
725   _roll_deg = _location->getRoll_deg();
726   _pitch_deg = _location->getPitch_deg();
727   _heading_deg = _location->getHeading_deg();
728
729   // Update viewer's postion data for the target (at object) location
730   if (_type == FG_LOOKAT) {
731     _target_lon_deg = _target_location->getLongitude_deg();
732     _target_lat_deg = _target_location->getLatitude_deg();
733     _target_alt_ft = _target_location->getAltitudeASL_ft();
734     _target_roll_deg = _target_location->getRoll_deg();
735     _target_pitch_deg = _target_location->getPitch_deg();
736     _target_heading_deg = _target_location->getHeading_deg();
737   }
738
739   // copy coordinates to outputs for viewer...
740   sgCopyVec3(_zero_elev, _zero_elev_view_pos);
741   sgCopyVec3(_view_pos, _relative_view_pos);
742 }
743
744 double
745 FGViewer::get_h_fov()
746 {
747     switch (_scaling_type) {
748     case FG_SCALING_WIDTH:  // h_fov == fov
749         return _fov_deg;
750     case FG_SCALING_MAX:
751         if (_aspect_ratio < 1.0) {
752             // h_fov == fov
753             return _fov_deg;
754         } else {
755             // v_fov == fov
756             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) / _aspect_ratio) *
757                 SG_RADIANS_TO_DEGREES * 2;
758         }
759     default:
760         assert(false);
761     }
762     return 0.0;
763 }
764
765
766
767 double
768 FGViewer::get_v_fov()
769 {
770     switch (_scaling_type) {
771     case FG_SCALING_WIDTH:  // h_fov == fov
772         return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
773             SG_RADIANS_TO_DEGREES * 2;
774     case FG_SCALING_MAX:
775         if (_aspect_ratio < 1.0) {
776             // h_fov == fov
777             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
778                 SG_RADIANS_TO_DEGREES * 2;
779         } else {
780             // v_fov == fov
781             return _fov_deg;
782         }
783     default:
784         assert(false);
785     }
786     return 0.0;
787 }
788
789 void
790 FGViewer::update (double dt)
791 {
792   int i;
793   int dt_ms = int(dt * 1000);
794   for ( i = 0; i < dt_ms; i++ ) {
795     if ( fabs( _goal_heading_offset_deg - _heading_offset_deg) < 1 ) {
796       setHeadingOffset_deg( _goal_heading_offset_deg );
797       break;
798     } else {
799       // move current_view.headingoffset towards
800       // current_view.goal_view_offset
801       if ( _goal_heading_offset_deg > _heading_offset_deg )
802         {
803           if ( _goal_heading_offset_deg - _heading_offset_deg < 180 ){
804             incHeadingOffset_deg( 0.5 );
805           } else {
806             incHeadingOffset_deg( -0.5 );
807           }
808         } else {
809           if ( _heading_offset_deg - _goal_heading_offset_deg < 180 ){
810             incHeadingOffset_deg( -0.5 );
811           } else {
812             incHeadingOffset_deg( 0.5 );
813           }
814         }
815       if ( _heading_offset_deg > 360 ) {
816         incHeadingOffset_deg( -360 );
817       } else if ( _heading_offset_deg < 0 ) {
818         incHeadingOffset_deg( 360 );
819       }
820     }
821   }
822
823   for ( i = 0; i < dt_ms; i++ ) {
824     if ( fabs( _goal_pitch_offset_deg - _pitch_offset_deg ) < 1 ) {
825       setPitchOffset_deg( _goal_pitch_offset_deg );
826       break;
827     } else {
828       // move current_view.pitch_offset_deg towards
829       // current_view.goal_pitch_offset
830       if ( _goal_pitch_offset_deg > _pitch_offset_deg )
831         {
832           incPitchOffset_deg( 1.0 );
833         } else {
834             incPitchOffset_deg( -1.0 );
835         }
836       if ( _pitch_offset_deg > 90 ) {
837         setPitchOffset_deg(90);
838       } else if ( _pitch_offset_deg < -90 ) {
839         setPitchOffset_deg( -90 );
840       }
841     }
842   }
843
844   for ( i = 0; i < dt_ms; i++ ) {
845     if ( fabs( _goal_roll_offset_deg - _roll_offset_deg ) < 1 ) {
846       setRollOffset_deg( _goal_roll_offset_deg );
847       break;
848     } else {
849       // move current_view.roll_offset_deg towards
850       // current_view.goal_roll_offset
851       if ( _goal_roll_offset_deg > _roll_offset_deg )
852         {
853           incRollOffset_deg( 1.0 );
854         } else {
855             incRollOffset_deg( -1.0 );
856         }
857       if ( _roll_offset_deg > 90 ) {
858         setRollOffset_deg(90);
859       } else if ( _roll_offset_deg < -90 ) {
860         setRollOffset_deg( -90 );
861       }
862     }
863   }
864 }