1
0
Fork 0

Merge /u/cgdae/flightgear/ branch next into next

https://sourceforge.net/p/flightgear/flightgear/merge-requests/166/
This commit is contained in:
James Turner 2019-07-18 20:21:36 +00:00
commit ed0a009224
7 changed files with 639 additions and 88 deletions

View file

@ -293,6 +293,10 @@ FGInterface::bind ()
&FGInterface::get_Psi_deg,
&FGInterface::set_Psi_deg, false);
fgSetArchivable("/orientation/heading-deg");
_tiedProperties.Tie("/orientation/true-heading-deg", this,
&FGInterface::get_Psi_deg,
&FGInterface::set_Psi_deg, false);
fgSetArchivable("/orientation/true-heading-deg");
_tiedProperties.Tie("/orientation/track-deg", this,
&FGInterface::get_Track); // read-only
_tiedProperties.Tie("/orientation/path-deg", this,

View file

@ -37,11 +37,14 @@
#include <errno.h>
#include <simgear/misc/stdint.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/timing/timestamp.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/structure/commands.hxx>
#include <simgear/structure/event_mgr.hxx>
#include <simgear/misc/strutils.hxx>
#include <AIModel/AIManager.hxx>
#include <AIModel/AIMultiplayer.hxx>
@ -959,7 +962,10 @@ FGMultiplayMgr::FGMultiplayMgr()
pMultiPlayDebugLevel = fgGetNode("/sim/multiplay/debug-level", true);
pMultiPlayTransmitPropertyBase = fgGetNode("/sim/multiplay/transmit-filter-property-base", true);
pMultiPlayRange = fgGetNode("/sim/multiplay/visibility-range-nm", true);
pMultiPlayRange->setIntValue(100);
if (pMultiPlayRange->getIntValue() == 0) {
/* Only set if currently zero - this allows override e.g. in ~/.fgfsrc. */
pMultiPlayRange->setIntValue(100);
}
} // FGMultiplayMgr::FGMultiplayMgr()
//////////////////////////////////////////////////////////////////////
@ -2316,6 +2322,30 @@ FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
}
/* If <from>/<path> exists and <to>/<path> doesn't, copy the former to the
latter. */
static void copy_default(SGPropertyNode* from, const char* path, SGPropertyNode* to) {
SGPropertyNode* from_ = from->getNode(path);
if (from_) {
if (!to->getNode(path)) {
to->setDoubleValue(path, from_->getDoubleValue());
}
}
}
static std::string makeStringPropertyNameSafe(const std::string& s)
{
std::string ret;
for (size_t i=0; i<s.size(); ++i) {
char c = s[i];
if (i==0 && !isalpha(c) && c!='_') c = '_';
if (!isalnum(c) && c!='.' && c!='_' && c!='-') c = '_';
ret += c;
}
return ret;
}
FGAIMultiplayer*
FGMultiplayMgr::addMultiplayer(const std::string& callsign,
const std::string& modelName,
@ -2338,7 +2368,125 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign,
for (unsigned i = 0; i < numProperties; ++i)
mp->addPropertyId(sIdPropertyList[i].id, sIdPropertyList[i].name);
}
/* Try to find a -set.xml for <modelName>, so that we can use its view
parameters. If found, we install it into a 'set' property node.
If we are reusing an old entry in /ai/models/multiplayer[], there
might be an old set/ node, so remove it.
todo: maybe we should cache the -set.xml nodes in memory and/or share them in
properties?
*/
mp->_getProps()->removeChildren("set");
SGPropertyNode* set = NULL;
if (simgear::strutils::ends_with(modelName, ".xml")
&& simgear::strutils::starts_with(modelName, "Aircraft/")) {
std::string tail = modelName.substr(strlen("Aircraft/"));
PathList dirs(globals->get_aircraft_paths());
/* Need to append <fgdata>/Aircraft, otherwise we won't be able to find
c172p. */
SGPath fgdata_aircraft = globals->get_fg_root();
fgdata_aircraft.append("Aircraft");
dirs.push_back(fgdata_aircraft);
SGPath model_file;
PathList::const_iterator it = std::find_if(dirs.begin(), dirs.end(),
[&](SGPath dir) {
model_file = dir;
model_file.append(tail);
return model_file.exists();
});
if (it != dirs.end()) {
/* We've found the model file.
Now try each -set.xml file in <modelName> aircraft directory. In theory
an aircraft could have a -set.xml in an unrelated directory so we should
scan all directories in globals->get_aircraft_paths(), but in practice
most -set.xml files and models are in the same aircraft directory. */
std::string model_file_head = it->str() + '/';
std::string model_file_tail = model_file.str().substr(model_file_head.size());
ssize_t p = model_file_tail.find('/');
std::string aircraft_dir = model_file_head + model_file_tail.substr(0, p);
simgear::Dir dir(aircraft_dir);
std::vector<SGPath> dir_contents = dir.children(0 /*types*/, "-set.xml");
/* simgear::Dir::children() claims that second param is glob, but
actually it's just a suffix. */
for (auto path: dir_contents) {
set = mp->_getProps()->addChild("set");
bool ok = true;
try {
readProperties(path, set);
}
catch ( const std::exception &e ) {
ok = false;
}
if (ok) {
SGPropertyNode* sim_model_path = set->getNode("sim/model/path");
if (sim_model_path && sim_model_path->getStringValue() == modelName) {
/* We've found (and loaded) a matching -set.xml. */
break;
}
}
mp->_getProps()->removeChildren("set");
set = NULL;
}
}
}
/* Copy [set]/sim/chase-distance-m (or -25 if not present) into
mp->set/sim/view[]/config/z-offset-m if not there. This attempts to mimic
what fgdata/defaults.xml does when it defines default views. Also copy
view[1]'s config into other views if not specified. */
double sim_chase_distance_m = -25;
if (set) {
sim_chase_distance_m = set->getDoubleValue("sim/chase-distance-m", sim_chase_distance_m);
}
else {
set = mp->_getProps()->addChild("set");
set->setDoubleValue("sim/chase-distance-m", sim_chase_distance_m);
}
SGPropertyNode* set_sim = set->getNode("sim");
/* For views that are similar to Helicopter View, copy across Helicopter View
target offsets if not specified. E.g. this allows Tower View AGL to work on
aircraft that don't know about it but need non-zero target-*-offset-m values
to centre the view on the middle of the aircraft.
This mimics what fgdata:Nasal/view.nas:manager does for the user aircraft's
views.
*/
SGPropertyNode* view_1 = set_sim->getNode("view", 1);
std::initializer_list<int> views_with_default_z_offset_m = {1, 2, 3, 5, 7, 8};
for (int j: views_with_default_z_offset_m) {
SGPropertyNode* v = set_sim->getChild("view", j);
if (!v) {
v = set_sim->addChild("view", j, false /*append*/);
}
SGPropertyNode* z_offset_m = v->getChild("config/z-offset-m");
if (!z_offset_m) {
v->setDoubleValue("config/z-offset-m", sim_chase_distance_m);
}
copy_default(view_1, "config/target-x-offset-m", v);
copy_default(view_1, "config/target-y-offset-m", v);
copy_default(view_1, "config/target-z-offset-m", v);
}
/* Create a node /ai/models/callsigns/<callsign> containing the index of the
callsign's aircraft's entry in /ai/models/multiplayer[]. This isn't strictly
necessary, but simpifies debugging a lot and seems pretty lightweight. Note
that we need to avoid special characters in the node name, otherwise the
property system forces a fatal error. */
std::string path = "/ai/models/callsigns/" + makeStringPropertyNameSafe(callsign);
globals->get_props()->setIntValue(path, mp->_getProps()->getIndex());
return mp;
}

