first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
include(FlightGearComponent)
set(SOURCES
cockpitDisplayManager.cxx
panel.cxx
panel_io.cxx
built_in/FGMagRibbon.cxx
agradar.cxx
groundradar.cxx
od_gauge.cxx
render_area_2d.cxx
wxradar.cxx
NavDisplay.cxx
)
set(HEADERS
cockpitDisplayManager.hxx
panel.hxx
panel_io.hxx
built_in/FGMagRibbon.hxx
agradar.hxx
groundradar.hxx
od_gauge.hxx
render_area_2d.hxx
wxradar.hxx
NavDisplay.hxx
)
flightgear_component(Cockpit "${SOURCES}" "${HEADERS}")

1461
src/Cockpit/NavDisplay.cxx Normal file

File diff suppressed because it is too large Load Diff

211
src/Cockpit/NavDisplay.hxx Normal file
View File

@@ -0,0 +1,211 @@
// Wx Radar background texture
//
// Written by Harald JOHNSEN, started May 2005.
// With major amendments by Vivian MEAZZA May 2007
// Ported to OSG by Tim MOORE Jun 2007
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef _INST_ND_HXX
#define _INST_ND_HXX
#include <osg/ref_ptr>
#include <osg/Geode>
#include <osg/Texture2D>
#include <osgText/Text>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <vector>
#include <string>
#include <memory>
#include <Navaids/positioned.hxx>
class FGODGauge;
class FGRouteMgr;
class FGNavRecord;
class SymbolInstance;
class SymbolDef;
class SymbolRule;
namespace flightgear
{
class Waypt;
}
typedef std::set<std::string> string_set;
typedef std::vector<SymbolRule*> SymbolRuleVector;
typedef std::vector<SymbolDef*> SymbolDefVector;
class NavDisplay : public SGSubsystem
{
public:
NavDisplay(SGPropertyNode *node);
virtual ~NavDisplay();
// Subsystem API.
void init() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "navigation-display"; }
void invalidatePositionedCache()
{
_cachedItemsValid = false;
}
double textureSize() const
{ return _textureSize; }
void forceUpdate()
{ _forceUpdate = true; }
bool anyRuleForType(const std::string& type) const;
bool isPositionedShown(FGPositioned* pos);
protected:
std::string _name;
int _num;
double _time;
double _updateInterval;
bool _forceUpdate;
SGPropertyNode_ptr _serviceable_node;
SGPropertyNode_ptr _Instrument;
SGPropertyNode_ptr _radar_mode_control_node;
SGPropertyNode_ptr _user_heading_node;
SGPropertyNode_ptr _testModeNode;
SGPropertyNode_ptr _userLatNode, _userLonNode, _userPositionEnable;
FGODGauge *_odg;
// Convenience function for creating a property node with a
// default value
template<typename DefaultType>
SGPropertyNode *getInstrumentNode(const char *name, DefaultType value);
private:
friend class SymbolRule;
friend class SymbolDef;
void addRule(SymbolRule*);
void addSymbolsToScene();
void addSymbolToScene(SymbolInstance* sym);
void limitDisplayedSymbols();
void findItems();
void isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules);
void foundPositionedItem(FGPositioned* pos);
void computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading);
void computePositionedState(FGPositioned* pos, string_set& states);
void processRoute();
void computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading);
void processNavRadios();
FGNavRecord* processNavRadio(const SGPropertyNode_ptr& radio);
void processAI();
void computeAIStates(const SGPropertyNode* ai, string_set& states);
void computeCustomSymbolStates(const SGPropertyNode* sym, string_set& states);
void processCustomSymbols();
void findRules(const std::string& type, const string_set& states, SymbolRuleVector& rules);
SymbolInstance* addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars);
void addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color);
osg::Vec2 projectBearingRange(double bearingDeg, double rangeNm) const;
osg::Vec2 projectGeod(const SGGeod& geod) const;
bool isProjectedClipped(const osg::Vec2& projected) const;
void updateFont();
void addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars);
void addTestSymbols();
std::string _texture_path;
unsigned int _textureSize;
float _scale; // factor to convert nm to display units
float _view_heading;
SGPropertyNode_ptr _Radar_controls;
SGPropertyNode_ptr _font_node;
SGPropertyNode_ptr _ai_enabled_node;
SGPropertyNode_ptr _navRadio1Node;
SGPropertyNode_ptr _navRadio2Node;
SGPropertyNode_ptr _xCenterNode, _yCenterNode;
SGPropertyNode_ptr _viewHeadingNode;
osg::ref_ptr<osg::Texture2D> _symbolTexture;
osg::ref_ptr<osg::Geode> _radarGeode;
osg::ref_ptr<osg::Geode> _textGeode;
osg::Geometry *_geom;
osg::DrawArrays* _symbolPrimSet;
osg::Vec2Array *_vertices;
osg::Vec2Array *_texCoords;
osg::Vec4Array* _quadColors;
osg::Geometry* _lineGeometry;
osg::DrawArrays* _linePrimSet;
osg::Vec2Array* _lineVertices;
osg::Vec4Array* _lineColors;
osg::Matrixf _centerTrans;
osg::Matrixf _projectMat;
osg::ref_ptr<osgText::Font> _font;
osg::Vec4 _font_color;
float _font_size;
float _font_spacing;
FGRouteMgr* _route;
SGGeod _pos;
double _rangeNm;
SGPropertyNode_ptr _rangeNode;
SymbolDefVector _definitions;
SymbolRuleVector _rules;
FGNavRecord* _nav1Station;
FGNavRecord* _nav2Station;
std::vector<SymbolInstance*> _symbols;
std::set<FGPositioned*> _routeSources;
bool _cachedItemsValid;
SGVec3d _cachedPos;
FGPositionedList _itemsInRange;
SGPropertyNode_ptr _excessDataNode;
int _maxSymbols;
SGPropertyNode_ptr _customSymbols;
class CacheListener;
std::unique_ptr<CacheListener> _cacheListener;
class ForceUpdateListener;
std::unique_ptr<ForceUpdateListener> _forceUpdateListener;
};
#endif // _INST_ND_HXX

9
src/Cockpit/README Normal file
View File

@@ -0,0 +1,9 @@
src/Cockpit/ - instrument and display-related code
This directory contains various subsystems and code modules related to
the cockpit, including the HUD and the 2D panel. The code from here
is gradually being sorted out into separate code modules -- please do
not add anything new here. All new code for gauges and avionics
should go in src/Instrumentation/, and all new code aircraft systems
(like the electrical or vacuum systems) should go in src/Systems/.
Any new 2D or 3D modelling code should go in src/Model/.

329
src/Cockpit/agradar.cxx Normal file
View File

@@ -0,0 +1,329 @@
// Air Ground Radar
//
// Written by Vivian MEAZZA, started Feb 2008.
//
//
// Copyright (C) 2008 Vivian Meazza
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include "agradar.hxx"
agRadar::agRadar(SGPropertyNode *node) : wxRadarBg(node)
{
_name = node->getStringValue("name", "air-ground-radar");
_num = node->getIntValue("number", 0);
}
agRadar::~agRadar ()
{
}
void
agRadar::init ()
{
_user_hdg_deg_node = fgGetNode("/orientation/heading-deg", true);
_user_pitch_deg_node = fgGetNode("/orientation/pitch-deg", true);
_user_roll_deg_node = fgGetNode("/orientation/roll-deg", true);
_terrain_warning_node = fgGetNode("/sim/alarms/terrain-warning", true);
_terrain_warning_node->setBoolValue(false);
wxRadarBg::init();
// those properties are used by a radar instrument of a MFD
// input switch = OFF | TST | STBY | ON
// input mode = WX | WXA | MAP | TW
// output status = STBY | TEST | WX | WXA | MAP | blank
// input lightning = true | false
// input TRK = +/- n degrees
// input TILT = +/- n degree
// input autotilt = true | false
// input range = n nm (20/40/80)
// input display-mode = arc | rose | map | plan
_Instrument->setFloatValue("trk", 0.0);
_Instrument->setFloatValue("tilt",-2.5);
_Instrument->setStringValue("status","");
_Instrument->setIntValue("mode-control", 5);
_Instrument->setBoolValue("stabilisation/roll", false);
_Instrument->setBoolValue("stabilisation/pitch", false);
_xOffsetMNode = getInstrumentNode("antenna/x-offset-m", 0.0);
_yOffsetMNode = getInstrumentNode("antenna/y-offset-m", 0.0);
_zOffsetMNode = getInstrumentNode("antenna/z-offset-m", 0.0);
_elevLimitDegNode = getInstrumentNode("terrain-warning/elev-limit-deg", 2.0);
_elevStepDegNode = getInstrumentNode("terrain-warning/elev-step-deg", 1.0);
_azLimitDegNode = getInstrumentNode("terrain-warning/az-limit-deg", 1.0);
_azStepDegNode = getInstrumentNode("terrain-warning/az-step-deg", 1.5);
_maxRangeMNode = getInstrumentNode("terrain-warning/max-range-m", 4000.0);
_minRangeMNode = getInstrumentNode("terrain-warning/min-range-m", 250.0);
_tiltNode = getInstrumentNode("terrain-warning/tilt", -2.0);
_brgDegNode = getInstrumentNode("terrain-warning/hit/brg-deg", 0.0);
_rangeMNode = getInstrumentNode("terrain-warning/hit/range-m", 0.0);
_elevationMNode = getInstrumentNode("terrain-warning/hit/elevation-m", 0.0);
_materialNode = getInstrumentNode("terrain-warning/hit/material", "");
_bumpinessNode = getInstrumentNode("terrain-warning/hit/bumpiness", 0.0);
_rollStabNode = getInstrumentNode("terrain-warning/stabilisation/roll",
true);
_pitchStabNode = getInstrumentNode("terrain-warning/stabilisation/pitch",
false);
// cout << "init done" << endl;
}
void
agRadar::update (double delta_time_sec)
{
if (!_sceneryLoaded->getBoolValue())
return;
if ( !_odg || ! _serviceable_node->getBoolValue() ) {
_Instrument->setStringValue("status","");
return;
}
_time += delta_time_sec;
if (_time < _interval)
return;
_time = 0.0;
update_terrain();
// wxRadarBg::update(delta_time_sec);
}
void
agRadar::setUserPos()
{
userpos.setLatitudeDeg(_user_lat_node->getDoubleValue());
userpos.setLongitudeDeg(_user_lon_node->getDoubleValue());
userpos.setElevationM(_user_alt_node->getDoubleValue() * SG_FEET_TO_METER);
}
SGVec3d
agRadar::getCartUserPos() const {
SGVec3d cartUserPos = SGVec3d::fromGeod(userpos);
return cartUserPos;
}
SGVec3d
agRadar::getCartAntennaPos() const {
float yaw = _user_hdg_deg_node->getDoubleValue();
float pitch = _user_pitch_deg_node->getDoubleValue();
float roll = _user_roll_deg_node->getDoubleValue();
double x_offset_m =_xOffsetMNode->getDoubleValue();
double y_offset_m =_yOffsetMNode->getDoubleValue();
double z_offset_m =_zOffsetMNode->getDoubleValue();
// convert geodetic positions to geocentered
SGVec3d cartuserPos = getCartUserPos();
// Transform to the right coordinate frame, configuration is done in
// the x-forward, y-right, z-up coordinates (feet), computation
// in the simulation usual body x-forward, y-right, z-down coordinates
// (meters) )
SGVec3d _off(x_offset_m, y_offset_m, -z_offset_m);
// Transform the user position to the horizontal local coordinate system.
SGQuatd hlTrans = SGQuatd::fromLonLat(userpos);
// and postrotate the orientation of the user model wrt the horizontal
// local frame
hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw,pitch,roll);
// The offset converted to the usual body fixed coordinate system
// rotated to the earth-fixed coordinates axis
SGVec3d off = hlTrans.backTransform(_off);
// Add the position offset of the user model to get the geocentered position
SGVec3d offsetPos = cartuserPos + off;
return offsetPos;
}
void
agRadar::setAntennaPos() {
SGGeodesy::SGCartToGeod(getCartAntennaPos(), antennapos);
}
void
agRadar::setUserVec(double az, double el)
{
float yaw = _user_hdg_deg_node->getDoubleValue();
float pitch = _user_pitch_deg_node->getDoubleValue();
float roll = _user_roll_deg_node->getDoubleValue();
double tilt = _Instrument->getDoubleValue("tilt");
double trk = _Instrument->getDoubleValue("trk");
bool roll_stab = _Instrument->getBoolValue("stabilisation/roll");
bool pitch_stab = _Instrument->getBoolValue("stabilisation/pitch");
SGQuatd offset = SGQuatd::fromYawPitchRollDeg(az + trk, el + tilt, 0);
// Transform the antenna position to the horizontal local coordinate system.
SGQuatd hlTrans = SGQuatd::fromLonLat(antennapos);
// and postrotate the orientation of the radar wrt the horizontal
// local frame
hlTrans *= SGQuatd::fromYawPitchRollDeg(yaw,
pitch_stab ? 0 :pitch,
roll_stab ? 0 : roll);
hlTrans *= offset;
// now rotate the rotation vector back into the
// earth centered frames coordinates
SGVec3d angleaxis(1,0,0);
uservec = hlTrans.backTransform(angleaxis);
}
bool
agRadar::getMaterial(){
const simgear::BVHMaterial* mat = 0;
if (globals->get_scenery()->get_elevation_m(hitpos, _elevation_m, &mat)){
//_ht_agl_ft = pos.getElevationFt() - _elevation_m * SG_METER_TO_FEET;
const SGMaterial* material = dynamic_cast<const SGMaterial*>(mat);
if (material) {
const std::vector<std::string>& names = material->get_names();
_solid = material->get_solid();
_load_resistance = material->get_load_resistance();
_frictionFactor = material->get_friction_factor();
_bumpinessFactor = material->get_bumpiness();
if (!names.empty())
_mat_name = names[0];
else
_mat_name = "";
}
/*cout << "material " << mat_name
<< " solid " << _solid
<< " load " << _load_resistance
<< " frictionFactor " << frictionFactor
<< " _bumpinessFactor " << _bumpinessFactor
<< endl;*/
return true;
} else {
return false;
}
}
void
agRadar::update_terrain()
{
int mode = _radar_mode_control_node->getIntValue();
double el_limit = 1;
double el_step = 1;
double az_limit = 50;
double az_step = 10;
double max_range = 40000;
double min_range = 250;
double tilt = -2.5;
bool roll_stab = _rollStabNode->getBoolValue();
bool pitch_stab = _pitchStabNode->getBoolValue();
const char* status = "";
bool hdg_mkr = true;
if (mode == 5){
status = "TW";
hdg_mkr = false;
tilt = _tiltNode->getDoubleValue();
el_limit = _elevLimitDegNode->getDoubleValue();
el_step = _elevStepDegNode->getDoubleValue();
az_limit = _azLimitDegNode->getDoubleValue();
az_step = _azStepDegNode->getDoubleValue();
max_range = _maxRangeMNode->getDoubleValue();
min_range = _minRangeMNode->getDoubleValue();
}
_Instrument->setDoubleValue("tilt", tilt);
_Instrument->setBoolValue("stabilisation/roll", roll_stab);
_Instrument->setBoolValue("stabilisation/pitch", pitch_stab);
_Instrument->setStringValue("status", status);
_Instrument->setDoubleValue("limit-deg", az_limit);
_Instrument->setBoolValue("heading-marker", hdg_mkr);
setUserPos();
setAntennaPos();
SGVec3d cartantennapos = getCartAntennaPos();
for(double brg = -az_limit; brg <= az_limit; brg += az_step){
for(double elev = el_limit; elev >= - el_limit; elev -= el_step){
setUserVec(brg, elev);
SGVec3d nearestHit;
globals->get_scenery()->get_cart_ground_intersection(cartantennapos, uservec, nearestHit);
SGGeodesy::SGCartToGeod(nearestHit, hitpos);
double course1, course2, distance;
SGGeodesy::inverse(hitpos, antennapos, course1, course2, distance);
if (distance >= min_range && distance <= max_range) {
_terrain_warning_node->setBoolValue(true);
getMaterial();
_brgDegNode->setDoubleValue(course2);
_rangeMNode->setDoubleValue(distance);
_materialNode->setStringValue(_mat_name.c_str());
_bumpinessNode->setDoubleValue(_bumpinessFactor);
_elevationMNode->setDoubleValue(_elevation_m);
} else {
_terrain_warning_node->setBoolValue(false);
_brgDegNode->setDoubleValue(0);
_rangeMNode->setDoubleValue(0);
_materialNode->setStringValue("");
_bumpinessNode->setDoubleValue(0);
_elevationMNode->setDoubleValue(0);
}
//cout << "usr hdg " << _user_hdg_deg_node->getDoubleValue()
// << " ant brg " << course2
// << " elev " << _Instrument->getDoubleValue("tilt")
// << " gnd rng nm " << distance * SG_METER_TO_NM
// << " ht " << hitpos.getElevationFt()
// << " mat " << _mat_name
// << " solid " << _solid
// << " bumpiness " << _bumpinessFactor
// << endl;
}
}
}
// Register the subsystem.
#if 0
SGSubsystemMgr::Registrant<agRadar> registrantagRadar;
#endif

