]> git.mxchange.org Git - flightgear.git/blob - src/Main/viewer.cxx
64a6d906f6dcaf57ee378d5261e5c81b3ef3dbf5
[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 //#include <Main/location.hxx>
43
44 #include <simgear/math/vector.hxx>
45 #include <Main/globals.hxx>
46 #include <Model/acmodel.hxx>
47
48 #include "viewer.hxx"
49
50 \f
51 //////////////////////////////////////////////////////////////////
52 // Norman's Optimized matrix rotators!                          //
53 //////////////////////////////////////////////////////////////////
54
55 static void fgMakeLOCAL( sgMat4 dst, const double Theta,
56                                 const double Phi, const double Psi)
57 {
58     SGfloat cosTheta = (SGfloat) cos(Theta);
59     SGfloat sinTheta = (SGfloat) sin(Theta);
60     SGfloat cosPhi   = (SGfloat) cos(Phi);
61     SGfloat sinPhi   = (SGfloat) sin(Phi);
62     SGfloat sinPsi   = (SGfloat) sin(Psi) ;
63     SGfloat cosPsi   = (SGfloat) cos(Psi) ;
64         
65     dst[0][0] = cosPhi * cosTheta;
66     dst[0][1] = sinPhi * cosPsi + cosPhi * -sinTheta * -sinPsi;
67     dst[0][2] = sinPhi * sinPsi + cosPhi * -sinTheta * cosPsi;
68     dst[0][3] = SG_ZERO;
69
70     dst[1][0] = -sinPhi * cosTheta;
71     dst[1][1] = cosPhi * cosPsi + -sinPhi * -sinTheta * -sinPsi;
72     dst[1][2] = cosPhi * sinPsi + -sinPhi * -sinTheta * cosPsi;
73     dst[1][3] = SG_ZERO ;
74         
75     dst[2][0] = sinTheta;
76     dst[2][1] = cosTheta * -sinPsi;
77     dst[2][2] = cosTheta * cosPsi;
78     dst[2][3] = SG_ZERO;
79         
80     dst[3][0] = SG_ZERO;
81     dst[3][1] = SG_ZERO;
82     dst[3][2] = SG_ZERO;
83     dst[3][3] = SG_ONE ;
84 }
85
86
87 // Since these are pure rotation matrices we can save some bookwork
88 // by considering them to be 3x3 until the very end -- NHV
89 static void MakeVIEW_OFFSET( sgMat4 dst,
90                       const float angle1, const sgVec3 axis1,
91                       const float angle2, const sgVec3 axis2 )
92 {
93     // make rotmatrix1 from angle and axis
94     float s = (float) sin ( angle1 ) ;
95     float c = (float) cos ( angle1 ) ;
96     float t = SG_ONE - c ;
97
98     sgMat3 mat1;
99     float tmp = t * axis1[0];
100     mat1[0][0] = tmp * axis1[0] + c ;
101     mat1[0][1] = tmp * axis1[1] + s * axis1[2] ;
102     mat1[0][2] = tmp * axis1[2] - s * axis1[1] ;
103
104     tmp = t * axis1[1];
105     mat1[1][0] = tmp * axis1[0] - s * axis1[2] ;
106     mat1[1][1] = tmp * axis1[1] + c ;
107     mat1[1][2] = tmp * axis1[2] + s * axis1[0] ;
108
109     tmp = t * axis1[2];
110     mat1[2][0] = tmp * axis1[0] + s * axis1[1] ;
111     mat1[2][1] = tmp * axis1[1] - s * axis1[0] ;
112     mat1[2][2] = tmp * axis1[2] + c ;
113
114     // make rotmatrix2 from angle and axis
115     s = (float) sin ( angle2 ) ;
116     c = (float) cos ( angle2 ) ;
117     t = SG_ONE - c ;
118
119     sgMat3 mat2;
120     tmp = t * axis2[0];
121     mat2[0][0] = tmp * axis2[0] + c ;
122     mat2[0][1] = tmp * axis2[1] + s * axis2[2] ;
123     mat2[0][2] = tmp * axis2[2] - s * axis2[1] ;
124
125     tmp = t * axis2[1];
126     mat2[1][0] = tmp * axis2[0] - s * axis2[2] ;
127     mat2[1][1] = tmp * axis2[1] + c ;
128     mat2[1][2] = tmp * axis2[2] + s * axis2[0] ;
129
130     tmp = t * axis2[2];
131     mat2[2][0] = tmp * axis2[0] + s * axis2[1] ;
132     mat2[2][1] = tmp * axis2[1] - s * axis2[0] ;
133     mat2[2][2] = tmp * axis2[2] + c ;
134
135     // multiply matrices
136     for ( int j = 0 ; j < 3 ; j++ ) {
137         dst[0][j] = mat2[0][0] * mat1[0][j] +
138                     mat2[0][1] * mat1[1][j] +
139                     mat2[0][2] * mat1[2][j];
140
141         dst[1][j] = mat2[1][0] * mat1[0][j] +
142                     mat2[1][1] * mat1[1][j] +
143                     mat2[1][2] * mat1[2][j];
144
145         dst[2][j] = mat2[2][0] * mat1[0][j] +
146                     mat2[2][1] * mat1[1][j] +
147                     mat2[2][2] * mat1[2][j];
148     }
149     // fill in 4x4 matrix elements
150     dst[0][3] = SG_ZERO; 
151     dst[1][3] = SG_ZERO; 
152     dst[2][3] = SG_ZERO;
153     dst[3][0] = SG_ZERO;
154     dst[3][1] = SG_ZERO;
155     dst[3][2] = SG_ZERO;
156     dst[3][3] = SG_ONE;
157 }
158
159 // Taking advantage of the 3x3 nature of this -- NHV
160 inline static void MakeWithWorldUp( sgMat4 dst, const sgMat4 UP, const sgMat4 LOCAL )
161 {
162     sgMat4 tmp;
163
164     float a = UP[0][0];
165     float b = UP[1][0];
166     float c = UP[2][0];
167     tmp[0][0] = a*LOCAL[0][0] + b*LOCAL[0][1] + c*LOCAL[0][2] ;
168     tmp[1][0] = a*LOCAL[1][0] + b*LOCAL[1][1] + c*LOCAL[1][2] ;
169     tmp[2][0] = a*LOCAL[2][0] + b*LOCAL[2][1] + c*LOCAL[2][2] ;
170     tmp[3][0] = SG_ZERO ;
171
172     a = UP[0][1];
173     b = UP[1][1];
174     c = UP[2][1];
175     tmp[0][1] = a*LOCAL[0][0] + b*LOCAL[0][1] + c*LOCAL[0][2] ;
176     tmp[1][1] = a*LOCAL[1][0] + b*LOCAL[1][1] + c*LOCAL[1][2] ;
177     tmp[2][1] = a*LOCAL[2][0] + b*LOCAL[2][1] + c*LOCAL[2][2] ;
178     tmp[3][1] = SG_ZERO ;
179
180     a = UP[0][2];
181     c = UP[2][2];
182     tmp[0][2] = a*LOCAL[0][0] + c*LOCAL[0][2] ;
183     tmp[1][2] = a*LOCAL[1][0] + c*LOCAL[1][2] ;
184     tmp[2][2] = a*LOCAL[2][0] + c*LOCAL[2][2] ;
185     tmp[3][2] = SG_ZERO ;
186
187     tmp[0][3] = SG_ZERO ;
188     tmp[1][3] = SG_ZERO ;
189     tmp[2][3] = SG_ZERO ;
190     tmp[3][3] = SG_ONE ;
191     sgCopyMat4(dst, tmp);
192 }
193
194
195 ////////////////////////////////////////////////////////////////////////
196 // Implementation of FGViewer.
197 ////////////////////////////////////////////////////////////////////////
198
199 // Constructor
200 FGViewer::FGViewer( fgViewType Type, bool from_model, int from_model_index, bool at_model, int at_model_index ):
201     _scaling_type(FG_SCALING_MAX),
202     _fov_deg(55.0),
203     _dirty(true),
204     _lon_deg(0),
205     _lat_deg(0),
206     _alt_ft(0),
207     _target_lon_deg(0),
208     _target_lat_deg(0),
209     _target_alt_ft(0),
210     _roll_deg(0),
211     _pitch_deg(0),
212     _heading_deg(0),
213     _x_offset_m(0),
214     _y_offset_m(0),
215     _z_offset_m(0),
216     _heading_offset_deg(0),
217     _pitch_offset_deg(0),
218     _roll_offset_deg(0),
219     _goal_heading_offset_deg(0.0),
220     _goal_pitch_offset_deg(0.0)
221 {
222     sgdZeroVec3(_absolute_view_pos);
223     _type = Type;
224     _from_model = from_model;
225     _from_model_index = from_model_index;
226     _at_model = at_model;
227     _at_model_index = at_model_index;
228     //a reasonable guess for init, so that the math doesn't blow up
229 }
230
231
232 // Destructor
233 FGViewer::~FGViewer( void ) {
234 }
235
236 void
237 FGViewer::init ()
238 {
239   if ( _from_model )
240     _location = (FGLocation *) globals->get_aircraft_model()->get3DModel()->getFGLocation();
241   else
242     _location = (FGLocation *) new FGLocation;
243
244   if ( _type == FG_LOOKAT ) {
245     if ( _at_model )
246       _target_location = (FGLocation *) globals->get_aircraft_model()->get3DModel()->getFGLocation();
247     else
248       _target_location = (FGLocation *) new FGLocation;
249   }
250 }
251
252 void
253 FGViewer::bind ()
254 {
255 }
256
257 void
258 FGViewer::unbind ()
259 {
260 }
261
262 void
263 FGViewer::setType ( int type )
264 {
265   if (type == 0)
266     _type = FG_LOOKFROM;
267   if (type == 1)
268     _type = FG_LOOKAT;
269 }
270
271 void
272 FGViewer::setLongitude_deg (double lon_deg)
273 {
274   _dirty = true;
275   _lon_deg = lon_deg;
276 }
277
278 void
279 FGViewer::setLatitude_deg (double lat_deg)
280 {
281   _dirty = true;
282   _lat_deg = lat_deg;
283 }
284
285 void
286 FGViewer::setAltitude_ft (double alt_ft)
287 {
288   _dirty = true;
289   _alt_ft = alt_ft;
290 }
291
292 void
293 FGViewer::setPosition (double lon_deg, double lat_deg, double alt_ft)
294 {
295   _dirty = true;
296   _lon_deg = lon_deg;
297   _lat_deg = lat_deg;
298   _alt_ft = alt_ft;
299 }
300
301 void
302 FGViewer::setTargetLongitude_deg (double lon_deg)
303 {
304   _dirty = true;
305   _target_lon_deg = lon_deg;
306 }
307
308 void
309 FGViewer::setTargetLatitude_deg (double lat_deg)
310 {
311   _dirty = true;
312   _target_lat_deg = lat_deg;
313 }
314
315 void
316 FGViewer::setTargetAltitude_ft (double alt_ft)
317 {
318   _dirty = true;
319   _target_alt_ft = alt_ft;
320 }
321
322 void
323 FGViewer::setTargetPosition (double lon_deg, double lat_deg, double alt_ft)
324 {
325   _dirty = true;
326   _target_lon_deg = lon_deg;
327   _target_lat_deg = lat_deg;
328   _target_alt_ft = alt_ft;
329 }
330
331 void
332 FGViewer::setRoll_deg (double roll_deg)
333 {
334   _dirty = true;
335   _roll_deg = roll_deg;
336 }
337
338 void
339 FGViewer::setPitch_deg (double pitch_deg)
340 {
341   _dirty = true;
342   _pitch_deg = pitch_deg;
343 }
344
345 void
346 FGViewer::setHeading_deg (double heading_deg)
347 {
348   _dirty = true;
349   _heading_deg = heading_deg;
350 }
351
352 void
353 FGViewer::setOrientation (double roll_deg, double pitch_deg, double heading_deg)
354 {
355   _dirty = true;
356   _roll_deg = roll_deg;
357   _pitch_deg = pitch_deg;
358   _heading_deg = heading_deg;
359 }
360
361 void
362 FGViewer::setTargetRoll_deg (double target_roll_deg)
363 {
364   _dirty = true;
365   _target_roll_deg = target_roll_deg;
366 }
367
368 void
369 FGViewer::setTargetPitch_deg (double target_pitch_deg)
370 {
371   _dirty = true;
372   _target_pitch_deg = target_pitch_deg;
373 }
374
375 void
376 FGViewer::setTargetHeading_deg (double target_heading_deg)
377 {
378   _dirty = true;
379   _target_heading_deg = target_heading_deg;
380 }
381
382 void
383 FGViewer::setTargetOrientation (double target_roll_deg, double target_pitch_deg, double target_heading_deg)
384 {
385   _dirty = true;
386   _target_roll_deg = target_roll_deg;
387   _target_pitch_deg = target_pitch_deg;
388   _target_heading_deg = target_heading_deg;
389 }
390
391 void
392 FGViewer::setXOffset_m (double x_offset_m)
393 {
394   _dirty = true;
395   _x_offset_m = x_offset_m;
396 }
397
398 void
399 FGViewer::setYOffset_m (double y_offset_m)
400 {
401   _dirty = true;
402   _y_offset_m = y_offset_m;
403 }
404
405 void
406 FGViewer::setZOffset_m (double z_offset_m)
407 {
408   _dirty = true;
409   _z_offset_m = z_offset_m;
410 }
411
412 void
413 FGViewer::setPositionOffsets (double x_offset_m, double y_offset_m, double z_offset_m)
414 {
415   _dirty = true;
416   _x_offset_m = x_offset_m;
417   _y_offset_m = y_offset_m;
418   _z_offset_m = z_offset_m;
419 }
420
421 void
422 FGViewer::setRollOffset_deg (double roll_offset_deg)
423 {
424   _dirty = true;
425   _roll_offset_deg = roll_offset_deg;
426 }
427
428 void
429 FGViewer::setPitchOffset_deg (double pitch_offset_deg)
430 {
431   _dirty = true;
432   _pitch_offset_deg = pitch_offset_deg;
433 }
434
435 void
436 FGViewer::setHeadingOffset_deg (double heading_offset_deg)
437 {
438   _dirty = true;
439   _heading_offset_deg = heading_offset_deg;
440 }
441
442 void
443 FGViewer::setGoalRollOffset_deg (double goal_roll_offset_deg)
444 {
445   _dirty = true;
446   _goal_roll_offset_deg = goal_roll_offset_deg;
447 }
448
449 void
450 FGViewer::setGoalPitchOffset_deg (double goal_pitch_offset_deg)
451 {
452   _dirty = true;
453   _goal_pitch_offset_deg = goal_pitch_offset_deg;
454   if ( _goal_pitch_offset_deg < -90 ) {
455     _goal_pitch_offset_deg = -90.0;
456   }
457   if ( _goal_pitch_offset_deg > 90.0 ) {
458     _goal_pitch_offset_deg = 90.0;
459   }
460
461 }
462
463 void
464 FGViewer::setGoalHeadingOffset_deg (double goal_heading_offset_deg)
465 {
466   _dirty = true;
467   _goal_heading_offset_deg = goal_heading_offset_deg;
468   while ( _goal_heading_offset_deg < 0.0 ) {
469     _goal_heading_offset_deg += 360;
470   }
471   while ( _goal_heading_offset_deg > 360 ) {
472     _goal_heading_offset_deg -= 360;
473   }
474 }
475
476 void
477 FGViewer::setOrientationOffsets (double roll_offset_deg, double pitch_offset_deg, double heading_offset_deg)
478 {
479   _dirty = true;
480   _roll_offset_deg = roll_offset_deg;
481   _pitch_offset_deg = pitch_offset_deg;
482   _heading_offset_deg = heading_offset_deg;
483 }
484
485 double *
486 FGViewer::get_absolute_view_pos () 
487 {
488   if (_dirty)
489     recalc();
490   return _absolute_view_pos;
491 }
492
493 float *
494 FGViewer::getRelativeViewPos () 
495 {
496   if (_dirty)
497     recalc();
498   return _relative_view_pos;
499 }
500
501 float *
502 FGViewer::getZeroElevViewPos () 
503 {
504   if (_dirty)
505     recalc();
506   return _zero_elev_view_pos;
507 }
508
509 void
510 FGViewer::updateFromModelLocation (FGLocation * location)
511 {
512   sgCopyMat4(LOCAL, location->getCachedTransformMatrix());
513   _lon_deg = location->getLongitude_deg();
514   _lat_deg = location->getLatitude_deg();
515   _alt_ft = location->getAltitudeASL_ft();
516   _roll_deg = location->getRoll_deg();
517   _pitch_deg = location->getPitch_deg();
518   _heading_deg = location->getHeading_deg();
519   sgCopyVec3(_zero_elev_view_pos,  location->get_zero_elev());
520   sgCopyVec3(_relative_view_pos, location->get_view_pos());
521   sgdCopyVec3(_absolute_view_pos, location->get_absolute_view_pos());
522   sgCopyMat4(UP, location->getCachedUpMatrix());
523   sgCopyVec3(_world_up, location->get_world_up());
524   // these are the vectors that the sun and moon code like to get...
525   sgCopyVec3(_surface_east, location->get_surface_east());
526   sgCopyVec3(_surface_south, location->get_surface_south());
527 }
528
529 void
530 FGViewer::recalcOurOwnLocation (double lon_deg, double lat_deg, double alt_ft, 
531                         double roll_deg, double pitch_deg, double heading_deg)
532 {
533   // update from our own data...
534   _location->setPosition( lon_deg, lat_deg, alt_ft );
535   _location->setOrientation( roll_deg, pitch_deg, heading_deg );
536   sgCopyMat4(LOCAL, _location->getTransformMatrix());
537   sgCopyVec3(_zero_elev_view_pos,  _location->get_zero_elev());
538   sgCopyVec3(_relative_view_pos, _location->get_view_pos());
539   sgdCopyVec3(_absolute_view_pos, _location->get_absolute_view_pos());
540   // these are the vectors that the sun and moon code like to get...
541   sgCopyVec3(_surface_east, _location->get_surface_east());
542   sgCopyVec3(_surface_south, _location->get_surface_south());
543 }
544
545 // recalc() is done every time one of the setters is called (making the 
546 // cached data "dirty") on the next "get".  It calculates all the outputs 
547 // for viewer.
548 void
549 FGViewer::recalc ()
550 {
551   sgVec3 minus_z, right, forward, tilt;
552   sgMat4 tmpROT;  // temp rotation work matrices
553   sgVec3 eye_pos, at_pos;
554
555   // The position vectors originate from the view point or target location
556   // depending on the type of view.
557
558   if (_type == FG_LOOKFROM) {
559     // LOOKFROM mode...
560     if ( _from_model ) {
561       // update or data from model location
562       updateFromModelLocation(_location);
563     } else {
564       // update from our own data...
565       recalcOurOwnLocation( _lon_deg, _lat_deg, _alt_ft, 
566             _roll_deg, _pitch_deg, _heading_deg );
567       // get world up data from just recalced location
568       sgCopyMat4(UP, _location->getUpMatrix());
569       sgCopyVec3(_world_up, _location->get_world_up());
570     }
571
572   } else {
573
574     // LOOKAT mode...
575     if ( _from_model ) {
576       // update or data from model location
577       updateFromModelLocation(_location);
578     } else {
579       // update from our own data, just the rotation here...
580       recalcOurOwnLocation( _lon_deg, _lat_deg, _alt_ft, 
581             _roll_deg, _pitch_deg, _heading_deg );
582       // get world up data from just recalced location
583       sgCopyMat4(UP, _location->getUpMatrix());
584       sgCopyVec3(_world_up, _location->get_world_up());
585     }
586     // save they eye positon...
587     sgCopyVec3(eye_pos,  _location->get_view_pos());
588     // save the eye rotation before getting target values!!!
589     sgCopyMat4(tmpROT, LOCAL);
590
591     if ( _at_model ) {
592       // update or data from model location
593       updateFromModelLocation(_target_location);
594     } else {
595       // if not model then calculate our own target position...
596       recalcOurOwnLocation( _target_lon_deg, _target_lat_deg, _target_alt_ft, 
597             _target_roll_deg, _target_pitch_deg, _target_heading_deg );
598     }
599     // restore the eye rotation (the from position rotation)
600     sgCopyMat4(LOCAL, tmpROT);
601
602   }
603
604   // the coordinates generated by the above "recalcPositionVectors"
605   sgCopyVec3(_zero_elev, _zero_elev_view_pos);
606   sgCopyVec3(_view_pos, _relative_view_pos);
607
608   // FIXME:
609   // Doing this last recalc here for published values...where the airplane is
610   // This should be per aircraft or model (for published values) before
611   // multiple FDM can be done.
612   // This info should come directly from the model (not through viewer), 
613   // because in some cases there is no model directly assigned as a lookfrom
614   // position.  The problem that arises is related to the FDM interface looking
615   // indirectly to the viewer to find the altitude of the aircraft on the runway.
616   //
617   // Note that recalcPositionVectors can be removed from viewer when this 
618   // issue is addressed.
619   //
620   if (!_from_model) {
621     recalcPositionVectors(fgGetDouble("/position/longitude-deg"),
622                         fgGetDouble("/position/latitude-deg"),
623                         fgGetDouble("/position/altitude-ft"));
624   }
625
626   // make sg vectors view up, right and forward vectors from LOCAL
627   sgSetVec3( _view_up, LOCAL[2][0], LOCAL[2][1], LOCAL[2][2] );
628   sgSetVec3( right, LOCAL[1][0], LOCAL[1][1], LOCAL[1][2] );
629   sgSetVec3( forward, -LOCAL[0][0], -LOCAL[0][1], -LOCAL[0][2] );
630
631   // create the (xyz) eye Position offsets Vector
632   sgVec3 position_offset;
633   sgSetVec3( position_offset, -_z_offset_m, _x_offset_m, _y_offset_m );
634
635
636   if (_type == FG_LOOKAT) {
637
638     // Note that when in "lookat" view the "world up" vector is always applied
639     // to the viewer.  World up is based on verticle at a given lon/lat (see
640     // matrix "UP" above).
641
642     // Orientation Offsets matrix
643     MakeVIEW_OFFSET( VIEW_OFFSET,
644       (_heading_offset_deg -_heading_deg) * SG_DEGREES_TO_RADIANS, _world_up,
645       _pitch_offset_deg * SG_DEGREES_TO_RADIANS, right );
646    
647     // Eye Position Offsets to vector
648     sgXformVec3( position_offset, position_offset, UP );
649
650     // add in the Orientation Offsets here
651     sgXformVec3( position_offset, position_offset, VIEW_OFFSET );
652
653     // add the Position offsets from object to the eye position
654     sgAddVec3( eye_pos, eye_pos, position_offset );
655
656     // at position (what we are looking at)
657     sgCopyVec3( at_pos, _view_pos );
658
659     // Make the VIEW matrix for a "LOOKAT".
660     sgMakeLookAtMat4( VIEW, eye_pos, at_pos, _view_up );
661   }
662
663   if (_type == FG_LOOKFROM) {
664
665     // Note that when in "lookfrom" view the "view up" vector is always applied
666     // to the viewer.  View up is based on verticle of the aircraft itself. (see
667     // "LOCAL" matrix above)
668
669     // Orientation Offsets matrix
670     MakeVIEW_OFFSET( VIEW_OFFSET,
671       _heading_offset_deg  * SG_DEGREES_TO_RADIANS, _view_up,
672       _pitch_offset_deg  * SG_DEGREES_TO_RADIANS, right );
673
674     // Eye Position Offsets to vector
675     sgXformVec3( position_offset, position_offset, LOCAL);
676
677     // add the offsets including rotations to the translation vector
678     sgAddVec3( _view_pos, position_offset );
679
680     // Make the VIEW matrix.
681     sgSetVec4(VIEW[0], right[0], right[1], right[2],SG_ZERO);
682     sgSetVec4(VIEW[1], forward[0], forward[1], forward[2],SG_ZERO);
683     sgSetVec4(VIEW[2], _view_up[0], _view_up[1], _view_up[2],SG_ZERO);
684     sgSetVec4(VIEW[3], SG_ZERO, SG_ZERO, SG_ZERO,SG_ONE);
685     // multiply the OFFSETS (for heading and pitch) into the VIEW
686     sgPostMultMat4(VIEW, VIEW_OFFSET);
687
688     // add the position data to the matrix
689     sgSetVec4(VIEW[3], _view_pos[0], _view_pos[1], _view_pos[2],SG_ONE);
690
691   }
692
693   set_clean();
694 }
695
696 void
697 FGViewer::recalcPositionVectors (double lon_deg, double lat_deg, double alt_ft) const
698 {
699   double sea_level_radius_m;
700   double lat_geoc_rad;
701
702
703                                 // Convert from geodetic to geocentric
704                                 // coordinates.
705   sgGeodToGeoc(lat_deg * SGD_DEGREES_TO_RADIANS,
706                alt_ft * SG_FEET_TO_METER,
707                &sea_level_radius_m,
708                &lat_geoc_rad);
709
710                                 // Calculate the cartesian coordinates
711                                 // of point directly below at sea level.
712                                 // aka Zero Elevation Position
713   Point3D p = Point3D(lon_deg * SG_DEGREES_TO_RADIANS,
714                       lat_geoc_rad,
715                       sea_level_radius_m);
716   Point3D tmp = sgPolarToCart3d(p) - scenery.get_next_center();
717   sgSetVec3(_zero_elev_view_pos, tmp[0], tmp[1], tmp[2]);
718
719                                 // Calculate the absolute view position
720                                 // in fgfs coordinates.
721                                 // aka Absolute View Position
722   p.setz(p.radius() + alt_ft * SG_FEET_TO_METER);
723   tmp = sgPolarToCart3d(p);
724   sgdSetVec3(_absolute_view_pos, tmp[0], tmp[1], tmp[2]);
725
726                                 // Calculate the relative view position
727                                 // from the scenery center.
728                                 // aka Relative View Position
729   sgdVec3 scenery_center;
730   sgdSetVec3(scenery_center,
731              scenery.get_next_center().x(),
732              scenery.get_next_center().y(),
733              scenery.get_next_center().z());
734   sgdVec3 view_pos;
735   sgdSubVec3(view_pos, _absolute_view_pos, scenery_center);
736   sgSetVec3(_relative_view_pos, view_pos);
737
738 }
739
740 double
741 FGViewer::get_h_fov()
742 {
743     switch (_scaling_type) {
744     case FG_SCALING_WIDTH:  // h_fov == fov
745         return _fov_deg;
746     case FG_SCALING_MAX:
747         if (_aspect_ratio < 1.0) {
748             // h_fov == fov
749             return _fov_deg;
750         } else {
751             // v_fov == fov
752             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) / _aspect_ratio) *
753                 SG_RADIANS_TO_DEGREES * 2;
754         }
755     default:
756         assert(false);
757     }
758 }
759
760 double
761 FGViewer::get_v_fov()
762 {
763     switch (_scaling_type) {
764     case FG_SCALING_WIDTH:  // h_fov == fov
765         return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
766             SG_RADIANS_TO_DEGREES * 2;
767     case FG_SCALING_MAX:
768         if (_aspect_ratio < 1.0) {
769             // h_fov == fov
770             return atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS) * _aspect_ratio) *
771                 SG_RADIANS_TO_DEGREES * 2;
772         } else {
773             // v_fov == fov
774             return _fov_deg;
775         }
776     default:
777         assert(false);
778     }
779 }
780
781 void
782 FGViewer::update (int dt)
783 {
784   int i;
785   for ( i = 0; i < dt; i++ ) {
786     if ( fabs( _goal_heading_offset_deg - _heading_offset_deg) < 1 ) {
787       setHeadingOffset_deg( _goal_heading_offset_deg );
788       break;
789     } else {
790       // move current_view.headingoffset towards
791       // current_view.goal_view_offset
792       if ( _goal_heading_offset_deg > _heading_offset_deg )
793         {
794           if ( _goal_heading_offset_deg - _heading_offset_deg < 180 ){
795             incHeadingOffset_deg( 0.5 );
796           } else {
797             incHeadingOffset_deg( -0.5 );
798           }
799         } else {
800           if ( _heading_offset_deg - _goal_heading_offset_deg < 180 ){
801             incHeadingOffset_deg( -0.5 );
802           } else {
803             incHeadingOffset_deg( 0.5 );
804           }
805         }
806       if ( _heading_offset_deg > 360 ) {
807         incHeadingOffset_deg( -360 );
808       } else if ( _heading_offset_deg < 0 ) {
809         incHeadingOffset_deg( 360 );
810       }
811     }
812   }
813
814   for ( i = 0; i < dt; i++ ) {
815     if ( fabs( _goal_pitch_offset_deg - _pitch_offset_deg ) < 1 ) {
816       setPitchOffset_deg( _goal_pitch_offset_deg );
817       break;
818     } else {
819       // move current_view.pitch_offset_deg towards
820       // current_view.goal_pitch_offset
821       if ( _goal_pitch_offset_deg > _pitch_offset_deg )
822         {
823           incPitchOffset_deg( 1.0 );
824         } else {
825             incPitchOffset_deg( -1.0 );
826         }
827       if ( _pitch_offset_deg > 90 ) {
828         setPitchOffset_deg(90);
829       } else if ( _pitch_offset_deg < -90 ) {
830         setPitchOffset_deg( -90 );
831       }
832     }
833   }
834 }
835
836
837
838
839
840
841