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