98
src/Cockpit/agradar.hxx Normal file
View File

@@ -0,0 +1,98 @@
// Air Ground Radar
//
// Written by Vivian MEAZZA, started Feb 2008.
//
//
// Copyright (C) 2008 Vivain MEAZZA - vivian.meazza@lineone.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//
#ifndef _INST_AGRADAR_HXX
#define _INST_AGRADAR_HXX
#include <simgear/structure/subsystem_mgr.hxx>
#include <Scenery/scenery.hxx>
#include <simgear/scene/material/mat.hxx>
#include "wxradar.hxx"
class agRadar : public wxRadarBg
{
public:
agRadar ( SGPropertyNode *node );
agRadar ();
virtual ~agRadar ();
// Subsystem API.
void init() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "air-ground-radar"; }
void setUserPos();
void setUserVec(double az, double el);
void update_terrain();
void setAntennaPos();
bool getMaterial();
double _load_resistance; // ground load resistanc N/m^2
double _frictionFactor; // dimensionless modifier for Coefficient of Friction
double _bumpinessFactor; // dimensionless modifier for Bumpiness
double _elevation_m; // ground elevation in meters
bool _solid; // if true ground is solid for FDMs
std::string _mat_name; // ground material
SGVec3d getCartUserPos() const;
SGVec3d getCartAntennaPos()const;
SGVec3d uservec;
SGPropertyNode_ptr _user_hdg_deg_node;
SGPropertyNode_ptr _user_roll_deg_node;
SGPropertyNode_ptr _user_pitch_deg_node;
SGPropertyNode_ptr _terrain_warning_node;
SGPropertyNode_ptr _xOffsetMNode;
SGPropertyNode_ptr _yOffsetMNode;
SGPropertyNode_ptr _zOffsetMNode;
SGPropertyNode_ptr _elevLimitDegNode;
SGPropertyNode_ptr _elevStepDegNode;
SGPropertyNode_ptr _azLimitDegNode;
SGPropertyNode_ptr _azStepDegNode;
SGPropertyNode_ptr _maxRangeMNode;
SGPropertyNode_ptr _minRangeMNode;
SGPropertyNode_ptr _tiltNode;
SGPropertyNode_ptr _brgDegNode;
SGPropertyNode_ptr _rangeMNode;
SGPropertyNode_ptr _elevationMNode;
SGPropertyNode_ptr _materialNode;
SGPropertyNode_ptr _bumpinessNode;
SGPropertyNode_ptr _rollStabNode;
SGPropertyNode_ptr _pitchStabNode;
SGGeod userpos;
SGGeod hitpos;
SGGeod antennapos;
};
#endif // _INST_AGRADAR_HXX

View File

@@ -0,0 +1,86 @@
// FGMagRibbon.cxx - Built-in layer for the magnetic compass ribbon layer.
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "FGMagRibbon.hxx"
FGMagRibbon::FGMagRibbon (int w, int h)
: FGTexturedLayer(w, h)
{
FGCroppedTexture texture("Aircraft/Instruments/Textures/compass-ribbon.rgb");
setTexture(texture);
_magcompass_node =
fgGetNode("/instrumentation/magnetic-compass/indicated-heading-deg",
true);
}
void
FGMagRibbon::draw (osg::State& state)
{
double heading = _magcompass_node->getDoubleValue();
double xoffset, yoffset;
while (heading >= 360.0) {
heading -= 360.0;
}
while (heading < 0.0) {
heading += 360.0;
}
if (heading >= 60.0 && heading <= 180.0) {
xoffset = heading / 240.0;
yoffset = 0.75;
} else if (heading >= 150.0 && heading <= 270.0) {
xoffset = (heading - 90.0) / 240.0;
yoffset = 0.50;
} else if (heading >= 240.0 && heading <= 360.0) {
xoffset = (heading - 180.0) / 240.0;
yoffset = 0.25;
} else {
if (heading < 270.0)
heading += 360.0;
xoffset = (heading - 270.0) / 240.0;
yoffset = 0.0;
}
xoffset = 1.0 - xoffset;
// Adjust to put the number in the centre
xoffset -= 0.25;
FGCroppedTexture *t = getTexture();
t->setCrop(xoffset, yoffset, xoffset + 0.5, yoffset + 0.25);
static osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet;
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
state.pushStateSet(stateSet.get());
state.apply();
FGTexturedLayer::draw(state);
state.popStateSet();
state.apply();
}
// end of FGMagRibbon.cxx

View File

@@ -0,0 +1,45 @@
// FGMagRibbon.hxx - Built-in layer for the magnetic compass ribbon layer.
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef __FG_MAG_RIBBON_HXX
#define __FG_MAG_RIBBON_HXX
#include <Main/fg_props.hxx>
#include "../panel.hxx"
////////////////////////////////////////////////////////////////////////
// Built-in layer for the magnetic compass ribbon layer.
////////////////////////////////////////////////////////////////////////
class FGMagRibbon : public FGTexturedLayer
{
public:
FGMagRibbon (int w, int h);
virtual ~FGMagRibbon () {}
virtual void draw (osg::State& state);
private:
SGPropertyNode_ptr _magcompass_node;
};
#endif // __FG_MAG_RIBBON_HXX
// end of FGMagRibbon.hxx

View File

@@ -0,0 +1,2 @@
This is special directory for built-in
layers of various sorts.

View File

@@ -0,0 +1,129 @@
// cockpitDisplayManager.cxx -- manage cockpit displays, typically
// rendered using a sub-camera or render-texture
//
// Copyright (C) 2012 James Turner zakalawe@mac.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <config.h>
#include "cockpitDisplayManager.hxx"
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/sg_inlines.h>
#include <simgear/props/props_io.hxx>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include "agradar.hxx"
#include "NavDisplay.hxx"
#include "groundradar.hxx"
#include "wxradar.hxx"
namespace flightgear
{
CockpitDisplayManager::CockpitDisplayManager ()
{
}
CockpitDisplayManager::~CockpitDisplayManager ()
{
}
SGSubsystem::InitStatus CockpitDisplayManager::incrementalInit()
{
init();
return INIT_DONE;
}
void CockpitDisplayManager::init()
{
SGPropertyNode_ptr config_props = new SGPropertyNode;
SGPropertyNode* path_n = fgGetNode("/sim/instrumentation/path");
if (!path_n) {
return;
}
SGPath config = globals->resolve_aircraft_path(path_n->getStringValue());
if (!config.exists()) {
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AircraftSystems,
"CockpitDisplaysManager: Missing instrumentation file", config);
return;
}
SG_LOG( SG_COCKPIT, SG_INFO, "Reading cockpit displays from " << config );
try {
readProperties( config, config_props );
if (!build(config_props)) {
throw sg_exception(
"Detected an internal inconsistency in the instrumentation\n"
"system specification file. See earlier errors for details.");
}
} catch (const sg_exception& e) {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::AircraftSystems,
"Failed to load cockpit displays system:" + e.getFormattedMessage(),
e.getLocation());
}
SGSubsystemGroup::init();
}
bool CockpitDisplayManager::build (SGPropertyNode* config_props)
{
for ( int i = 0; i < config_props->nChildren(); ++i ) {
SGPropertyNode *node = config_props->getChild(i);
std::string name = node->getNameString();
std::ostringstream subsystemname;
subsystemname << "instrument-" << i << '-'
<< node->getStringValue("name", name.c_str());
int index = node->getIntValue("number", 0);
if (index > 0)
subsystemname << '['<< index << ']';
std::string id = subsystemname.str();
if ( name == "radar" ) {
set_subsystem( id, new wxRadarBg ( node ) );
} else if ( name == "groundradar" ) {
set_subsystem( id, new GroundRadar( node ) );
} else if ( name == "air-ground-radar" ) {
set_subsystem( id, new agRadar( node ) );
} else if ( name == "navigation-display" ) {
set_subsystem( id, new NavDisplay( node ) );
} else {
// probably a regular instrument
continue;
}
// only add to our list if we build a display
_displays.push_back(id);
}
return true;
}
// Register the subsystem.
SGSubsystemMgr::Registrant<CockpitDisplayManager> registrantCockpitDisplayManager(
SGSubsystemMgr::DISPLAY);
} // of namespace flightgear

View File

@@ -0,0 +1,57 @@
// cockpitDisplayManager.hxx -- manage cockpit displays, typically
// rendered using a sub-camera or render-texture
//
// Copyright (C) 2012 James Turner zakalawe@mac.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef COCKPIT_DISPLAY_MGR_HXX
#define COCKPIT_DISPLAY_MGR_HXX 1
#include <simgear/compiler.h>
#include <vector>
#include <simgear/structure/subsystem_mgr.hxx>
class SGPropertyNode;
namespace flightgear
{
/**
* Manage aircraft displays.
*/
class CockpitDisplayManager : public SGSubsystemGroup
{
public:
CockpitDisplayManager ();
virtual ~CockpitDisplayManager ();
// Subsystem API.
void init() override;
InitStatus incrementalInit() override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "cockpit-displays"; }
private:
bool build (SGPropertyNode* config_props);
std::vector<std::string> _displays;
};
} // of namespace lfightgear
#endif // COCKPIT_DISPLAY_MGR_HXX

359
src/Cockpit/groundradar.cxx Normal file
View File

