1 // viewmgr.cxx -- class for managing all the views in the flightgear world.
3 // Written by Curtis Olson, started October 2000.
4 // partially rewritten by Jim Wilson March 2002
6 // Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <string.h> // strcmp
28 #include "viewmgr.hxx"
29 #include "fg_props.hxx"
33 FGViewMgr::FGViewMgr( void ) :
42 FGViewMgr::~FGViewMgr( void ) {
49 string viewpath, nodepath, strdata;
50 bool from_model = false;
51 bool at_model = false;
52 int from_model_index = 0;
53 int at_model_index = 0;
55 double damp_roll = 0.0, damp_pitch = 0.0, damp_heading = 0.0;
56 double x_offset_m, y_offset_m, z_offset_m, fov_deg;
57 double aspect_ratio_multiplier = 1.0;
58 double heading_offset_deg, pitch_offset_deg, roll_offset_deg;
59 double target_x_offset_m, target_y_offset_m, target_z_offset_m;
63 for (int i = 0; i < fgGetInt("/sim/number-views"); i++) {
64 viewpath = "/sim/view";
65 sprintf(stridx, "[%d]", i);
68 // find out what type of view this is...
71 strdata = fgGetString(nodepath.c_str());
73 // find out if this is an internal view (e.g. in cockpit, low near plane)
74 internal = false; // default
76 nodepath += "/internal";
77 internal = fgGetBool(nodepath.c_str());
80 // this is assumed to be an aircraft model...we will need to read
81 // model-from-type as well.
82 // find out if this is a model we are looking from...
84 nodepath += "/config/from-model";
85 from_model = fgGetBool(nodepath.c_str());
87 // get model index (which model)
90 nodepath += "/config/from-model-idx";
91 from_model_index = fgGetInt(nodepath.c_str());
94 if ( strcmp("lookat",strdata.c_str()) == 0 ) {
95 // find out if this is a model we are looking at...
97 nodepath += "/config/at-model";
98 at_model = fgGetBool(nodepath.c_str());
100 // get model index (which model)
103 nodepath += "/config/at-model-idx";
104 at_model_index = fgGetInt(nodepath.c_str());
107 nodepath += "/config/at-model-roll-damping";
108 damp_roll = fgGetDouble(nodepath.c_str(), 0.0);
110 nodepath += "/config/at-model-pitch-damping";
111 damp_pitch = fgGetDouble(nodepath.c_str(), 0.0);
113 nodepath += "/config/at-model-heading-damping";
114 damp_heading = fgGetDouble(nodepath.c_str(), 0.0);
119 nodepath += "/config/x-offset-m";
120 x_offset_m = fgGetDouble(nodepath.c_str());
122 nodepath += "/config/y-offset-m";
123 y_offset_m = fgGetDouble(nodepath.c_str());
125 nodepath += "/config/z-offset-m";
126 z_offset_m = fgGetDouble(nodepath.c_str());
128 nodepath += "/config/pitch-offset-deg";
129 pitch_offset_deg = fgGetDouble(nodepath.c_str());
130 fgSetDouble(nodepath.c_str(),pitch_offset_deg);
132 nodepath += "/config/heading-offset-deg";
133 heading_offset_deg = fgGetDouble(nodepath.c_str());
134 fgSetDouble(nodepath.c_str(),heading_offset_deg);
136 nodepath += "/config/roll-offset-deg";
137 roll_offset_deg = fgGetDouble(nodepath.c_str());
138 fgSetDouble(nodepath.c_str(),roll_offset_deg);
140 nodepath += "/config/default-field-of-view-deg";
141 fov_deg = fgGetDouble(nodepath.c_str());
143 // target offsets for lookat mode only...
145 nodepath += "/config/target-x-offset-m";
146 target_x_offset_m = fgGetDouble(nodepath.c_str());
148 nodepath += "/config/target-y-offset-m";
149 target_y_offset_m = fgGetDouble(nodepath.c_str());
151 nodepath += "/config/target-z-offset-m";
152 target_z_offset_m = fgGetDouble(nodepath.c_str());
155 nodepath += "/config/ground-level-nearplane-m";
156 near_m = fgGetDouble(nodepath.c_str());
158 // supporting two types now "lookat" = 1 and "lookfrom" = 0
159 if ( strcmp("lookat",strdata.c_str()) == 0 )
160 add_view(new FGViewer ( FG_LOOKAT, from_model, from_model_index,
161 at_model, at_model_index,
162 damp_roll, damp_pitch, damp_heading,
163 x_offset_m, y_offset_m,z_offset_m,
164 heading_offset_deg, pitch_offset_deg,
165 roll_offset_deg, fov_deg, aspect_ratio_multiplier,
166 target_x_offset_m, target_y_offset_m,
167 target_z_offset_m, near_m, internal ));
169 add_view(new FGViewer ( FG_LOOKFROM, from_model, from_model_index,
170 false, 0, 0.0, 0.0, 0.0,
171 x_offset_m, y_offset_m, z_offset_m,
172 heading_offset_deg, pitch_offset_deg,
173 roll_offset_deg, fov_deg, aspect_ratio_multiplier,
174 0, 0, 0, near_m, internal ));
185 string viewpath, nodepath, strdata;
188 // reset offsets and fov to configuration defaults
190 for (int i = 0; i < fgGetInt("/sim/number-views"); i++) {
191 viewpath = "/sim/view";
192 sprintf(stridx, "[%d]", i);
198 nodepath += "/config/x-offset-m";
199 fgSetDouble("/sim/current-view/x-offset-m",fgGetDouble(nodepath.c_str()));
202 nodepath += "/config/y-offset-m";
203 fgSetDouble("/sim/current-view/y-offset-m",fgGetDouble(nodepath.c_str()));
206 nodepath += "/config/z-offset-m";
207 fgSetDouble("/sim/current-view/z-offset-m",fgGetDouble(nodepath.c_str()));
210 nodepath += "/config/pitch-offset-deg";
211 fgSetDouble("/sim/current-view/pitch-offset-deg",
212 fgGetDouble(nodepath.c_str()));
215 nodepath += "/config/heading-offset-deg";
216 fgSetDouble("/sim/current-view/heading-offset-deg",
217 fgGetDouble(nodepath.c_str()));
220 nodepath += "/config/roll-offset-deg";
221 fgSetDouble("/sim/current-view/roll-offset-deg",
222 fgGetDouble(nodepath.c_str()));
225 nodepath += "/config/default-field-of-view-deg";
226 fov_deg = fgGetDouble(nodepath.c_str());
227 if (fov_deg < 10.0) {
230 fgSetDouble("/sim/current-view/field-of-view",fov_deg);
232 // target offsets for lookat mode only...
234 nodepath += "/config/target-x-offset-m";
235 fgSetDouble("/sim/current-view/target-x-offset-deg",
236 fgGetDouble(nodepath.c_str()));
239 nodepath += "/config/target-y-offset-m";
240 fgSetDouble("/sim/current-view/target-y-offset-deg",
241 fgGetDouble(nodepath.c_str()));
244 nodepath += "/config/target-z-offset-m";
245 fgSetDouble("/sim/current-view/target-z-offset-deg",
246 fgGetDouble(nodepath.c_str()));
254 typedef double (FGViewMgr::*double_getter)() const;
259 // these are bound to the current view properties
260 fgTie("/sim/current-view/heading-offset-deg", this,
261 &FGViewMgr::getViewHeadingOffset_deg,
262 &FGViewMgr::setViewHeadingOffset_deg);
263 fgSetArchivable("/sim/current-view/heading-offset-deg");
264 fgTie("/sim/current-view/goal-heading-offset-deg", this,
265 &FGViewMgr::getViewGoalHeadingOffset_deg,
266 &FGViewMgr::setViewGoalHeadingOffset_deg);
267 fgSetArchivable("/sim/current-view/goal-heading-offset-deg");
268 fgTie("/sim/current-view/pitch-offset-deg", this,
269 &FGViewMgr::getViewPitchOffset_deg,
270 &FGViewMgr::setViewPitchOffset_deg);
271 fgSetArchivable("/sim/current-view/pitch-offset-deg");
272 fgTie("/sim/current-view/goal-pitch-offset-deg", this,
273 &FGViewMgr::getGoalViewPitchOffset_deg,
274 &FGViewMgr::setGoalViewPitchOffset_deg);
275 fgSetArchivable("/sim/current-view/goal-pitch-offset-deg");
276 fgTie("/sim/current-view/roll-offset-deg", this,
277 &FGViewMgr::getViewRollOffset_deg,
278 &FGViewMgr::setViewRollOffset_deg);
279 fgSetArchivable("/sim/current-view/roll-offset-deg");
280 fgTie("/sim/current-view/goal-roll-offset-deg", this,
281 &FGViewMgr::getGoalViewRollOffset_deg,
282 &FGViewMgr::setGoalViewRollOffset_deg);
283 fgSetArchivable("/sim/current-view/goal-roll-offset-deg");
285 fgTie("/sim/current-view/view-number", this,
286 &FGViewMgr::getView, &FGViewMgr::setView);
287 fgSetArchivable("/sim/current-view/view-number", FALSE);
289 fgTie("/sim/current-view/axes/long", this,
290 (double_getter)0, &FGViewMgr::setViewAxisLong);
291 fgSetArchivable("/sim/current-view/axes/long");
293 fgTie("/sim/current-view/axes/lat", this,
294 (double_getter)0, &FGViewMgr::setViewAxisLat);
295 fgSetArchivable("/sim/current-view/axes/lat");
297 fgTie("/sim/current-view/field-of-view", this,
298 &FGViewMgr::getFOV_deg, &FGViewMgr::setFOV_deg);
299 fgSetArchivable("/sim/current-view/field-of-view");
301 fgTie("/sim/current-view/aspect-ratio-multiplier", this,
302 &FGViewMgr::getARM_deg, &FGViewMgr::setARM_deg);
303 fgSetArchivable("/sim/current-view/field-of-view");
305 fgTie("/sim/current-view/ground-level-nearplane-m", this,
306 &FGViewMgr::getNear_m, &FGViewMgr::setNear_m);
307 fgSetArchivable("/sim/current-view/ground-level-nearplane-m");
315 // need to redo these bindings to the new locations (move to viewer?)
316 fgUntie("/sim/current-view/heading-offset-deg");
317 fgUntie("/sim/current-view/goal-heading-offset-deg");
318 fgUntie("/sim/current-view/pitch-offset-deg");
319 fgUntie("/sim/current-view/goal-pitch-offset-deg");
320 fgUntie("/sim/current-view/field-of-view");
321 fgUntie("/sim/current-view/aspect-ratio-multiplier");
322 fgUntie("/sim/current-view/view-number");
323 fgUntie("/sim/current-view/axes/long");
324 fgUntie("/sim/current-view/axes/lat");
325 fgUntie("/sim/current-view/ground-level-nearplane-m");
329 FGViewMgr::update (double dt)
332 string viewpath, nodepath;
333 double lon_deg, lat_deg, alt_ft, roll_deg, pitch_deg, heading_deg;
335 FGViewer * view = get_current_view();
341 viewpath = "/sim/view";
342 sprintf(stridx, "[%d]", i);
345 FGViewer *loop_view = (FGViewer *)get_view( i );
347 // Set up view location and orientation
350 nodepath += "/config/from-model";
351 if (!fgGetBool(nodepath.c_str())) {
353 nodepath += "/config/eye-lon-deg-path";
354 lon_deg = fgGetDouble(fgGetString(nodepath.c_str()));
356 nodepath += "/config/eye-lat-deg-path";
357 lat_deg = fgGetDouble(fgGetString(nodepath.c_str()));
359 nodepath += "/config/eye-alt-ft-path";
360 alt_ft = fgGetDouble(fgGetString(nodepath.c_str()));
362 nodepath += "/config/eye-roll-deg-path";
363 roll_deg = fgGetDouble(fgGetString(nodepath.c_str()));
365 nodepath += "/config/eye-pitch-deg-path";
366 pitch_deg = fgGetDouble(fgGetString(nodepath.c_str()));
368 nodepath += "/config/eye-heading-deg-path";
369 heading_deg = fgGetDouble(fgGetString(nodepath.c_str()));
370 loop_view->setPosition(lon_deg, lat_deg, alt_ft);
371 loop_view->setOrientation(roll_deg, pitch_deg, heading_deg);
373 // force recalc in viewer
374 loop_view->set_dirty();
377 // if lookat (type 1) then get target data...
378 if (loop_view->getType() == FG_LOOKAT) {
380 nodepath += "/config/from-model";
381 if (!fgGetBool(nodepath.c_str())) {
383 nodepath += "/config/target-lon-deg-path";
384 lon_deg = fgGetDouble(fgGetString(nodepath.c_str()));
386 nodepath += "/config/target-lat-deg-path";
387 lat_deg = fgGetDouble(fgGetString(nodepath.c_str()));
389 nodepath += "/config/target-alt-ft-path";
390 alt_ft = fgGetDouble(fgGetString(nodepath.c_str()));
392 nodepath += "/config/target-roll-deg-path";
393 roll_deg = fgGetDouble(fgGetString(nodepath.c_str()));
395 nodepath += "/config/target-pitch-deg-path";
396 pitch_deg = fgGetDouble(fgGetString(nodepath.c_str()));
398 nodepath += "/config/target-heading-deg-path";
399 heading_deg = fgGetDouble(fgGetString(nodepath.c_str()));
401 loop_view->setTargetPosition(lon_deg, lat_deg, alt_ft);
402 loop_view->setTargetOrientation(roll_deg, pitch_deg, heading_deg);
404 loop_view->set_dirty();
408 setViewXOffset_m(fgGetDouble("/sim/current-view/x-offset-m"));
409 setViewYOffset_m(fgGetDouble("/sim/current-view/y-offset-m"));
410 setViewZOffset_m(fgGetDouble("/sim/current-view/z-offset-m"));
412 setViewTargetXOffset_m(fgGetDouble("/sim/current-view/target-x-offset-m"));
413 setViewTargetYOffset_m(fgGetDouble("/sim/current-view/target-y-offset-m"));
414 setViewTargetZOffset_m(fgGetDouble("/sim/current-view/target-z-offset-m"));
416 // Update the current view
422 FGViewMgr::copyToCurrent()
425 string viewpath, nodepath;
428 viewpath = "/sim/view";
429 sprintf(stridx, "[%d]", i);
432 // copy certain view config data for default values
434 nodepath += "/config/default-heading-offset-deg";
435 fgSetDouble("/sim/current-view/config/heading-offset-deg",
436 fgGetDouble(nodepath.c_str()));
439 nodepath += "/config/pitch-offset-deg";
440 fgSetDouble("/sim/current-view/config/pitch-offset-deg",
441 fgGetDouble(nodepath.c_str()));
444 nodepath += "/config/roll-offset-deg";
445 fgSetDouble("/sim/current-view/config/roll-offset-deg",
446 fgGetDouble(nodepath.c_str()));
449 nodepath += "/config/default-field-of-view-deg";
450 fgSetDouble("/sim/current-view/config/default-field-of-view-deg",
451 fgGetDouble(nodepath.c_str()));
454 nodepath += "/config/from-model";
455 fgSetBool("/sim/current-view/config/from-model",
456 fgGetBool(nodepath.c_str()));
460 fgSetDouble("/sim/current-view/x-offset-m", getViewXOffset_m());
461 fgSetDouble("/sim/current-view/y-offset-m", getViewYOffset_m());
462 fgSetDouble("/sim/current-view/z-offset-m", getViewZOffset_m());
463 fgSetDouble("/sim/current-view/goal-heading-offset-deg",
464 get_current_view()->getGoalHeadingOffset_deg());
465 fgSetDouble("/sim/current-view/goal-pitch-offset-deg",
466 get_current_view()->getGoalPitchOffset_deg());
467 fgSetDouble("/sim/current-view/goal-roll-offset-deg",
468 get_current_view()->getRollOffset_deg());
469 fgSetDouble("/sim/current-view/heading-offset-deg",
470 get_current_view()->getHeadingOffset_deg());
471 fgSetDouble("/sim/current-view/pitch-offset-deg",
472 get_current_view()->getPitchOffset_deg());
473 fgSetDouble("/sim/current-view/roll-offset-deg",
474 get_current_view()->getRollOffset_deg());
475 fgSetDouble("/sim/current-view/target-x-offset-m",
476 get_current_view()->getTargetXOffset_m());
477 fgSetDouble("/sim/current-view/target-y-offset-m",
478 get_current_view()->getTargetYOffset_m());
479 fgSetDouble("/sim/current-view/target-z-offset-m",
480 get_current_view()->getTargetZOffset_m());
482 fgSetBool("/sim/current-view/internal",
483 get_current_view()->getInternal());
489 FGViewMgr::getViewHeadingOffset_deg () const
491 const FGViewer * view = get_current_view();
492 return (view == 0 ? 0 : view->getHeadingOffset_deg());
496 FGViewMgr::setViewHeadingOffset_deg (double offset)
498 FGViewer * view = get_current_view();
500 view->setGoalHeadingOffset_deg(offset);
501 view->setHeadingOffset_deg(offset);
506 FGViewMgr::getViewGoalHeadingOffset_deg () const
508 const FGViewer * view = get_current_view();
509 return (view == 0 ? 0 : view->getGoalHeadingOffset_deg());
513 FGViewMgr::setViewGoalHeadingOffset_deg (double offset)
515 FGViewer * view = get_current_view();
517 view->setGoalHeadingOffset_deg(offset);
521 FGViewMgr::getViewPitchOffset_deg () const
523 const FGViewer * view = get_current_view();
524 return (view == 0 ? 0 : view->getPitchOffset_deg());
528 FGViewMgr::setViewPitchOffset_deg (double tilt)
530 FGViewer * view = get_current_view();
532 view->setGoalPitchOffset_deg(tilt);
533 view->setPitchOffset_deg(tilt);
538 FGViewMgr::getGoalViewPitchOffset_deg () const
540 const FGViewer * view = get_current_view();
541 return (view == 0 ? 0 : view->getGoalPitchOffset_deg());
545 FGViewMgr::setGoalViewPitchOffset_deg (double tilt)
547 FGViewer * view = get_current_view();
549 view->setGoalPitchOffset_deg(tilt);
553 FGViewMgr::getViewRollOffset_deg () const
555 const FGViewer * view = get_current_view();
556 return (view == 0 ? 0 : view->getRollOffset_deg());
560 FGViewMgr::setViewRollOffset_deg (double tilt)
562 FGViewer * view = get_current_view();
564 view->setGoalRollOffset_deg(tilt);
565 view->setRollOffset_deg(tilt);
570 FGViewMgr::getGoalViewRollOffset_deg () const
572 const FGViewer * view = get_current_view();
573 return (view == 0 ? 0 : view->getGoalRollOffset_deg());
577 FGViewMgr::setGoalViewRollOffset_deg (double tilt)
579 FGViewer * view = get_current_view();
581 view->setGoalRollOffset_deg(tilt);
585 FGViewMgr::getViewXOffset_m () const
587 const FGViewer * view = get_current_view();
589 return ((FGViewer *)view)->getXOffset_m();
596 FGViewMgr::setViewXOffset_m (double x)
598 FGViewer * view = get_current_view();
600 view->setXOffset_m(x);
605 FGViewMgr::getViewYOffset_m () const
607 const FGViewer * view = get_current_view();
609 return ((FGViewer *)view)->getYOffset_m();
616 FGViewMgr::setViewYOffset_m (double y)
618 FGViewer * view = get_current_view();
620 view->setYOffset_m(y);
625 FGViewMgr::getViewZOffset_m () const
627 const FGViewer * view = get_current_view();
629 return ((FGViewer *)view)->getZOffset_m();
636 FGViewMgr::setViewZOffset_m (double z)
638 FGViewer * view = get_current_view();
640 view->setZOffset_m(z);
645 FGViewMgr::getViewTargetXOffset_m () const
647 const FGViewer * view = get_current_view();
649 return ((FGViewer *)view)->getTargetXOffset_m();
656 FGViewMgr::setViewTargetXOffset_m (double x)
658 FGViewer * view = get_current_view();
660 view->setTargetXOffset_m(x);
665 FGViewMgr::getViewTargetYOffset_m () const
667 const FGViewer * view = get_current_view();
669 return ((FGViewer *)view)->getTargetYOffset_m();
676 FGViewMgr::setViewTargetYOffset_m (double y)
678 FGViewer * view = get_current_view();
680 view->setTargetYOffset_m(y);
685 FGViewMgr::getViewTargetZOffset_m () const
687 const FGViewer * view = get_current_view();
689 return ((FGViewer *)view)->getTargetZOffset_m();
696 FGViewMgr::setViewTargetZOffset_m (double z)
698 FGViewer * view = get_current_view();
700 view->setTargetZOffset_m(z);
705 FGViewMgr::getView () const
711 FGViewMgr::setView (int newview )
713 // if newview number too low wrap to last view...
715 newview = (int)views.size() -1;
717 // if newview number to high wrap to zero...
718 if ( newview > ((int)views.size() -1) ) {
729 FGViewMgr::getFOV_deg () const
731 const FGViewer * view = get_current_view();
732 return (view == 0 ? 0 : view->get_fov());
736 FGViewMgr::setFOV_deg (double fov)
738 FGViewer * view = get_current_view();
744 FGViewMgr::getARM_deg () const
746 const FGViewer * view = get_current_view();
747 return (view == 0 ? 0 : view->get_aspect_ratio_multiplier());
751 FGViewMgr::setARM_deg (double aspect_ratio_multiplier)
753 FGViewer * view = get_current_view();
755 view->set_aspect_ratio_multiplier(aspect_ratio_multiplier);
759 FGViewMgr::getNear_m () const
761 const FGViewer * view = get_current_view();
762 return (view == 0 ? 0.5f : view->getNear_m());
766 FGViewMgr::setNear_m (double near_m)
768 FGViewer * view = get_current_view();
770 view->setNear_m(near_m);
774 FGViewMgr::setViewAxisLong (double axis)
780 FGViewMgr::setViewAxisLat (double axis)
786 FGViewMgr::do_axes ()
788 // Take no action when hat is centered
789 if ( ( axis_long < 0.01 ) &&
790 ( axis_long > -0.01 ) &&
791 ( axis_lat < 0.01 ) &&
796 double viewDir = 999;
798 /* Do all the quick and easy cases */
799 if (axis_long < 0) { // Longitudinal axis forward
800 if (axis_lat == axis_long)
801 viewDir = fgGetDouble("/sim/view/config/front-left-direction-deg");
802 else if (axis_lat == - axis_long)
803 viewDir = fgGetDouble("/sim/view/config/front-right-direction-deg");
804 else if (axis_lat == 0)
805 viewDir = fgGetDouble("/sim/view/config/front-direction-deg");
806 } else if (axis_long > 0) { // Longitudinal axis backward
807 if (axis_lat == - axis_long)
808 viewDir = fgGetDouble("/sim/view/config/back-left-direction-deg");
809 else if (axis_lat == axis_long)
810 viewDir = fgGetDouble("/sim/view/config/back-right-direction-deg");
811 else if (axis_lat == 0)
812 viewDir = fgGetDouble("/sim/view/config/back-direction-deg");
813 } else if (axis_long == 0) { // Longitudinal axis neutral
815 viewDir = fgGetDouble("/sim/view/config/left-direction-deg");
816 else if (axis_lat > 0)
817 viewDir = fgGetDouble("/sim/view/config/right-direction-deg");
818 else return; /* And assertion failure maybe? */
821 // Do all the difficult cases
823 viewDir = SGD_RADIANS_TO_DEGREES * atan2 ( -axis_lat, -axis_long );
824 if ( viewDir < -1 ) viewDir += 360;
826 get_current_view()->setGoalHeadingOffset_deg(viewDir);