#endif
#include <simgear/compiler.h>
+#include <cassert>
#include "fg_props.hxx"
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
#include <simgear/debug/logstream.hxx>
#include <simgear/constants.h>
-#include <simgear/math/polar3d.hxx>
-#include <simgear/math/sg_geodesy.hxx>
#include <simgear/scene/model/placement.hxx>
-#include <simgear/math/vector.hxx>
#include <Main/globals.hxx>
#include <Scenery/scenery.hxx>
double target_x_offset_m, double target_y_offset_m,
double target_z_offset_m, double near_m, bool internal ):
_dirty(true),
- _lon_deg(0),
- _lat_deg(0),
- _alt_ft(0),
- _target_lon_deg(0),
- _target_lat_deg(0),
- _target_alt_ft(0),
_roll_deg(0),
_pitch_deg(0),
_heading_deg(0),
- _damp_sync(0),
- _damp_roll(0),
- _damp_pitch(0),
- _damp_heading(0),
_scaling_type(FG_SCALING_MAX),
+ _aspect_ratio(0),
_cameraGroup(CameraGroup::getDefault())
{
_absolute_view_pos = SGVec3d(0, 0, 0);
_internal = internal;
+ _dampFactor = SGVec3d::zeros();
+ _dampOutput = SGVec3d::zeros();
+ _dampTarget = SGVec3d::zeros();
+
if (damp_roll > 0.0)
- _damp_roll = 1.0 / pow(10.0, fabs(damp_roll));
+ _dampFactor[0] = 1.0 / pow(10.0, fabs(damp_roll));
if (damp_pitch > 0.0)
- _damp_pitch = 1.0 / pow(10.0, fabs(damp_pitch));
+ _dampFactor[1] = 1.0 / pow(10.0, fabs(damp_pitch));
if (damp_heading > 0.0)
- _damp_heading = 1.0 / pow(10.0, fabs(damp_heading));
+ _dampFactor[2] = 1.0 / pow(10.0, fabs(damp_heading));
_offset_m.x() = x_offset_m;
_offset_m.y() = y_offset_m;
} else {
_fov_deg = 55;
}
+ _aspect_ratio = 1;
_aspect_ratio_multiplier = aspect_ratio_multiplier;
_target_offset_m.x() = target_x_offset_m;
_target_offset_m.y() = target_y_offset_m;
_internal = internal;
}
-void
-FGViewer::setLongitude_deg (double lon_deg)
-{
- _dirty = true;
- _lon_deg = lon_deg;
-}
-
-void
-FGViewer::setLatitude_deg (double lat_deg)
-{
- _dirty = true;
- _lat_deg = lat_deg;
-}
-
-void
-FGViewer::setAltitude_ft (double alt_ft)
-{
- _dirty = true;
- _alt_ft = alt_ft;
-}
-
void
FGViewer::setPosition (double lon_deg, double lat_deg, double alt_ft)
{
_dirty = true;
- _lon_deg = lon_deg;
- _lat_deg = lat_deg;
- _alt_ft = alt_ft;
-}
-
-void
-FGViewer::setTargetLongitude_deg (double lon_deg)
-{
- _dirty = true;
- _target_lon_deg = lon_deg;
-}
-
-void
-FGViewer::setTargetLatitude_deg (double lat_deg)
-{
- _dirty = true;
- _target_lat_deg = lat_deg;
-}
-
-void
-FGViewer::setTargetAltitude_ft (double alt_ft)
-{
- _dirty = true;
- _target_alt_ft = alt_ft;
+ _position = SGGeod::fromDegFt(lon_deg, lat_deg, alt_ft);
}
void
FGViewer::setTargetPosition (double lon_deg, double lat_deg, double alt_ft)
{
_dirty = true;
- _target_lon_deg = lon_deg;
- _target_lat_deg = lat_deg;
- _target_alt_ft = alt_ft;
+ _target = SGGeod::fromDegFt(lon_deg, lat_deg, alt_ft);
}
void
FGViewer::setHeadingOffset_deg (double heading_offset_deg)
{
_dirty = true;
- _heading_offset_deg = heading_offset_deg;
+ if (_at_model && (_offset_m.x() == 0.0)&&(_offset_m.z() == 0.0))
+ {
+ /* avoid optical effects (e.g. rotating sky) when "looking at" with
+ * heading offsets x==z==0 (view heading cannot change). */
+ _heading_offset_deg = 0.0;
+ }
+ else
+ _heading_offset_deg = heading_offset_deg;
}
void
FGViewer::setGoalHeadingOffset_deg (double goal_heading_offset_deg)
{
_dirty = true;
+ if (_at_model && (_offset_m.x() == 0.0)&&(_offset_m.z() == 0.0))
+ {
+ /* avoid optical effects (e.g. rotating sky) when "looking at" with
+ * heading offsets x==z==0 (view heading cannot change). */
+ _goal_heading_offset_deg = 0.0;
+ return;
+ }
+
_goal_heading_offset_deg = goal_heading_offset_deg;
while ( _goal_heading_offset_deg < 0.0 ) {
_goal_heading_offset_deg += 360;
FGViewer::recalcLookFrom ()
{
// Update location data ...
- double lat, lon, alt, head, pitch, roll;
if ( _from_model ) {
SGModelPlacement* placement = globals->get_aircraft_model()->get3DModel();
- lat = placement->getLatitudeDeg();
- lon = placement->getLongitudeDeg();
- alt = placement->getElevationFt();
-
- head = placement->getHeadingDeg();
- pitch = placement->getPitchDeg();
- roll = placement->getRollDeg();
- } else {
- lat = _lat_deg;
- lon = _lon_deg;
- alt = _alt_ft;
- head = _heading_deg;
- pitch = _pitch_deg;
- roll = _roll_deg;
+ _position = placement->getPosition();
+
+ _heading_deg = placement->getHeadingDeg();
+ _pitch_deg = placement->getPitchDeg();
+ _roll_deg = placement->getRollDeg();
}
+
+ double head = _heading_deg;
+ double pitch = _pitch_deg;
+ double roll = _roll_deg;
if ( !_from_model ) {
// update from our own data...
- dampEyeData(roll, pitch, head);
+ setDampTarget(roll, pitch, head);
+ getDampOutput(roll, pitch, head);
}
- // The geodetic position of our base view position
- SGGeod geodPos = SGGeod::fromDegFt(lon, lat, alt);
// The rotation rotating from the earth centerd frame to
- // the horizontal local OpenGL frame
- SGQuatd hlOr = SGQuatd::viewHL(geodPos);
+ // the horizontal local frame
+ SGQuatd hlOr = SGQuatd::fromLonLat(_position);
- // the rotation from the horizontal local frame to the basic view orientation
+ // The rotation from the horizontal local frame to the basic view orientation
SGQuatd hlToBody = SGQuatd::fromYawPitchRollDeg(head, pitch, roll);
- hlToBody = SGQuatd::simToView(hlToBody);
- // The cartesian position of the basic view coordinate
- SGVec3d position = SGVec3d::fromGeod(geodPos);
- // the rotation offset, don't know why heading is negative here ...
- SGQuatd viewOffsetOr = SGQuatd::simToView(
- SGQuatd::fromYawPitchRollDeg(-_heading_offset_deg, _pitch_offset_deg,
- _roll_offset_deg));
+ // The rotation offset, don't know why heading is negative here ...
+ mViewOffsetOr
+ = SGQuatd::fromYawPitchRollDeg(-_heading_offset_deg, _pitch_offset_deg,
+ _roll_offset_deg);
// Compute the eyepoints orientation and position
// wrt the earth centered frame - that is global coorinates
SGQuatd ec2body = hlOr*hlToBody;
- _absolute_view_pos = position + ec2body.backTransform(_offset_m);
- mViewOrientation = ec2body*viewOffsetOr;
+
+ // The cartesian position of the basic view coordinate
+ SGVec3d position = SGVec3d::fromGeod(_position);
+
+ // This is rotates the x-forward, y-right, z-down coordinate system the where
+ // simulation runs into the OpenGL camera system with x-right, y-up, z-back.
+ SGQuatd q(-0.5, -0.5, 0.5, 0.5);
+
+ _absolute_view_pos = position + (ec2body*q).backTransform(_offset_m);
+ mViewOrientation = ec2body*mViewOffsetOr*q;
}
void
FGViewer::recalcLookAt ()
{
// The geodetic position of our target to look at
- SGGeod geodTargetPos;
- SGQuatd geodTargetOr;
if ( _at_model ) {
SGModelPlacement* placement = globals->get_aircraft_model()->get3DModel();
- double lat = placement->getLatitudeDeg();
- double lon = placement->getLongitudeDeg();
- double alt = placement->getElevationFt();
- geodTargetPos = SGGeod::fromDegFt(lon, lat, alt);
-
- double head = placement->getHeadingDeg();
- double pitch = placement->getPitchDeg();
- double roll = placement->getRollDeg();
- geodTargetOr = SGQuatd::fromYawPitchRollDeg(head, pitch, roll);
+ _target = placement->getPosition();
+ _target_heading_deg = placement->getHeadingDeg();
+ _target_pitch_deg = placement->getPitchDeg();
+ _target_roll_deg = placement->getRollDeg();
} else {
- dampEyeData(_target_roll_deg, _target_pitch_deg, _target_heading_deg);
-
// if not model then calculate our own target position...
- geodTargetPos = SGGeod::fromDegFt(_target_lon_deg,
- _target_lat_deg,
- _target_alt_ft);
- geodTargetOr = SGQuatd::fromYawPitchRollDeg(_target_heading_deg,
- _target_pitch_deg,
- _target_roll_deg);
+ setDampTarget(_target_roll_deg, _target_pitch_deg, _target_heading_deg);
+ getDampOutput(_target_roll_deg, _target_pitch_deg, _target_heading_deg);
}
- SGQuatd geodTargetHlOr = SGQuatd::fromLonLat(geodTargetPos);
+
+ SGQuatd geodTargetOr = SGQuatd::fromYawPitchRollDeg(_target_heading_deg,
+ _target_pitch_deg,
+ _target_roll_deg);
+ SGQuatd geodTargetHlOr = SGQuatd::fromLonLat(_target);
- SGGeod geodEyePos;
- SGQuatd geodEyeOr;
if ( _from_model ) {
SGModelPlacement* placement = globals->get_aircraft_model()->get3DModel();
- double lat = placement->getLatitudeDeg();
- double lon = placement->getLongitudeDeg();
- double alt = placement->getElevationFt();
- geodEyePos = SGGeod::fromDegFt(lon, lat, alt);
-
- double head = placement->getHeadingDeg();
- double pitch = placement->getPitchDeg();
- double roll = placement->getRollDeg();
- geodEyeOr = SGQuatd::fromYawPitchRollDeg(head, pitch, roll);
+ _position = placement->getPosition();
+ _heading_deg = placement->getHeadingDeg();
+ _pitch_deg = placement->getPitchDeg();
+ _roll_deg = placement->getRollDeg();
} else {
- dampEyeData(_roll_deg, _pitch_deg, _heading_deg);
-
// update from our own data, just the rotation here...
- geodEyePos = SGGeod::fromDegFt(_lon_deg, _lat_deg, _alt_ft);
- geodEyeOr = SGQuatd::fromYawPitchRollDeg(_heading_deg,
- _pitch_deg,
- _roll_deg);
+ setDampTarget(_roll_deg, _pitch_deg, _heading_deg);
+ getDampOutput(_roll_deg, _pitch_deg, _heading_deg);
}
- SGQuatd geodEyeHlOr = SGQuatd::fromLonLat(geodEyePos);
+ SGQuatd geodEyeOr = SGQuatd::fromYawPitchRollDeg(_heading_deg, _pitch_deg, _roll_deg);
+ SGQuatd geodEyeHlOr = SGQuatd::fromLonLat(_position);
// the rotation offset, don't know why heading is negative here ...
- SGQuatd eyeOffsetOr =
+ mViewOffsetOr =
SGQuatd::fromYawPitchRollDeg(-_heading_offset_deg + 180, _pitch_offset_deg,
_roll_offset_deg);
// Offsets to the eye position
SGVec3d eyeOff(-_offset_m.z(), _offset_m.x(), -_offset_m.y());
SGQuatd ec2eye = geodEyeHlOr*geodEyeOr;
- SGVec3d eyeCart = SGVec3d::fromGeod(geodEyePos);
- eyeCart += (ec2eye*eyeOffsetOr).backTransform(eyeOff);
+ SGVec3d eyeCart = SGVec3d::fromGeod(_position);
+ eyeCart += (ec2eye*mViewOffsetOr).backTransform(eyeOff);
- SGVec3d atCart = SGVec3d::fromGeod(geodTargetPos);
+ SGVec3d atCart = SGVec3d::fromGeod(_target);
// add target offsets to at_position...
SGVec3d target_pos_off(-_target_offset_m.z(), _target_offset_m.x(),
SGVec3d dir = normalize(atCart - eyeCart);
// the up directon
SGVec3d up = ec2eye.backTransform(SGVec3d(0, 0, -1));
- // rotate dir to the 0-th unit vector
- // rotate up to 2-th unit vector
+ // rotate -dir to the 2-th unit vector
+ // rotate up to 1-th unit vector
+ // Note that this matches the OpenGL camera coordinate system
+ // with x-right, y-up, z-back.
mViewOrientation = SGQuatd::fromRotateTo(-dir, 2, up, 1);
}
void
-FGViewer::dampEyeData(double &roll_deg, double &pitch_deg, double &heading_deg)
+FGViewer::setDampTarget(double roll, double pitch, double heading)
{
- const double interval = 0.01;
+ _dampTarget = SGVec3d(roll, pitch, heading);
+}
+
+void
+FGViewer::getDampOutput(double& roll, double& pitch, double& heading)
+{
+ roll = _dampOutput[0];
+ pitch = _dampOutput[1];
+ heading = _dampOutput[2];
+}
+
+void
+FGViewer::updateDampOutput(double dt)
+{
static FGViewer *last_view = 0;
- if (last_view != this) {
- _damp_sync = 0.0;
- _damped_roll_deg = roll_deg;
- _damped_pitch_deg = pitch_deg;
- _damped_heading_deg = heading_deg;
+ if ((last_view != this) || (dt > 1.0)) {
+ _dampOutput = _dampTarget;
last_view = this;
return;
}
-
- if (_damp_sync < interval) {
- if (_damp_roll > 0.0)
- roll_deg = _damped_roll_deg;
- if (_damp_pitch > 0.0)
- pitch_deg = _damped_pitch_deg;
- if (_damp_heading > 0.0)
- heading_deg = _damped_heading_deg;
- return;
- }
-
- while (_damp_sync >= interval) {
- _damp_sync -= interval;
-
- double d;
- if (_damp_roll > 0.0) {
- d = _damped_roll_deg - roll_deg;
- if (d >= 180.0)
- _damped_roll_deg -= 360.0;
- else if (d < -180.0)
- _damped_roll_deg += 360.0;
- roll_deg = _damped_roll_deg = roll_deg * _damp_roll + _damped_roll_deg * (1 - _damp_roll);
- }
-
- if (_damp_pitch > 0.0) {
- d = _damped_pitch_deg - pitch_deg;
- if (d >= 180.0)
- _damped_pitch_deg -= 360.0;
- else if (d < -180.0)
- _damped_pitch_deg += 360.0;
- pitch_deg = _damped_pitch_deg = pitch_deg * _damp_pitch + _damped_pitch_deg * (1 - _damp_pitch);
- }
-
- if (_damp_heading > 0.0) {
- d = _damped_heading_deg - heading_deg;
- if (d >= 180.0)
- _damped_heading_deg -= 360.0;
- else if (d < -180.0)
- _damped_heading_deg += 360.0;
- heading_deg = _damped_heading_deg = heading_deg * _damp_heading + _damped_heading_deg * (1 - _damp_heading);
- }
- }
+
+ const double interval = 0.01;
+ while (dt > interval) {
+
+ for (unsigned int i=0; i<3; ++i) {
+ if (_dampFactor[i] <= 0.0) {
+ // axis is un-damped, set output to target directly
+ _dampOutput[i] = _dampTarget[i];
+ continue;
+ }
+
+ double d = _dampOutput[i] - _dampTarget[i];
+ if (d > 180.0) {
+ _dampOutput[i] -= 360.0;
+ } else if (d < -180.0) {
+ _dampOutput[i] += 360.0;
+ }
+
+ _dampOutput[i] = (_dampTarget[i] * _dampFactor[i]) +
+ (_dampOutput[i] * (1.0 - _dampFactor[i]));
+ } // of axis iteration
+
+ dt -= interval;
+ } // of dt subdivision by interval
}
double
void
FGViewer::update (double dt)
{
- _damp_sync += dt;
-
+ updateDampOutput(dt);
+
int i;
int dt_ms = int(dt * 1000);
for ( i = 0; i < dt_ms; i++ ) {
}
}
recalc();
- _cameraGroup->update(_absolute_view_pos.osg(), mViewOrientation.osg());
- _cameraGroup->setCameraParameters(get_v_fov(), get_aspect_ratio());
+ if( fgGetBool( "/sim/rendering/draw-otw", true ) ) {
+ _cameraGroup->update(toOsg(_absolute_view_pos), toOsg(mViewOrientation));
+ _cameraGroup->setCameraParameters(get_v_fov(), get_aspect_ratio());
+ }
}