@@ -0,0 +1,359 @@
// groundradar.cxx - Background layer for the ATC radar.
//
// Copyright (C) 2007 Csaba Halasz.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <cassert>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Camera>
#include <osg/Texture2D>
#include <osgViewer/Viewer>
#include <osgText/Text>
#include <osgDB/Registry>
#include <osgDB/ReaderWriter>
#include <osgUtil/Tessellator>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Viewer/renderer.hxx>
#include <Cockpit/panel.hxx>
#include <Airports/airport.hxx>
#include <Airports/runways.hxx>
#include <Airports/pavement.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/math/beziercurve.hxx>
#include "groundradar.hxx"
static const char* airport_source_node_name = "airport-id-source";
static const char* default_airport_node_name = "/sim/airport/closest-airport-id";
static const char* texture_node_name = "texture-name";
static const char* default_texture_name = "Aircraft/Instruments/Textures/od_groundradar.rgb";
static const char* range_source_node_name = "range-source";
static const char* default_range_node_name = "/instrumentation/radar/range";
struct SingleFrameCallback : public osg::Camera::DrawCallback
{
virtual void operator () (const osg::Camera& camera) const
{
const_cast<osg::Camera&>(camera).setNodeMask(0);
}
};
GroundRadar::GroundRadar(SGPropertyNode *node)
{
_airport_node = fgGetNode(node->getStringValue(airport_source_node_name, default_airport_node_name), true);
_range_node = fgGetNode(node->getStringValue(range_source_node_name, default_range_node_name), true);
createTexture(node->getStringValue(texture_node_name, default_texture_name));
updateTexture();
_airport_node->addChangeListener(this);
_range_node->addChangeListener(this);
}
GroundRadar::~GroundRadar()
{
_airport_node->removeChangeListener(this);
_range_node->removeChangeListener(this);
}
void GroundRadar::update (double /* dt */)
{
}
void GroundRadar::valueChanged(SGPropertyNode*)
{
updateTexture();
}
inline static osg::Vec3 fromPolar(double fi, double r)
{
return osg::Vec3(sin(fi * SGD_DEGREES_TO_RADIANS) * r, cos(fi * SGD_DEGREES_TO_RADIANS) * r, 0);
}
void GroundRadar::createTexture(const std::string& texture_name)
{
setSize(TextureHalfSize + TextureHalfSize);
allocRT();
_geode = new osg::Geode();
osg::StateSet* stateset = _geode->getOrCreateStateSet();
stateset->setMode(GL_BLEND, osg::StateAttribute::OFF);
osg::Vec4Array* taxi_color = new osg::Vec4Array;
taxi_color->push_back(osg::Vec4(0.0f, 0.5f, 0.0f, 1.0f));
osg::Vec4Array* rwy_color = new osg::Vec4Array;
rwy_color->push_back(osg::Vec4(0.0f, 0.5f, 0.5f, 1.0f));
osg::Geometry *taxi_geom = new osg::Geometry();
taxi_geom->setColorArray(taxi_color);
taxi_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
taxi_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Taxiways
_geode->addDrawable(taxi_geom);
osg::Geometry *pvt_geom = new osg::Geometry();
pvt_geom->setColorArray(taxi_color);
pvt_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
// no primitive set for the moment. It needs tessellation
_geode->addDrawable(pvt_geom);
osg::Geometry *rwy_geom = new osg::Geometry();
rwy_geom->setColorArray(rwy_color);
rwy_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
rwy_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); // Runways
_geode->addDrawable(rwy_geom);
osg::Camera* camera = getCamera();
camera->setPostDrawCallback(new SingleFrameCallback());
camera->addChild(_geode.get());
camera->setNodeMask(0);
camera->setProjectionMatrixAsOrtho2D(0, getTexture()->getTextureWidth(), 0, getTexture()->getTextureHeight());
// Texture in the 2D panel system
FGTextureManager::addTexture(texture_name, getTexture());
}
void GroundRadar::addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices)
{
double az1, az2, dist_m;
geo_inverse_wgs_84(aTowerLat, aTowerLon, aRunway->latitude(), aRunway->longitude(), &az1, &az2, &dist_m);
osg::Vec3 center = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
osg::Vec3 leftcenter = fromPolar(aRunway->headingDeg(), aRunway->lengthM() * aScale / 2) + center;
osg::Vec3 lefttop = fromPolar(aRunway->headingDeg() - 90, aRunway->widthM() * aScale / 2) + leftcenter;
osg::Vec3 leftbottom = leftcenter * 2 - lefttop;
osg::Vec3 rightbottom = center * 2 - lefttop;
osg::Vec3 righttop = center * 2 - leftbottom;
aVertices->push_back(lefttop);
aVertices->push_back(leftbottom);
aVertices->push_back(rightbottom);
aVertices->push_back(righttop);
}
osg::Geometry *GroundRadar::addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale)
{
osg::ref_ptr<osgUtil::Tessellator> tess = new osgUtil::Tessellator;
osg::ref_ptr<osg::Geometry> polygon = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> pts = new osg::Vec3Array;
double az1, az2, dist_m;
const FGPavement::NodeList &nodeLst = aPavement->getNodeList();
FGPavement::NodeList::const_iterator it = nodeLst.begin(),
loopBegin = it;
while ( it != nodeLst.end() )
{
bool close = (*it)->mClose;
geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
const FGPavement::BezierNode *bn = dynamic_cast<const FGPavement::BezierNode *>( it->ptr() );
if ( bn != 0 )
{
geo_inverse_wgs_84(aTowerLat, aTowerLon, bn->mControl.getLatitudeDeg(), bn->mControl.getLongitudeDeg(), &az1, &az2, &dist_m);
osg::Vec3 p2 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0),
p3;
++it;
if ( it == nodeLst.end() || close )
{
geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
}
else
{
geo_inverse_wgs_84(aTowerLat, aTowerLon, (*it)->mPos.getLatitudeDeg(), (*it)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
}
p3 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
simgear::BezierCurve<osg::Vec3> bCurv( p1, p2, p3 );
simgear::BezierCurve<osg::Vec3>::PointList &ptList = bCurv.pointList();
for ( simgear::BezierCurve<osg::Vec3>::PointList::iterator ii = ptList.begin(); ii != ptList.end(); ++ii )
{
pts->push_back( *ii );
}
pts->pop_back(); // Last point belongs to next segment
}
else
{
pts->push_back( p1 );
++it;
}
if ( close ) // One loop for the moment
break;
}
geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
pts->push_back( p1 );
polygon->setVertexArray( pts.get() );
polygon->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, 0, pts->size() ) );
tess->setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
tess->setBoundaryOnly( false );
tess->setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
tess->retessellatePolygons( *polygon );
return polygon.release();
}
void GroundRadar::updateTexture()
{
osg::ref_ptr<osg::Vec3Array> rwy_vertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> taxi_vertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> pvt_vertices = new osg::Vec3Array;
const std::string airport_name = _airport_node->getStringValue();
const FGAirport* airport = fgFindAirportID(airport_name);
if (airport == 0)
return;
const SGGeod& tower_location = airport->getTowerLocation();
const double tower_lat = tower_location.getLatitudeDeg();
const double tower_lon = tower_location.getLongitudeDeg();
double scale = SG_METER_TO_NM * 200 / _range_node->getDoubleValue();
const FGAirport* apt = fgFindAirportID(airport_name);
assert(apt);
for (unsigned int i=0; i<apt->numTaxiways(); ++i)
{
FGTaxiway* txwy(apt->getTaxiwayByIndex(i));
addRunwayVertices(txwy, tower_lat, tower_lon, scale, taxi_vertices.get());
}
osg::Geometry *taxi_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(0));
taxi_geom->setVertexArray(taxi_vertices.get());
osg::DrawArrays* taxi = dynamic_cast<osg::DrawArrays*>(taxi_geom->getPrimitiveSet(0));
taxi->setCount(taxi_vertices->size());
osg::Geometry *pvt_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(1));
osg::Geometry::PrimitiveSetList &pvt_prim_list = pvt_geom->getPrimitiveSetList();
pvt_prim_list.clear();
auto pavementlist = airport->getPavements();
for (auto pvtiter = pavementlist.begin(); pvtiter != pavementlist.end(); ++pvtiter)
{
osg::ref_ptr<osg::Geometry> geom = addPavementGeometry(*pvtiter, tower_lat, tower_lon, scale);
osg::Geometry::PrimitiveSetList &prim_list = geom->getPrimitiveSetList();
osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array *>(geom->getVertexArray());
size_t before = pvt_vertices->size(),
count = vertices->size();
for (size_t i = 0; i < count; ++i )
{
pvt_vertices->push_back( (*vertices)[i] );
}
for (osg::Geometry::PrimitiveSetList::iterator ii = prim_list.begin(); ii != prim_list.end(); ++ii )
{
osg::DrawArrays *da;
osg::DrawElementsUByte *de1;
osg::DrawElementsUShort *de2;
osg::DrawElementsUInt *de3;
if ((da = dynamic_cast<osg::DrawArrays *>(ii->get())) != 0)
{
osg::DrawArrays *ps = new osg::DrawArrays(*da);
ps->setFirst(da->getFirst() + before);
pvt_prim_list.push_back(ps);
}
else if ((de1 = dynamic_cast<osg::DrawElementsUByte *>(ii->get())) != 0)
{
if (before + count <= 255)
{
osg::DrawElementsUByte *ps = new osg::DrawElementsUByte(*de1);
for (size_t j = 0; j < ps->size(); ++j)
{
(*ps)[j] += before;
}
pvt_prim_list.push_back(ps);
}
else if (before + count <= 65535)
{
osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(de1->getMode(), de1->begin(), de1->end());
for (size_t j = 0; j < ps->size(); ++j)
{
(*ps)[j] += before;
}
pvt_prim_list.push_back(ps);
}
else
{
osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de1->getMode(), de1->begin(), de1->end());
for (size_t j = 0; j < ps->size(); ++j)
{
(*ps)[j] += before;
}
pvt_prim_list.push_back(ps);
}
}
else if ((de2 = dynamic_cast<osg::DrawElementsUShort *>(ii->get())) != 0)
{
if (before + count <= 65535)
{
osg::DrawElementsUShort *ps = new osg::DrawElementsUShort(*de2);
for (size_t j = 0; j < ps->size(); ++j)
{
(*ps)[j] += before;
}
pvt_prim_list.push_back(ps);
}
else
{
osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(de2->getMode(), de2->begin(), de2->end());
for (size_t j = 0; j < ps->size(); ++j)
{
(*ps)[j] += before;
}
pvt_prim_list.push_back(ps);
}
}
else if ((de3 = dynamic_cast<osg::DrawElementsUInt *>(ii->get())) != 0)
{
osg::DrawElementsUInt *ps = new osg::DrawElementsUInt(*de3);
for (size_t j = 0; j < ps->size(); ++j)
{
(*ps)[j] += before;
}
pvt_prim_list.push_back(ps);
}
}
}
pvt_geom->setVertexArray(pvt_vertices.get());
FGRunwayList rwys(apt->getRunwaysWithoutReciprocals());
for (unsigned int i=0; i<rwys.size(); ++i)
{
addRunwayVertices(rwys[i], tower_lat, tower_lon, scale, rwy_vertices.get());
}
osg::Geometry *rwy_geom = dynamic_cast<osg::Geometry *>(_geode->getDrawable(2));
rwy_geom->setVertexArray(rwy_vertices.get());
osg::DrawArrays* rwy = dynamic_cast<osg::DrawArrays*>(rwy_geom->getPrimitiveSet(0));
rwy->setCount(rwy_vertices->size());
getCamera()->setNodeMask(0xffffffff);
}
// Register the subsystem.
#if 0
SGSubsystemMgr::Registrant<GroundRadar> registrantGroundRadar;
#endif
// end of GroundRadar.cxx

View File

@@ -0,0 +1,65 @@
// groundradar.hxx - Background layer for the ATC radar.
//
// Copyright (C) 2007 Csaba Halasz.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef __INST_GROUNDRADAR_HXX
#define __INST_GROUNDRADAR_HXX
#include <osg/ref_ptr>
#include <osg/Geometry>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include "od_gauge.hxx"
// forward decls
class FGRunwayBase;
class FGPavement;
////////////////////////////////////////////////////////////////////////
// Built-in layer for the atc radar.
////////////////////////////////////////////////////////////////////////
class GroundRadar : public SGSubsystem,
public SGPropertyChangeListener,
private FGODGauge
{
public:
// Subsystem identification.
static const char* staticSubsystemClassId() { return "groundradar"; }
static const int TextureHalfSize = 256;
GroundRadar(SGPropertyNode* node);
virtual ~GroundRadar();
void updateTexture();
virtual void valueChanged(SGPropertyNode*);
virtual void update (double dt);
protected:
void createTexture(const std::string& texture_name);
void addRunwayVertices(const FGRunwayBase* aRunway, double aTowerLat, double aTowerLon, double aScale, osg::Vec3Array* aVertices);
osg::Geometry *addPavementGeometry(const FGPavement* aPavement, double aTowerLat, double aTowerLon, double aScale);
osg::ref_ptr<osg::Geode> _geode;
SGPropertyNode_ptr _airport_node;
SGPropertyNode_ptr _range_node;
};
#endif // __INST_GROUNDRADAR_HXX

333
src/Cockpit/od_gauge.cxx Normal file
View File

