]> git.mxchange.org Git - flightgear.git/blob - src/Main/viewmgr.cxx
8f865265c64c1dc8055b3663f27a74d7cbe4259a
[flightgear.git] / src / Main / viewmgr.cxx
1 // viewmgr.cxx -- class for managing all the views in the flightgear world.
2 //
3 // Written by Curtis Olson, started October 2000.
4 //   partially rewritten by Jim Wilson March 2002
5 //
6 // Copyright (C) 2000  Curtis L. Olson  - http://www.flightgear.org/~curt
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "viewmgr.hxx"
29
30 #include <string.h>             // strcmp
31
32 #include <simgear/compiler.h>
33 #include <simgear/sound/soundmgr_openal.hxx>
34 #include <Model/acmodel.hxx>
35 #include <Main/viewer.hxx>
36 #include <Main/fg_props.hxx>
37
38 // Constructor
39 FGViewMgr::FGViewMgr( void ) :
40   axis_long(0),
41   axis_lat(0),
42   view_number(fgGetNode("/sim/current-view/view-number", true)),
43   config_list(fgGetNode("/sim", true)->getChildren("view")),
44   current(0)
45 {
46 }
47
48 // Destructor
49 FGViewMgr::~FGViewMgr( void ) {
50 }
51
52 void
53 FGViewMgr::init ()
54 {
55   double aspect_ratio_multiplier
56       = fgGetDouble("/sim/current-view/aspect-ratio-multiplier");
57
58   for (unsigned int i = 0; i < config_list.size(); i++) {
59     SGPropertyNode *n = config_list[i];
60
61     // find out if this is an internal view (e.g. in cockpit, low near plane)
62     bool internal = n->getBoolValue("internal", false);
63
64     // FIXME:
65     // this is assumed to be an aircraft model...we will need to read
66     // model-from-type as well.
67
68     // find out if this is a model we are looking from...
69     bool from_model = n->getBoolValue("config/from-model");
70     int from_model_index = n->getIntValue("config/from-model-idx");
71
72     double x_offset_m = n->getDoubleValue("config/x-offset-m");
73     double y_offset_m = n->getDoubleValue("config/y-offset-m");
74     double z_offset_m = n->getDoubleValue("config/z-offset-m");
75
76     double heading_offset_deg = n->getDoubleValue("config/heading-offset-deg");
77     n->setDoubleValue("config/heading-offset-deg", heading_offset_deg);
78     double pitch_offset_deg = n->getDoubleValue("config/pitch-offset-deg");
79     n->setDoubleValue("config/pitch-offset-deg", pitch_offset_deg);
80     double roll_offset_deg = n->getDoubleValue("config/roll-offset-deg");
81     n->setDoubleValue("config/roll-offset-deg", roll_offset_deg);
82
83     double fov_deg = n->getDoubleValue("config/default-field-of-view-deg");
84     double near_m = n->getDoubleValue("config/ground-level-nearplane-m");
85
86     // supporting two types "lookat" = 1 and "lookfrom" = 0
87     const char *type = n->getStringValue("type");
88     if (!strcmp(type, "lookat")) {
89
90       bool at_model = n->getBoolValue("config/at-model");
91       int at_model_index = n->getIntValue("config/at-model-idx");
92
93       double damp_roll = n->getDoubleValue("config/at-model-roll-damping");
94       double damp_pitch = n->getDoubleValue("config/at-model-pitch-damping");
95       double damp_heading = n->getDoubleValue("config/at-model-heading-damping");
96
97       double target_x_offset_m = n->getDoubleValue("config/target-x-offset-m");
98       double target_y_offset_m = n->getDoubleValue("config/target-y-offset-m");
99       double target_z_offset_m = n->getDoubleValue("config/target-z-offset-m");
100
101       add_view(new FGViewer ( FG_LOOKAT, from_model, from_model_index,
102                               at_model, at_model_index,
103                               damp_roll, damp_pitch, damp_heading,
104                               x_offset_m, y_offset_m,z_offset_m,
105                               heading_offset_deg, pitch_offset_deg,
106                               roll_offset_deg, fov_deg, aspect_ratio_multiplier,
107                               target_x_offset_m, target_y_offset_m,
108                               target_z_offset_m, near_m, internal ));
109     } else {
110       add_view(new FGViewer ( FG_LOOKFROM, from_model, from_model_index,
111                               false, 0, 0.0, 0.0, 0.0,
112                               x_offset_m, y_offset_m, z_offset_m,
113                               heading_offset_deg, pitch_offset_deg,
114                               roll_offset_deg, fov_deg, aspect_ratio_multiplier,
115                               0, 0, 0, near_m, internal ));
116     }
117   }
118
119   copyToCurrent();
120 }
121
122 void
123 FGViewMgr::reinit ()
124 {
125   // reset offsets and fov to configuration defaults
126   for (unsigned int i = 0; i < config_list.size(); i++) {
127     SGPropertyNode *n = config_list[i];
128     setView(i);
129
130     fgSetDouble("/sim/current-view/x-offset-m",
131         n->getDoubleValue("config/x-offset-m"));
132     fgSetDouble("/sim/current-view/y-offset-m",
133         n->getDoubleValue("config/y-offset-m"));
134     fgSetDouble("/sim/current-view/z-offset-m",
135         n->getDoubleValue("config/z-offset-m"));
136     fgSetDouble("/sim/current-view/pitch-offset-deg",
137         n->getDoubleValue("config/pitch-offset-deg"));
138     fgSetDouble("/sim/current-view/heading-offset-deg",
139         n->getDoubleValue("config/heading-offset-deg"));
140     fgSetDouble("/sim/current-view/roll-offset-deg",
141         n->getDoubleValue("config/roll-offset-deg"));
142
143     double fov_deg = n->getDoubleValue("config/default-field-of-view-deg");
144     if (fov_deg < 10.0)
145       fov_deg = 55.0;
146     fgSetDouble("/sim/current-view/field-of-view", fov_deg);
147
148     // target offsets for lookat mode only...
149     fgSetDouble("/sim/current-view/target-x-offset-m",
150         n->getDoubleValue("config/target-x-offset-m"));
151     fgSetDouble("/sim/current-view/target-y-offset-m",
152         n->getDoubleValue("config/target-y-offset-m"));
153     fgSetDouble("/sim/current-view/target-z-offset-m",
154         n->getDoubleValue("config/target-z-offset-m"));
155   }
156   setView(0);
157 }
158
159 typedef double (FGViewMgr::*double_getter)() const;
160
161 void
162 FGViewMgr::bind ()
163 {
164   // these are bound to the current view properties
165   fgTie("/sim/current-view/heading-offset-deg", this,
166         &FGViewMgr::getViewHeadingOffset_deg,
167         &FGViewMgr::setViewHeadingOffset_deg);
168   fgSetArchivable("/sim/current-view/heading-offset-deg");
169   fgTie("/sim/current-view/goal-heading-offset-deg", this,
170         &FGViewMgr::getViewGoalHeadingOffset_deg,
171         &FGViewMgr::setViewGoalHeadingOffset_deg);
172   fgSetArchivable("/sim/current-view/goal-heading-offset-deg");
173   fgTie("/sim/current-view/pitch-offset-deg", this,
174         &FGViewMgr::getViewPitchOffset_deg,
175         &FGViewMgr::setViewPitchOffset_deg);
176   fgSetArchivable("/sim/current-view/pitch-offset-deg");
177   fgTie("/sim/current-view/goal-pitch-offset-deg", this,
178         &FGViewMgr::getGoalViewPitchOffset_deg,
179         &FGViewMgr::setGoalViewPitchOffset_deg);
180   fgSetArchivable("/sim/current-view/goal-pitch-offset-deg");
181   fgTie("/sim/current-view/roll-offset-deg", this,
182         &FGViewMgr::getViewRollOffset_deg,
183         &FGViewMgr::setViewRollOffset_deg);
184   fgSetArchivable("/sim/current-view/roll-offset-deg");
185   fgTie("/sim/current-view/goal-roll-offset-deg", this,
186         &FGViewMgr::getGoalViewRollOffset_deg,
187         &FGViewMgr::setGoalViewRollOffset_deg);
188   fgSetArchivable("/sim/current-view/goal-roll-offset-deg");
189
190   fgTie("/sim/current-view/view-number", this,
191                       &FGViewMgr::getView, &FGViewMgr::setView);
192   fgSetArchivable("/sim/current-view/view-number", FALSE);
193
194   fgTie("/sim/current-view/axes/long", this,
195         (double_getter)0, &FGViewMgr::setViewAxisLong);
196   fgSetArchivable("/sim/current-view/axes/long");
197
198   fgTie("/sim/current-view/axes/lat", this,
199         (double_getter)0, &FGViewMgr::setViewAxisLat);
200   fgSetArchivable("/sim/current-view/axes/lat");
201
202   fgTie("/sim/current-view/field-of-view", this,
203         &FGViewMgr::getFOV_deg, &FGViewMgr::setFOV_deg);
204   fgSetArchivable("/sim/current-view/field-of-view");
205
206   fgTie("/sim/current-view/aspect-ratio-multiplier", this,
207         &FGViewMgr::getARM_deg, &FGViewMgr::setARM_deg);
208   fgSetArchivable("/sim/current-view/field-of-view");
209
210   fgTie("/sim/current-view/ground-level-nearplane-m", this,
211         &FGViewMgr::getNear_m, &FGViewMgr::setNear_m);
212   fgSetArchivable("/sim/current-view/ground-level-nearplane-m");
213
214   SGPropertyNode *n = fgGetNode("/sim/current-view", true);
215   n->tie("viewer-x-m", SGRawValuePointer<double>(&abs_viewer_position[0]));
216   n->tie("viewer-y-m", SGRawValuePointer<double>(&abs_viewer_position[1]));
217   n->tie("viewer-z-m", SGRawValuePointer<double>(&abs_viewer_position[2]));
218 }
219
220 void
221 FGViewMgr::unbind ()
222 {
223   // FIXME:
224   // need to redo these bindings to the new locations (move to viewer?)
225   fgUntie("/sim/current-view/heading-offset-deg");
226   fgUntie("/sim/current-view/goal-heading-offset-deg");
227   fgUntie("/sim/current-view/pitch-offset-deg");
228   fgUntie("/sim/current-view/goal-pitch-offset-deg");
229   fgUntie("/sim/current-view/field-of-view");
230   fgUntie("/sim/current-view/aspect-ratio-multiplier");
231   fgUntie("/sim/current-view/view-number");
232   fgUntie("/sim/current-view/axes/long");
233   fgUntie("/sim/current-view/axes/lat");
234   fgUntie("/sim/current-view/ground-level-nearplane-m");
235   fgUntie("/sim/current-view/viewer-x-m");
236   fgUntie("/sim/current-view/viewer-y-m");
237   fgUntie("/sim/current-view/viewer-z-m");
238 }
239
240 void
241 FGViewMgr::update (double dt)
242 {
243   FGViewer * view = get_current_view();
244   if (view == 0)
245     return;
246
247   FGViewer *loop_view = (FGViewer *)get_view(current);
248   SGPropertyNode *n = config_list[current];
249   double lon_deg, lat_deg, alt_ft, roll_deg, pitch_deg, heading_deg;
250
251   // Set up view location and orientation
252
253   if (!n->getBoolValue("config/from-model")) {
254     lon_deg = fgGetDouble(n->getStringValue("config/eye-lon-deg-path"));
255     lat_deg = fgGetDouble(n->getStringValue("config/eye-lat-deg-path"));
256     alt_ft = fgGetDouble(n->getStringValue("config/eye-alt-ft-path"));
257     roll_deg = fgGetDouble(n->getStringValue("config/eye-roll-deg-path"));
258     pitch_deg = fgGetDouble(n->getStringValue("config/eye-pitch-deg-path"));
259     heading_deg = fgGetDouble(n->getStringValue("config/eye-heading-deg-path"));
260
261     loop_view->setPosition(lon_deg, lat_deg, alt_ft);
262     loop_view->setOrientation(roll_deg, pitch_deg, heading_deg);
263   } else {
264     // force recalc in viewer
265     loop_view->set_dirty();
266   }
267
268   // if lookat (type 1) then get target data...
269   if (loop_view->getType() == FG_LOOKAT) {
270     if (!n->getBoolValue("config/from-model")) {
271       lon_deg = fgGetDouble(n->getStringValue("config/target-lon-deg-path"));
272       lat_deg = fgGetDouble(n->getStringValue("config/target-lat-deg-path"));
273       alt_ft = fgGetDouble(n->getStringValue("config/target-alt-ft-path"));
274       roll_deg = fgGetDouble(n->getStringValue("config/target-roll-deg-path"));
275       pitch_deg = fgGetDouble(n->getStringValue("config/target-pitch-deg-path"));
276       heading_deg = fgGetDouble(n->getStringValue("config/target-heading-deg-path"));
277
278       loop_view->setTargetPosition(lon_deg, lat_deg, alt_ft);
279       loop_view->setTargetOrientation(roll_deg, pitch_deg, heading_deg);
280     } else {
281       loop_view->set_dirty();
282     }
283   }
284
285   setViewXOffset_m(fgGetDouble("/sim/current-view/x-offset-m"));
286   setViewYOffset_m(fgGetDouble("/sim/current-view/y-offset-m"));
287   setViewZOffset_m(fgGetDouble("/sim/current-view/z-offset-m"));
288
289   setViewTargetXOffset_m(fgGetDouble("/sim/current-view/target-x-offset-m"));
290   setViewTargetYOffset_m(fgGetDouble("/sim/current-view/target-y-offset-m"));
291   setViewTargetZOffset_m(fgGetDouble("/sim/current-view/target-z-offset-m"));
292
293   // Update the current view
294   do_axes();
295   view->update(dt);
296   abs_viewer_position = loop_view->getViewPosition();
297
298   // update audio listener values
299   static SGSoundMgr *smgr = 0;
300   if (!smgr) smgr = (SGSoundMgr *)globals->get_subsystem("soundmgr");
301   if (smgr) {
302     // set the viewer posotion in Cartesian coordinates in meters
303     smgr->set_position(abs_viewer_position);
304     smgr->set_orientation(loop_view->getViewOrientation());
305 //TODO: should be in meters per second
306 //  smr->set_veloicty(SGVec3f(0,0,0));
307   }
308 }
309
310 void
311 FGViewMgr::copyToCurrent()
312 {
313     SGPropertyNode *n = config_list[current];
314     fgSetString("/sim/current-view/name", n->getStringValue("name"));
315     fgSetString("/sim/current-view/type", n->getStringValue("type"));
316
317     // copy certain view config data for default values
318     fgSetDouble("/sim/current-view/config/heading-offset-deg",
319                 n->getDoubleValue("config/default-heading-offset-deg"));
320     fgSetDouble("/sim/current-view/config/pitch-offset-deg",
321                 n->getDoubleValue("config/pitch-offset-deg"));
322     fgSetDouble("/sim/current-view/config/roll-offset-deg",
323                 n->getDoubleValue("config/roll-offset-deg"));
324     fgSetDouble("/sim/current-view/config/default-field-of-view-deg",
325                 n->getDoubleValue("config/default-field-of-view-deg"));
326     fgSetBool("/sim/current-view/config/from-model",
327                 n->getBoolValue("config/from-model"));
328
329     // copy view data
330     fgSetDouble("/sim/current-view/x-offset-m", getViewXOffset_m());
331     fgSetDouble("/sim/current-view/y-offset-m", getViewYOffset_m());
332     fgSetDouble("/sim/current-view/z-offset-m", getViewZOffset_m());
333
334     fgSetDouble("/sim/current-view/goal-heading-offset-deg",
335                 get_current_view()->getGoalHeadingOffset_deg());
336     fgSetDouble("/sim/current-view/goal-pitch-offset-deg",
337                 get_current_view()->getGoalPitchOffset_deg());
338     fgSetDouble("/sim/current-view/goal-roll-offset-deg",
339                 get_current_view()->getRollOffset_deg());
340     fgSetDouble("/sim/current-view/heading-offset-deg",
341                 get_current_view()->getHeadingOffset_deg());
342     fgSetDouble("/sim/current-view/pitch-offset-deg",
343                 get_current_view()->getPitchOffset_deg());
344     fgSetDouble("/sim/current-view/roll-offset-deg",
345                 get_current_view()->getRollOffset_deg());
346     fgSetDouble("/sim/current-view/target-x-offset-m",
347                 get_current_view()->getTargetXOffset_m());
348     fgSetDouble("/sim/current-view/target-y-offset-m",
349                 get_current_view()->getTargetYOffset_m());
350     fgSetDouble("/sim/current-view/target-z-offset-m",
351                 get_current_view()->getTargetZOffset_m());
352     fgSetBool("/sim/current-view/internal",
353                 get_current_view()->getInternal());
354 }
355
356 void FGViewMgr::clear()
357 {
358   views.clear();
359 }
360
361 FGViewer*
362 FGViewMgr::get_current_view()
363 {
364         if ( current < (int)views.size() ) {
365             return views[current];
366         } else {
367             return NULL;
368         }
369 }
370
371 const FGViewer*
372 FGViewMgr::get_current_view() const
373 {
374         if ( current < (int)views.size() ) {
375             return views[current];
376         } else {
377             return NULL;
378         }
379 }
380
381
382 FGViewer*
383 FGViewMgr::get_view( int i )
384 {
385         if ( i < 0 ) { i = 0; }
386         if ( i >= (int)views.size() ) { i = views.size() - 1; }
387         return views[i];
388 }
389
390 const FGViewer*
391 FGViewMgr::get_view( int i ) const
392 {
393         if ( i < 0 ) { i = 0; }
394         if ( i >= (int)views.size() ) { i = views.size() - 1; }
395         return views[i];
396 }
397
398 FGViewer*
399 FGViewMgr::next_view()
400 {
401         setView((current+1 < (int)views.size()) ? (current + 1) : 0);
402         view_number->fireValueChanged();
403         return views[current];
404 }
405
406 FGViewer*
407 FGViewMgr::prev_view()
408 {
409         setView((0 < current) ? (current - 1) : (views.size() - 1));
410         view_number->fireValueChanged();
411         return views[current];
412 }
413
414 void
415 FGViewMgr::add_view( FGViewer * v )
416 {
417   views.push_back(v);
418   v->init();
419 }
420     
421 double
422 FGViewMgr::getViewHeadingOffset_deg () const
423 {
424   const FGViewer * view = get_current_view();
425   return (view == 0 ? 0 : view->getHeadingOffset_deg());
426 }
427
428 void
429 FGViewMgr::setViewHeadingOffset_deg (double offset)
430 {
431   FGViewer * view = get_current_view();
432   if (view != 0) {
433     view->setGoalHeadingOffset_deg(offset);
434     view->setHeadingOffset_deg(offset);
435   }
436 }
437
438 double
439 FGViewMgr::getViewGoalHeadingOffset_deg () const
440 {
441   const FGViewer * view = get_current_view();
442   return (view == 0 ? 0 : view->getGoalHeadingOffset_deg());
443 }
444
445 void
446 FGViewMgr::setViewGoalHeadingOffset_deg (double offset)
447 {
448   FGViewer * view = get_current_view();
449   if (view != 0)
450     view->setGoalHeadingOffset_deg(offset);
451 }
452
453 double
454 FGViewMgr::getViewPitchOffset_deg () const
455 {
456   const FGViewer * view = get_current_view();
457   return (view == 0 ? 0 : view->getPitchOffset_deg());
458 }
459
460 void
461 FGViewMgr::setViewPitchOffset_deg (double tilt)
462 {
463   FGViewer * view = get_current_view();
464   if (view != 0) {
465     view->setGoalPitchOffset_deg(tilt);
466     view->setPitchOffset_deg(tilt);
467   }
468 }
469
470 double
471 FGViewMgr::getGoalViewPitchOffset_deg () const
472 {
473   const FGViewer * view = get_current_view();
474   return (view == 0 ? 0 : view->getGoalPitchOffset_deg());
475 }
476
477 void
478 FGViewMgr::setGoalViewPitchOffset_deg (double tilt)
479 {
480   FGViewer * view = get_current_view();
481   if (view != 0)
482     view->setGoalPitchOffset_deg(tilt);
483 }
484
485 double
486 FGViewMgr::getViewRollOffset_deg () const
487 {
488   const FGViewer * view = get_current_view();
489   return (view == 0 ? 0 : view->getRollOffset_deg());
490 }
491
492 void
493 FGViewMgr::setViewRollOffset_deg (double tilt)
494 {
495   FGViewer * view = get_current_view();
496   if (view != 0) {
497     view->setGoalRollOffset_deg(tilt);
498     view->setRollOffset_deg(tilt);
499   }
500 }
501
502 double
503 FGViewMgr::getGoalViewRollOffset_deg () const
504 {
505   const FGViewer * view = get_current_view();
506   return (view == 0 ? 0 : view->getGoalRollOffset_deg());
507 }
508
509 void
510 FGViewMgr::setGoalViewRollOffset_deg (double tilt)
511 {
512   FGViewer * view = get_current_view();
513   if (view != 0)
514     view->setGoalRollOffset_deg(tilt);
515 }
516
517 double
518 FGViewMgr::getViewXOffset_m () const
519 {
520   const FGViewer * view = get_current_view();
521   if (view != 0) {
522     return ((FGViewer *)view)->getXOffset_m();
523   } else {
524     return 0;
525   }
526 }
527
528 void
529 FGViewMgr::setViewXOffset_m (double x)
530 {
531   FGViewer * view = get_current_view();
532   if (view != 0) {
533     view->setXOffset_m(x);
534   }
535 }
536
537 double
538 FGViewMgr::getViewYOffset_m () const
539 {
540   const FGViewer * view = get_current_view();
541   if (view != 0) {
542     return ((FGViewer *)view)->getYOffset_m();
543   } else {
544     return 0;
545   }
546 }
547
548 void
549 FGViewMgr::setViewYOffset_m (double y)
550 {
551   FGViewer * view = get_current_view();
552   if (view != 0) {
553     view->setYOffset_m(y);
554   }
555 }
556
557 double
558 FGViewMgr::getViewZOffset_m () const
559 {
560   const FGViewer * view = get_current_view();
561   if (view != 0) {
562     return ((FGViewer *)view)->getZOffset_m();
563   } else {
564     return 0;
565   }
566 }
567
568 void
569 FGViewMgr::setViewZOffset_m (double z)
570 {
571   FGViewer * view = get_current_view();
572   if (view != 0) {
573     view->setZOffset_m(z);
574   }
575 }
576
577 double
578 FGViewMgr::getViewTargetXOffset_m () const
579 {
580   const FGViewer * view = get_current_view();
581   if (view != 0) {
582     return ((FGViewer *)view)->getTargetXOffset_m();
583   } else {
584     return 0;
585   }
586 }
587
588 void
589 FGViewMgr::setViewTargetXOffset_m (double x)
590 {
591   FGViewer * view = get_current_view();
592   if (view != 0) {
593     view->setTargetXOffset_m(x);
594   }
595 }
596
597 double
598 FGViewMgr::getViewTargetYOffset_m () const
599 {
600   const FGViewer * view = get_current_view();
601   if (view != 0) {
602     return ((FGViewer *)view)->getTargetYOffset_m();
603   } else {
604     return 0;
605   }
606 }
607
608 void
609 FGViewMgr::setViewTargetYOffset_m (double y)
610 {
611   FGViewer * view = get_current_view();
612   if (view != 0) {
613     view->setTargetYOffset_m(y);
614   }
615 }
616
617 double
618 FGViewMgr::getViewTargetZOffset_m () const
619 {
620   const FGViewer * view = get_current_view();
621   if (view != 0) {
622     return ((FGViewer *)view)->getTargetZOffset_m();
623   } else {
624     return 0;
625   }
626 }
627
628 void
629 FGViewMgr::setViewTargetZOffset_m (double z)
630 {
631   FGViewer * view = get_current_view();
632   if (view != 0) {
633     view->setTargetZOffset_m(z);
634   }
635 }
636
637 int
638 FGViewMgr::getView () const
639 {
640   return ( current );
641 }
642
643 void
644 FGViewMgr::setView (int newview)
645 {
646   // negative numbers -> set view with node index -newview
647   if (newview < 0) {
648     for (int i = 0; i < (int)config_list.size(); i++) {
649       int index = -config_list[i]->getIndex();
650       if (index == newview)
651         newview = i;
652     }
653     if (newview < 0)
654       return;
655   }
656
657   // if newview number too low wrap to last view...
658   if (newview < 0)
659     newview = (int)views.size() - 1;
660
661   // if newview number to high wrap to zero...
662   if (newview >= (int)views.size())
663     newview = 0;
664
665   // set new view
666   set_view(newview);
667   // copy in view data
668   copyToCurrent();
669
670   // Copy the fdm's position into the SGLocation which is shared with
671   // some views ...
672   globals->get_aircraft_model()->update(0);
673   // Do the update ...
674   update(0);
675 }
676
677
678 double
679 FGViewMgr::getFOV_deg () const
680 {
681   const FGViewer * view = get_current_view();
682   return (view == 0 ? 0 : view->get_fov());
683 }
684
685 void
686 FGViewMgr::setFOV_deg (double fov)
687 {
688   FGViewer * view = get_current_view();
689   if (view != 0)
690     view->set_fov(fov);
691 }
692
693 double
694 FGViewMgr::getARM_deg () const
695 {
696   const FGViewer * view = get_current_view();
697   return (view == 0 ? 0 : view->get_aspect_ratio_multiplier());
698 }
699
700 void
701 FGViewMgr::setARM_deg (double aspect_ratio_multiplier)
702 {
703   FGViewer * view = get_current_view();
704   if (view != 0)
705     view->set_aspect_ratio_multiplier(aspect_ratio_multiplier);
706 }
707
708 double
709 FGViewMgr::getNear_m () const
710 {
711   const FGViewer * view = get_current_view();
712   return (view == 0 ? 0.5f : view->getNear_m());
713 }
714
715 void
716 FGViewMgr::setNear_m (double near_m)
717 {
718   FGViewer * view = get_current_view();
719   if (view != 0)
720     view->setNear_m(near_m);
721 }
722
723 void
724 FGViewMgr::setViewAxisLong (double axis)
725 {
726   axis_long = axis;
727 }
728
729 void
730 FGViewMgr::setViewAxisLat (double axis)
731 {
732   axis_lat = axis;
733 }
734
735 void
736 FGViewMgr::do_axes ()
737 {
738                                 // Take no action when hat is centered
739   if ( ( axis_long <  0.01 ) &&
740        ( axis_long > -0.01 ) &&
741        ( axis_lat  <  0.01 ) &&
742        ( axis_lat  > -0.01 )
743      )
744     return;
745
746   double viewDir = 999;
747
748   /* Do all the quick and easy cases */
749   if (axis_long < 0) {          // Longitudinal axis forward
750     if (axis_lat == axis_long)
751       viewDir = fgGetDouble("/sim/view/config/front-left-direction-deg");
752     else if (axis_lat == - axis_long)
753       viewDir = fgGetDouble("/sim/view/config/front-right-direction-deg");
754     else if (axis_lat == 0)
755       viewDir = fgGetDouble("/sim/view/config/front-direction-deg");
756   } else if (axis_long > 0) {   // Longitudinal axis backward
757     if (axis_lat == - axis_long)
758       viewDir = fgGetDouble("/sim/view/config/back-left-direction-deg");
759     else if (axis_lat == axis_long)
760       viewDir = fgGetDouble("/sim/view/config/back-right-direction-deg");
761     else if (axis_lat == 0)
762       viewDir = fgGetDouble("/sim/view/config/back-direction-deg");
763   } else if (axis_long == 0) {  // Longitudinal axis neutral
764     if (axis_lat < 0)
765       viewDir = fgGetDouble("/sim/view/config/left-direction-deg");
766     else if (axis_lat > 0)
767       viewDir = fgGetDouble("/sim/view/config/right-direction-deg");
768     else return; /* And assertion failure maybe? */
769   }
770
771                                 // Do all the difficult cases
772   if ( viewDir > 900 )
773     viewDir = SGD_RADIANS_TO_DEGREES * atan2 ( -axis_lat, -axis_long );
774   if ( viewDir < -1 ) viewDir += 360;
775
776   get_current_view()->setGoalHeadingOffset_deg(viewDir);
777 }