1 // view.cxx -- class for managing a view in the flightgear world.
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
8 // Copyright (C) 1997 - 2000 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32 #include <simgear/compiler.h>
35 #include <simgear/debug/logstream.hxx>
36 #include <simgear/constants.h>
37 #include <simgear/scene/model/placement.hxx>
38 #include <simgear/scene/util/OsgMath.hxx>
40 #include <Main/fg_props.hxx>
41 #include <Main/globals.hxx>
42 #include "CameraGroup.hxx"
44 using namespace flightgear;
46 ////////////////////////////////////////////////////////////////////////
47 // Implementation of FGViewer.
48 ////////////////////////////////////////////////////////////////////////
51 View::View( ViewType Type, bool from_model, int from_model_index,
52 bool at_model, int at_model_index,
53 double damp_roll, double damp_pitch, double damp_heading,
54 double x_offset_m, double y_offset_m, double z_offset_m,
55 double heading_offset_deg, double pitch_offset_deg,
56 double roll_offset_deg,
57 double fov_deg, double aspect_ratio_multiplier,
58 double target_x_offset_m, double target_y_offset_m,
59 double target_z_offset_m, double near_m, bool internal ):
66 _target_heading_deg(0),
67 _scaling_type(FG_SCALING_MAX)
69 _absolute_view_pos = SGVec3d(0, 0, 0);
71 _from_model = from_model;
72 _from_model_index = from_model_index;
74 _at_model_index = at_model_index;
78 _dampFactor = SGVec3d::zeros();
79 _dampOutput = SGVec3d::zeros();
80 _dampTarget = SGVec3d::zeros();
83 _dampFactor[0] = 1.0 / pow(10.0, fabs(damp_roll));
85 _dampFactor[1] = 1.0 / pow(10.0, fabs(damp_pitch));
86 if (damp_heading > 0.0)
87 _dampFactor[2] = 1.0 / pow(10.0, fabs(damp_heading));
89 _offset_m.x() = x_offset_m;
90 _offset_m.y() = y_offset_m;
91 _offset_m.z() = z_offset_m;
92 _configOffset_m = _offset_m;
94 _heading_offset_deg = heading_offset_deg;
95 _pitch_offset_deg = pitch_offset_deg;
96 _roll_offset_deg = roll_offset_deg;
97 _goal_heading_offset_deg = heading_offset_deg;
98 _goal_pitch_offset_deg = pitch_offset_deg;
99 _goal_roll_offset_deg = roll_offset_deg;
101 _configHeadingOffsetDeg = heading_offset_deg;
102 _configPitchOffsetDeg = pitch_offset_deg;
103 _configRollOffsetDeg = roll_offset_deg;
111 _configFOV_deg = _fov_deg;
113 _aspect_ratio_multiplier = aspect_ratio_multiplier;
114 _target_offset_m.x() = target_x_offset_m;
115 _target_offset_m.y() = target_y_offset_m;
116 _target_offset_m.z() = target_z_offset_m;
117 _configTargetOffset_m = _target_offset_m;
119 _ground_level_nearplane_m = near_m;
120 // a reasonable guess for init, so that the math doesn't blow up
123 View* View::createFromProperties(SGPropertyNode_ptr config)
125 double aspect_ratio_multiplier
126 = fgGetDouble("/sim/current-view/aspect-ratio-multiplier");
128 // find out if this is an internal view (e.g. in cockpit, low near plane)
129 // FIXME : should be a child of config
130 bool internal = config->getParent()->getBoolValue("internal", false);
134 // this is assumed to be an aircraft model...we will need to read
135 // model-from-type as well.
137 // find out if this is a model we are looking from...
138 bool from_model = config->getBoolValue("from-model");
139 int from_model_index = config->getIntValue("from-model-idx");
141 double x_offset_m = config->getDoubleValue("x-offset-m");
142 double y_offset_m = config->getDoubleValue("y-offset-m");
143 double z_offset_m = config->getDoubleValue("z-offset-m");
145 double heading_offset_deg = config->getDoubleValue("heading-offset-deg");
146 // config->setDoubleValue("heading-offset-deg", heading_offset_deg);
147 double pitch_offset_deg = config->getDoubleValue("pitch-offset-deg");
148 // config->setDoubleValue("pitch-offset-deg", pitch_offset_deg);
149 double roll_offset_deg = config->getDoubleValue("roll-offset-deg");
150 // config->setDoubleValue("roll-offset-deg", roll_offset_deg);
152 double fov_deg = config->getDoubleValue("default-field-of-view-deg");
153 double near_m = config->getDoubleValue("ground-level-nearplane-m");
156 // supporting two types "lookat" = 1 and "lookfrom" = 0
157 const char *type = config->getParent()->getStringValue("type");
158 if (!strcmp(type, "lookat")) {
159 bool at_model = config->getBoolValue("at-model");
160 int at_model_index = config->getIntValue("at-model-idx");
162 double damp_roll = config->getDoubleValue("at-model-roll-damping");
163 double damp_pitch = config->getDoubleValue("at-model-pitch-damping");
164 double damp_heading = config->getDoubleValue("at-model-heading-damping");
166 double target_x_offset_m = config->getDoubleValue("target-x-offset-m");
167 double target_y_offset_m = config->getDoubleValue("target-y-offset-m");
168 double target_z_offset_m = config->getDoubleValue("target-z-offset-m");
170 v = new View ( FG_LOOKAT, from_model, from_model_index,
171 at_model, at_model_index,
172 damp_roll, damp_pitch, damp_heading,
173 x_offset_m, y_offset_m,z_offset_m,
174 heading_offset_deg, pitch_offset_deg,
175 roll_offset_deg, fov_deg, aspect_ratio_multiplier,
176 target_x_offset_m, target_y_offset_m,
177 target_z_offset_m, near_m, internal );
179 v->_targetProperties.init(config, "target-");
182 v = new View ( FG_LOOKFROM, from_model, from_model_index,
183 false, 0, 0.0, 0.0, 0.0,
184 x_offset_m, y_offset_m, z_offset_m,
185 heading_offset_deg, pitch_offset_deg,
186 roll_offset_deg, fov_deg, aspect_ratio_multiplier,
187 0, 0, 0, near_m, internal );
191 v->_eyeProperties.init(config, "eye-");
194 v->_name = config->getParent()->getStringValue("name");
195 v->_typeString = type;
196 v->_configHeadingOffsetDeg = config->getDoubleValue("default-heading-offset-deg");
205 _tiedProperties.Untie();
216 _tiedProperties.setRoot(fgGetNode("/sim/current-view", true));
217 _tiedProperties.Tie("heading-offset-deg", this,
218 &View::getHeadingOffset_deg,
219 &View::setHeadingOffset_deg_property,
220 false /* do not set current property value */);
222 fgSetArchivable("/sim/current-view/heading-offset-deg");
224 _tiedProperties.Tie("goal-heading-offset-deg", this,
225 &View::getGoalHeadingOffset_deg,
226 &View::setGoalHeadingOffset_deg,
227 false /* do not set current property value */);
229 fgSetArchivable("/sim/current-view/goal-heading-offset-deg");
231 _tiedProperties.Tie("pitch-offset-deg", this,
232 &View::getPitchOffset_deg,
233 &View::setPitchOffset_deg_property,
234 false /* do not set current property value */);
235 fgSetArchivable("/sim/current-view/pitch-offset-deg");
236 _tiedProperties.Tie("goal-pitch-offset-deg", this,
237 &View::getGoalPitchOffset_deg,
238 &View::setGoalPitchOffset_deg,
239 false /* do not set current property value */);
240 fgSetArchivable("/sim/current-view/goal-pitch-offset-deg");
241 _tiedProperties.Tie("roll-offset-deg", this,
242 &View::getRollOffset_deg,
243 &View::setRollOffset_deg_property,
244 false /* do not set current property value */);
245 fgSetArchivable("/sim/current-view/roll-offset-deg");
246 _tiedProperties.Tie("goal-roll-offset-deg", this,
247 &View::getGoalRollOffset_deg,
248 &View::setGoalRollOffset_deg,
249 false /* do not set current property value */);
250 fgSetArchivable("/sim/current-view/goal-roll-offset-deg");
253 _tiedProperties.Tie("field-of-view", this,
254 &View::get_fov, &View::set_fov,
256 fgSetArchivable("/sim/current-view/field-of-view");
259 _tiedProperties.Tie("aspect-ratio-multiplier", this,
260 &View::get_aspect_ratio_multiplier,
261 &View::set_aspect_ratio_multiplier,
264 _tiedProperties.Tie("ground-level-nearplane-m", this,
265 &View::getNear_m, &View::setNear_m, false);
266 fgSetArchivable("/sim/current-view/ground-level-nearplane-m");
269 _tiedProperties.Tie("viewer-lon-deg", this, &View::getLon_deg);
270 _tiedProperties.Tie("viewer-lat-deg", this, &View::getLat_deg);
271 _tiedProperties.Tie("viewer-elev-ft", this, &View::getElev_ft);
273 _tiedProperties.Tie("x-offset-m", this, &View::getXOffset_m,
274 &View::setXOffset_m, false);
275 _tiedProperties.Tie("y-offset-m", this, &View::getYOffset_m,
276 &View::setYOffset_m, false);
277 _tiedProperties.Tie("z-offset-m", this, &View::getZOffset_m,
278 &View::setZOffset_m, false);
280 _tiedProperties.Tie("target-x-offset-m", this, &View::getTargetXOffset_m,
281 &View::setTargetXOffset_m, false);
282 _tiedProperties.Tie("target-y-offset-m", this, &View::getTargetYOffset_m,
283 &View::setTargetYOffset_m, false);
284 _tiedProperties.Tie("target-z-offset-m", this, &View::getTargetZOffset_m,
285 &View::setTargetZOffset_m, false);
287 // expose various quaternions under the debug/ subtree
288 _tiedProperties.Tie("debug/orientation-w", this, &View::getOrientation_w);
289 _tiedProperties.Tie("debug/orientation-x", this, &View::getOrientation_x);
290 _tiedProperties.Tie("debug/orientation-y", this, &View::getOrientation_y);
291 _tiedProperties.Tie("debug/orientation-z", this, &View::getOrientation_z);
293 _tiedProperties.Tie("debug/orientation_offset-w", this,
294 &View::getOrOffset_w);
295 _tiedProperties.Tie("debug/orientation_offset-x", this,
296 &View::getOrOffset_x);
297 _tiedProperties.Tie("debug/orientation_offset-y", this,
298 &View::getOrOffset_y);
299 _tiedProperties.Tie("debug/orientation_offset-z", this,
300 &View::getOrOffset_z);
302 _tiedProperties.Tie("debug/frame-w", this, &View::getFrame_w);
303 _tiedProperties.Tie("debug/frame-x", this, &View::getFrame_x);
304 _tiedProperties.Tie("debug/frame-y", this, &View::getFrame_y);
305 _tiedProperties.Tie("debug/frame-z", this, &View::getFrame_z);
308 // expose the raw (OpenGL) orientation to the property tree,
309 // for the sound-manager
310 _tiedProperties.Tie("raw-orientation", 0, this, &View::getRawOrientation_w);
311 _tiedProperties.Tie("raw-orientation", 1, this, &View::getRawOrientation_x);
312 _tiedProperties.Tie("raw-orientation", 2, this, &View::getRawOrientation_y);
313 _tiedProperties.Tie("raw-orientation", 3, this, &View::getRawOrientation_z);
315 _tiedProperties.Tie("viewer-x-m", this, &View::getAbsolutePosition_x);
316 _tiedProperties.Tie("viewer-y-m", this, &View::getAbsolutePosition_y);
317 _tiedProperties.Tie("viewer-z-m", this, &View::getAbsolutePosition_z);
319 // following config properties are exposed on current-view but don't change,
320 // so we can simply copy them here.
321 _tiedProperties.getRoot()->setStringValue("name", _name);
322 _tiedProperties.getRoot()->setStringValue("type", _typeString);
323 _tiedProperties.getRoot()->setBoolValue("internal", _internal);
325 SGPropertyNode_ptr config = _tiedProperties.getRoot()->getChild("config", 0, true);
326 config->setBoolValue("from-model", _from_model);
327 config->setDoubleValue("heading-offset-deg", _configHeadingOffsetDeg);
328 config->setDoubleValue("pitch-offset-deg", _configPitchOffsetDeg);
329 config->setDoubleValue("roll-offset-deg", _configRollOffsetDeg);
330 config->setDoubleValue("default-field-of-view-deg", _configFOV_deg);
336 _tiedProperties.Untie();
339 void View::resetOffsetsAndFOV()
341 _target_offset_m = _configTargetOffset_m;
342 _offset_m = _configOffset_m;
343 _pitch_offset_deg = _configPitchOffsetDeg;
344 _heading_offset_deg = _configHeadingOffsetDeg;
345 _roll_offset_deg = _configRollOffsetDeg;
346 _fov_deg = _configFOV_deg;
350 View::setType ( int type )
359 View::setInternal ( bool internal )
361 _internal = internal;
365 View::setPosition (const SGGeod& geod)
372 View::setTargetPosition (const SGGeod& geod)
379 View::setRoll_deg (double roll_deg)
382 _roll_deg = roll_deg;
386 View::setPitch_deg (double pitch_deg)
389 _pitch_deg = pitch_deg;
393 View::setHeading_deg (double heading_deg)
396 _heading_deg = heading_deg;
400 View::setOrientation (double roll_deg, double pitch_deg, double heading_deg)
403 _roll_deg = roll_deg;
404 _pitch_deg = pitch_deg;
405 _heading_deg = heading_deg;
409 View::setTargetRoll_deg (double target_roll_deg)
412 _target_roll_deg = target_roll_deg;
416 View::setTargetPitch_deg (double target_pitch_deg)
419 _target_pitch_deg = target_pitch_deg;
423 View::setTargetHeading_deg (double target_heading_deg)
426 _target_heading_deg = target_heading_deg;
430 View::setTargetOrientation (double target_roll_deg, double target_pitch_deg, double target_heading_deg)
433 _target_roll_deg = target_roll_deg;
434 _target_pitch_deg = target_pitch_deg;
435 _target_heading_deg = target_heading_deg;
439 View::setXOffset_m (double x_offset_m)
442 _offset_m.x() = x_offset_m;
446 View::setYOffset_m (double y_offset_m)
449 _offset_m.y() = y_offset_m;
453 View::setZOffset_m (double z_offset_m)
456 _offset_m.z() = z_offset_m;
460 View::setTargetXOffset_m (double target_x_offset_m)
463 _target_offset_m.x() = target_x_offset_m;
467 View::setTargetYOffset_m (double target_y_offset_m)
470 _target_offset_m.y() = target_y_offset_m;
474 View::setTargetZOffset_m (double target_z_offset_m)
477 _target_offset_m.z() = target_z_offset_m;
481 View::setPositionOffsets (double x_offset_m, double y_offset_m, double z_offset_m)
484 _offset_m.x() = x_offset_m;
485 _offset_m.y() = y_offset_m;
486 _offset_m.z() = z_offset_m;
490 View::setRollOffset_deg (double roll_offset_deg)
493 _roll_offset_deg = roll_offset_deg;
497 View::setPitchOffset_deg (double pitch_offset_deg)
500 _pitch_offset_deg = pitch_offset_deg;
504 View::setHeadingOffset_deg (double heading_offset_deg)
507 if (_at_model && (_offset_m.x() == 0.0)&&(_offset_m.z() == 0.0))
509 /* avoid optical effects (e.g. rotating sky) when "looking at" with
510 * heading offsets x==z==0 (view heading cannot change). */
511 _heading_offset_deg = 0.0;
514 _heading_offset_deg = heading_offset_deg;
518 View::setHeadingOffset_deg_property (double heading_offset_deg)
520 setHeadingOffset_deg(heading_offset_deg);
521 setGoalHeadingOffset_deg(heading_offset_deg);
525 View::setPitchOffset_deg_property (double pitch_offset_deg)
527 setPitchOffset_deg(pitch_offset_deg);
528 setGoalPitchOffset_deg(pitch_offset_deg);
532 View::setRollOffset_deg_property (double roll_offset_deg)
534 setRollOffset_deg(roll_offset_deg);
535 setGoalRollOffset_deg(roll_offset_deg);
539 View::setGoalRollOffset_deg (double goal_roll_offset_deg)
542 _goal_roll_offset_deg = goal_roll_offset_deg;
546 View::setGoalPitchOffset_deg (double goal_pitch_offset_deg)
549 _goal_pitch_offset_deg = goal_pitch_offset_deg;
550 /* The angle is set to 1/1000th of a degree from the poles to avoid the
551 * singularity where the azimuthal angle becomes undefined, inducing optical
552 * artefacts. The arbitrary angle offset is visually unnoticeable while
553 * avoiding any possible floating point truncation artefacts. */
554 if ( _goal_pitch_offset_deg < -89.999 ) {
555 _goal_pitch_offset_deg = -89.999;
557 if ( _goal_pitch_offset_deg > 89.999 ) {
558 _goal_pitch_offset_deg = 89.999;
564 View::setGoalHeadingOffset_deg (double goal_heading_offset_deg)
567 if (_at_model && (_offset_m.x() == 0.0)&&(_offset_m.z() == 0.0))
569 /* avoid optical effects (e.g. rotating sky) when "looking at" with
570 * heading offsets x==z==0 (view heading cannot change). */
571 _goal_heading_offset_deg = 0.0;
575 _goal_heading_offset_deg = goal_heading_offset_deg;
576 while ( _goal_heading_offset_deg < 0.0 ) {
577 _goal_heading_offset_deg += 360;
579 while ( _goal_heading_offset_deg > 360 ) {
580 _goal_heading_offset_deg -= 360;
585 View::setOrientationOffsets (double roll_offset_deg, double pitch_offset_deg, double heading_offset_deg)
588 _roll_offset_deg = roll_offset_deg;
589 _pitch_offset_deg = pitch_offset_deg;
590 _heading_offset_deg = heading_offset_deg;
593 // recalc() is done every time one of the setters is called (making the
594 // cached data "dirty") on the next "get". It calculates all the outputs
599 if (_type == FG_LOOKFROM) {
608 // recalculate for LookFrom view type...
610 View::recalcLookFrom ()
612 // Update location data ...
614 _position = globals->get_aircraft_position();
615 globals->get_aircraft_orientation(_heading_deg, _pitch_deg, _roll_deg);
618 double head = _heading_deg;
619 double pitch = _pitch_deg;
620 double roll = _roll_deg;
621 if ( !_from_model ) {
622 // update from our own data...
623 setDampTarget(roll, pitch, head);
624 getDampOutput(roll, pitch, head);
627 // The rotation rotating from the earth centerd frame to
628 // the horizontal local frame
629 SGQuatd hlOr = SGQuatd::fromLonLat(_position);
631 // The rotation from the horizontal local frame to the basic view orientation
632 SGQuatd hlToBody = SGQuatd::fromYawPitchRollDeg(head, pitch, roll);
634 // The rotation offset, don't know why heading is negative here ...
636 = SGQuatd::fromYawPitchRollDeg(-_heading_offset_deg, _pitch_offset_deg,
639 // Compute the eyepoints orientation and position
640 // wrt the earth centered frame - that is global coorinates
641 SGQuatd ec2body = hlOr*hlToBody;
643 // The cartesian position of the basic view coordinate
644 SGVec3d position = SGVec3d::fromGeod(_position);
646 // This is rotates the x-forward, y-right, z-down coordinate system the where
647 // simulation runs into the OpenGL camera system with x-right, y-up, z-back.
648 SGQuatd q(-0.5, -0.5, 0.5, 0.5);
650 _absolute_view_pos = position + (ec2body*q).backTransform(_offset_m);
651 mViewOrientation = ec2body*mViewOffsetOr*q;
655 View::recalcLookAt ()
657 // The geodetic position of our target to look at
659 _target = globals->get_aircraft_position();
660 globals->get_aircraft_orientation(_target_heading_deg,
664 // if not model then calculate our own target position...
665 setDampTarget(_target_roll_deg, _target_pitch_deg, _target_heading_deg);
666 getDampOutput(_target_roll_deg, _target_pitch_deg, _target_heading_deg);
669 SGQuatd geodTargetOr = SGQuatd::fromYawPitchRollDeg(_target_heading_deg,
672 SGQuatd geodTargetHlOr = SGQuatd::fromLonLat(_target);
676 _position = globals->get_aircraft_position();
677 globals->get_aircraft_orientation(_heading_deg, _pitch_deg, _roll_deg);
679 // update from our own data, just the rotation here...
680 setDampTarget(_roll_deg, _pitch_deg, _heading_deg);
681 getDampOutput(_roll_deg, _pitch_deg, _heading_deg);
683 SGQuatd geodEyeOr = SGQuatd::fromYawPitchRollDeg(_heading_deg, _pitch_deg, _roll_deg);
684 SGQuatd geodEyeHlOr = SGQuatd::fromLonLat(_position);
686 // the rotation offset, don't know why heading is negative here ...
688 SGQuatd::fromYawPitchRollDeg(-_heading_offset_deg + 180, _pitch_offset_deg,
691 // Offsets to the eye position
692 SGVec3d eyeOff(-_offset_m.z(), _offset_m.x(), -_offset_m.y());
693 SGQuatd ec2eye = geodEyeHlOr*geodEyeOr;
694 SGVec3d eyeCart = SGVec3d::fromGeod(_position);
695 eyeCart += (ec2eye*mViewOffsetOr).backTransform(eyeOff);
697 SGVec3d atCart = SGVec3d::fromGeod(_target);
699 // add target offsets to at_position...
700 SGVec3d target_pos_off(-_target_offset_m.z(), _target_offset_m.x(),
701 -_target_offset_m.y());
702 target_pos_off = (geodTargetHlOr*geodTargetOr).backTransform(target_pos_off);
703 atCart += target_pos_off;
704 eyeCart += target_pos_off;
706 // Compute the eyepoints orientation and position
707 // wrt the earth centered frame - that is global coorinates
708 _absolute_view_pos = eyeCart;
710 // the view direction
711 SGVec3d dir = normalize(atCart - eyeCart);
713 SGVec3d up = ec2eye.backTransform(SGVec3d(0, 0, -1));
714 // rotate -dir to the 2-th unit vector
715 // rotate up to 1-th unit vector
716 // Note that this matches the OpenGL camera coordinate system
717 // with x-right, y-up, z-back.
718 mViewOrientation = SGQuatd::fromRotateTo(-dir, 2, up, 1);
722 View::setDampTarget(double roll, double pitch, double heading)
724 _dampTarget = SGVec3d(roll, pitch, heading);
728 View::getDampOutput(double& roll, double& pitch, double& heading)
730 roll = _dampOutput[0];
731 pitch = _dampOutput[1];
732 heading = _dampOutput[2];
737 View::updateDampOutput(double dt)
739 static View *last_view = 0;
740 if ((last_view != this) || (dt > 1.0)) {
741 _dampOutput = _dampTarget;
746 const double interval = 0.01;
747 while (dt > interval) {
749 for (unsigned int i=0; i<3; ++i) {
750 if (_dampFactor[i] <= 0.0) {
751 // axis is un-damped, set output to target directly
752 _dampOutput[i] = _dampTarget[i];
756 double d = _dampOutput[i] - _dampTarget[i];
758 _dampOutput[i] -= 360.0;
759 } else if (d < -180.0) {
760 _dampOutput[i] += 360.0;
763 _dampOutput[i] = (_dampTarget[i] * _dampFactor[i]) +
764 (_dampOutput[i] * (1.0 - _dampFactor[i]));
765 } // of axis iteration
768 } // of dt subdivision by interval
774 double aspectRatio = get_aspect_ratio();
775 switch (_scaling_type) {
776 case FG_SCALING_WIDTH: // h_fov == fov
779 if (aspectRatio < 1.0) {
785 atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS)
786 / (aspectRatio*_aspect_ratio_multiplier))
787 * SG_RADIANS_TO_DEGREES * 2;
800 double aspectRatio = get_aspect_ratio();
801 switch (_scaling_type) {
802 case FG_SCALING_WIDTH: // h_fov == fov
804 atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS)
805 * (aspectRatio*_aspect_ratio_multiplier))
806 * SG_RADIANS_TO_DEGREES * 2;
808 if (aspectRatio < 1.0) {
811 atan(tan(_fov_deg/2 * SG_DEGREES_TO_RADIANS)
812 * (aspectRatio*_aspect_ratio_multiplier))
813 * SG_RADIANS_TO_DEGREES * 2;
828 SGGeod pos = _eyeProperties.position();
829 SGVec3d att = _eyeProperties.attitude();
831 setOrientation(att[2], att[1], att[0]);
836 // if lookat (type 1) then get target data...
837 if (getType() == FG_LOOKAT) {
839 SGGeod pos = _targetProperties.position();
840 SGVec3d att = _targetProperties.attitude();
841 setTargetPosition(pos);
842 setTargetOrientation(att[2], att[1], att[0]);
850 View::update (double dt)
854 updateDampOutput(dt);
857 int dt_ms = int(dt * 1000);
858 for ( i = 0; i < dt_ms; i++ ) {
859 if ( fabs( _goal_heading_offset_deg - _heading_offset_deg) < 1 ) {
860 setHeadingOffset_deg( _goal_heading_offset_deg );
863 // move current_view.headingoffset towards
864 // current_view.goal_view_offset
865 if ( _goal_heading_offset_deg > _heading_offset_deg )
867 if ( _goal_heading_offset_deg - _heading_offset_deg < 180 ){
868 incHeadingOffset_deg( 0.5 );
870 incHeadingOffset_deg( -0.5 );
873 if ( _heading_offset_deg - _goal_heading_offset_deg < 180 ){
874 incHeadingOffset_deg( -0.5 );
876 incHeadingOffset_deg( 0.5 );
879 if ( _heading_offset_deg > 360 ) {
880 incHeadingOffset_deg( -360 );
881 } else if ( _heading_offset_deg < 0 ) {
882 incHeadingOffset_deg( 360 );
887 for ( i = 0; i < dt_ms; i++ ) {
888 if ( fabs( _goal_pitch_offset_deg - _pitch_offset_deg ) < 1 ) {
889 setPitchOffset_deg( _goal_pitch_offset_deg );
892 // move current_view.pitch_offset_deg towards
893 // current_view.goal_pitch_offset
894 if ( _goal_pitch_offset_deg > _pitch_offset_deg )
896 incPitchOffset_deg( 1.0 );
898 incPitchOffset_deg( -1.0 );
900 if ( _pitch_offset_deg > 90 ) {
901 setPitchOffset_deg(90);
902 } else if ( _pitch_offset_deg < -90 ) {
903 setPitchOffset_deg( -90 );
909 for ( i = 0; i < dt_ms; i++ ) {
910 if ( fabs( _goal_roll_offset_deg - _roll_offset_deg ) < 1 ) {
911 setRollOffset_deg( _goal_roll_offset_deg );
914 // move current_view.roll_offset_deg towards
915 // current_view.goal_roll_offset
916 if ( _goal_roll_offset_deg > _roll_offset_deg )
918 incRollOffset_deg( 1.0 );
920 incRollOffset_deg( -1.0 );
922 if ( _roll_offset_deg > 90 ) {
923 setRollOffset_deg(90);
924 } else if ( _roll_offset_deg < -90 ) {
925 setRollOffset_deg( -90 );
932 double View::getAbsolutePosition_x() const
934 return _absolute_view_pos.x();
937 double View::getAbsolutePosition_y() const
939 return _absolute_view_pos.y();
942 double View::getAbsolutePosition_z() const
944 return _absolute_view_pos.z();
947 double View::getRawOrientation_w() const
949 return mViewOrientation.w();
952 double View::getRawOrientation_x() const
954 return mViewOrientation.x();
957 double View::getRawOrientation_y() const
959 return mViewOrientation.y();
962 double View::getRawOrientation_z() const
964 return mViewOrientation.z();
967 // This takes the conventional aviation XYZ body system
968 // i.e. x=forward, y=starboard, z=bottom
969 // which is widely used in FGFS
970 // and rotates it into the OpenGL camera system
971 // i.e. Xprime=starboard, Yprime=top, Zprime=aft.
972 static const SGQuatd fsb2sta()
974 return SGQuatd(-0.5, -0.5, 0.5, 0.5);
977 // reference frame orientation.
978 // This is the view orientation you get when you have no
979 // view offset, i.e. the offset operator is the identity.
981 // For example, in the familiar "cockpit lookfrom" view,
982 // the reference frame is equal to the aircraft attitude,
983 // i.e. it is the view looking towards 12:00 straight ahead.
985 // FIXME: Somebody needs to figure out what is the reference
986 // frame view for the other view modes.
988 // Conceptually, this quat represents a rotation relative
989 // to the ECEF reference orientation, as described at
990 // http://www.av8n.com/physics/coords.htm#sec-orientation
992 // See the NOTE concerning reference orientations, below.
994 // The components of this quat are expressed in
995 // the conventional aviation basis set,
996 // i.e. x=forward, y=starboard, z=bottom
997 double View::getFrame_w() const
999 return ((mViewOrientation*conj(fsb2sta())*conj(mViewOffsetOr))).w();
1002 double View::getFrame_x() const
1004 return ((mViewOrientation*conj(fsb2sta())*conj(mViewOffsetOr))).x();
1007 double View::getFrame_y() const
1009 return ((mViewOrientation*conj(fsb2sta())*conj(mViewOffsetOr))).y();
1012 double View::getFrame_z() const
1014 return ((mViewOrientation*conj(fsb2sta())*conj(mViewOffsetOr))).z();
1019 // This rotation takes you from the aforementioned
1020 // reference frame view orientation to whatever
1021 // actual current view orientation is.
1023 // The components of this quaternion are expressed in
1024 // the conventional aviation basis set,
1025 // i.e. x=forward, y=starboard, z=bottom
1026 double View::getOrOffset_w() const{
1027 return mViewOffsetOr.w();
1029 double View::getOrOffset_x() const{
1030 return mViewOffsetOr.x();
1032 double View::getOrOffset_y() const{
1033 return mViewOffsetOr.y();
1035 double View::getOrOffset_z() const{
1036 return mViewOffsetOr.z();
1040 // current view orientation.
1041 // This is a rotation relative to the earth-centered (ec)
1044 // NOTE: Here we remove a factor of fsb2sta so that
1045 // the components of this quat are displayed using the
1046 // conventional ECEF basis set. This is *not* the way
1047 // the view orientation is stored in the views[] array,
1048 // but is easier for non-graphics hackers to understand.
1049 // If we did not remove this factor of fsb2sta here and
1050 // in getCurrentViewFrame, that would be equivalent to
1051 // the following peculiar reference orientation:
1052 // Suppose you are over the Gulf of Guinea, at (lat,lon) = (0,0).
1053 // Then the reference frame orientation can be achieved via:
1054 // -- The aircraft X-axis (nose) headed south.
1055 // -- The aircraft Y-axis (starboard wingtip) pointing up.
1056 // -- The aircraft Z-axis (belly) pointing west.
1057 // To say the same thing in other words, and perhaps more to the
1058 // point: If we use the OpenGL camera orientation conventions,
1059 // i.e. Xprime=starboard, Yprime=top, Zprime=aft, then the
1060 // aforementioned peculiar reference orientation at (lat,lon)
1061 // = (0,0) can be described as:
1062 // -- aircraft Xprime axis (starboard) pointed up
1063 // -- aircraft Yprime axis (top) pointed east
1064 // -- aircraft Zprime axis (aft) pointed north
1065 // meaning the OpenGL axes are aligned with the ECEF axes.
1066 double View::getOrientation_w() const{
1067 return (mViewOrientation * conj(fsb2sta())).w();
1069 double View::getOrientation_x() const{
1070 return (mViewOrientation * conj(fsb2sta())).x();
1072 double View::getOrientation_y() const{
1073 return (mViewOrientation * conj(fsb2sta())).y();
1075 double View::getOrientation_z() const{
1076 return (mViewOrientation * conj(fsb2sta())).z();
1079 double View::get_aspect_ratio() const
1081 return flightgear::CameraGroup::getDefault()->getMasterAspectRatio();
1084 double View::getLon_deg() const
1086 return _position.getLongitudeDeg();
1089 double View::getLat_deg() const
1091 return _position.getLatitudeDeg();
1094 double View::getElev_ft() const
1096 return _position.getElevationFt();
1099 View::PositionAttitudeProperties::PositionAttitudeProperties()
1103 View::PositionAttitudeProperties::~PositionAttitudeProperties()
1107 void View::PositionAttitudeProperties::init(SGPropertyNode_ptr parent, const std::string& prefix)
1109 _lonPathProp = parent->getNode(prefix + "lon-deg-path", true);
1110 _latPathProp = parent->getNode(prefix + "lat-deg-path", true);
1111 _altPathProp = parent->getNode(prefix + "alt-ft-path", true);
1112 _headingPathProp = parent->getNode(prefix + "heading-deg-path", true);
1113 _pitchPathProp = parent->getNode(prefix + "pitch-deg-path", true);
1114 _rollPathProp = parent->getNode(prefix + "roll-deg-path", true);
1116 // update the real properties now
1119 _lonPathProp->addChangeListener(this);
1120 _latPathProp->addChangeListener(this);
1121 _altPathProp->addChangeListener(this);
1122 _headingPathProp->addChangeListener(this);
1123 _pitchPathProp->addChangeListener(this);
1124 _rollPathProp->addChangeListener(this);
1127 void View::PositionAttitudeProperties::valueChanged(SGPropertyNode* node)
1129 _lonProp = resolvePathProperty(_lonPathProp);
1130 _latProp = resolvePathProperty(_latPathProp);
1131 _altProp = resolvePathProperty(_altPathProp);
1132 _headingProp = resolvePathProperty(_headingPathProp);
1133 _pitchProp = resolvePathProperty(_pitchPathProp);
1134 _rollProp = resolvePathProperty(_rollPathProp);
1137 SGPropertyNode_ptr View::PositionAttitudeProperties::resolvePathProperty(SGPropertyNode_ptr p)
1140 return SGPropertyNode_ptr();
1142 std::string path = p->getStringValue();
1144 return SGPropertyNode_ptr();
1146 return fgGetNode(path, true);
1149 SGGeod View::PositionAttitudeProperties::position() const
1151 double lon = _lonProp ? _lonProp->getDoubleValue() : 0.0;
1152 double lat = _latProp ? _latProp->getDoubleValue() : 0.0;
1153 double alt = _altProp ? _altProp->getDoubleValue() : 0.0;
1154 return SGGeod::fromDegFt(lon, lat, alt);
1157 SGVec3d View::PositionAttitudeProperties::attitude() const
1159 double heading = _headingProp ? _headingProp->getDoubleValue() : 0.0;
1160 double pitch = _pitchProp ? _pitchProp->getDoubleValue() : 0.0;
1161 double roll = _rollProp ? _rollProp->getDoubleValue() : 0.0;
1162 return SGVec3d(heading, pitch, roll);