@@ -0,0 +1,333 @@
// Owner Drawn Gauge helper class
//
// Written by Harald JOHNSEN, started May 2005.
//
// Copyright (C) 2005 Harald JOHNSEN
//
// Ported to OSG by Tim Moore - Jun 2007
//
// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
// the texture in the scene by certain filter criteria
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <osg/Texture2D>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/Camera>
#include <osg/Geode>
#include <osg/NodeVisitor>
#include <osg/Matrix>
#include <osg/PolygonMode>
#include <osg/ShadeModel>
#include <osg/StateSet>
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
#include <osgDB/FileNameUtils>
#include <simgear/canvas/CanvasObjectPlacement.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <Main/globals.hxx>
#include <Scenery/scenery.hxx>
#include "od_gauge.hxx"
#include <cassert>
//------------------------------------------------------------------------------
FGODGauge::FGODGauge()
{
}
//------------------------------------------------------------------------------
FGODGauge::~FGODGauge()
{
}
/*
* Used to remember the located groups that require modification
*/
typedef struct {
osg::ref_ptr<osg::Group> parent;
osg::ref_ptr<osg::Geode> node;
unsigned int unit;
} GroupListItem;
/**
* Replace a texture in the airplane model with the gauge texture.
*/
class ReplaceStaticTextureVisitor:
public osg::NodeVisitor
{
public:
typedef osg::ref_ptr<osg::Group> GroupPtr;
typedef osg::ref_ptr<osg::Material> MaterialPtr;
ReplaceStaticTextureVisitor( const char* name,
osg::Texture2D* new_texture ):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_tex_name( osgDB::getSimpleFileName(name) ),
_new_texture(new_texture),
_cull_callback(0)
{}
ReplaceStaticTextureVisitor( SGPropertyNode* placement,
osg::Texture2D* new_texture,
osg::NodeCallback* cull_callback = 0,
const simgear::canvas::CanvasWeakPtr& canvas =
simgear::canvas::CanvasWeakPtr() ):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_tex_name( osgDB::getSimpleFileName(
placement->getStringValue("texture"))
),
_node_name( placement->getStringValue("node") ),
_parent_name( placement->getStringValue("parent") ),
_node(placement),
_new_texture(new_texture),
_cull_callback(cull_callback),
_canvas(canvas)
{
if( _tex_name.empty()
&& _node_name.empty()
&& _parent_name.empty() )
SG_LOG
(
SG_GL,
SG_WARN,
"No filter criterion for replacing texture. "
" Every texture will be replaced!"
);
}
/**
* Get a list of groups which have been inserted into the scene graph to
* replace the given texture
*/
simgear::canvas::Placements& getPlacements()
{
return _placements;
}
virtual void apply(osg::Geode& node)
{
simgear::EffectGeode* effectGeode = dynamic_cast<simgear::EffectGeode*>(&node);
if( !effectGeode )
return;
simgear::Effect* eff = effectGeode->getEffect();
if (!eff)
return;
osg::StateSet* ss = eff->getDefaultStateSet();
if( !ss )
return;
osg::Group *parent = node.getParent(0);
if( !_node_name.empty() && getNodeName(*parent) != _node_name )
return;
if( !_parent_name.empty() )
{
// Traverse nodes upwards starting at the parent node (skip current
// node)
const osg::NodePath& np = getNodePath();
bool found = false;
for( int i = static_cast<int>(np.size()) - 2; i >= 0; --i )
{
const osg::Node* path_segment = np[i];
const osg::Node* path_parent = path_segment->getParent(0);
// A node without a name is always the parent of the root node of
// the model just containing the file name
if( path_parent && path_parent->getName().empty() )
return;
if( path_segment->getName() == _parent_name )
{
found = true;
break;
}
}
if( !found )
return;
}
for( unsigned int unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit )
{
osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>
(
ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)
);
if( !tex || !tex->getImage() || tex == _new_texture )
continue;
if( !_tex_name.empty() )
{
std::string tex_name = tex->getImage()->getFileName();
std::string tex_name_simple = osgDB::getSimpleFileName(tex_name);
if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) )
continue;
}
/*
* remember this group for modification once the scenegraph has been traversed
*/
groups_to_modify.push_back({ parent, &node, unit });
return;
}
}
/*
* this section of code used to be in the apply method above, however to work this requires modification of the scenegraph nodes
* that are currently iterating, so instead the apply method will locate the groups to be modified and when finished then the
* nodes can actually be modified safely. Initially found thanks to the debug RTL in MSVC2015 throwing an exception.
* should be called immediately after the visitor to ensure that the groups are still valid and that nothing else has modified these groups.
*/
void modify_groups()
{
for (auto g : groups_to_modify) {
// insert a new group between the geode an it's parent which overrides
// the texture
GroupPtr group = new osg::Group;
group->setName("canvas texture group");
group->addChild(g.node);
g.parent->removeChild(g.node);
g.parent->addChild(group);
if (_cull_callback)
group->setCullCallback(_cull_callback);
osg::StateSet* stateSet = group->getOrCreateStateSet();
stateSet->setTextureAttribute(g.unit, _new_texture,
osg::StateAttribute::OVERRIDE);
stateSet->setTextureMode(g.unit, GL_TEXTURE_2D,
osg::StateAttribute::ON);
_placements.push_back(simgear::canvas::PlacementPtr(
new simgear::canvas::ObjectPlacement(_node, group, _canvas)
));
SG_LOG
(
SG_GL,
SG_INFO,
"Replaced texture '" << _tex_name << "'"
<< " for object '" << g.parent->getName() << "'"
<< (!_parent_name.empty() ? " with parent '" + _parent_name + "'"
: "")
);
}
groups_to_modify.clear();
}
protected:
std::string _tex_name, ///<! Name of texture to be replaced
_node_name, ///<! Only replace if node name matches
_parent_name; ///<! Only replace if any parent node matches
/// given name (all the tree upwards)
SGPropertyNode_ptr _node;
osg::Texture2D *_new_texture;
osg::NodeCallback *_cull_callback;
typedef std::vector<GroupListItem> GroupList;
GroupList groups_to_modify;
simgear::canvas::CanvasWeakPtr _canvas;
simgear::canvas::Placements _placements;
const std::string& getNodeName(const osg::Node& node) const
{
if( !node.getName().empty() )
return node.getName();
// Special handling for pick animation which clears the name of the object
// and instead sets the name of a parent group with one or two groups
// attached (one for normal rendering and one for the picking highlight).
osg::Group const* parent = node.getParent(0);
if( parent->getName() == "pick render group" )
return parent->getParent(0)->getName();
return node.getName();
}
};
//------------------------------------------------------------------------------
simgear::canvas::Placements
FGODGauge::set_texture( osg::Node* branch,
const char * name,
osg::Texture2D* new_texture )
{
ReplaceStaticTextureVisitor visitor(name, new_texture);
branch->accept(visitor);
visitor.modify_groups();
return visitor.getPlacements();
}
//------------------------------------------------------------------------------
simgear::canvas::Placements
FGODGauge::set_aircraft_texture( const char* name,
osg::Texture2D* new_texture )
{
return set_texture
(
globals->get_scenery()->get_aircraft_branch(),
name,
new_texture
);
}
//------------------------------------------------------------------------------
simgear::canvas::Placements
FGODGauge::set_texture( osg::Node* branch,
SGPropertyNode* placement,
osg::Texture2D* new_texture,
osg::NodeCallback* cull_callback,
const simgear::canvas::CanvasWeakPtr& canvas )
{
ReplaceStaticTextureVisitor visitor( placement,
new_texture,
cull_callback,
canvas );
branch->accept(visitor);
visitor.modify_groups();
return visitor.getPlacements();
}
//------------------------------------------------------------------------------
simgear::canvas::Placements
FGODGauge::set_aircraft_texture( SGPropertyNode* placement,
osg::Texture2D* new_texture,
osg::NodeCallback* cull_callback,
const simgear::canvas::CanvasWeakPtr& canvas )
{
return set_texture
(
globals->get_scenery()->get_aircraft_branch(),
placement,
new_texture,
cull_callback,
canvas
);
}

110
src/Cockpit/od_gauge.hxx Normal file
View File

@@ -0,0 +1,110 @@
// Owner Drawn Gauge helper class
//
// Moved to SimGear by Thomas Geymayer - October 2012
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//
#ifndef _OD_GAUGE_HXX
#define _OD_GAUGE_HXX
#include <simgear/canvas/ODGauge.hxx>
#include <simgear/canvas/CanvasPlacement.hxx>
class SGPropertyNode;
/**
* Owner Drawn Gauge helper class
*/
class FGODGauge:
public simgear::canvas::ODGauge
{
public:
FGODGauge();
virtual ~FGODGauge();
/**
* Replace an opengl texture name inside a given branch of the scene graph.
* This is to replace a static texture by a dynamic one
*
* @param branch Scene graph branch to use for search
* @param name texture filename
* @param new_texture dynamic texture to replace the old one
* @return A list of groups which override the given texture
*/
static
simgear::canvas::Placements set_texture( osg::Node* branch,
const char * name,
osg::Texture2D* new_texture );
/**
* Replace an opengl texture name inside the aircraft scene graph.
* This is to replace a static texture by a dynamic one
*
* @param branch Scene graph branch to search for matching
* @param name texture filename
* @param new_texture dynamic texture to replace the old one
* @return A list of groups which override the given texture
*/
static
simgear::canvas::Placements
set_aircraft_texture( const char * name,
osg::Texture2D* new_texture );
/**
* Replace an opengl texture name inside a given branch of the scene graph.
* This is to replace a static texture by a dynamic one. The replacement
* is base on certain filtering criteria which have to be stored in string
* value childs of the placement node. Recognized nodes are:
* - texture Match the name of the texture
* - node Match the name of the object
* - parent Match any of the object parents names (all the tree upwards)
*
* @param placement the node containing the replacement criteria
* @param new_texture dynamic texture to replace the old one
* @param an optional cull callback which will be installed on any matching
* object
* @return A list of groups which override the given texture
*/
static
simgear::canvas::Placements
set_texture( osg::Node* branch,
SGPropertyNode* placement,
osg::Texture2D* new_texture,
osg::NodeCallback* cull_callback = 0,
const simgear::canvas::CanvasWeakPtr& canvas =
simgear::canvas::CanvasWeakPtr() );
/**
* Replace an opengl texture name inside the aircraft scene graph.
*
* @param placement the node containing the replacement criteria
* @param new_texture dynamic texture to replace the old one
* @param an optional cull callback which will be installed on any matching
* object
* @return A list of groups which override the given texture
*/
static
simgear::canvas::Placements
set_aircraft_texture( SGPropertyNode* placement,
osg::Texture2D* new_texture,
osg::NodeCallback* cull_callback = 0,
const simgear::canvas::CanvasWeakPtr& canvas =
simgear::canvas::CanvasWeakPtr() );
};
#endif // _OD_GAUGE_HXX

1249
src/Cockpit/panel.cxx Normal file

File diff suppressed because it is too large Load Diff

578
src/Cockpit/panel.hxx Normal file
View File