View file

@ -68,6 +68,7 @@ public:
void SendTextMessage(const std::string &sMsgText);
// receiver
FGAIMultiplayer* getMultiplayer(const std::string& callsign);
private:
friend class MPPropertyListener;
@ -94,7 +95,6 @@ private:
FGAIMultiplayer* addMultiplayer(const std::string& callsign,
const std::string& modelName,
const int fallback_model_index);
FGAIMultiplayer* getMultiplayer(const std::string& callsign);
void FillMsgHdr(T_MsgHdr *MsgHdr, int iMsgId, unsigned _len = 0u);
void ProcessPosMsg(const MsgBuf& Msg, const simgear::IPAddress& SenderAddress,
long stamp);

View file

@ -347,6 +347,7 @@ bool FGScenerySwitchCallback::scenery_enabled = false;
FGRenderer::FGRenderer() :
_sky(NULL),
MaximumTextureSize(0),
_ambientFactor( new osg::Uniform( "fg_SunAmbientColor", osg::Vec4f() ) ),
_sunDiffuse( new osg::Uniform( "fg_SunDiffuseColor", osg::Vec4f() ) ),
_sunSpecular( new osg::Uniform( "fg_SunSpecularColor", osg::Vec4f() ) ),
@ -356,8 +357,7 @@ FGRenderer::FGRenderer() :
_fogDensity( new osg::Uniform( "fg_FogDensity", 0.0001f ) ),
_shadowNumber( new osg::Uniform( "fg_ShadowNumber", (int)4 ) ),
_shadowDistances( new osg::Uniform( "fg_ShadowDistances", osg::Vec4f(5.0, 50.0, 500.0, 5000.0 ) ) ),
_depthInColor( new osg::Uniform( "fg_DepthInColor", false ) ),
MaximumTextureSize(0)
_depthInColor( new osg::Uniform( "fg_DepthInColor", false ) )
{
// it's not the real root, whatever that means
_root = new osg::Group;

View file

@ -39,10 +39,13 @@
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Scenery/scenery.hxx>
#include <MultiPlayer/multiplaymgr.hxx>
#include <AIModel/AIMultiplayer.hxx>
#include "CameraGroup.hxx"
using namespace flightgear;
////////////////////////////////////////////////////////////////////////
// Implementation of FGViewer.
////////////////////////////////////////////////////////////////////////
@ -56,7 +59,9 @@ View::View( ViewType Type, bool from_model, int from_model_index,
double roll_offset_deg,
double fov_deg, double aspect_ratio_multiplier,
double target_x_offset_m, double target_y_offset_m,
double target_z_offset_m, double near_m, bool internal ):
double target_z_offset_m, double near_m, bool internal,
bool lookat_agl, double lookat_agl_damping,
int view_index ):
_dirty(true),
_roll_deg(0),
_pitch_deg(0),
@ -64,6 +69,7 @@ View::View( ViewType Type, bool from_model, int from_model_index,
_target_roll_deg(0),
_target_pitch_deg(0),
_target_heading_deg(0),
_lookat_agl_damping(lookat_agl_damping /*damping*/, 0 /*min*/, 0 /*max*/),
_scaling_type(FG_SCALING_MAX)
{
_absolute_view_pos = SGVec3d(0, 0, 0);
@ -73,8 +79,10 @@ View::View( ViewType Type, bool from_model, int from_model_index,
_at_model = at_model;
_at_model_index = at_model_index;
_internal = internal;
_internal = internal; _internal = true;
_lookat_agl = lookat_agl;
_view_index = view_index;
_dampFactor = SGVec3d::zeros();
_dampOutput = SGVec3d::zeros();
_dampTarget = SGVec3d::zeros();
@ -109,6 +117,8 @@ View::View( ViewType Type, bool from_model, int from_model_index,
}
_configFOV_deg = _fov_deg;
_fov_user_deg = _fov_deg;
_aspect_ratio_multiplier = aspect_ratio_multiplier;
_target_offset_m.x() = target_x_offset_m;
@ -120,7 +130,7 @@ View::View( ViewType Type, bool from_model, int from_model_index,
// a reasonable guess for init, so that the math doesn't blow up
}
View* View::createFromProperties(SGPropertyNode_ptr config)
View* View::createFromProperties(SGPropertyNode_ptr config, int view_index)
{
double aspect_ratio_multiplier
= fgGetDouble("/sim/current-view/aspect-ratio-multiplier");
@ -128,7 +138,9 @@ View* View::createFromProperties(SGPropertyNode_ptr config)
// find out if this is an internal view (e.g. in cockpit, low near plane)
// FIXME : should be a child of config
bool internal = config->getParent()->getBoolValue("internal", false);
std::string root = config->getPath();
// Will typically be /sim/view[]/config.
// FIXME:
// this is assumed to be an aircraft model...we will need to read
@ -166,6 +178,8 @@ View* View::createFromProperties(SGPropertyNode_ptr config)
double target_x_offset_m = config->getDoubleValue("target-x-offset-m");
double target_y_offset_m = config->getDoubleValue("target-y-offset-m");
double target_z_offset_m = config->getDoubleValue("target-z-offset-m");
bool lookat_agl = config->getBoolValue("lookat-agl");
double lookat_agl_damping = config->getDoubleValue("lookat-agl-damping");
v = new View ( FG_LOOKAT, from_model, from_model_index,
at_model, at_model_index,
@ -174,7 +188,8 @@ View* View::createFromProperties(SGPropertyNode_ptr config)
heading_offset_deg, pitch_offset_deg,
roll_offset_deg, fov_deg, aspect_ratio_multiplier,
target_x_offset_m, target_y_offset_m,
target_z_offset_m, near_m, internal );
target_z_offset_m, near_m, internal, lookat_agl,
lookat_agl_damping, view_index );
if (!from_model) {
v->_targetProperties.init(config, "target-");
}
@ -184,7 +199,7 @@ View* View::createFromProperties(SGPropertyNode_ptr config)
x_offset_m, y_offset_m, z_offset_m,
heading_offset_deg, pitch_offset_deg,
roll_offset_deg, fov_deg, aspect_ratio_multiplier,
0, 0, 0, near_m, internal );
0, 0, 0, near_m, internal, false, 0.0, view_index );
}
if (!from_model) {
@ -194,6 +209,7 @@ View* View::createFromProperties(SGPropertyNode_ptr config)
v->_name = config->getParent()->getStringValue("name");
v->_typeString = type;
v->_configHeadingOffsetDeg = config->getDoubleValue("default-heading-offset-deg");
v->_config = config;
return v;
}
@ -251,7 +267,7 @@ View::bind ()
_tiedProperties.Tie("field-of-view", this,
&View::get_fov, &View::set_fov,
&View::get_fov_user, &View::set_fov_user,
false);
fgSetArchivable("/sim/current-view/field-of-view");
@ -270,12 +286,12 @@ View::bind ()
_tiedProperties.Tie("viewer-lat-deg", this, &View::getLat_deg);
_tiedProperties.Tie("viewer-elev-ft", this, &View::getElev_ft);
_tiedProperties.Tie("x-offset-m", this, &View::getXOffset_m,
&View::setXOffset_m, false);
_tiedProperties.Tie("y-offset-m", this, &View::getYOffset_m,
&View::setYOffset_m, false);
_tiedProperties.Tie("z-offset-m", this, &View::getZOffset_m,
&View::setZOffset_m, false);
_tiedProperties.Tie("x-offset-m", this, &View::getAdjustXOffset_m,
&View::setAdjustXOffset_m, false);
_tiedProperties.Tie("y-offset-m", this, &View::getAdjustYOffset_m,
&View::setAdjustYOffset_m, false);
_tiedProperties.Tie("z-offset-m", this, &View::getAdjustZOffset_m,
&View::setAdjustZOffset_m, false);
_tiedProperties.Tie("target-x-offset-m", this, &View::getTargetXOffset_m,
&View::setTargetXOffset_m, false);
@ -340,6 +356,7 @@ void View::resetOffsetsAndFOV()
{
_target_offset_m = _configTargetOffset_m;
_offset_m = _configOffset_m;
_adjust_offset_m = SGVec3d();
_pitch_offset_deg = _configPitchOffsetDeg;
_heading_offset_deg = _configHeadingOffsetDeg;
_roll_offset_deg = _configRollOffsetDeg;
@ -477,6 +494,27 @@ View::setTargetZOffset_m (double target_z_offset_m)
_target_offset_m.z() = target_z_offset_m;
}
void
View::setAdjustXOffset_m (double x_offset_m)
{
_dirty = true;
_adjust_offset_m.x() = x_offset_m;
}
void
View::setAdjustYOffset_m (double y_offset_m)
{
_dirty = true;
_adjust_offset_m.y() = y_offset_m;
}
void
View::setAdjustZOffset_m (double z_offset_m)
{
_dirty = true;
_adjust_offset_m.z() = z_offset_m;
}
void
View::setPositionOffsets (double x_offset_m, double y_offset_m, double z_offset_m)
{
@ -605,24 +643,154 @@ View::recalc ()
set_clean();
}
/* Gets position and orientation of user aircraft or multiplayer aircraft.
root:
Location of aircraft position and oriention properties. Use '' for user's
aircraft, or /ai/models/multiplayer[] for a multiplayer aircraft.
position:
head:
pitch:
roll:
Out parameters.
*/
static void getAircraftPositionOrientation(
const std::string& root,
SGGeod& position,
double& head,
double& pitch,
double& roll
)
{
position = SGGeod::fromDegFt(
globals->get_props()->getDoubleValue(root + "/position/longitude-deg"),
globals->get_props()->getDoubleValue(root + "/position/latitude-deg"),
globals->get_props()->getDoubleValue(root + "/position/altitude-ft")
);
head = globals->get_props()->getDoubleValue(root + "/orientation/true-heading-deg");
pitch = globals->get_props()->getDoubleValue(root + "/orientation/pitch-deg");
roll = globals->get_props()->getDoubleValue(root + "/orientation/roll-deg");
}
/* Finds the offset of a view position from an aircraft model's origin.
We look in either /sim/view[]/config/ for user's aircraft, or
/ai/models/multiplayer[]/set/sim/view[]/config/ for multiplayer aircraft.
If offset information is not available because we can't find the -set.xml files
for a multiplayer aircraft), we set all elements of offset_m to zero.
root:
Path that defines which aircraft. Either '' to use the user's aircraft, or
/ai/models/multiplayer[].
view_index:
The view number. We will look in .../sim/view[view_index].
infix:
We look at .../view[view_index]/config/<infix>x-offset-m etc. Views appear
to use 'z-offset-m' to define the location of pilot's eyes in cockpit
view, but 'target-z-offset' when defining viewpoint of other views such as
Helicopter View. So one should typically set <infix> to '' or 'target-'.
adjust:
Added on to the return value. Can be used to add in the affects of the user
adjusting the view position.
offset_m:
Out param.
Returns true if any component (x, y or z) was found, otherwise false.
*/
static bool getViewOffsets(
const std::string& root,
int view_index,
const std::string& infix,
const SGVec3d& adjust,
SGVec3d& offset_m
)
{
SGPropertyNode* sim = NULL;
if (root == "") {
/* View offsets are in /sim/view[]/config/. */
sim = globals->get_props()->getNode("sim");
}
else {
/* View offsets are in /ai/models/multiplayer[]/set/sim/view[]/config/. We assume
that <root> is /ai/models/multiplayer[]. */
std::string callsign = globals->get_props()->getStringValue(root + "/callsign");
FGAIMultiplayer* multiplayer = globals->get_subsystem<FGMultiplayMgr>()->getMultiplayer(callsign);
if (multiplayer) {
sim = multiplayer->getPropertyRoot()->getNode("set/sim");
}
}
bool found_any = false;
if (sim) {
/* Most (all?) -set.xml files don't define an offset for view 7, Model
View, so we instead use the offsets for view 1, Helicopter View. */
SGPropertyNode* sim_view = sim->getNode("view", view_index == 7 ? 1 : view_index);
offset_m.x() = sim_view->getDoubleValue("config/" + infix + "x-offset-m", 0);
offset_m.y() = sim_view->getDoubleValue("config/" + infix + "y-offset-m", 0);
offset_m.z() = sim_view->getDoubleValue("config/" + infix + "z-offset-m", 0);
}
else
{
offset_m = SGVec3d::zeros();
}
offset_m += adjust;
return found_any;
}
// recalculate for LookFrom view type...
// E.g. Cockpit View and Tower View Look From.
void
View::recalcLookFrom ()
{
// Update location data ...
if ( _from_model ) {
_position = globals->get_aircraft_position();
globals->get_aircraft_orientation(_heading_deg, _pitch_deg, _roll_deg);
std::string root = std::string(_config->getStringValue("root"));
if (root == "/") root = "";
/* <root> will be either '' which means we are viewing the user's aircraft,
or /ai/models/multiplayer[] which means we are viewing a multiplayer
aircraft. */
double head;
double pitch;
double roll;
if (_from_model ) {
/* Look up aircraft position; this works for user's aircaft or multiplayer
aircraft. */
getAircraftPositionOrientation( root, _position, head, pitch, roll);
}
double head = _heading_deg;
double pitch = _pitch_deg;
double roll = _roll_deg;
if ( !_from_model ) {
// update from our own data...
setDampTarget(roll, pitch, head);
getDampOutput(roll, pitch, head);
else {
/* Tower View Look From. This isn't multiplayer, so <root> will be ''.
<_config> will be /sim/view[4]/config, so <_config>/eye-lon-deg-path
will be /sim/view[4]/config/eye-lon-deg-path which will contain
/sim/tower/longitude-deg (see fgdata:defaults.xml).
<root> will be '' so we'll end up looking at /sim/tower/longitude-deg
[Might be nice to make a 'TowerView Look From Multiplayer' that uses the
tower that is nearest to a particular multiplayer aircraft. We'd end up
looking at /ai/models/multiplayer[]/sim/tower/longitude-deg, so we'd need
to somehow set up /ai/models/multiplayer[]/sim/tower. ]
*/
_position = SGGeod::fromDegFt(
globals->get_props()->getDoubleValue(root + "/" + _config->getStringValue("eye-lon-deg-path")),
globals->get_props()->getDoubleValue(root + "/" + _config->getStringValue("eye-lat-deg-path")),
globals->get_props()->getDoubleValue(root + "/" + _config->getStringValue("eye-alt-ft-path"))
);
head = globals->get_props()->getDoubleValue(root + "/" + _config->getStringValue("eye-heading-deg-path"));
pitch = globals->get_props()->getDoubleValue(root + "/" + _config->getStringValue("eye-pitch-deg-path"));
roll = globals->get_props()->getDoubleValue(root + "/" + _config->getStringValue("eye-roll-deg-path"));
}
/* Find the offset of the view position relative to the aircraft model's
origin. */
SGVec3d offset_m;
getViewOffsets(root, _view_index, "" /*infix*/, _adjust_offset_m, offset_m);
set_fov(_fov_user_deg);
// The rotation rotating from the earth centerd frame to
// the horizontal local frame
@ -646,41 +814,204 @@ View::recalcLookFrom ()
// 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);
_absolute_view_pos = position + (ec2body*q).backTransform(offset_m);
mViewOrientation = ec2body*mViewOffsetOr*q;
}
/* Change angle and field of view so that we can see the aircraft and the
ground immediately below it. */
/* Some aircraft appear to have elevation that is slightly below ground level
when on the ground, e.g. SenecaII, which makes get_elevation_m() fail. So we
pass a slightly incremented elevation. */
void View::handleAGL(const std::string& root)
{
/* Change angle and field of view so that we can see the aircraft and the
ground immediately below it. */
/* Some aircraft appear to have elevation that is slightly below ground
level when on the ground, e.g. SenecaII, which makes get_elevation_m()
fail. So we pass a slightly incremented elevation. */
double ground_altitude = 0;
const simgear::BVHMaterial* material = NULL;
SGGeod target_plus = _target;
target_plus.setElevationM(target_plus.getElevationM() + 1);
bool ok = globals->get_scenery()->get_elevation_m(target_plus, ground_altitude, &material);
if (!ok)
{
/* Just in case get_elevation_m() fails, we may as well use sea level. */
ground_altitude = 0;
SG_LOG(SG_FLIGHT, SG_DEBUG, "get_elevation_m() failed. _target=" << _target << "\n");
}
double h_distance = SGGeodesy::distanceM(_position, _target);
if (h_distance == 0) {
/* Not sure this should ever happen, but we need to handle this here
otherwise we'll get divide-by-zero. Just use whatever field of view the
user has set. */
set_fov(_fov_user_deg);
}
else {
/* Find vertical region we want to be able to see. */
double relative_height_target = _target.getElevationM() - _position.getElevationM();
double relative_height_ground = ground_altitude - _position.getElevationM();
/* We expand the field of view so that it hopefully shows the
whole aircraft and a little more of the ground.
We use chase-distance as a crude measure of the aircraft's size. There
doesn't seem to be any more definitive information.
We damp our measure of ground level, to avoid the view jumping around if
an aircraft flies over buildings.
*/
relative_height_ground -= 2;
double chase_distance_m;
if (root == "") {
chase_distance_m = globals->get_props()->getDoubleValue("sim/chase-distance-m");
}
else {
chase_distance_m = globals->get_props()->getDoubleValue(root + "/set/sim/chase-distance-m");
if (chase_distance_m == 0) chase_distance_m = 50;
}
double aircraft_size_vertical = fabs(chase_distance_m) * 0.3;
double aircraft_size_horizontal = fabs(chase_distance_m) * 0.9;
double relative_height_target_plus = relative_height_target + aircraft_size_vertical;
double relative_height_ground_ = relative_height_ground;
_lookat_agl_damping.updateTarget(relative_height_ground);
if (relative_height_ground > relative_height_target) {
/* Damping of relative_height_ground can result in it being
temporarily above the aircraft, so we ensure the aircraft is visible.
*/
relative_height_ground = relative_height_ground_;
}
double angle_v_target = atan(relative_height_target_plus / h_distance);
double angle_v_ground = atan(relative_height_ground / h_distance);
/* The target we want to use is determined by the midpoint of the two
angles we've calculated. */
double angle_v_mid = (angle_v_target + angle_v_ground) / 2;
_target.setElevationM(_position.getElevationM() + h_distance * tan(angle_v_mid));
/* Set field of view. We use fabs to avoid things being upside down if
target is below ground level (e.g. new multiplayer aircraft are briefly
at -9999ft). */
double fov_v = fabs(angle_v_target - angle_v_ground);
double fov_v_deg = fov_v / 3.1415 * 180;
set_fov( fov_v_deg);
/* Ensure that we can see entire horizontal extent of the aircraft
(assuming airplane is horizontal), and also correct things if display
aspect ratio results in set_fov() not setting the vertical field of view
that we want. */
double fov_h;
fov_h = 2 * atan(aircraft_size_horizontal / 2 / h_distance);
double fov_h_deg = fov_h / 3.1415 * 180;
double correction_v = fov_v_deg / get_v_fov();
double correction_h = fov_h_deg / get_h_fov();
double correction = std::max(correction_v, correction_h);
if (correction > 1) {
fov_v_deg *= correction;
}
/* Apply scaling from user field of view setting. */
fov_v_deg *= _fov_user_deg / _configFOV_deg;
set_fov(fov_v_deg);
SG_LOG(SG_FLIGHT, SG_DEBUG, ""
<< " _position=" << _position
<< " _target=" << _target
<< " ground_altitude=" << ground_altitude
<< " relative_height_target_plus=" << relative_height_target_plus
<< " relative_height_ground=" << relative_height_ground
);
}
}
/* Views of an aircraft e.g. Helicopter View. */
void
View::recalcLookAt ()
{
// The geodetic position of our target to look at
std::string root = std::string(_config->getStringValue("root"));
if (root == "/") root = "";
/* <root> will be either '' which means we are viewing the user's aircraft,
or /ai/models/multiplayer[] which means we are viewing a multiplayer
aircraft. */
/* 2019-06-12: i think maybe all LookAt views have _at_model=true? */
if ( _at_model ) {
_target = globals->get_aircraft_position();
globals->get_aircraft_orientation(_target_heading_deg,
_target_pitch_deg,
_target_roll_deg);
} else {
// if not model then calculate our own target position...
setDampTarget(_target_roll_deg, _target_pitch_deg, _target_heading_deg);
getDampOutput(_target_roll_deg, _target_pitch_deg, _target_heading_deg);
getAircraftPositionOrientation(
root,
_target,
_target_heading_deg,
_target_pitch_deg,
_target_roll_deg
);
}
double eye_heading;
double eye_roll;
double eye_pitch;
{
SGPropertyNode* sim = globals->get_props()->getNode("sim");
SGPropertyNode* sim_view = sim->getNode("view", _view_index);
eye_heading = globals->get_props()->getDoubleValue(root + sim_view->getStringValue("config/eye-heading-deg-path"));
eye_roll = globals->get_props()->getDoubleValue(root + sim_view->getStringValue("config/eye-roll-deg-path"));
eye_pitch = globals->get_props()->getDoubleValue(root + sim_view->getStringValue("config/eye-pitch-deg-path"));
setDampTarget(eye_roll, eye_pitch, eye_heading);
getDampOutput(eye_roll, eye_pitch, eye_heading);
}
SGQuatd geodTargetOr = SGQuatd::fromYawPitchRollDeg(_target_heading_deg,
_target_pitch_deg,
_target_roll_deg);
SGQuatd geodTargetHlOr = SGQuatd::fromLonLat(_target);
SGVec3d target_pos_off;
getViewOffsets(root, _view_index, "target-", SGVec3d(), target_pos_off);
target_pos_off = SGVec3d(
-target_pos_off.z(),
target_pos_off.x(),
-target_pos_off.y()
);
target_pos_off = (geodTargetHlOr*geodTargetOr).backTransform(target_pos_off);
if ( _from_model ) {
_position = globals->get_aircraft_position();
globals->get_aircraft_orientation(_heading_deg, _pitch_deg, _roll_deg);
} else {
// update from our own data, just the rotation here...
setDampTarget(_roll_deg, _pitch_deg, _heading_deg);
getDampOutput(_roll_deg, _pitch_deg, _heading_deg);
SGVec3d targetCart = SGVec3d::fromGeod(_target);
SGVec3d targetCart2 = targetCart + target_pos_off;
SGGeodesy::SGCartToGeod(targetCart2, _target);
_position = _target;
{
const char* eye_lon_deg_path = _config->getStringValue("eye-lon-deg-path");
const char* eye_lat_deg_path = _config->getStringValue("eye-lat-deg-path");
const char* eye_alt_ft_path = _config->getStringValue("eye-alt-ft-path");
if (eye_lon_deg_path && eye_lon_deg_path[0]) _position.setLongitudeDeg(globals->get_props()->getDoubleValue(eye_lon_deg_path));
if (eye_lat_deg_path && eye_lon_deg_path[0]) _position.setLatitudeDeg(globals->get_props()->getDoubleValue(eye_lat_deg_path));
if (eye_alt_ft_path && eye_alt_ft_path[0]) _position.setElevationFt(globals->get_props()->getDoubleValue(eye_alt_ft_path));
}
SGQuatd geodEyeOr = SGQuatd::fromYawPitchRollDeg(_heading_deg, _pitch_deg, _roll_deg);
if (_lookat_agl) {
handleAGL(root);
}
else {
set_fov(_fov_user_deg);
}
SGQuatd geodEyeOr = SGQuatd::fromYawPitchRollDeg(eye_heading, eye_pitch, eye_roll);
SGQuatd geodEyeHlOr = SGQuatd::fromLonLat(_position);
// the rotation offset, don't know why heading is negative here ...
@ -689,6 +1020,7 @@ View::recalcLookAt ()
_roll_offset_deg);
// Offsets to the eye position
getViewOffsets(root, _view_index, "", _adjust_offset_m, _offset_m);
SGVec3d eyeOff(-_offset_m.z(), _offset_m.x(), -_offset_m.y());
SGQuatd ec2eye = geodEyeHlOr*geodEyeOr;
SGVec3d eyeCart = SGVec3d::fromGeod(_position);
@ -697,12 +1029,6 @@ View::recalcLookAt ()
SGVec3d atCart = SGVec3d::fromGeod(_target);
// add target offsets to at_position...
SGVec3d target_pos_off(-_target_offset_m.z(), _target_offset_m.x(),
-_target_offset_m.y());
target_pos_off = (geodTargetHlOr*geodTargetOr).backTransform(target_pos_off);
atCart += target_pos_off;
eyeCart += target_pos_off;
// Compute the eyepoints orientation and position
// wrt the earth centered frame - that is global coorinates
_absolute_view_pos = eyeCart;
@ -768,6 +1094,59 @@ View::updateDampOutput(double dt)
} // of dt subdivision by interval
}
View::Damping::Damping(double damping, double min, double max)
: _id(NULL), _min(min), _max(max), _target(0), _factor(pow(10, -damping)), _current(0)
{
}
void View::Damping::setTarget(double target)
{
_target = target;
}
void View::Damping::update(double dt, void* id)
{
if (id != _id || dt > 1.0) {
_current = _target;
_id = id;
return;
}
const double interval = 0.01;
while (dt > interval) {
if (_factor <= 0.0) {
// un-damped, set output to target directly
_current = _target;
continue;
}
double d = _current - _target;
if (_max > _min) {
if (d > _max) {
_current -= (_max - _min);
} else if (d < _min) {
_current += (_max - _min);
}
}
_current = (_target * _factor) + _current * (1.0 - _factor);
dt -= interval;
}
}
double View::Damping::get()
{
return _current;
}
void View::Damping::updateTarget(double& io)
{
setTarget(io);
io = get();
}
double
View::get_h_fov()
{
@ -821,37 +1200,12 @@ View::get_v_fov()
return 0.0;
}
void
View::updateData()
{
if (!_from_model) {
SGGeod pos = _eyeProperties.position();
SGVec3d att = _eyeProperties.attitude();
setPosition(pos);
setOrientation(att[2], att[1], att[0]);
} else {
set_dirty();
}
// if lookat (type 1) then get target data...
if (getType() == FG_LOOKAT) {
if (!_from_model) {
SGGeod pos = _targetProperties.position();
SGVec3d att = _targetProperties.attitude();
setTargetPosition(pos);
setTargetOrientation(att[2], att[1], att[0]);
} else {
set_dirty();
}
}
}
void
View::update (double dt)
{
updateData();
recalc();
recalc();
updateDampOutput(dt);
_lookat_agl_damping.update(dt, NULL /*id*/);
int i;
int dt_ms = int(dt * 1000);

View file

@ -58,7 +58,9 @@ public:
FG_LOOKAT = 1
};
static View* createFromProperties(SGPropertyNode_ptr props);
// view_index is to allow us to look up corresponding view information for
// multiplayer aircraft.
static View* createFromProperties(SGPropertyNode_ptr props, int view_index=-1);
// Destructor
virtual ~View();
@ -129,6 +131,12 @@ public:
void setPositionOffsets (double x_offset_m,
double y_offset_m,
double z_offset_m);
double getAdjustXOffset_m () const { return _adjust_offset_m.x(); }
double getAdjustYOffset_m () const { return _adjust_offset_m.y(); }
double getAdjustZOffset_m () const { return _adjust_offset_m.z(); }
void setAdjustXOffset_m (double x_adjust_offset_m);
void setAdjustYOffset_m (double y_adjust_offset_m);
void setAdjustZOffset_m (double z_adjust_offset_m);
// Reference orientation rotations...
// These are rotations that represent the plane attitude effect on
@ -196,10 +204,10 @@ private:
double roll_offset_deg,
double fov_deg, double aspect_ratio_multiplier,
double target_x_offset_m, double target_y_offset_m,
double target_z_offset_m, double near_m, bool internal );
double target_z_offset_m, double near_m, bool internal,
bool lookat_agl, double lookat_agl_damping, int view_index );
void set_clean() { _dirty = false; }
void updateData();
void setHeadingOffset_deg_property (double heading_offset_deg);
void setPitchOffset_deg_property(double pitch_offset_deg);
@ -259,6 +267,7 @@ private:
void setTargetHeading_deg (double heading_deg);
void setTargetOrientation (double roll_deg, double pitch_deg, double heading_deg);
void handleAGL(const std::string& root);
// Orientation offsets rotations from reference orientation.
// Goal settings are for smooth transition from prior
@ -297,10 +306,14 @@ private:
_fov_deg = fov_deg;
}
double get_fov_user() const { return _fov_user_deg; }
void set_fov_user( double fov_deg ) { _fov_user_deg = fov_deg; }
//////////////////////////////////////////////////////////////////
// private data //
//////////////////////////////////////////////////////////////////
SGPropertyNode_ptr _config;
std::string _name, _typeString;
// flag forcing a recalc of derived view parameters
@ -329,12 +342,34 @@ private:
SGVec3d _dampTarget; ///< current target value we are damping towards
SGVec3d _dampOutput; ///< current output of damping filter
SGVec3d _dampFactor; ///< weighting of the damping filter
/* Generic damping support. */
struct Damping {
Damping(double factor, double min, double max);
void setTarget(double target);
void update(double dt, void* id);
double get();
void updateTarget(double& io);
private:
void* _id;
double _min;
double _max;
double _target;
double _factor;
double _current;
};
Damping _lookat_agl_damping;
// Position offsets from FDM origin. The X axis is positive
// out the tail, Y is out the right wing, and Z is positive up.
// distance in meters
SGVec3d _offset_m;
SGVec3d _configOffset_m;
SGVec3d _adjust_offset_m;
// Target offsets from FDM origin (for "lookat" targets) The X
// axis is positive out the tail, Y is out the right wing, and Z
@ -358,6 +393,12 @@ private:
// internal view (e.g. cockpit) flag
bool _internal;
// Dynamically update view angle and field of view so that we always
// include the target and the ground below it.
bool _lookat_agl;
int _view_index;
// view is looking from a model
bool _from_model;
@ -366,6 +407,10 @@ private:
// view is looking at a model
bool _at_model;
int _at_model_index; // number of model (for multi model)
// Field of view as requested by user. Usually copied directly into the
// actual field of view, except for Tower AGL view.
double _fov_user_deg;
// the nominal field of view (angle, in degrees)
double _fov_deg;

View file

@ -62,7 +62,7 @@ FGViewMgr::init ()
SGPropertyNode *n = config_list[i];
SGPropertyNode *config = n->getChild("config", 0, true);
flightgear::View* v = flightgear::View::createFromProperties(config);
flightgear::View* v = flightgear::View::createFromProperties(config, n->getIndex());
if (v) {
add_view(v);
}