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 - curt@flightgear.org
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;
54 double damp_alt, damp_roll, damp_pitch, damp_heading;
55 double x_offset_m, y_offset_m, z_offset_m, fov_deg;
56 double heading_offset_deg, pitch_offset_deg, roll_offset_deg;
57 double target_x_offset_m, target_y_offset_m, target_z_offset_m;
60 for (int i = 0; i < fgGetInt("/sim/number-views"); i++) {
61 viewpath = "/sim/view";
62 sprintf(stridx, "[%d]", i);
65 // find out what type of view this is...
68 strdata = fgGetString(nodepath.c_str());
71 // this is assumed to be an aircraft model...we will need to read
72 // model-from-type as well.
73 // find out if this is a model we are looking from...
75 nodepath += "/config/from-model";
76 from_model = fgGetBool(nodepath.c_str());
78 // get model index (which model)
81 nodepath += "/config/from-model-idx";
82 from_model_index = fgGetInt(nodepath.c_str());
85 if ( strcmp("lookat",strdata.c_str()) == 0 ) {
86 // find out if this is a model we are looking at...
88 nodepath += "/config/at-model";
89 at_model = fgGetBool(nodepath.c_str());
91 // get model index (which model)
94 nodepath += "/config/at-model-idx";
95 at_model_index = fgGetInt(nodepath.c_str());
98 nodepath += "/config/at-model-roll-damping";
99 damp_roll = fgGetDouble(nodepath.c_str(), 0.0);
101 nodepath += "/config/at-model-pitch-damping";
102 damp_pitch = fgGetDouble(nodepath.c_str(), 0.0);
104 nodepath += "/config/at-model-heading-damping";
105 damp_heading = fgGetDouble(nodepath.c_str(), 0.0);
110 nodepath += "/config/x-offset-m";
111 x_offset_m = fgGetDouble(nodepath.c_str());
113 nodepath += "/config/y-offset-m";
114 y_offset_m = fgGetDouble(nodepath.c_str());
116 nodepath += "/config/z-offset-m";
117 z_offset_m = fgGetDouble(nodepath.c_str());
119 nodepath += "/config/pitch-offset-deg";
120 pitch_offset_deg = fgGetDouble(nodepath.c_str());
121 fgSetDouble(nodepath.c_str(),pitch_offset_deg);
123 nodepath += "/config/heading-offset-deg";
124 heading_offset_deg = fgGetDouble(nodepath.c_str());
125 fgSetDouble(nodepath.c_str(),heading_offset_deg);
127 nodepath += "/config/roll-offset-deg";
128 roll_offset_deg = fgGetDouble(nodepath.c_str());
129 fgSetDouble(nodepath.c_str(),roll_offset_deg);
131 nodepath += "/config/default-field-of-view-deg";
132 fov_deg = fgGetDouble(nodepath.c_str());
134 // target offsets for lookat mode only...
136 nodepath += "/config/target-x-offset-m";
137 target_x_offset_m = fgGetDouble(nodepath.c_str());
139 nodepath += "/config/target-y-offset-m";
140 target_y_offset_m = fgGetDouble(nodepath.c_str());
142 nodepath += "/config/target-z-offset-m";
143 target_z_offset_m = fgGetDouble(nodepath.c_str());
146 nodepath += "/config/ground-level-nearplane-m";
147 near_m = fgGetDouble(nodepath.c_str());
149 // supporting two types now "lookat" = 1 and "lookfrom" = 0
150 if ( strcmp("lookat",strdata.c_str()) == 0 )
151 add_view(new FGViewer ( FG_LOOKAT, from_model, from_model_index,
152 at_model, at_model_index,
153 damp_roll, damp_pitch, damp_heading,
154 x_offset_m, y_offset_m,z_offset_m,
155 heading_offset_deg, pitch_offset_deg,
156 roll_offset_deg, fov_deg,
157 target_x_offset_m, target_y_offset_m,
158 target_z_offset_m, near_m ));
160 add_view(new FGViewer ( FG_LOOKFROM, from_model, from_model_index,
161 false, 0, 0.0, 0.0, 0.0,
162 x_offset_m, y_offset_m, z_offset_m,
163 heading_offset_deg, pitch_offset_deg,
164 roll_offset_deg, fov_deg, 0, 0, 0, near_m ));
175 string viewpath, nodepath, strdata;
178 // reset offsets and fov to configuration defaults
180 for (int i = 0; i < fgGetInt("/sim/number-views"); i++) {
181 viewpath = "/sim/view";
182 sprintf(stridx, "[%d]", i);
188 nodepath += "/config/x-offset-m";
189 fgSetDouble("/sim/current-view/x-offset-m",fgGetDouble(nodepath.c_str()));
192 nodepath += "/config/y-offset-m";
193 fgSetDouble("/sim/current-view/y-offset-m",fgGetDouble(nodepath.c_str()));
196 nodepath += "/config/z-offset-m";
197 fgSetDouble("/sim/current-view/z-offset-m",fgGetDouble(nodepath.c_str()));
200 nodepath += "/config/pitch-offset-deg";
201 fgSetDouble("/sim/current-view/pitch-offset-deg",
202 fgGetDouble(nodepath.c_str()));
205 nodepath += "/config/heading-offset-deg";
206 fgSetDouble("/sim/current-view/heading-offset-deg",
207 fgGetDouble(nodepath.c_str()));
210 nodepath += "/config/roll-offset-deg";
211 fgSetDouble("/sim/current-view/roll-offset-deg",
212 fgGetDouble(nodepath.c_str()));
215 nodepath += "/config/default-field-of-view-deg";
216 fov_deg = fgGetDouble(nodepath.c_str());
217 if (fov_deg < 10.0) {
220 fgSetDouble("/sim/current-view/field-of-view",fov_deg);
222 // target offsets for lookat mode only...
224 nodepath += "/config/target-x-offset-m";
225 fgSetDouble("/sim/current-view/target-x-offset-deg",
226 fgGetDouble(nodepath.c_str()));
229 nodepath += "/config/target-y-offset-m";
230 fgSetDouble("/sim/current-view/target-y-offset-deg",
231 fgGetDouble(nodepath.c_str()));
234 nodepath += "/config/target-z-offset-m";
235 fgSetDouble("/sim/current-view/target-z-offset-deg",
236 fgGetDouble(nodepath.c_str()));
244 typedef double (FGViewMgr::*double_getter)() const;
249 // these are bound to the current view properties
250 fgTie("/sim/current-view/heading-offset-deg", this,
251 &FGViewMgr::getViewHeadingOffset_deg,
252 &FGViewMgr::setViewHeadingOffset_deg);
253 fgSetArchivable("/sim/current-view/heading-offset-deg");
254 fgTie("/sim/current-view/goal-heading-offset-deg", this,
255 &FGViewMgr::getViewGoalHeadingOffset_deg,
256 &FGViewMgr::setViewGoalHeadingOffset_deg);
257 fgSetArchivable("/sim/current-view/goal-heading-offset-deg");
258 fgTie("/sim/current-view/pitch-offset-deg", this,
259 &FGViewMgr::getViewPitchOffset_deg,
260 &FGViewMgr::setViewPitchOffset_deg);
261 fgSetArchivable("/sim/current-view/pitch-offset-deg");
262 fgTie("/sim/current-view/goal-pitch-offset-deg", this,
263 &FGViewMgr::getGoalViewPitchOffset_deg,
264 &FGViewMgr::setGoalViewPitchOffset_deg);
265 fgSetArchivable("/sim/current-view/goal-pitch-offset-deg");
267 fgTie("/sim/current-view/view-number", this,
268 &FGViewMgr::getView, &FGViewMgr::setView);
269 fgSetArchivable("/sim/current-view/view-number", FALSE);
271 fgTie("/sim/current-view/axes/long", this,
272 (double_getter)0, &FGViewMgr::setViewAxisLong);
273 fgSetArchivable("/sim/current-view/axes/long");
275 fgTie("/sim/current-view/axes/lat", this,
276 (double_getter)0, &FGViewMgr::setViewAxisLat);
277 fgSetArchivable("/sim/current-view/axes/lat");
279 fgTie("/sim/current-view/field-of-view", this,
280 &FGViewMgr::getFOV_deg, &FGViewMgr::setFOV_deg);
281 fgSetArchivable("/sim/current-view/field-of-view");
283 fgTie("/sim/current-view/ground-level-nearplane-m", this,
284 &FGViewMgr::getNear_m, &FGViewMgr::setNear_m);
285 fgSetArchivable("/sim/current-view/ground-level-nearplane-m");
293 // need to redo these bindings to the new locations (move to viewer?)
294 fgUntie("/sim/current-view/heading-offset-deg");
295 fgUntie("/sim/current-view/goal-heading-offset-deg");
296 fgUntie("/sim/current-view/pitch-offset-deg");
297 fgUntie("/sim/current-view/goal-pitch-offset-deg");
298 fgUntie("/sim/field-of-view");
299 fgUntie("/sim/current-view/view-number");
300 fgUntie("/sim/current-view/axes/long");
301 fgUntie("/sim/current-view/axes/lat");
302 fgUntie("/sim/current-view/ground-level-nearplane-m");
306 FGViewMgr::update (double dt)
309 string viewpath, nodepath;
310 double lon_deg, lat_deg, alt_ft, roll_deg, pitch_deg, heading_deg;
312 FGViewer * view = get_current_view();
318 viewpath = "/sim/view";
319 sprintf(stridx, "[%d]", i);
322 FGViewer *loop_view = (FGViewer *)get_view( i );
324 // Set up view location and orientation
327 nodepath += "/config/from-model";
328 if (!fgGetBool(nodepath.c_str())) {
330 nodepath += "/config/eye-lon-deg-path";
331 lon_deg = fgGetDouble(fgGetString(nodepath.c_str()));
333 nodepath += "/config/eye-lat-deg-path";
334 lat_deg = fgGetDouble(fgGetString(nodepath.c_str()));
336 nodepath += "/config/eye-alt-ft-path";
337 alt_ft = fgGetDouble(fgGetString(nodepath.c_str()));
339 nodepath += "/config/eye-roll-deg-path";
340 roll_deg = fgGetDouble(fgGetString(nodepath.c_str()));
342 nodepath += "/config/eye-pitch-deg-path";
343 pitch_deg = fgGetDouble(fgGetString(nodepath.c_str()));
345 nodepath += "/config/eye-heading-deg-path";
346 heading_deg = fgGetDouble(fgGetString(nodepath.c_str()));
347 loop_view->setPosition(lon_deg, lat_deg, alt_ft);
348 loop_view->setOrientation(roll_deg, pitch_deg, heading_deg);
350 // force recalc in viewer
351 loop_view->set_dirty();
354 // if lookat (type 1) then get target data...
355 if (loop_view->getType() == FG_LOOKAT) {
357 nodepath += "/config/from-model";
358 if (!fgGetBool(nodepath.c_str())) {
360 nodepath += "/config/target-lon-deg-path";
361 lon_deg = fgGetDouble(fgGetString(nodepath.c_str()));
363 nodepath += "/config/target-lat-deg-path";
364 lat_deg = fgGetDouble(fgGetString(nodepath.c_str()));
366 nodepath += "/config/target-alt-ft-path";
367 alt_ft = fgGetDouble(fgGetString(nodepath.c_str()));
369 nodepath += "/config/target-roll-deg-path";
370 roll_deg = fgGetDouble(fgGetString(nodepath.c_str()));
372 nodepath += "/config/target-pitch-deg-path";
373 pitch_deg = fgGetDouble(fgGetString(nodepath.c_str()));
375 nodepath += "/config/target-heading-deg-path";
376 heading_deg = fgGetDouble(fgGetString(nodepath.c_str()));
378 loop_view->setTargetPosition(lon_deg, lat_deg, alt_ft);
379 loop_view->setTargetOrientation(roll_deg, pitch_deg, heading_deg);
381 loop_view->set_dirty();
385 setViewXOffset_m(fgGetDouble("/sim/current-view/x-offset-m"));
386 setViewYOffset_m(fgGetDouble("/sim/current-view/y-offset-m"));
387 setViewZOffset_m(fgGetDouble("/sim/current-view/z-offset-m"));
389 setViewTargetXOffset_m(fgGetDouble("/sim/current-view/target-x-offset-m"));
390 setViewTargetYOffset_m(fgGetDouble("/sim/current-view/target-y-offset-m"));
391 setViewTargetZOffset_m(fgGetDouble("/sim/current-view/target-z-offset-m"));
393 // Update the current view
399 FGViewMgr::copyToCurrent()
402 string viewpath, nodepath;
405 viewpath = "/sim/view";
406 sprintf(stridx, "[%d]", i);
409 // copy certain view config data for default values
411 nodepath += "/config/default-heading-offset-deg";
412 fgSetDouble("/sim/current-view/config/heading-offset-deg",
413 fgGetDouble(nodepath.c_str()));
416 nodepath += "/config/pitch-offset-deg";
417 fgSetDouble("/sim/current-view/config/pitch-offset-deg",
418 fgGetDouble(nodepath.c_str()));
421 nodepath += "/config/roll-offset-deg";
422 fgSetDouble("/sim/current-view/config/roll-offset-deg",
423 fgGetDouble(nodepath.c_str()));
426 nodepath += "/config/default-field-of-view-deg";
427 fgSetDouble("/sim/current-view/config/default-field-of-view-deg",
428 fgGetDouble(nodepath.c_str()));
431 fgSetDouble("/sim/current-view/x-offset-m", getViewXOffset_m());
432 fgSetDouble("/sim/current-view/y-offset-m", getViewYOffset_m());
433 fgSetDouble("/sim/current-view/z-offset-m", getViewZOffset_m());
434 fgSetDouble("/sim/current-view/goal-heading-offset-deg",
435 get_current_view()->getGoalHeadingOffset_deg());
436 fgSetDouble("/sim/current-view/goal-pitch-offset-deg",
437 get_current_view()->getGoalPitchOffset_deg());
438 fgSetDouble("/sim/current-view/goal-roll-offset-deg",
439 get_current_view()->getRollOffset_deg());
440 fgSetDouble("/sim/current-view/heading-offset-deg",
441 get_current_view()->getHeadingOffset_deg());
442 fgSetDouble("/sim/current-view/pitch-offset-deg",
443 get_current_view()->getPitchOffset_deg());
444 fgSetDouble("/sim/current-view/roll-offset-deg",
445 get_current_view()->getRollOffset_deg());
446 fgSetDouble("/sim/current-view/target-x-offset-m",
447 get_current_view()->getTargetXOffset_m());
448 fgSetDouble("/sim/current-view/target-y-offset-m",
449 get_current_view()->getTargetYOffset_m());
450 fgSetDouble("/sim/current-view/target-z-offset-m",
451 get_current_view()->getTargetZOffset_m());
456 FGViewMgr::getViewHeadingOffset_deg () const
458 const FGViewer * view = get_current_view();
459 return (view == 0 ? 0 : view->getHeadingOffset_deg());
463 FGViewMgr::setViewHeadingOffset_deg (double offset)
465 FGViewer * view = get_current_view();
467 view->setGoalHeadingOffset_deg(offset);
468 view->setHeadingOffset_deg(offset);
473 FGViewMgr::getViewGoalHeadingOffset_deg () const
475 const FGViewer * view = get_current_view();
476 return (view == 0 ? 0 : view->getGoalHeadingOffset_deg());
480 FGViewMgr::setViewGoalHeadingOffset_deg (double offset)
482 FGViewer * view = get_current_view();
484 view->setGoalHeadingOffset_deg(offset);
488 FGViewMgr::getViewPitchOffset_deg () const
490 const FGViewer * view = get_current_view();
491 return (view == 0 ? 0 : view->getPitchOffset_deg());
495 FGViewMgr::setViewPitchOffset_deg (double tilt)
497 FGViewer * view = get_current_view();
499 view->setGoalPitchOffset_deg(tilt);
500 view->setPitchOffset_deg(tilt);
505 FGViewMgr::getGoalViewPitchOffset_deg () const
507 const FGViewer * view = get_current_view();
508 return (view == 0 ? 0 : view->getGoalPitchOffset_deg());
512 FGViewMgr::setGoalViewPitchOffset_deg (double tilt)
514 FGViewer * view = get_current_view();
516 view->setGoalPitchOffset_deg(tilt);
520 FGViewMgr::getViewXOffset_m () const
522 const FGViewer * view = get_current_view();
524 return ((FGViewer *)view)->getXOffset_m();
531 FGViewMgr::setViewXOffset_m (double x)
533 FGViewer * view = get_current_view();
535 view->setXOffset_m(x);
540 FGViewMgr::getViewYOffset_m () const
542 const FGViewer * view = get_current_view();
544 return ((FGViewer *)view)->getYOffset_m();
551 FGViewMgr::setViewYOffset_m (double y)
553 FGViewer * view = get_current_view();
555 view->setYOffset_m(y);
560 FGViewMgr::getViewZOffset_m () const
562 const FGViewer * view = get_current_view();
564 return ((FGViewer *)view)->getZOffset_m();
571 FGViewMgr::setViewZOffset_m (double z)
573 FGViewer * view = get_current_view();
575 view->setZOffset_m(z);
580 FGViewMgr::getViewTargetXOffset_m () const
582 const FGViewer * view = get_current_view();
584 return ((FGViewer *)view)->getTargetXOffset_m();
591 FGViewMgr::setViewTargetXOffset_m (double x)
593 FGViewer * view = get_current_view();
595 view->setTargetXOffset_m(x);
600 FGViewMgr::getViewTargetYOffset_m () const
602 const FGViewer * view = get_current_view();
604 return ((FGViewer *)view)->getTargetYOffset_m();
611 FGViewMgr::setViewTargetYOffset_m (double y)
613 FGViewer * view = get_current_view();
615 view->setTargetYOffset_m(y);
620 FGViewMgr::getViewTargetZOffset_m () const
622 const FGViewer * view = get_current_view();
624 return ((FGViewer *)view)->getTargetZOffset_m();
631 FGViewMgr::setViewTargetZOffset_m (double z)
633 FGViewer * view = get_current_view();
635 view->setTargetZOffset_m(z);
640 FGViewMgr::getView () const
646 FGViewMgr::setView (int newview )
648 // if newview number too low wrap to last view...
650 newview = (int)views.size() -1;
652 // if newview number to high wrap to zero...
653 if ( newview > ((int)views.size() -1) ) {
664 FGViewMgr::getFOV_deg () const
666 const FGViewer * view = get_current_view();
667 return (view == 0 ? 0 : view->get_fov());
671 FGViewMgr::setFOV_deg (double fov)
673 FGViewer * view = get_current_view();
679 FGViewMgr::getNear_m () const
681 const FGViewer * view = get_current_view();
682 return (view == 0 ? 0.5f : view->getNear_m());
686 FGViewMgr::setNear_m (double near_m)
688 FGViewer * view = get_current_view();
690 view->setNear_m(near_m);
694 FGViewMgr::setViewAxisLong (double axis)
700 FGViewMgr::setViewAxisLat (double axis)
706 FGViewMgr::do_axes ()
708 // Take no action when hat is centered
709 if ( ( axis_long < 0.01 ) &&
710 ( axis_long > -0.01 ) &&
711 ( axis_lat < 0.01 ) &&
716 double viewDir = 999;
718 /* Do all the quick and easy cases */
719 if (axis_long < 0) { // Longitudinal axis forward
720 if (axis_lat == axis_long)
721 viewDir = fgGetDouble("/sim/view/config/front-left-direction-deg");
722 else if (axis_lat == - axis_long)
723 viewDir = fgGetDouble("/sim/view/config/front-right-direction-deg");
724 else if (axis_lat == 0)
725 viewDir = fgGetDouble("/sim/view/config/front-direction-deg");
726 } else if (axis_long > 0) { // Longitudinal axis backward
727 if (axis_lat == - axis_long)
728 viewDir = fgGetDouble("/sim/view/config/back-left-direction-deg");
729 else if (axis_lat == axis_long)
730 viewDir = fgGetDouble("/sim/view/config/back-right-direction-deg");
731 else if (axis_lat == 0)
732 viewDir = fgGetDouble("/sim/view/config/back-direction-deg");
733 } else if (axis_long == 0) { // Longitudinal axis neutral
735 viewDir = fgGetDouble("/sim/view/config/left-direction-deg");
736 else if (axis_lat > 0)
737 viewDir = fgGetDouble("/sim/view/config/right-direction-deg");
738 else return; /* And assertion failure maybe? */
741 // Do all the difficult cases
743 viewDir = SGD_RADIANS_TO_DEGREES * atan2 ( -axis_lat, -axis_long );
744 if ( viewDir < -1 ) viewDir += 360;
746 get_current_view()->setGoalHeadingOffset_deg(viewDir);