@@ -0,0 +1,578 @@
// panel.hxx - generic support classes for a 2D panel.
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef __PANEL_HXX
#define __PANEL_HXX
#include <osg/ref_ptr>
#include <osg/StateSet>
#include <osg/Texture2D>
#include <simgear/compiler.h>
#include <simgear/props/props.hxx>
#include <simgear/props/propertyObject.hxx>
#include <simgear/structure/SGBinding.hxx>
#include <simgear/math/interpolater.hxx>
#include <simgear/timing/timestamp.hxx>
#include <cmath>
#include <vector>
#include <map>
#include <Main/fg_props.hxx>
class FGPanelInstrument;
class fntFont;
class DCLGPS;
class IntRect;
////////////////////////////////////////////////////////////////////////
// Texture management.
////////////////////////////////////////////////////////////////////////
/**
* Texture manager (should migrate out into FGFS).
*
* This class ensures that no texture is loaded more than once.
*/
class FGTextureManager
{
public:
static osg::Texture2D* createTexture(const std::string &relativePath,
bool staticTexture = true);
static void addTexture(const std::string &relativePath, osg::Texture2D* texture);
private:
static std::map<std::string,osg::ref_ptr<osg::Texture2D> > _textureMap;
};
/**
* Cropped texture (should migrate out into FGFS).
*
* This structure wraps an SSG texture with cropping information.
*/
class FGCroppedTexture
{
public:
FGCroppedTexture ();
FGCroppedTexture (const std::string &path,
float _minX = 0.0, float _minY = 0.0,
float _maxX = 1.0, float _maxY = 1.0);
virtual ~FGCroppedTexture ();
virtual void setPath (const std::string &path) { _path = path; }
virtual const std::string &getPath () const { return _path; }
virtual osg::StateSet* getTexture ();
virtual void setCrop (float minX, float minY, float maxX, float maxY) {
_minX = minX; _minY = minY; _maxX = maxX; _maxY = maxY;
}
virtual float getMinX () const { return _minX; }
virtual float getMinY () const { return _minY; }
virtual float getMaxX () const { return _maxX; }
virtual float getMaxY () const { return _maxY; }
private:
std::string _path;
osg::ref_ptr<osg::StateSet> _texture;
float _minX, _minY, _maxX, _maxY;
};
////////////////////////////////////////////////////////////////////////
// Top-level panel.
////////////////////////////////////////////////////////////////////////
/**
* Instrument panel class.
*
* The panel is a container that has a background texture and holds
* zero or more instruments. The panel will order the instruments to
* redraw themselves when necessary, and will pass mouse clicks on to
* the appropriate instruments for processing.
*/
class FGPanel : public SGReferenced
{
public:
FGPanel ();
virtual ~FGPanel ();
virtual void draw (osg::State& state);
virtual void updateMouseDelay(double dt);
// transfer pointer ownership!!!
virtual void addInstrument (FGPanelInstrument * instrument);
// Background texture.
virtual void setBackground (osg::Texture2D* texture);
// Background multiple textures.
virtual void setMultiBackground (osg::Texture2D* texture, int idx);
virtual void setWidth (int width) { _width = width; }
virtual int getWidth () const { return _width; }
// Full height of panel.
virtual void setHeight (int height) { _height = height; }
virtual int getHeight () const { return _height; }
// X-offset
virtual void setXOffset (int offset);
virtual int getXOffset () const { return _x_offset->getIntValue(); }
// Y-offset.
virtual void setYOffset (int offset);
virtual int getYOffset () const { return _y_offset->getIntValue(); }
// View height.
// virtual void setViewHeight (int height) { _view_height = height; }
// virtual int getViewHeight () const { return _view_height; }
/**
* find the actual logical extend of the panel, including all instruments
* and actions.
*/
void getLogicalExtent(int &x0, int& y0, int& x1, int &y1);
// Handle a mouse click.
virtual bool doMouseAction (int button, int updown, int x, int y);
virtual bool doLocalMouseAction(int button, int updown, int x, int y);
virtual void setDepthTest (bool enable);
bool getAutohide(void) const { return _autohide; };
void setAutohide(bool enable) { _autohide = enable; };
double getAspectScale() const;
private:
void setupVirtualCockpit();
void cleanupVirtualCockpit();
mutable bool _mouseDown;
mutable int _mouseButton, _mouseX, _mouseY;
double _mouseActionRepeat;
mutable FGPanelInstrument * _mouseInstrument;
typedef std::vector<FGPanelInstrument *> instrument_list_type;
int _width;
int _height;
// int _view_height;
SGPropertyNode_ptr _x_offset;
SGPropertyNode_ptr _y_offset;
SGPropertyNode_ptr _jitter;
SGPropertyNode_ptr _flipx;
SGConstPropertyNode_ptr _xsize_node;
SGConstPropertyNode_ptr _ysize_node;
osg::ref_ptr<osg::StateSet> _bg;
osg::ref_ptr<osg::StateSet> _mbg[8];
// List of instruments in panel.
instrument_list_type _instruments;
bool _enable_depth_test;
bool _autohide;
SGPropObjBool _drawPanelHotspots;
};
////////////////////////////////////////////////////////////////////////
// Actions
////////////////////////////////////////////////////////////////////////
/**
* Class for user actions.
*
* The actions are command bindings, like bindings for the keyboard
* or joystick, but they are tied to specific mouse actions in
* rectangular areas of the panel.
*/
class FGPanelAction : public SGConditional
{
public:
FGPanelAction ();
FGPanelAction (int button, int x, int y, int w, int h, bool repeatable);
virtual ~FGPanelAction ();
// Getters.
virtual int getButton () const { return _button; }
virtual int getX () const { return _x; }
virtual int getY () const { return _y; }
virtual int getWidth () const { return _w; }
virtual int getHeight () const { return _h; }
// Setters.
// transfer pointer ownership
virtual void addBinding (SGBinding * binding, int updown);
virtual void setButton (int button) { _button = button; }
virtual void setX (int x) { _x = x; }
virtual void setY (int y) { _y = y; }
virtual void setWidth (int w) { _w = w; }
virtual void setHeight (int h) { _h = h; }
// Check whether we're in the area.
virtual bool inArea (int button, int x, int y)
{
return (button == _button &&
x >= _x &&
x < _x + _w &&
y >= _y &&
y < _y + _h);
}
// Perform the action.
virtual bool doAction (int updown);
private:
typedef std::vector<SGBinding *> binding_list_t;
int _button;
int _x;
int _y;
int _w;
int _h;
bool _repeatable;
int _last_state;
binding_list_t _bindings[2];
};
////////////////////////////////////////////////////////////////////////
// Transformations.
////////////////////////////////////////////////////////////////////////
/**
* A transformation for a layer.
*/
class FGPanelTransformation : public SGConditional
{
public:
enum Type {
XSHIFT,
YSHIFT,
ROTATION
};
FGPanelTransformation ();
virtual ~FGPanelTransformation ();
Type type;
SGConstPropertyNode_ptr node;
float min;
float max;
bool has_mod;
float mod;
float factor;
float offset;
SGInterpTable * table;
};
////////////////////////////////////////////////////////////////////////
// Layers
////////////////////////////////////////////////////////////////////////
/**
* A single layer of a multi-layered instrument.
*
* Each layer can be subject to a series of transformations based
* on current FGFS instrument readings: for example, a texture
* representing a needle can rotate to show the airspeed.
*/
class FGInstrumentLayer : public SGConditional
{
public:
FGInstrumentLayer (int w = -1, int h = -1);
virtual ~FGInstrumentLayer ();
virtual void draw (osg::State& state) = 0;
virtual void transform () const;
virtual int getWidth () const { return _w; }
virtual int getHeight () const { return _h; }
virtual void setWidth (int w) { _w = w; }
virtual void setHeight (int h) { _h = h; }
// Transfer pointer ownership!!
// DEPRECATED
virtual void addTransformation (FGPanelTransformation * transformation);
protected:
int _w, _h;
typedef std::vector<FGPanelTransformation *> transformation_list;
transformation_list _transformations;
};
////////////////////////////////////////////////////////////////////////
// Instruments.
////////////////////////////////////////////////////////////////////////
/**
* Abstract base class for a panel instrument.
*
* A panel instrument consists of zero or more actions, associated
* with mouse clicks in rectangular areas. Currently, the only
* concrete class derived from this is FGLayeredInstrument, but others
* may show up in the future (some complex instruments could be
* entirely hand-coded, for example).
*/
class FGPanelInstrument : public SGConditional
{
public:
FGPanelInstrument ();
FGPanelInstrument (int x, int y, int w, int h);
virtual ~FGPanelInstrument ();
virtual void draw (osg::State& state) = 0;
virtual void drawHotspots(osg::State& state);
virtual void setPosition(int x, int y);
virtual void setSize(int w, int h);
virtual int getXPos () const;
virtual int getYPos () const;
virtual int getWidth () const;
virtual int getHeight () const;
// Coordinates relative to centre.
// Transfer pointer ownership!!
virtual void addAction (FGPanelAction * action);
// Coordinates relative to centre.
virtual bool doMouseAction (int button, int updown, int x, int y);
void extendRect(IntRect& r) const;
protected:
int _x, _y, _w, _h;
typedef std::vector<FGPanelAction *> action_list_type;
action_list_type _actions;
};
/**
* An instrument constructed of multiple layers.
*
* Each individual layer can be rotated or shifted to correspond
* to internal FGFS instrument readings.
*/
class FGLayeredInstrument : public FGPanelInstrument
{
public:
FGLayeredInstrument (int x, int y, int w, int h);
virtual ~FGLayeredInstrument ();
virtual void draw (osg::State& state);
// Transfer pointer ownership!!
virtual int addLayer (FGInstrumentLayer *layer);
virtual int addLayer (const FGCroppedTexture &texture, int w = -1, int h = -1);
// Transfer pointer ownership!!
virtual void addTransformation (FGPanelTransformation * transformation);
protected:
typedef std::vector<FGInstrumentLayer *> layer_list;
layer_list _layers;
};
/**
* An empty-shell instrument that exists soley in
* order to redirect commands from the panel to a
* complex instrument inherited from SGSubsystem.
*
* Currently the only complex instrument is the KLN89,
* which we've hardwired this to for now.
*/
class FGSpecialInstrument : public FGPanelInstrument
{
public:
FGSpecialInstrument(DCLGPS* sb);
//FGSpecialInstrument (int x, int y, int w, int h);
virtual ~FGSpecialInstrument ();
virtual void draw (osg::State& state);
protected:
DCLGPS* complex;
};
/**
* An instrument layer containing a group of sublayers.
*
* This class is useful for gathering together a group of related
* layers, either to hold in an external file or to work under
* the same condition.
*/
class FGGroupLayer : public FGInstrumentLayer
{
public:
FGGroupLayer ();
virtual ~FGGroupLayer ();
virtual void draw (osg::State& state);
// transfer pointer ownership
virtual void addLayer (FGInstrumentLayer * layer);
protected:
std::vector<FGInstrumentLayer *> _layers;
};
/**
* A textured layer of an instrument.
*
* This is a layer holding a single texture. Normally, the texture's
* backgound should be transparent so that lower layers and the panel
* background can show through.
*/
class FGTexturedLayer : public FGInstrumentLayer
{
public:
FGTexturedLayer (int w = -1, int h = -1) : FGInstrumentLayer(w, h) {}
FGTexturedLayer (const FGCroppedTexture &texture, int w = -1, int h = -1);
virtual ~FGTexturedLayer ();
virtual void draw (osg::State& state);
virtual void setTexture (const FGCroppedTexture &texture) {
_texture = texture;
}
virtual const FGCroppedTexture &getTexture () const { return _texture; }
virtual FGCroppedTexture *getTexture() { return &_texture; }
void setEmissive(bool e) { _emissive = e; }
private:
FGCroppedTexture _texture;
bool _emissive;
};
/**
* A text layer of an instrument.
*
* This is a layer holding a string of static and/or generated text.
* It is useful for instruments that have text displays, such as
* a chronometer, GPS, or NavCom radio.
*/
class FGTextLayer : public FGInstrumentLayer
{
public:
enum ChunkType {
TEXT,
TEXT_VALUE,
DOUBLE_VALUE
};
class Chunk : public SGConditional
{
public:
Chunk (const std::string &text, const std::string &fmt = "%s");
Chunk (ChunkType type, const SGPropertyNode * node,
const std::string &fmt = "", float mult = 1.0, float offs = 0.0,
bool truncation = false);
const char * getValue () const;
private:
ChunkType _type;
std::string _text;
SGConstPropertyNode_ptr _node;
std::string _fmt;
float _mult;
float _offs;
bool _trunc;
mutable char _buf[1024];
};
FGTextLayer (int w = -1, int h = -1);
virtual ~FGTextLayer ();
virtual void draw (osg::State& state);
// Transfer pointer!!
virtual void addChunk (Chunk * chunk);
virtual void setColor (float r, float g, float b);
virtual void setPointSize (float size);
virtual void setFontName ( const std::string &name );
virtual void setFont (fntFont * font);
private:
void recalc_value () const;
typedef std::vector<Chunk *> chunk_list;
chunk_list _chunks;
float _color[4];
float _pointSize;
mutable std::string _font_name;
mutable std::string _value;
mutable SGTimeStamp _then;
mutable SGTimeStamp _now;
};
/**
* A group layer that switches among its children.
*
* The first layer that passes its condition will be drawn, and
* any following layers will be ignored.
*/
class FGSwitchLayer : public FGGroupLayer
{
public:
// Transfer pointers!!
FGSwitchLayer ();
virtual void draw (osg::State& state);
};
#endif // __PANEL_HXX
// end of panel.hxx

868
src/Cockpit/panel_io.cxx Normal file
View File

