Allow most default views to work on multiplayer aircraft as well as the user's aircraft.
Currently this works for all the default views except for Tower Look From (for which it doesn't really make sense) and Fly-by View. We now search for and load a -set.xml that matches the model .xml, when new multiplayer aircraft is set up. This allows us to find view offsets etc, e.g. allowing cockpit and helicopter views to work with multiplayer aircraft. Properties from the -set.xml are placed into /ai/models/multiplayer[]/set, so for example viewing offsets are in /ai/models/multiplayer[]/set/sim/view[]/config/target-{x,y,z}-offset-m. We also copy the aircraft's chase-distance into the view config params, similar to how fgdata:defaults.xml does for the user's aircraft. And we also fill in views' missing offsets from the Helicopter View config; e.g. this enables the new Tower View AGL to show aircraft correctly centred, despite aircraft currently not having this view defined in their -set.xml. [We don't currently attempt to cache or reuse -set.xml data.] Have ensured that view position responds to mouse movement in the same way for viewing the user's aircraft as for multiplayer aircraft (previously, Model View reversed the affect of vertical mouse movements). Added new Tower AGL view. Behaves similarly to Tower view, but automatically scales and pans vertically in order to always show the vertical range extending from just above the aircraft down to the ground immediately below the aircraft. We use aircrafts chase-distance as an indication of size. We damp the ground level value to reduce the viewing jumping around too much e.g. if the aircraft flies over buildings. The amount of damping is set by fgdata:defaults.xml's lookat-agl-damping value. Fixed problem where Tower View eye position moves slightly as target aircraft heading changes. This was caused by us unnecessarily applying the aircraft-centre correction to the eye position. src/FDM/flight.cxx: also make /orientation/true-heading-deg. This allows local orientation to be used like multiplayer orientation, which only has /ai/models/multiplayer[]/orientation/true-heading-deg. [A better fix might be to replace all occurrencies of /orientation/true-heading-deg with /orientation/heading-deg, but this would be a rather large commit.] Details: src/Viewer/view.*: removed View::updateData() as is no longer required. src/MultiPlayer/multiplaymgr.cxx: use helicopter view target offsets as defaults. E.g. in tower view agl, aircraft won't currently be defining these offsets. More generally, this allows aircraft to define target offsets only in helicoter view. src/FDM/flight.cxx FGMultiplayMgr::FillMsgHdr(): Added tie of /orientation/true-heading-deg to get_Psi_deg, so it duplictes the existing /orientation/heading-deg. src/MultiPlayer/multiplaymgr.cxx src/MultiPlayer/multiplaymgr.hxx FGMultiplayMgr::addMultiplayer(): look for and load -set.xml that matches the model. Patch various view-related things up in similar way to what we do for the user's aircraft, so that multiplayer views work. Made FGMultiplayMgr::getMultiplayer() public, so it can be used by src/Viewer/view.cxx. src/Viewer/view.cxx src/Viewer/view.hxx View::View(): Added lookat_agl, lookat_agl_damping params for new Tower View AGL. Preserve user's field-of-view in separate variable so that Tower View AGL can modify the actual field of view independently. Added view_index param so that we can find multiplayer view[]/config/ properties. getViewOffsets(): new fn that finds view offsets for user aircraft or multiplayer aircraft. View::recalcLookFrom() View::recalcLookAt: Lots of changes to allow things to work with multiplayer aircraft. View::recalcLookAt() can now do Tower View AGL. View::Damping: support for damping, used by Tower View AGL. Might be good to use this for other damping. View::updateData(): removed, as calculations are now all done inside View::recalc*(). put properties {x,y,z}-offset-m into new View::_adjust_offset_m member instead of _offset_m. This avoids confusion between a view's offsets and the offsets added in by the user via the 'Adjust View Position' dialogue. src/Viewer/viewmgr.cxx FGViewMgr::init(): pass view number to flightgear::View::createFromProperties().
This commit is contained in:
parent
34a6cb3c74
commit
5772325bc6
6 changed files with 667 additions and 85 deletions
|
@ -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,
|
||||
|
|
|
@ -37,9 +37,11 @@
|
|||
#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>
|
||||
|
||||
|
@ -2316,6 +2318,44 @@ FGMultiplayMgr::FillMsgHdr(T_MsgHdr *MsgHdr, int MsgId, unsigned _len)
|
|||
MsgHdr->Callsign[MAX_CALLSIGN_LEN - 1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static bool starts_with(const std::string& s, const std::string& prefix)
|
||||
{
|
||||
return s.substr(0, prefix.size()) == prefix;
|
||||
}
|
||||
static bool ends_with(const std::string& s, const std::string& suffix)
|
||||
{
|
||||
return s.substr(s.size()-suffix.size()) == suffix;
|
||||
}
|
||||
|
||||
/* Outputs a SGPropertyNode to a stream, for debugging. */
|
||||
static std::ostream& output(std::ostream& s, const SGPropertyNode& node, std::string prefix="")
|
||||
{
|
||||
s << prefix << node.getName() << ": "
|
||||
<< "index=" << node.getIndex() << ": "
|
||||
<< "position=" << node.getPosition() << ": "
|
||||
;
|
||||
node.printOn(s);
|
||||
s << "\n";
|
||||
|
||||
for (int i=0; i<node.nChildren(); ++i) {
|
||||
const SGPropertyNode* node_child = node.getChild(i);
|
||||
output(s, *node_child, prefix + " ");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FGAIMultiplayer*
|
||||
FGMultiplayMgr::addMultiplayer(const std::string& callsign,
|
||||
const std::string& modelName,
|
||||
|
@ -2338,7 +2378,138 @@ 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 (ends_with(modelName, ".xml") && 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;
|
||||
std::string model_file_head;
|
||||
std::string model_file_tail;
|
||||
std::string aircraft_dir;
|
||||
PathList::const_iterator it;
|
||||
for ( it = dirs.begin(); it != dirs.end(); ++it) {
|
||||
model_file = *it;
|
||||
model_file.append(tail);
|
||||
if (model_file.exists()) {
|
||||
model_file_head = it->str() + '/';
|
||||
model_file_tail = model_file.str().substr(model_file_head.size());
|
||||
ssize_t p = model_file_tail.find('/');
|
||||
aircraft_dir = model_file_head + model_file_tail.substr(0, p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it == dirs.end()) {
|
||||
/* We failed to find model file. */
|
||||
}
|
||||
else {
|
||||
/* 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. */
|
||||
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 (size_t i=0; i<dir_contents.size(); ++i) {
|
||||
set = mp->_getProps()->addChild("set");
|
||||
bool ok = true;
|
||||
try {
|
||||
readProperties(dir_contents[i], 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);
|
||||
int views_with_default_z_offset_m[] = {1, 2, 3, 5, 7, 8};
|
||||
for (unsigned i=0;
|
||||
i != sizeof(views_with_default_z_offset_m)/sizeof(views_with_default_z_offset_m[0]);
|
||||
++i) {
|
||||
int j = views_with_default_z_offset_m[i];
|
||||
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/";
|
||||
for (size_t i=0; i<callsign.size(); ++i) {
|
||||
char c = callsign[i];
|
||||
if (i==0 && !isalpha(c) && c!='_') c = '_';
|
||||
if (!isalnum(c) && c!='.' && c!='_' && c!='-') c = '_';
|
||||
path += c;
|
||||
}
|
||||
globals->get_props()->setIntValue( path, mp->_getProps()->getIndex());
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,176 @@ View::recalc ()
|
|||
set_clean();
|
||||
}
|
||||
|
||||
static FGAIMultiplayer* get_multiplayer(const std::string& callsign)
|
||||
{
|
||||
return dynamic_cast<FGMultiplayMgr*>(globals->get_subsystem("mp"))->getMultiplayer(callsign);
|
||||
}
|
||||
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
|
||||
/* If <node>/<relative_path> exists, set <found>=true and return as a
|
||||
double. Otherwise leaves <found> unchanged and returns <default_value). */
|
||||
static double getDoubleValue(
|
||||
SGPropertyNode* node,
|
||||
const std::string& relative_path,
|
||||
double default_value,
|
||||
bool& found
|
||||
)
|
||||
{
|
||||
node = node->getNode(relative_path);
|
||||
if (!node) return default_value;
|
||||
found = true;
|
||||
return node->getDoubleValue();
|
||||
}
|
||||
|
||||
/* 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 = get_multiplayer(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() = getDoubleValue(sim_view, "config/" + infix + "x-offset-m", 0, found_any);
|
||||
offset_m.y() = getDoubleValue(sim_view, "config/" + infix + "y-offset-m", 0, found_any);
|
||||
offset_m.z() = getDoubleValue(sim_view, "config/" + infix + "z-offset-m", 0, found_any);
|
||||
}
|
||||
else
|
||||
{
|
||||
offset_m.x() = 0;
|
||||
offset_m.y() = 0;
|
||||
offset_m.z() = 0;
|
||||
}
|
||||
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 +836,191 @@ 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;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
/* 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.setGet(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
|
||||
);
|
||||
}
|
||||
}
|
||||
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 +1029,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 +1038,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 +1103,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::setGet(double& io)
|
||||
{
|
||||
setTarget(io);
|
||||
io = get();
|
||||
}
|
||||
|
||||
double
|
||||
View::get_h_fov()
|
||||
{
|
||||
|
@ -821,37 +1209,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);
|
||||
|
|
|
@ -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);
|
||||
|
@ -297,10 +305,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 +341,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 setGet(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 +392,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 +406,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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue