1
0
Fork 0
flightgear/src/Cockpit/hud.cxx
ehofman aa78341698 James Turner:
Convert FGRunway to be heap-based, and inherit FGPositioned. This is a large, ugly change, since FGRunway was essentially a plain struct, with no accessors or abstraction. This change adds various helpers and accessors to FGRunway, but doesn't change many places to use them - that will be a follow up series of patches. It's still a large patch, but outside of FGAirport and FGRunway, mostly mechanical search-and-replace.

An interesting part of this change is that reciprocal runways now exist as independent objects, rather than being created on the fly by the search methods. This simplifies some pieces of code that search for and iterate runways. For users who only want one 'end' of a runway, the new 'isReciprocal' predicate allows them to ignore the 'other' end. Current the only user of this is the 'ground-radar' ATC feature. If we had data on which runways are truly 'single-ended', it would now be trivial to use this in the airport loader to *not* create the reciprocal.
2008-09-11 08:38:09 +00:00

638 lines
19 KiB
C++

// hud.cxx -- hud defines and prototypes
//
// Written by Michele America, started September 1997.
//
// Copyright (C) 1997 Michele F. America - micheleamerica@geocities.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$
#include <simgear/compiler.h>
#include <simgear/structure/exception.hxx>
#include <string>
#include <fstream>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <stdlib.h>
#include <stdio.h> // char related functions
#include <string.h> // strcmp()
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props_io.hxx>
#include <osg/GLU>
#include <Aircraft/aircraft.hxx>
//#include <Autopilot/xmlauto.hxx>
#include <GUI/new_gui.hxx> // FGFontCache
#include <Main/globals.hxx>
#include <Scenery/scenery.hxx>
#include <Airports/runways.hxx>
#include "hud.hxx"
static HUD_Properties *HUDprop = 0;
static char units[5];
deque<SGSharedPtr<instr_item> > HUD_deque;
fgTextList HUD_TextList;
fgLineList HUD_LineList;
fgLineList HUD_StippleLineList;
fntRenderer *HUDtext = 0;
fntTexFont *HUD_Font = 0;
float HUD_TextSize = 0;
int HUD_style = 0;
float HUD_matrix[16];
int readHud( istream &input );
int readInstrument ( const SGPropertyNode * node);
static void drawHUD(osg::State*);
static void fgUpdateHUDVirtual(osg::State*);
class locRECT {
public:
RECT rect;
locRECT( UINT left, UINT top, UINT right, UINT bottom);
RECT get_rect(void) { return rect; }
};
locRECT :: locRECT( UINT left, UINT top, UINT right, UINT bottom)
{
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
}
// #define DEBUG
int readInstrument(const SGPropertyNode * node)
{
static const SGPropertyNode *startup_units_node
= fgGetNode("/sim/startup/units");
instr_item *HIptr;
if ( !strcmp(startup_units_node->getStringValue(), "feet") ) {
strcpy(units, " ft");
} else {
strcpy(units, " m");
}
const SGPropertyNode * ladder_group = node->getNode("ladders");
if (ladder_group != 0) {
int nLadders = ladder_group->nChildren();
for (int j = 0; j < nLadders; j++) {
HIptr = static_cast<instr_item *>(new HudLadder(ladder_group->getChild(j)));
HUD_deque.insert(HUD_deque.begin(), HIptr);
}
}
const SGPropertyNode * card_group = node->getNode("cards");
if (card_group != 0) {
int nCards = card_group->nChildren();
for (int j = 0; j < nCards; j++) {
const char *type = card_group->getChild(j)->getStringValue("type", "gauge");
if (!strcmp(type, "gauge"))
HIptr = static_cast<instr_item *>(new gauge_instr(card_group->getChild(j)));
else if (!strcmp(type, "dial") || !strcmp(type, "tape"))
HIptr = static_cast<instr_item *>(new hud_card(card_group->getChild(j)));
else {
SG_LOG(SG_INPUT, SG_WARN, "HUD: unknown 'card' type: " << type);
continue;
}
HUD_deque.insert(HUD_deque.begin(), HIptr);
}
}
const SGPropertyNode * label_group = node->getNode("labels");
if (label_group != 0) {
int nLabels = label_group->nChildren();
for (int j = 0; j < nLabels; j++) {
HIptr = static_cast<instr_item *>(new instr_label(label_group->getChild(j)));
HUD_deque.insert(HUD_deque.begin(), HIptr);
}
}
const SGPropertyNode * tbi_group = node->getNode("tbis");
if (tbi_group != 0) {
int nTbis = tbi_group->nChildren();
for (int j = 0; j < nTbis; j++) {
HIptr = static_cast<instr_item *>(new fgTBI_instr(tbi_group->getChild(j)));
HUD_deque.insert(HUD_deque.begin(), HIptr);
}
}
const SGPropertyNode * rwy_group = node->getNode("runways");
if (rwy_group != 0) {
int nRwy = rwy_group->nChildren();
for (int j = 0; j < nRwy; j++) {
HIptr = static_cast<instr_item *>(new runway_instr(rwy_group->getChild(j)));
HUD_deque.insert(HUD_deque.begin(), HIptr);
}
}
return 0;
} //end readinstrument
int readHud( istream &input )
{
SGPropertyNode root;
try {
readProperties(input, &root);
} catch (const sg_exception &e) {
guiErrorMessage("Error reading HUD: ", e);
return 0;
}
SG_LOG(SG_INPUT, SG_INFO, "Read properties for " <<
root.getStringValue("name"));
if (!root.getNode("depreciated"))
SG_LOG(SG_INPUT, SG_ALERT, "WARNING: use of depreciated old HUD");
HUD_deque.erase( HUD_deque.begin(), HUD_deque.end());
SG_LOG(SG_INPUT, SG_INFO, "Reading Hud instruments");
const SGPropertyNode * instrument_group = root.getChild("instruments");
int nInstruments = instrument_group->nChildren();
for (int i = 0; i < nInstruments; i++) {
const SGPropertyNode * node = instrument_group->getChild(i);
SGPath path( globals->get_fg_root() );
path.append(node->getStringValue("path"));
SG_LOG(SG_INPUT, SG_INFO, "Reading Instrument "
<< node->getName()
<< " from "
<< path.str());
SGPropertyNode root2;
try {
readProperties(path.str(), &root2);
} catch (const sg_exception &e) {
guiErrorMessage("Error reading HUD instrument: ", e);
continue;
}
readInstrument(&root2);
}//for loop(i)
return 0;
}
// fgHUDInit
//
// Constructs a HUD object and then adds in instruments. At the present
// the instruments are hard coded into the routine. Ultimately these need
// to be defined by the aircraft's instrumentation records so that the
// display for a Piper Cub doesn't show the speed range of a North American
// mustange and the engine readouts of a B36!
//
int fgHUDInit( fgAIRCRAFT * /* current_aircraft */ )
{
HUD_style = 1;
SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
string hud_path =
fgGetString("/sim/hud/path", "Huds/Default/default.xml");
SGPath path(globals->get_fg_root());
path.append(hud_path);
ifstream input(path.c_str());
if (!input.good()) {
SG_LOG(SG_INPUT, SG_ALERT,
"Cannot read Hud configuration from " << path.str());
} else {
readHud(input);
input.close();
}
if ( HUDtext ) {
// this chunk of code is not necessarily thread safe if the
// compiler optimizer reorders these statements. Note that
// "delete ptr" does not set "ptr = NULL". We have to do that
// ourselves.
fntRenderer *tmp = HUDtext;
HUDtext = NULL;
delete tmp;
}
FGFontCache *fc = globals->get_fontcache();
HUD_Font = fc->getTexFont(fgGetString("/sim/hud/font/name", "Helvetica.txf"));
if (!HUD_Font)
throw sg_throwable(string("/sim/hud/font/name is not a texture font"));
HUD_TextSize = fgGetFloat("/sim/hud/font/size", 10);
HUDtext = new fntRenderer();
HUDtext->setFont(HUD_Font);
HUDtext->setPointSize(HUD_TextSize);
HUD_TextList.setFont( HUDtext );
if (!HUDprop)
HUDprop = new HUD_Properties;
return 0; // For now. Later we may use this for an error code.
}
int fgHUDInit2( fgAIRCRAFT * /* current_aircraft */ )
{
HUD_style = 2;
SG_LOG( SG_COCKPIT, SG_INFO, "Initializing current aircraft HUD" );
SGPath path(globals->get_fg_root());
path.append("Huds/Minimal/default.xml");
ifstream input(path.c_str());
if (!input.good()) {
SG_LOG(SG_INPUT, SG_ALERT,
"Cannot read Hud configuration from " << path.str());
} else {
readHud(input);
input.close();
}
if (!HUDprop)
HUDprop = new HUD_Properties;
return 0; // For now. Later we may use this for an error code.
}
//$$$ End - added, Neetha, 28 Nov 2k
// fgUpdateHUD
//
// Performs a once around the list of calls to instruments installed in
// the HUD object with requests for redraw. Kinda. It will when this is
// all C++.
//
void fgUpdateHUD( osg::State* state ) {
static const SGPropertyNode *enable3d_node = fgGetNode("/sim/hud/enable3d");
if ( HUD_style == 1 && enable3d_node->getBoolValue() ) {
fgUpdateHUDVirtual(state);
return;
}
static const float normal_aspect = float(640) / float(480);
// note: aspect_ratio is Y/X
float current_aspect = 1.0f/globals->get_current_view()->get_aspect_ratio();
if ( current_aspect > normal_aspect ) {
float aspect_adjust = current_aspect / normal_aspect;
float adjust = 320.0f*aspect_adjust - 320.0f;
fgUpdateHUD( state, -adjust, 0.0f, 640.0f+adjust, 480.0f );
} else {
float aspect_adjust = normal_aspect / current_aspect;
float adjust = 240.0f*aspect_adjust - 240.0f;
fgUpdateHUD( state, 0.0f, -adjust, 640.0f, 480.0f+adjust );
}
}
void fgUpdateHUDVirtual(osg::State* state)
{
FGViewer* view = globals->get_current_view();
// Standard fgfs projection, with essentially meaningless clip
// planes (we'll map the whole HUD plane to z=-1)
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluPerspective(view->get_v_fov(), 1/view->get_aspect_ratio(), 0.1, 10);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Standard fgfs view direction computation
float lookat[3];
lookat[0] = -sin(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
lookat[1] = tan(SG_DEGREES_TO_RADIANS * view->getPitchOffset_deg());
lookat[2] = -cos(SG_DEGREES_TO_RADIANS * view->getHeadingOffset_deg());
if (fabs(lookat[1]) > 9999)
lookat[1] = 9999; // FPU sanity
gluLookAt(0, 0, 0, lookat[0], lookat[1], lookat[2], 0, 1, 0);
// Map the -1:1 square to a 55.0x41.25 degree wide patch at z=1.
// This is the default fgfs field of view, which the HUD files are
// written to assume.
float dx = 0.52056705; // tan(55/2)
float dy = dx * 0.75; // assumes 4:3 aspect ratio
float m[16];
m[0] = dx; m[4] = 0; m[ 8] = 0; m[12] = 0;
m[1] = 0; m[5] = dy; m[ 9] = 0; m[13] = 0;
m[2] = 0; m[6] = 0; m[10] = 1; m[14] = 0;
m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1;
glMultMatrixf(m);
// Convert the 640x480 "HUD standard" coordinate space to a square
// about the origin in the range [-1:1] at depth of -1
glScalef(1./320, 1./240, 1);
glTranslatef(-320, -240, -1);
// Do the deed
drawHUD(state);
// Clean up our mess
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void fgUpdateHUD( osg::State* state, GLfloat x_start, GLfloat y_start,
GLfloat x_end, GLfloat y_end )
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(x_start, x_end, y_start, y_end);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
drawHUD(state);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void drawHUD(osg::State* state)
{
if ( !HUD_deque.size() ) // Trust everyone, but ALWAYS cut the cards!
return;
HUD_TextList.erase();
HUD_LineList.erase();
// HUD_StippleLineList.erase();
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
static const SGPropertyNode *heading_enabled
= fgGetNode("/autopilot/locks/heading", true);
static const SGPropertyNode *altitude_enabled
= fgGetNode("/autopilot/locks/altitude", true);
static char hud_hdg_text[256];
static char hud_wp0_text[256];
static char hud_wp1_text[256];
static char hud_wp2_text[256];
static char hud_alt_text[256];
glEnable(GL_BLEND);
if (HUDprop->isTransparent())
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
else
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (HUDprop->isAntialiased()) {
glEnable(GL_LINE_SMOOTH);
glAlphaFunc(GL_GREATER, HUDprop->alphaClamp());
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
//glLineWidth(1.5);
} else {
//glLineWidth(1.0);
}
HUDprop->setColor();
for_each(HUD_deque.begin(), HUD_deque.end(), HUDdraw());
//HUD_TextList.add( fgText(40, 10, get_formated_gmt_time(), 0) );
int apY = 480 - 80;
if (strcmp( heading_enabled->getStringValue(), "dg-heading-hold") == 0 ) {
snprintf( hud_hdg_text, 256, "hdg = %.1f\n",
fgGetDouble("/autopilot/settings/heading-bug-deg") );
HUD_TextList.add( fgText( 40, apY, hud_hdg_text ) );
apY -= 15;
} else if ( strcmp(heading_enabled->getStringValue(), "true-heading-hold") == 0 ) {
snprintf( hud_hdg_text, 256, "hdg = %.1f\n",
fgGetDouble("/autopilot/settings/true-heading-deg") );
HUD_TextList.add( fgText( 40, apY, hud_hdg_text ) );
apY -= 15;
string wp0_id = fgGetString( "/autopilot/route-manager/wp[0]/id" );
if ( wp0_id.length() > 0 ) {
snprintf( hud_wp0_text, 256, "%5s %6.1fnm %s", wp0_id.c_str(),
fgGetDouble( "/autopilot/route-manager/wp[0]/dist" ),
fgGetString( "/autopilot/route-manager/wp[0]/eta" ) );
HUD_TextList.add( fgText( 40, apY, hud_wp0_text ) );
apY -= 15;
}
string wp1_id = fgGetString( "/autopilot/route-manager/wp[1]/id" );
if ( wp1_id.length() > 0 ) {
snprintf( hud_wp1_text, 256, "%5s %6.1fnm %s", wp1_id.c_str(),
fgGetDouble( "/autopilot/route-manager/wp[1]/dist" ),
fgGetString( "/autopilot/route-manager/wp[1]/eta" ) );
HUD_TextList.add( fgText( 40, apY, hud_wp1_text ) );
apY -= 15;
}
string wp2_id = fgGetString( "/autopilot/route-manager/wp-last/id" );
if ( wp2_id.length() > 0 ) {
snprintf( hud_wp2_text, 256, "%5s %6.1fnm %s", wp2_id.c_str(),
fgGetDouble( "/autopilot/route-manager/wp-last/dist" ),
fgGetString( "/autopilot/route-manager/wp-last/eta" ) );
HUD_TextList.add( fgText( 40, apY, hud_wp2_text ) );
apY -= 15;
}
}
if ( strcmp( altitude_enabled->getStringValue(), "altitude-hold" ) == 0 ) {
snprintf( hud_alt_text, 256, "alt = %.0f\n",
fgGetDouble("/autopilot/settings/target-altitude-ft") );
HUD_TextList.add( fgText( 40, apY, hud_alt_text ) );
apY -= 15;
} else if ( strcmp( altitude_enabled->getStringValue(), "agl-hold" ) == 0 ){
snprintf( hud_alt_text, 256, "agl = %.0f\n",
fgGetDouble("/autopilot/settings/target-agl-ft") );
HUD_TextList.add( fgText( 40, apY, hud_alt_text ) );
apY -= 15;
}
HUD_TextList.draw();
HUD_LineList.draw();
// glEnable(GL_LINE_STIPPLE);
// glLineStipple( 1, 0x00FF );
// HUD_StippleLineList.draw();
// glDisable(GL_LINE_STIPPLE);
if (HUDprop->isAntialiased()) {
glDisable(GL_ALPHA_TEST);
glDisable(GL_LINE_SMOOTH);
//glLineWidth(1.0);
}
if (HUDprop->isTransparent())
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
}
void fgTextList::draw()
{
if (!Font)
return;
vector<fgText>::iterator curString = List.begin();
vector<fgText>::iterator lastString = List.end();
glPushAttrib(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
if (HUDprop->isTransparent())
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
else
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (HUDprop->isAntialiased()) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, HUDprop->alphaClamp());
}
Font->begin();
for (; curString != lastString; curString++)
curString->Draw(Font);
Font->end();
glDisable(GL_TEXTURE_2D);
glPopAttrib();
}
// HUD property listener class
//
HUD_Properties::HUD_Properties() :
_current(fgGetNode("/sim/hud/current-color", true)),
_visibility(fgGetNode("/sim/hud/visibility", true)),
_antialiasing(fgGetNode("/sim/hud/color/antialiased", true)),
_transparency(fgGetNode("/sim/hud/color/transparent", true)),
_red(fgGetNode("/sim/hud/color/red", true)),
_green(fgGetNode("/sim/hud/color/green", true)),
_blue(fgGetNode("/sim/hud/color/blue", true)),
_alpha(fgGetNode("/sim/hud/color/alpha", true)),
_alpha_clamp(fgGetNode("/sim/hud/color/alpha-clamp", true)),
_brightness(fgGetNode("/sim/hud/color/brightness", true)),
_visible(false),
_antialiased(false),
_transparent(false),
_a(0.67),
_cl(0.01)
{
_visibility->addChangeListener(this);
_antialiasing->addChangeListener(this);
_transparency->addChangeListener(this);
_red->addChangeListener(this);
_green->addChangeListener(this);
_blue->addChangeListener(this);
_alpha->addChangeListener(this);
_alpha_clamp->addChangeListener(this);
_brightness->addChangeListener(this);
_current->addChangeListener(this, true);
}
void HUD_Properties::valueChanged(SGPropertyNode *node)
{
if (!strcmp(node->getName(), "current-color")) {
int i = node->getIntValue();
if (i < 0)
i = 0;
SGPropertyNode *n = fgGetNode("/sim/hud/palette", true);
if ((n = n->getChild("color", i, false))) {
if (n->hasValue("red"))
_red->setFloatValue(n->getFloatValue("red", 1.0));
if (n->hasValue("green"))
_green->setFloatValue(n->getFloatValue("green", 1.0));
if (n->hasValue("blue"))
_blue->setFloatValue(n->getFloatValue("blue", 1.0));
if (n->hasValue("alpha"))
_alpha->setFloatValue(n->getFloatValue("alpha", 0.67));
if (n->hasValue("alpha-clamp"))
_alpha_clamp->setFloatValue(n->getFloatValue("alpha-clamp", 0.01));
if (n->hasValue("brightness"))
_brightness->setFloatValue(n->getFloatValue("brightness", 0.75));
if (n->hasValue("antialiased"))
_antialiasing->setBoolValue(n->getBoolValue("antialiased", false));
if (n->hasValue("transparent"))
_transparency->setBoolValue(n->getBoolValue("transparent", false));
}
}
_visible = _visibility->getBoolValue();
_transparent = _transparency->getBoolValue();
_antialiased = _antialiasing->getBoolValue();
float brt = _brightness->getFloatValue();
_r = clamp(brt * _red->getFloatValue());
_g = clamp(brt * _green->getFloatValue());
_b = clamp(brt * _blue->getFloatValue());
_a = clamp(_alpha->getFloatValue());
_cl = clamp(_alpha_clamp->getFloatValue());
}
void HUD_Properties::setColor() const
{
if (_antialiased)
glColor4f(_r, _g, _b, _a);
else
glColor3f(_r, _g, _b);
}