@@ -0,0 +1,868 @@
// panel_io.cxx - I/O for 2D panel.
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <config.h>
#include <string.h> // for strcmp()
#include <simgear/compiler.h>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/debug/ErrorReportingCallback.hxx>
#include <istream>
#include <fstream>
#include <string>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
#include <Main/sentryIntegration.hxx>
#include <GUI/gui.h>
// #include "panel.hxx"
#include "panel_io.hxx"
#include <Instrumentation/KLN89/kln89.hxx>
//built-in layers
#include "built_in/FGMagRibbon.hxx"
using std::istream;
using std::ifstream;
using std::string;
////////////////////////////////////////////////////////////////////////
// Read and construct a panel.
//
// The panel is specified as a regular property list, and each of the
// instruments is its own, separate property list (and thus, a separate
// XML document). The functions in this section read in the files
// as property lists, then extract properties to set up the panel
// itself.
//
// A panel contains zero or more instruments.
//
// An instrument contains one or more layers and zero or more actions.
//
// A layer contains zero or more transformations.
//
// Some special types of layers also contain other objects, such as
// chunks of text or other layers.
//
// There are currently four types of layers:
//
// 1. Textured Layer (type="texture"), the default
// 2. Text Layer (type="text")
// 3. Switch Layer (type="switch")
// 4. Built-in Layer (type="built-in", must also specify class)
//
// The only built-in layer so far is the ribbon for the magnetic compass
// (class="compass-ribbon").
//
// There are three types of actions:
//
// 1. Adjust (type="adjust"), the default
// 2. Swap (type="swap")
// 3. Toggle (type="toggle")
//
// There are three types of transformations:
//
// 1. X shift (type="x-shift"), the default
// 2. Y shift (type="y-shift")
// 3. Rotation (type="rotation")
//
// Each of these may be associated with a property, so that a needle
// will rotate with the airspeed, for example, or may have a fixed
// floating-point value.
////////////////////////////////////////////////////////////////////////
/**
* Read a cropped texture from the instrument's property list.
*
* The x1 and y1 properties give the starting position of the texture
* (between 0.0 and 1.0), and the the x2 and y2 properties give the
* ending position. For example, to use the bottom-left quarter of a
* texture, x1=0.0, y1=0.0, x2=0.5, y2=0.5.
*/
static FGCroppedTexture
readTexture (const SGPropertyNode * node)
{
FGCroppedTexture texture(node->getStringValue("path"),
node->getFloatValue("x1"),
node->getFloatValue("y1"),
node->getFloatValue("x2", 1.0),
node->getFloatValue("y2", 1.0));
SG_LOG(SG_COCKPIT, SG_DEBUG, "Read texture " << node->getNameString());
return texture;
}
/**
* Test for a condition in the current node.
*/
////////////////////////////////////////////////////////////////////////
// Read a condition and use it if necessary.
////////////////////////////////////////////////////////////////////////
static void
readConditions (SGConditional *component, const SGPropertyNode *node)
{
const SGPropertyNode * conditionNode = node->getChild("condition");
if (conditionNode != 0)
// The top level is implicitly AND
component->setCondition(sgReadCondition(globals->get_props(),
conditionNode) );
}
/**
* Read an action from the instrument's property list.
*
* The action will be performed when the user clicks a mouse button
* within the specified region of the instrument. Actions always work
* by modifying the value of a property (see the SGPropertyNode
* class).
*
* The following action types are defined:
*
* "adjust" - modify the value of a floating-point property by
* the increment specified. This is the default.
*
* "swap" - swap the values of two-floating-point properties.
*
* "toggle" - toggle the value of a boolean property between true and
* false.
*
* For the adjust action, it is possible to specify an increment
* (use a negative number for a decrement), a minimum allowed value,
* a maximum allowed value, and a flag to indicate whether the value
* should freeze or wrap-around when it reachs the minimum or maximum.
*
* The action will be scaled automatically if the instrument is not
* being drawn at its regular size.
*/
static FGPanelAction *
readAction (const SGPropertyNode * node, float w_scale, float h_scale)
{
unsigned int i, j;
SGPropertyNode *binding;
vector<SGPropertyNode_ptr>bindings = node->getChildren("binding");
// button-less actions are fired initially
if (!node->hasValue("w") || !node->hasValue("h")) {
for (i = 0; i < bindings.size(); i++) {
SGBinding b(bindings[i], globals->get_props());
b.fire();
}
return 0;
}
string name = node->getStringValue("name");
int button = node->getIntValue("button");
int x = int(node->getIntValue("x") * w_scale);
int y = int(node->getIntValue("y") * h_scale);
int w = int(node->getIntValue("w") * w_scale);
int h = int(node->getIntValue("h") * h_scale);
bool repeatable = node->getBoolValue("repeatable", true);
FGPanelAction * action = new FGPanelAction(button, x, y, w, h, repeatable);
SGPropertyNode * dest = fgGetNode("/sim/bindings/panel", true);
for (i = 0; i < bindings.size(); i++) {
SG_LOG(SG_INPUT, SG_BULK, "Reading binding "
<< bindings[i]->getStringValue("command"));
j = 0;
while (dest->getChild("binding", j))
j++;
binding = dest->getChild("binding", j, true);
copyProperties(bindings[i], binding);
action->addBinding(new SGBinding(binding, globals->get_props()), 0);
}
if (node->hasChild("mod-up")) {
bindings = node->getChild("mod-up")->getChildren("binding");
for (i = 0; i < bindings.size(); i++) {
j = 0;
while (dest->getChild("binding", j))
j++;
binding = dest->getChild("binding", j, true);
copyProperties(bindings[i], binding);
action->addBinding(new SGBinding(binding, globals->get_props()), 1);
}
}
readConditions(action, node);
return action;
}
/**
* Read a transformation from the instrument's property list.
*
* The panel module uses the transformations to slide or spin needles,
* knobs, and other indicators, and to place layers in the correct
* positions. Every layer starts centered exactly on the x,y co-ordinate,
* and many layers need to be moved or rotated simply to display the
* instrument correctly.
*
* There are three types of transformations:
*
* "x-shift" - move the layer horizontally.
*
* "y-shift" - move the layer vertically.
*
* "rotation" - rotate the layer.
*
* Each transformation may have a fixed offset, and may also have
* a floating-point property value to add to the offset. The
* floating-point property may be clamped to a minimum and/or
* maximum range and scaled (after clamping).
*
* Note that because of the way OpenGL works, transformations will
* appear to be applied backwards.
*/
static FGPanelTransformation *
readTransformation (const SGPropertyNode * node, float w_scale, float h_scale)
{
FGPanelTransformation * t = new FGPanelTransformation;
string name = node->getNameString();
string type = node->getStringValue("type");
string propName = node->getStringValue("property", "");
SGPropertyNode * target = 0;
if (type.empty()) {
SG_LOG( SG_COCKPIT, SG_BULK,
"No type supplied for transformation " << name
<< " assuming \"rotation\"" );
type = "rotation";
}
if (!propName.empty()) {
target = fgGetNode(propName.c_str(), true);
}
t->node = target;
t->min = node->getFloatValue("min", -9999999);
t->max = node->getFloatValue("max", 99999999);
t->has_mod = node->hasChild("modulator");
if (t->has_mod)
t->mod = node->getFloatValue("modulator");
t->factor = node->getFloatValue("scale", 1.0);
t->offset = node->getFloatValue("offset", 0.0);
// Check for an interpolation table
const SGPropertyNode * trans_table = node->getNode("interpolation");
if (trans_table != 0) {
SG_LOG( SG_COCKPIT, SG_DEBUG, "Found interpolation table with "
<< trans_table->nChildren() << " children" );
t->table = new SGInterpTable(trans_table);
} else {
t->table = 0;
}
// Move the layer horizontally.
if (type == "x-shift") {
t->type = FGPanelTransformation::XSHIFT;
// t->min *= w_scale; //removed by Martin Dressler
// t->max *= w_scale; //removed by Martin Dressler
t->offset *= w_scale;
t->factor *= w_scale; //Added by Martin Dressler
}
// Move the layer vertically.
else if (type == "y-shift") {
t->type = FGPanelTransformation::YSHIFT;
//t->min *= h_scale; //removed
//t->max *= h_scale; //removed
t->offset *= h_scale;
t->factor *= h_scale; //Added
}
// Rotate the layer. The rotation
// is in degrees, and does not need
// to scale with the instrument size.
else if (type == "rotation") {
t->type = FGPanelTransformation::ROTATION;
}
else {
SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized transformation type " << type );
delete t;
return 0;
}
readConditions(t, node);
SG_LOG( SG_COCKPIT, SG_DEBUG, "Read transformation " << name );
return t;
}
/**
* Read a chunk of text from the instrument's property list.
*
* A text layer consists of one or more chunks of text. All chunks
* share the same font size and color (and eventually, font), but
* each can come from a different source. There are three types of
* text chunks:
*
* "literal" - a literal text string (the default)
*
* "text-value" - the current value of a string property
*
* "number-value" - the current value of a floating-point property.
*
* All three may also include a printf-style format string.
*/
FGTextLayer::Chunk *
readTextChunk (const SGPropertyNode * node)
{
FGTextLayer::Chunk * chunk;
string name = node->getStringValue("name");
string type = node->getStringValue("type");
string format = node->getStringValue("format");
// Default to literal text.
if (type.empty()) {
SG_LOG( SG_COCKPIT, SG_INFO, "No type provided for text chunk " << name
<< " assuming \"literal\"");
type = "literal";
}
// A literal text string.
if (type == "literal") {
string text = node->getStringValue("text");
chunk = new FGTextLayer::Chunk(text, format);
}
// The value of a string property.
else if (type == "text-value") {
SGPropertyNode * target =
fgGetNode(node->getStringValue("property"), true);
chunk = new FGTextLayer::Chunk(FGTextLayer::TEXT_VALUE, target, format);
}
// The value of a float property.
else if (type == "number-value") {
string propName = node->getStringValue("property");
float scale = node->getFloatValue("scale", 1.0);
float offset = node->getFloatValue("offset", 0.0);
bool truncation = node->getBoolValue("truncate", false);
SGPropertyNode * target = fgGetNode(propName.c_str(), true);
chunk = new FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE, target,
format, scale, offset, truncation);
}
// Unknown type.
else {
SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized type " << type
<< " for text chunk " << name );
return 0;
}
readConditions(chunk, node);
return chunk;
}
/**
* Read a single layer from an instrument's property list.
*
* Each instrument consists of one or more layers stacked on top
* of each other; the lower layers show through only where the upper
* layers contain an alpha component. Each layer can be moved
* horizontally and vertically and rotated using transformations.
*
* This module currently recognizes four kinds of layers:
*
* "texture" - a layer containing a texture (the default)
*
* "text" - a layer containing text
*
* "switch" - a layer that switches between two other layers
* based on the current value of a boolean property.
*
* "built-in" - a hard-coded layer supported by C++ code in FlightGear.
*
* Currently, the only built-in layer class is "compass-ribbon".
*/
static FGInstrumentLayer *
readLayer (const SGPropertyNode * node, float w_scale, float h_scale)
{
FGInstrumentLayer * layer = NULL;
string name = node->getStringValue("name");
string type = node->getStringValue("type");
int w = node->getIntValue("w", -1);
int h = node->getIntValue("h", -1);
bool emissive = node->getBoolValue("emissive", false);
if (w != -1)
w = int(w * w_scale);
if (h != -1)
h = int(h * h_scale);
if (type.empty()) {
SG_LOG( SG_COCKPIT, SG_BULK,
"No type supplied for layer " << name
<< " assuming \"texture\"" );
type = "texture";
}
// A textured instrument layer.
if (type == "texture") {
FGCroppedTexture texture = readTexture(node->getNode("texture"));
layer = new FGTexturedLayer(texture, w, h);
if (emissive) {
FGTexturedLayer *tl=(FGTexturedLayer*)layer;
tl->setEmissive(true);
}
}
// A group of sublayers.
else if (type == "group") {
layer = new FGGroupLayer();
for (int i = 0; i < node->nChildren(); i++) {
const SGPropertyNode * child = node->getChild(i);
if (child->getNameString() == "layer")
((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale));
}
}
// A textual instrument layer.
else if (type == "text") {
FGTextLayer * tlayer = new FGTextLayer(w, h); // FIXME
// Set the text color.
float red = node->getFloatValue("color/red", 0.0);
float green = node->getFloatValue("color/green", 0.0);
float blue = node->getFloatValue("color/blue", 0.0);
tlayer->setColor(red, green, blue);
// Set the point size.
float pointSize = node->getFloatValue("point-size", 10.0) * w_scale;
tlayer->setPointSize(pointSize);
// Set the font.
string fontName = node->getStringValue("font", "Helvetica");
tlayer->setFontName(fontName);
const SGPropertyNode * chunk_group = node->getNode("chunks");
if (chunk_group != 0) {
int nChunks = chunk_group->nChildren();
for (int i = 0; i < nChunks; i++) {
const SGPropertyNode * node = chunk_group->getChild(i);
if (node->getNameString() == "chunk") {
FGTextLayer::Chunk * chunk = readTextChunk(node);
if (chunk != 0)
tlayer->addChunk(chunk);
} else {
SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getNameString()
<< " in chunks" );
}
}
layer = tlayer;
}
}
// A switch instrument layer.
else if (type == "switch") {
layer = new FGSwitchLayer();
for (int i = 0; i < node->nChildren(); i++) {
const SGPropertyNode * child = node->getChild(i);
if (child->getNameString() == "layer")
((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale));
}
}
// A built-in instrument layer.
else if (type == "built-in") {
string layerclass = node->getStringValue("class");
if (layerclass == "mag-ribbon") {
layer = new FGMagRibbon(w, h);
}
else if (layerclass.empty()) {
SG_LOG( SG_COCKPIT, SG_ALERT, "No class provided for built-in layer "
<< name );
return 0;
}
else {
SG_LOG( SG_COCKPIT, SG_ALERT, "Unknown built-in layer class "
<< layerclass);
return 0;
}
}
// An unknown type.
else {
SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized layer type " << type );
delete layer;
return 0;
}
//
// Get the transformations for each layer.
//
const SGPropertyNode * trans_group = node->getNode("transformations");
if (trans_group != 0) {
int nTransformations = trans_group->nChildren();
for (int i = 0; i < nTransformations; i++) {
const SGPropertyNode * node = trans_group->getChild(i);
if (node->getNameString() == "transformation") {
FGPanelTransformation * t = readTransformation(node, w_scale, h_scale);
if (t != 0)
layer->addTransformation(t);
} else {
SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getNameString()
<< " in transformations" );
}
}
}
readConditions(layer, node);
SG_LOG( SG_COCKPIT, SG_DEBUG, "Read layer " << name );
return layer;
}
/**
* Read an instrument from a property list.
*
* The instrument consists of a preferred width and height
* (the panel may override these), together with a list of layers
* and a list of actions to be performed when the user clicks
* the mouse over the instrument. All co-ordinates are relative
* to the instrument's position, so instruments are fully relocatable;
* likewise, co-ordinates for actions and transformations will be
* scaled automatically if the instrument is not at its preferred size.
*/
static FGPanelInstrument *
readInstrument (const SGPropertyNode * node, const SGPath& path)
{
const string name = node->getStringValue("name");
int x = node->getIntValue("x", -1);
int y = node->getIntValue("y", -1);
int real_w = node->getIntValue("w", -1);
int real_h = node->getIntValue("h", -1);
int w = node->getIntValue("w-base", -1);
int h = node->getIntValue("h-base", -1);
if (x == -1 || y == -1) {
simgear::reportFailure(simgear::LoadFailure::Misconfigured, simgear::ErrorCode::AircraftSystems,
"Panel x and y positions must be specified and > 0", path);
return 0;
}
float w_scale = 1.0;
float h_scale = 1.0;
if (real_w != -1) {
w_scale = float(real_w) / float(w);
w = real_w;
}
if (real_h != -1) {
h_scale = float(real_h) / float(h);
h = real_h;
}
SG_LOG( SG_COCKPIT, SG_DEBUG, "Reading instrument " << name );
FGLayeredInstrument * instrument =
new FGLayeredInstrument(x, y, w, h);
//
// Get the actions for the instrument.
//
const SGPropertyNode * action_group = node->getNode("actions");
if (action_group != 0) {
int nActions = action_group->nChildren();
for (int i = 0; i < nActions; i++) {
const SGPropertyNode * node = action_group->getChild(i);
if (node->getNameString() == "action") {
FGPanelAction * action = readAction(node, w_scale, h_scale);
if (action != 0)
instrument->addAction(action);
} else {
SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getNameString()
<< " in actions" );
}
}
}
//
// Get the layers for the instrument.
//
const SGPropertyNode * layer_group = node->getNode("layers");
if (layer_group != 0) {
int nLayers = layer_group->nChildren();
for (int i = 0; i < nLayers; i++) {
const SGPropertyNode * node = layer_group->getChild(i);
if (node->getNameString() == "layer") {
FGInstrumentLayer * layer = readLayer(node, w_scale, h_scale);
if (layer != 0)
instrument->addLayer(layer);
} else {
SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getNameString()
<< " in layers" );
}
}
}
readConditions(instrument, node);
return instrument;
}
/**
* Construct the panel from a property tree.
*/
FGPanel *
readPanel (const SGPropertyNode * root, const SGPath& path)
{
SG_LOG( SG_COCKPIT, SG_INFO, "Reading properties for panel " <<
root->getStringValue("name", "[Unnamed Panel]") );
FGPanel * panel = new FGPanel();
panel->setWidth(root->getIntValue("w", 1024));
panel->setHeight(root->getIntValue("h", 443));
//
// Grab the visible external viewing area, default to
//
// panel->setViewHeight(root->getIntValue("view-height",
// 768 - panel->getHeight() + 2));
//
// Grab the panel's initial offsets, default to 0, 0.
//
if (!fgHasNode("/sim/panel/x-offset"))
fgSetInt("/sim/panel/x-offset", root->getIntValue("x-offset", 0));
// conditional removed by jim wilson to allow panel xml code
// with y-offset defined to work...
if (!fgHasNode("/sim/panel/y-offset"))
fgSetInt("/sim/panel/y-offset", root->getIntValue("y-offset", 0));
panel->setAutohide(root->getBoolValue("autohide", true));
//
// Assign the background texture, if any, or a bogus chequerboard.
//
string bgTexture = root->getStringValue("background");
if (bgTexture.empty())
bgTexture = "FOO";
panel->setBackground(FGTextureManager::createTexture(bgTexture.c_str()));
//
// Get multibackground if any...
//
string mbgTexture = root->getStringValue("multibackground[0]");
if (!mbgTexture.empty()) {
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 0);
mbgTexture = root->getStringValue("multibackground[1]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 1);
mbgTexture = root->getStringValue("multibackground[2]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 2);
mbgTexture = root->getStringValue("multibackground[3]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 3);
mbgTexture = root->getStringValue("multibackground[4]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 4);
mbgTexture = root->getStringValue("multibackground[5]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 5);
mbgTexture = root->getStringValue("multibackground[6]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 6);
mbgTexture = root->getStringValue("multibackground[7]");
if (mbgTexture.empty())
mbgTexture = "FOO";
panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 7);
}
//
// Create each instrument.
//
SG_LOG( SG_COCKPIT, SG_DEBUG, "Reading panel instruments" );
const SGPropertyNode * instrument_group = root->getChild("instruments");
if (instrument_group != 0) {
int nInstruments = instrument_group->nChildren();
for (int i = 0; i < nInstruments; i++) {
const SGPropertyNode * node = instrument_group->getChild(i);
if (node->getNameString() == "instrument") {
FGPanelInstrument * instrument = readInstrument(node, path);
if (instrument != 0)
panel->addInstrument(instrument);
} else if (node->getNameString() == "special-instrument") {
//cout << "Special instrument found in instruments section!\n";
const string name = node->getStringValue("name");
if (name == "KLN89 GPS") {
//cout << "Special instrument is KLN89\n";
int x = node->getIntValue("x", -1);
int y = node->getIntValue("y", -1);
int real_w = node->getIntValue("w", -1);
int real_h = node->getIntValue("h", -1);
// int w = node->getIntValue("w-base", -1);
// int h = node->getIntValue("h-base", -1);
if (x == -1 || y == -1) {
SG_LOG( SG_COCKPIT, SG_ALERT,
"x and y positions must be specified and > 0" );
return 0;
}
// float w_scale = 1.0;
// float h_scale = 1.0;
if (real_w != -1) {
// w_scale = float(real_w) / float(w);
// w = real_w;
}
if (real_h != -1) {
// h_scale = float(real_h) / float(h);
// h = real_h;
}
SG_LOG( SG_COCKPIT, SG_BULK, "Reading instrument " << name );
// Warning - hardwired size!!!
RenderArea2D* instrument = new RenderArea2D(158, 40, 158, 40, x, y);
KLN89* gps = (KLN89*)globals->get_subsystem("kln89");
if (gps == NULL) {
gps = new KLN89(instrument);
globals->add_subsystem("kln89", gps, SGSubsystemMgr::GENERAL);
}
//gps->init(); // init seems to get called automagically.
FGSpecialInstrument* gpsinst = new FGSpecialInstrument(gps);
panel->addInstrument(gpsinst);
} else {
SG_LOG( SG_COCKPIT, SG_WARN, "Unknown special instrument found" );
}
} else {
SG_LOG( SG_COCKPIT, SG_WARN, "Skipping " << node->getNameString()
<< " in instruments section" );
}
}
}
SG_LOG( SG_COCKPIT, SG_BULK, "Done reading panel instruments" );
//
// Return the new panel.
//
return panel;
}
/**
* Read a panel from a property list.
*
* Each panel instrument will appear in its own, separate
* property list. The top level simply names the panel and
* places the instruments in their appropriate locations (and
* optionally resizes them if necessary).
*
* Returns 0 if the read fails for any reason.
*/
FGPanel *
fgReadPanel (istream &input)
{
SGPropertyNode root;
try {
readProperties(input, &root);
} catch (const sg_exception &e) {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::AircraftSystems,
"Failed to load aircraft panel:" + e.getFormattedMessage());
return 0;
}
return readPanel(&root, SGPath{});
}
/**
* Read a panel from a property list.
*
* This function opens a stream to a file, then invokes the
* main fgReadPanel() function.
*/
FGPanel *
fgReadPanel (const string &relative_path)
{
SGPath path = globals->resolve_aircraft_path(relative_path);
SGPropertyNode root;
if (!path.exists()) {
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AircraftSystems,
"Missing panel file:" + relative_path, path);
return nullptr;
}
try {
readProperties(path, &root);
} catch (const sg_exception &e) {
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::AircraftSystems,
"Error parsing panel file:" + e.getFormattedMessage(), path);
return 0;
}
return readPanel(&root, path);
}
// end of panel_io.cxx

38
src/Cockpit/panel_io.hxx Normal file
View File

@@ -0,0 +1,38 @@
// panel_io.cxx - I/O for 2D panel.
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef __PANEL_IO_HXX
#define __PANEL_IO_HXX
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/compiler.h>
#include <iosfwd>
#include <string>
class FGPanel;
extern FGPanel * fgReadPanel (std::istream &input);
extern FGPanel * fgReadPanel (const std::string &relative_path);
#endif // __PANEL_IO_HXX

View File

@@ -0,0 +1,426 @@
// RenderArea2D.cxx - a class to manage 2D polygon-based drawing
// for a complex instrument (eg. GPS).
//
// Written by David Luff, started 2005.
//
// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "render_area_2d.hxx"
RA2DPrimitive::RA2DPrimitive() {
invert = false;
debug = false;
}
RenderArea2D::RenderArea2D(int logx, int logy, int sizex, int sizey, int posx, int posy) {
_logx = logx;
_logy = logy;
_sizex = sizex;
_sizey = sizey;
_posx = posx;
_posy = posy;
_clipx1 = 0;
_clipx2 = _logx - 1;
_clipy1 = 0;
_clipy2 = _logy - 1;
// Default to black background / white text.
_backgroundColor[0] = 0.0;
_backgroundColor[1] = 0.0;
_backgroundColor[2] = 0.0;
_backgroundColor[3] = 1.0;
_pixelColor[0] = 1.0;
_pixelColor[1] = 1.0;
_pixelColor[2] = 1.0;
_pixelColor[3] = 1.0;
_ra2d_debug = false;
}
void RenderArea2D::Draw(osg::State& state) {
static osg::ref_ptr<osg::StateSet> renderArea2DStateSet;
if(!renderArea2DStateSet.valid()) {
renderArea2DStateSet = new osg::StateSet;
renderArea2DStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
renderArea2DStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
}
state.pushStateSet(renderArea2DStateSet.get());
state.apply();
state.setActiveTextureUnit(0);
state.setClientActiveTextureUnit(0);
// DCL - the 2 lines below are copied verbatim from the hotspot drawing code.
// I am not sure if they are needed here or not.
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_COLOR_MATERIAL);
// FIXME - disabling all clip planes causes bleed-through through the splash screen.
glDisable(GL_CLIP_PLANE0);
glDisable(GL_CLIP_PLANE1);
glDisable(GL_CLIP_PLANE2);
glDisable(GL_CLIP_PLANE3);
DoDrawBackground();
for(unsigned int i = 0; i < drawing_list.size(); ++i) {
RA2DPrimitive prim = drawing_list[i];
switch(prim.type) {
case RA2D_LINE:
DoDrawLine(prim.x1, prim.y1, prim.x2, prim.y2);
break;
case RA2D_QUAD:
if(prim.debug) {
//cout << "Clipping = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
//cout << "Drawing quad " << prim.x1 << ", " << prim.y1 << " to " << prim.x2 << ", " << prim.y2 << '\n';
}
DoDrawQuad(prim.x1, prim.y1, prim.x2, prim.y2, prim.invert);
break;
case RA2D_PIXEL:
DoDrawPixel(prim.x1, prim.y1, prim.invert);
break;
}
}
drawing_list.clear();
glPopAttrib();
state.popStateSet();
state.apply();
state.setActiveTextureUnit(0);
state.setClientActiveTextureUnit(0);
}
void RenderArea2D::Flush() {
drawing_list.clear();
}
void RenderArea2D::SetPixelColor(const float* rgba) {
_pixelColor[0] = rgba[0];
_pixelColor[1] = rgba[1];
_pixelColor[2] = rgba[2];
_pixelColor[3] = rgba[3];
}
// Set clipping region in logical units
void RenderArea2D::SetClipRegion(int x1, int y1, int x2, int y2) {
_clipx1 = x1;
_clipx2 = x2;
_clipy1 = y1;
_clipy2 = y2;
//cout << "Set clip region, clip region = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
}
// Set clip region to be the same as the rendered area (default)
void RenderArea2D::ResetClipRegion() {
_clipx1 = 0;
_clipx2 = _logx - 1;
_clipy1 = 0;
_clipy2 = _logy - 1;
//cout << "Reset clip region, clip region = " << _clipx1 << ", " << _clipy1 << " to " << _clipx2 << ", " << _clipy2 << '\n';
}
void RenderArea2D::SetPosition(int posx, int posy) {
_posx = posx;
_posy = posy;
}
void RenderArea2D::SetLogicalSize(int logx, int logy) {
_logx = logx;
_logy = logy;
}
void RenderArea2D::SetActualSize(int sizex, int sizey) {
_sizex = sizex;
_sizey = sizey;
}
void RenderArea2D::DrawPixel(int x, int y, bool invert) {
// Clip
if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return;
RA2DPrimitive prim;
prim.x1 = x;
prim.y1 = y;
prim.x2 = 0;
prim.y2 = 0;
prim.type = RA2D_PIXEL;
prim.invert = invert;
drawing_list.push_back(prim);
}
void RenderArea2D::DoDrawPixel(int x, int y, bool invert) {
// Clip. In theory this shouldn't be necessary, since all input is clipped before adding
// to the drawing list, but it ensures that any errors in clipping lines etc will only
// spill over the clip area within the instrument, and still be clipped from straying
// outside the instrument.
if(x < _clipx1 || x > _clipx2 || y < _clipy1 || y > _clipy2) return;
// Scale to position within background
float fx1 = (float)x, fy1 = (float)y;
float rx = (float)_sizex / (float)_logx;
float ry = (float)_sizey / (float)_logy;
fx1 *= rx;
fy1 *= ry;
float fx2 = fx1 + rx;
float fy2 = fy1 + ry;
// Translate to final position
fx1 += (float)_posx;
fx2 += (float)_posx;
fy1 += (float)_posy;
fy2 += (float)_posy;
//cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n';
SetRenderColor(invert ? _backgroundColor : _pixelColor);
SGVec2f corners[4] = {
SGVec2f(fx1, fy1),
SGVec2f(fx2, fy1),
SGVec2f(fx2, fy2),
SGVec2f(fx1, fy2)
};
RenderQuad(corners);
}
void RenderArea2D::DrawLine(int x1, int y1, int x2, int y2) {
// We need to clip the line to the current region before storing it in the drawing
// list, since when we come to actually draw it the clip region may have changed.
// Liang-Barsky clipping algorithm
int p[4], q[4];
float u1 = 0.0f, u2 = 1.0f;
p[0] = -(x2 - x1); q[0] = (x1 - _clipx1);
p[1] = (x2 - x1); q[1] = (_clipx2 - x1);
p[2] = -(y2 - y1); q[2] = (y1 - _clipy1);
p[3] = (y2 - y1); q[3] = (_clipy2 - y1);
for(int i=0; i<4; ++i) {
if(p[i] == 0) {
if(q[i] < 0) {
// Then we have a trivial rejection of a line parallel to a clip plane
// completely outside the clip region.
return;
}
} else if(p[i] < 0) {
float r = (float)q[i]/(float)p[i];
u1 = (u1 > r ? u1 : r);
} else { // p[i] > 0
float r = (float)q[i]/(float)p[i];
u2 = (u2 < r ? u2 : r);
}
if(u1 > u2) {
// Then the line is completely outside the clip area.
return;
}
}
float fx1 = x1 + u1 * (float)(x2 - x1);
float fy1 = y1 + u1 * (float)(y2 - y1);
float fx2 = x1 + u2 * (float)(x2 - x1);
float fy2 = y1 + u2 * (float)(y2 - y1);
x1 = (int)(fx1 + 0.5);
y1 = (int)(fy1 + 0.5);
x2 = (int)(fx2 + 0.5);
y2 = (int)(fy2 + 0.5);
RA2DPrimitive prim;
prim.x1 = x1;
prim.y1 = y1;
prim.x2 = x2;
prim.y2 = y2;
prim.type = RA2D_LINE;
prim.invert = false;
drawing_list.push_back(prim);
}
void RenderArea2D::DoDrawLine(int x1, int y1, int x2, int y2) {
// Crude implementation of Bresenham line drawing algorithm.
// Our lines are non directional, so first order the points x-direction-wise to leave only 4 octants to consider.
if(x2 < x1) {
int tmp_x = x1;
int tmp_y = y1;
x1 = x2;
y1 = y2;
x2 = tmp_x;
y2 = tmp_y;
}
bool flip_y = (y1 > y2 ? true : false);
int dx = x2 - x1;
int dy = (flip_y ? y1 - y2 : y2 - y1);
if(dx > dy) {
// push the x dir
int y = y1;
int yn = dx/2;
for(int x=x1; x<=x2; ++x) {
DoDrawPixel(x, y);
yn += dy;
if(yn >= dx) {
yn -= dx;
y = (flip_y ? y - 1 : y + 1);
}
}
} else {
// push the y dir
int x = x1;
int xn = dy/2;
// Must be a more elegant way to roll the next two cases into one!
if(flip_y) {
for(int y=y1; y>=y2; --y) {
DoDrawPixel(x, y);
xn += dx;
if(xn >= dy) {
xn -= dy;
x++;
}
}
} else {
for(int y=y1; y<=y2; ++y) {
DoDrawPixel(x, y);
xn += dx;
if(xn >= dy) {
xn -= dy;
x++;
}
}
}
}
}
void RenderArea2D::DrawQuad(int x1, int y1, int x2, int y2, bool invert) {
// Force the input to be ordered with x1 < x2 and y1 < y2.
if(x1 > x2) {
int x = x2;
x2 = x1;
x1 = x;
}
if(y1 > y2) {
int y = y2;
y2 = y1;
y1 = y;
}
// Clip the input to the current drawing region.
x1 = x1 < _clipx1 ? _clipx1 : x1;
if(x1 > _clipx2) { return; }
x2 = x2 > _clipx2 ? _clipx2 : x2;
if(x2 < _clipx1) { return; }
y1 = y1 < _clipy1 ? _clipy1 : y1;
if(y1 > _clipy2) { return; }
y2 = y2 > _clipy2 ? _clipy2 : y2;
if(y2 < _clipy1) { return; }
RA2DPrimitive prim;
prim.x1 = x1;
prim.y1 = y1;
prim.x2 = x2;
prim.y2 = y2;
prim.type = RA2D_QUAD;
prim.invert = invert;
if(_ra2d_debug) prim.debug = true;
drawing_list.push_back(prim);
}
void RenderArea2D::DoDrawQuad(int x1, int y1, int x2, int y2, bool invert) {
// Scale to position within background
float fx1 = (float)x1, fy1 = (float)y1;
float fx2 = (float)x2, fy2 = (float)y2;
float rx = (float)_sizex / (float)_logx;
float ry = (float)_sizey / (float)_logy;
fx1 *= rx;
fy1 *= ry;
fx2 *= rx;
fy2 *= ry;
fx2 += rx;
fy2 += ry;
// Translate to final position
fx1 += (float)_posx;
fx2 += (float)_posx;
fy1 += (float)_posy;
fy2 += (float)_posy;
//cout << "DP: " << fx1 << ", " << fy1 << " ... " << fx2 << ", " << fy2 << '\n';
SetRenderColor(invert ? _backgroundColor : _pixelColor);
SGVec2f corners[4] = {
SGVec2f(fx1, fy1),
SGVec2f(fx2, fy1),
SGVec2f(fx2, fy2),
SGVec2f(fx1, fy2)
};
RenderQuad(corners);
}
void RenderArea2D::DrawBackground() {
// Currently a NO-OP
}
void RenderArea2D::DoDrawBackground() {
SetRenderColor(_backgroundColor);
SGVec2f corners[4] = {
SGVec2f(_posx, _posy),
SGVec2f(_posx + _sizex, _posy),
SGVec2f(_posx + _sizex, _posy + _sizey),
SGVec2f(_posx, _posy + _sizey)
};
RenderQuad(corners);
}
// -----------------------------------------
//
// Actual drawing routines copied from Atlas
//
// -----------------------------------------
void RenderArea2D::SetRenderColor( const float *rgba ) {
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, rgba);
glColor4fv( rgba );
}
void RenderArea2D::RenderQuad( const SGVec2f *p) {
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 0.0f);
glVertex2fv( p[0].data() );
glVertex2fv( p[1].data() );
glVertex2fv( p[2].data() );
glVertex2fv( p[3].data() );
glEnd();
}
void RenderArea2D::RenderQuad( const SGVec2f *p, const SGVec4f *color ) {
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 0.0f);
glColor4fv( color[0].data() ); glVertex2fv( p[0].data() );
glColor4fv( color[1].data() ); glVertex2fv( p[1].data() );
glColor4fv( color[2].data() ); glVertex2fv( p[2].data() );
glColor4fv( color[3].data() ); glVertex2fv( p[3].data() );
glEnd();
}

View File

@@ -0,0 +1,108 @@
// RenderArea2D.hxx - a class to manage 2D polygon-based drawing
// for a complex instrument (eg. GPS).
//
// Written by David Luff, started 2005.
//
// Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#pragma once
#include <osg/ref_ptr>
#include <osg/State>
#include <osg/StateSet>
#include <simgear/compiler.h>
#include <simgear/math/SGMath.hxx>
#include <vector>
enum RA2DDrawingType {
RA2D_LINE,
RA2D_QUAD,
RA2D_PIXEL
};
struct RA2DPrimitive {
RA2DPrimitive();
RA2DDrawingType type;
int x1, y1, x2, y2;
bool invert;
bool debug;
};
class RenderArea2D {
public:
RenderArea2D(int logx, int logy, int sizex, int sizey, int posx, int posy);
~RenderArea2D();
void SetPixelColor(const float* rgba);
void SetBackgroundColor(const float* rgba);
void SetPosition(int posx, int posy);
void SetLogicalSize(int logx, int logy);
void SetActualSize(int sizex, int sizey);
// Set clipping region in logical units
void SetClipRegion(int x1, int y1, int x2, int y2);
// Set clip region to be the same as the rendered area (default)
void ResetClipRegion();
// The DrawXXX functions place the shapes in the buffer, specified
// in logical units, and clipped to the current clip region.
void DrawPixel(int x, int y, bool invert = false);
void DrawLine(int x1, int y1, int x2, int y2);
void DrawQuad(int x1, int y1, int x2, int y2, bool invert = false);
void DrawBackground();
// Call Draw to have the buffer contents drawn and then cleared.
void Draw(osg::State& state);
// Clear the buffer contents
void Flush();
// Turn debugging on or off.
inline void SetDebugging(bool b) { _ra2d_debug = b; }
private:
int _logx, _logy; // logical size of rendered area
int _sizex, _sizey; // Actual size of rendered area
int _posx, _posy; // Position of rendered area
int _clipx1, _clipx2, _clipy1, _clipy2; // Current clipping region in *logical* units
float _backgroundColor[4];
float _pixelColor[4];
// Drawing specified in logical units
void DoDrawPixel(int x, int y, bool invert = false);
void DoDrawLine(int x1, int y1, int x2, int y2);
void DoDrawQuad(int x1, int y1, int x2, int y2, bool invert = false);
void DoDrawBackground();
// Actual rendering routines copied from Atlas
void SetRenderColor( const float *rgb );
void RenderQuad( const SGVec2f *p);
void RenderQuad( const SGVec2f *p, const SGVec4f *color );
std::vector<RA2DPrimitive> drawing_list;
// Control whether to output debugging output
bool _ra2d_debug;
};

1127
src/Cockpit/wxradar.cxx Normal file

File diff suppressed because it is too large Load Diff

208
src/Cockpit/wxradar.hxx Normal file
View File

@@ -0,0 +1,208 @@
// Wx Radar background texture
//
// Written by Harald JOHNSEN, started May 2005.
// With major amendments by Vivian MEAZZA May 2007
// Ported to OSG by Tim MOORE Jun 2007
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef _INST_WXRADAR_HXX
#define _INST_WXRADAR_HXX
#include <osg/ref_ptr>
#include <osg/Geode>
#include <osg/Texture2D>
#include <osgText/Text>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <vector>
#include <string>
class FGODGauge;
class wxRadarBg : public SGSubsystem,
public SGPropertyChangeListener
{
public:
wxRadarBg(SGPropertyNode *node);
wxRadarBg();
virtual ~wxRadarBg();
// Subsystem API.
void init() override;
void shutdown() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "radar"; }
virtual void valueChanged(SGPropertyNode *);
protected:
std::string _name;
int _num;
double _time;
double _interval;
double _elapsed_time;
double _persistance;
SGPropertyNode_ptr _serviceable_node;
SGPropertyNode_ptr _sceneryLoaded;
SGPropertyNode_ptr _Instrument;
SGPropertyNode_ptr _radar_mode_control_node;
SGPropertyNode_ptr _user_lat_node;
SGPropertyNode_ptr _user_lon_node;
SGPropertyNode_ptr _user_heading_node;
SGPropertyNode_ptr _user_alt_node;
FGODGauge *_odg;
typedef struct {
double bearing;
double range;
double elevation;
double bumpiness;
double elapsed_time;
} ground_echo;
typedef std::vector <ground_echo*> ground_echo_vector_type;
typedef ground_echo_vector_type::iterator ground_echo_vector_iterator;
ground_echo_vector_type ground_echoes;
ground_echo_vector_iterator ground_echoes_iterator;
// Convenience function for creating a property node with a
// default value
template<typename DefaultType>
SGPropertyNode *getInstrumentNode(const char *name, DefaultType value);
private:
std::string _texture_path;
typedef enum { ARC, MAP, PLAN, ROSE, BSCAN} DisplayMode;
DisplayMode _display_mode;
float _range_nm;
float _scale; // factor to convert nm to display units
float _angle_offset;
float _view_heading;
float _x_offset, _y_offset;
double _radar_ref_rng;
double _lat, _lon;
double _antenna_ht;
SGPropertyNode_ptr _Tacan;
SGPropertyNode_ptr _Radar_controls;
SGPropertyNode_ptr _user_speed_east_fps_node;
SGPropertyNode_ptr _user_speed_north_fps_node;
SGPropertyNode_ptr _tacan_serviceable_node;
SGPropertyNode_ptr _tacan_distance_node;
SGPropertyNode_ptr _tacan_name_node;
SGPropertyNode_ptr _tacan_bearing_node;
SGPropertyNode_ptr _tacan_in_range_node;
SGPropertyNode_ptr _radar_weather_node;
SGPropertyNode_ptr _radar_position_node;
SGPropertyNode_ptr _radar_data_node;
SGPropertyNode_ptr _radar_symbol_node;
SGPropertyNode_ptr _radar_centre_node;
SGPropertyNode_ptr _radar_coverage_node;
SGPropertyNode_ptr _radar_ref_rng_node;
SGPropertyNode_ptr _radar_hdg_marker_node;
SGPropertyNode_ptr _radar_rotate_node;
SGPropertyNode_ptr _radar_tcas_node;
SGPropertyNode_ptr _radar_absalt_node;
SGPropertyNode_ptr _font_node;
SGPropertyNode_ptr _ai_enabled_node;
osg::ref_ptr<osg::Texture2D> _resultTexture;
osg::ref_ptr<osg::Texture2D> _wxEcho;
osg::ref_ptr<osg::Geode> _radarGeode;
osg::ref_ptr<osg::Geode> _textGeode;
osg::Geometry *_geom;
osg::Vec2Array *_vertices;
osg::Vec2Array *_texCoords;
osg::Matrixf _centerTrans;
osg::ref_ptr<osgText::Font> _font;
osg::Vec4 _font_color;
osg::Vec4 _tcas_colors[4];
float _font_size;
float _font_spacing;
// FIXME: implementation of radar echoes missing
// list_of_SGWxRadarEcho _radarEchoBuffer;
void update_weather();
void update_aircraft();
void update_tacan();
void update_heading_marker();
void update_data(const SGPropertyNode *ac, double alt, double heading,
double radius, double bearing, bool selected);
bool update_tcas(const SGPropertyNode *model,double range,double user_alt,double alt,
double bearing,double radius, bool absMode);
void center_map();
void apply_map_offset();
void updateFont();
void calcRangeBearing(double lat, double lon, double lat2, double lon2,
double &range, double &bearing) const;
bool withinRadarHorizon(double user_alt, double alt, double range);
bool inRadarRange(double sigma, double range);
float calcRelBearing(float bearing, float heading);
float calcRelBearingDeg(float bearing, float heading);
};
template<> inline
SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, bool value)
{
SGPropertyNode *result = _Instrument->getNode(name, true);
if (!result->hasValue())
result->setBoolValue(value);
return result;
}
template<> inline
SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, double value)
{
SGPropertyNode *result = _Instrument->getNode(name, true);
if (!result->hasValue())
result->setDoubleValue(value);
return result;
}
template<> inline
SGPropertyNode *wxRadarBg::getInstrumentNode(const char *name, const char *value)
{
SGPropertyNode *result = _Instrument->getNode(name, true);
if (result->hasValue())
result->setStringValue(value);
return result;
}
#endif // _INST_WXRADAR_HXX