From c6cbef666a611e66401bbd4a988c695242749cb3 Mon Sep 17 00:00:00 2001 From: mfranz Date: Tue, 4 Jul 2006 15:32:55 +0000 Subject: [PATCH] new HUD (work in progress) --- src/Instrumentation/HUD/HUD.cxx | 443 ++++++++ src/Instrumentation/HUD/HUD.hxx | 672 ++++++++++++ src/Instrumentation/HUD/HUD_dial.cxx | 93 ++ src/Instrumentation/HUD/HUD_gauge.cxx | 291 ++++++ src/Instrumentation/HUD/HUD_instrument.cxx | 96 ++ src/Instrumentation/HUD/HUD_label.cxx | 208 ++++ src/Instrumentation/HUD/HUD_ladder.cxx | 792 +++++++++++++++ src/Instrumentation/HUD/HUD_runway.cxx | 443 ++++++++ src/Instrumentation/HUD/HUD_scale.cxx | 58 ++ src/Instrumentation/HUD/HUD_tape.cxx | 1074 ++++++++++++++++++++ src/Instrumentation/HUD/HUD_tbi.cxx | 212 ++++ 11 files changed, 4382 insertions(+) create mode 100644 src/Instrumentation/HUD/HUD.cxx create mode 100644 src/Instrumentation/HUD/HUD.hxx create mode 100644 src/Instrumentation/HUD/HUD_dial.cxx create mode 100644 src/Instrumentation/HUD/HUD_gauge.cxx create mode 100644 src/Instrumentation/HUD/HUD_instrument.cxx create mode 100644 src/Instrumentation/HUD/HUD_label.cxx create mode 100644 src/Instrumentation/HUD/HUD_ladder.cxx create mode 100644 src/Instrumentation/HUD/HUD_runway.cxx create mode 100644 src/Instrumentation/HUD/HUD_scale.cxx create mode 100644 src/Instrumentation/HUD/HUD_tape.cxx create mode 100644 src/Instrumentation/HUD/HUD_tbi.cxx diff --git a/src/Instrumentation/HUD/HUD.cxx b/src/Instrumentation/HUD/HUD.cxx new file mode 100644 index 000000000..22b4e6bdb --- /dev/null +++ b/src/Instrumentation/HUD/HUD.cxx @@ -0,0 +1,443 @@ +// HUD.cxx -- Head Up Display +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 +#include + +#include STL_STRING +#include STL_FSTREAM + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include SG_GLU_H + +#include +#include + +#include
+#include
+ +#include "HUD.hxx" + + +static float clamp(float f) +{ + return f < 0.0f ? 0.0f : f > 1.0f ? 1.0f : f; +} + + +HUD::HUD() : + _current(fgGetNode("/sim/hud/current-color", true)), + _visibility(fgGetNode("/sim/hud/visibility[1]", true)), + _3DenabledN(fgGetNode("/sim/hud/enable3d", 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), // FIXME better names + _cl(0.01), + // + _scr_widthN(fgGetNode("/sim/startup/xsize", true)), + _scr_heightN(fgGetNode("/sim/startup/ysize", true)), + _unitsN(fgGetNode("/sim/startup/units", true)), + _timer(0.0), + // + _font_renderer(new fntRenderer()), + _font(0), + _font_size(0.0), + _style(0) +{ + SG_LOG(SG_COCKPIT, SG_INFO, "Initializing HUD Instrument"); + + _visibility->addChangeListener(this); + _3DenabledN->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); + _scr_widthN->addChangeListener(this); + _scr_heightN->addChangeListener(this); + _unitsN->addChangeListener(this, true); +} + + +HUD::~HUD() +{ + _visibility->removeChangeListener(this); + _3DenabledN->removeChangeListener(this); + _antialiasing->removeChangeListener(this); + _transparency->removeChangeListener(this); + _red->removeChangeListener(this); + _green->removeChangeListener(this); + _blue->removeChangeListener(this); + _alpha->removeChangeListener(this); + _alpha_clamp->removeChangeListener(this); + _brightness->removeChangeListener(this); + _current->removeChangeListener(this); + _scr_widthN->removeChangeListener(this); + _scr_heightN->removeChangeListener(this); + _unitsN->removeChangeListener(this); + delete _font_renderer; + + deque::const_iterator it, end = _items.end(); + for (it = _items.begin(); it != end; ++it) + delete *it; +} + + +void HUD::init() +{ + _font_cache = globals->get_fontcache(); + if (!_font) + _font = _font_cache->getTexFont(fgGetString("/sim/hud/font/name", "Helvetica.txf")); + if (!_font) + throw sg_throwable(string("/sim/hud/font/name is not a texture font")); + + _font_size = fgGetFloat("/sim/hud/font/size", 10); + _font_renderer->setFont(_font); + _font_renderer->setPointSize(_font_size); + _text_list.setFont(_font_renderer); + + load(fgGetString("/hud", "Huds/default.xml")); +} + + +void HUD::update(double dt) +{ + _timer += dt; +} + + +void HUD::draw() +{ + if (!isVisible()) + return; + + if (!_items.size()) + return; + + if (is3D()) { + draw3D(); + return; + } + + const float normal_aspect = 640.0f / 480.0f; + // 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; + draw2D(-adjust, 0.0f, 640.0f + adjust, 480.0f); + + } else { + float aspect_adjust = normal_aspect / current_aspect; + float adjust = 240.0f * aspect_adjust - 240.0f; + draw2D(0.0f, -adjust, 640.0f, 480.0f + adjust); + } + + glViewport(0, 0, _scr_width, _scr_height); +} + + +void HUD::draw3D() +{ + 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.0 / 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.0 / 320, 1.0 / 240, 1); + glTranslatef(-320, -240, -1); + + common_draw(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + + +void HUD::draw2D( 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(); + + common_draw(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + + +void HUD::common_draw() +{ + _text_list.erase(); + _line_list.erase(); + _stipple_line_list.erase(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + glEnable(GL_BLEND); + if (isTransparent()) + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + else + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (isAntialiased()) { + glEnable(GL_LINE_SMOOTH); + glAlphaFunc(GL_GREATER, alphaClamp()); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + //glLineWidth(1.5); + } else { + //glLineWidth(1.0); + } + + setColor(); + deque::const_iterator it, end = _items.end(); + for (it = _items.begin(); it != end; ++it) + if ((*it)->isEnabled()) + (*it)->draw(); + + _text_list.draw(); + _line_list.draw(); + + if (_stipple_line_list.size()) { + glEnable(GL_LINE_STIPPLE); + glLineStipple(1, 0x00FF); + _stipple_line_list.draw(); + glDisable(GL_LINE_STIPPLE); + } + + if (isAntialiased()) { + glDisable(GL_ALPHA_TEST); + glDisable(GL_LINE_SMOOTH); + //glLineWidth(1.0); + } + + if (isTransparent()) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); +} + + +int HUD::load(const char *file, float x, float y, int level, const string& indent) +{ + const sgDebugPriority TREE = SG_INFO; + const int MAXNEST = 10; + + SGPath path(globals->get_fg_root()); + path.append(file); + + if (!level) { + SG_LOG(SG_INPUT, TREE, endl << "load " << file); + _items.erase(_items.begin(), _items.end()); + } else if (level > MAXNEST) { + SG_LOG(SG_INPUT, SG_ALERT, "HUD: files nested more than " << MAXNEST << " levels"); + return 0x1; + } else if (!file || !file[0]) { + SG_LOG(SG_INPUT, SG_ALERT, "HUD: invalid filename "); + return 0x2; + } + + int ret = 0; + ifstream input(path.c_str()); + if (!input.good()) { + SG_LOG(SG_INPUT, SG_ALERT, "HUD: Cannot read configuration from " << path.str()); + return 0x4; + } + + SGPropertyNode root; + try { + readProperties(input, &root); + } catch (const sg_exception &e) { + input.close(); + guiErrorMessage("HUD: Error ", e); + return 0x8; + } + + for (int i = 0; i < root.nChildren(); i++) { + SGPropertyNode *n = root.getChild(i); + const char *d = n->getStringValue("name", 0); + string desc; + if (d) + desc = string(": \"") + d + '"'; + + const char *name = n->getName(); + if (!strcmp(name, "name")) { + continue; + + } else if (!strcmp(name, "enable3d")) { + // set in the tree so that valueChanged() picks it up + fgSetBool("/sim/hud/enable3d", n->getBoolValue()); + continue; + + } else if (!strcmp(name, "import")) { + const char *fn = n->getStringValue("path", ""); + float xoffs = n->getFloatValue("x-offset", 0.0f); + float yoffs = n->getFloatValue("y-offset", 0.0f); + + SG_LOG(SG_INPUT, TREE, indent << "|__import " << fn << desc); + + string ind = indent + string(i + 1 < root.nChildren() ? "| " : " "); + ret |= load(fn, x + xoffs, y + yoffs, level + 1, ind); + continue; + } + + SG_LOG(SG_INPUT, TREE, indent << "|__" << name << desc); + + Item *item; + if (!strcmp(name, "label")) { + item = static_cast(new Label(this, n, x, y)); + } else if (!strcmp(name, "gauge")) { + item = static_cast(new Gauge(this, n, x, y)); + } else if (!strcmp(name, "tape")) { + item = static_cast(new Tape(this, n, x, y)); + } else if (!strcmp(name, "dial")) { + item = static_cast(new Dial(this, n, x, y)); + } else if (!strcmp(name, "turn-bank-indicator")) { + item = static_cast(new TurnBankIndicator(this, n, x, y)); + } else if (!strcmp(name, "ladder")) { + item = static_cast(new Ladder(this, n, x, y)); + } else if (!strcmp(name, "runway")) { + item = static_cast(new Runway(this, n, x, y)); + } else { + SG_LOG(SG_INPUT, TREE, indent << " \\...unsupported!"); + continue; + } + _items.insert(_items.begin(), item); + } + input.close(); + SG_LOG(SG_INPUT, TREE, indent); + return ret; +} + + +void HUD::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)); + } + } + _scr_width = _scr_widthN->getIntValue(); + _scr_height = _scr_heightN->getIntValue(); + + _visible = _visibility->getBoolValue(); + _3Denabled = _3DenabledN->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()); + + _units = strcmp(_unitsN->getStringValue(), "feet") ? METER : FEET; +} + + +void HUD::setColor() const +{ + if (_antialiased) + glColor4f(_r, _g, _b, _a); + else + glColor3f(_r, _g, _b); +} + + diff --git a/src/Instrumentation/HUD/HUD.hxx b/src/Instrumentation/HUD/HUD.hxx new file mode 100644 index 000000000..740c73569 --- /dev/null +++ b/src/Instrumentation/HUD/HUD.hxx @@ -0,0 +1,672 @@ +// HUD.hxx -- Head Up Display +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 _HUD_HXX +#define _HUD_HXX + +#include + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include STL_FSTREAM + +SG_USING_STD(deque); +SG_USING_STD(vector); +SG_USING_NAMESPACE(std); + +#include + +#include +#include +#include + +#include // FGRunway +#include // fntRenderer ? guiErrorMessage() +#include // FGFontCache, FGColor +#include +#include
+ + +class FGViewer; +class SGCondition; + + + +class LineSegment { +public: + LineSegment(GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1) + : _x0(x0), _y0(y0), _x1(x1), _y1(y1) {} + + void draw() const { + glVertex2f(_x0, _y0); + glVertex2f(_x1, _y1); + } + +private: + GLfloat _x0, _y0, _x1, _y1; +}; + + + +class LineList { +public: + void add(const LineSegment& seg) { _list.push_back(seg); } + void erase() { _list.erase(_list.begin(), _list.end()); } + inline unsigned int size() const { return _list.size(); } + void draw() { + glBegin(GL_LINES); + vector::const_iterator it, end = _list.end(); + for (it = _list.begin(); it != end; ++it) + it->draw(); + glEnd(); + } + +private: + vector _list; +}; + + + +class HUDText { +public: + HUDText(float x, float y, char *s, int d = 0) : _x(x), _y(y), _digits(d) { + strncpy(_msg, s, BUFSIZE); + } + + // this code is changed to display Numbers with big/small digits + // according to MIL Standards for example Altitude above 10000 ft + // is shown as 10ooo. + + void draw(fntRenderer *fnt) { + float orig_size = fnt->getPointSize(); + if (!_digits) { // show all digits in same size + fnt->setPointSize(orig_size * 0.8); + fnt->start2f(_x, _y); + fnt->puts(_msg); + + fnt->setPointSize(orig_size); + return; + } + + int c = 0, i = 0; + char *t = _msg; + int p = 4; + + if (t[0] == '-') { + //if negative value then increase the c and p values + //for '-' sign. + c++; // was moved to the comment. Unintentionally? TODO + p++; + } + char *tmp = _msg; + while (tmp[i] != '\0') { + if ((tmp[i] >= '0') && (tmp[i] <= '9')) + c++; + i++; + } + if (c > p) { + fnt->setPointSize(orig_size * 0.8); + int p1 = c - 3; + char *tmp1 = _msg + p1; + int p2 = p1 * 8; + + fnt->start2f(_x + p2, _y); + fnt->puts(tmp1); + + fnt->setPointSize(orig_size * 1.2); + char tmp2[BUFSIZE]; + strncpy(tmp2, _msg, p1); + tmp2[p1] = '\0'; + + fnt->start2f(_x, _y); + fnt->puts(tmp2); + } else { + fnt->setPointSize(orig_size * 1.2); + fnt->start2f(_x, _y); + fnt->puts(tmp); + } + fnt->setPointSize(orig_size); + } + +private: + float _x, _y; + int _digits; + static const int BUFSIZE = 64; + char _msg[BUFSIZE]; +}; + + + +class TextList { +public: + TextList() { _font = 0; } + + void setFont(fntRenderer *Renderer) { _font = Renderer; } + void add(const HUDText& String) { _list.push_back(String); } + void erase() { _list.erase(_list.begin(), _list.end()); } + void draw() { + assert(_font); + + // FIXME + glPushAttrib(GL_COLOR_BUFFER_BIT); + glEnable(GL_BLEND); + + _font->begin(); + vector::iterator it, end = _list.end(); + for (it = _list.begin(); it != end; ++it) + it->draw(_font); + _font->end(); + + glDisable(GL_TEXTURE_2D); + glPopAttrib(); + } + +private: + fntRenderer *_font; + vector _list; +}; + + + + + + +class HUD : public SGSubsystem, public SGPropertyChangeListener { +public: + HUD(); + ~HUD(); + void init(); + void update(double); + + typedef struct { + float x, y; + } Point; + + typedef struct { + float top, bottom, left, right; + } Rect; + + // called from Main/renderer.cxx to draw 2D and 3D HUD + void draw(); + + // listener callback to read various HUD related properties + void valueChanged(SGPropertyNode *); + // set current glColor + void setColor() const; + inline bool isVisible() const { return _visible; } + inline bool isAntialiased() const { return _antialiased; } + inline bool isTransparent() const { return _transparent; } + inline bool is3D() const { return _3Denabled; } + inline float alphaClamp() const { return _cl; } + inline double timer() const { return _timer; } + + enum Units { FEET, METER }; + Units getUnits() const { return _units; } + + enum { + AUTOTICKS = 0x0001, + VERT = 0x0002, + HORZ = 0x0000, + TOP = 0x0004, + BOTTOM = 0x0008, + LEFT = TOP, + RIGHT = BOTTOM, + BOTH = (LEFT|RIGHT), + NOTICKS = 0x0010, + ARITHTIC = 0x0020, + DECITICS = 0x0040, + NOTEXT = 0x0080, + }; + + enum Adjust { + LEFT_ALIGN, + CENTER_ALIGN, + RIGHT_ALIGN + }; + +protected: + void common_draw(); + int load(const char *, float x = 320.0f, float y = 240.0f, + int level = 0, const string& indent = ""); + +private: + void draw3D(); + void draw2D(GLfloat, GLfloat, GLfloat, GLfloat); + + class Input; + class Item; + class Label; + class Scale; + class Gauge; + class Tape; + class Dial; + class TurnBankIndicator; + class Ladder; + class Runway; + + deque _items; + + SGPropertyNode_ptr _current; + SGPropertyNode_ptr _visibility; + SGPropertyNode_ptr _3DenabledN; + SGPropertyNode_ptr _antialiasing; + SGPropertyNode_ptr _transparency; + SGPropertyNode_ptr _red, _green, _blue, _alpha; + SGPropertyNode_ptr _alpha_clamp; + SGPropertyNode_ptr _brightness; + bool _visible; + bool _3Denabled; + bool _antialiased; + bool _transparent; + float _r, _g, _b, _a, _cl; + + SGPropertyNode_ptr _scr_widthN, _scr_heightN; + int _scr_width, _scr_height; + SGPropertyNode_ptr _unitsN; + Units _units; + double _timer; + + fntRenderer *_font_renderer; + FGFontCache *_font_cache; + fntTexFont *_font; + float _font_size; + int _style; + + TextList _text_list; + LineList _line_list; + LineList _stipple_line_list; +}; + + + +class HUD::Input { +public: + Input(const SGPropertyNode *n, float factor = 1.0, float offset = 0.0, + float min = -SGLimitsf::max(), float max = SGLimitsf::max()) : + _valid(false), + _property(0), + _damped(SGLimitsf::max()) + { + if (!n) + return; + _factor = n->getFloatValue("factor", factor); + _offset = n->getFloatValue("offset", offset); + _min = n->getFloatValue("min", min); + _max = n->getFloatValue("max", max); + _coeff = n->getFloatValue("damp", 0.0); + SGPropertyNode *p = ((SGPropertyNode *)n)->getNode("property", false); + if (p) { + const char *path = p->getStringValue(); + if (path && path[0]) { + _property = fgGetNode(path, true); + _valid = true; + } + } + } + + const char *getStringValue() const { + assert(_property); + return _property->getStringValue(); + } + + float getFloatValue() { + assert(_property); + float f = _property->getFloatValue() * _factor + _offset; + if (_damped == SGLimitsf::max()) + _damped = f; + if (_coeff > 0.0f) + f = _damped = f * (1.0f - _coeff) + _damped * _coeff; + return f < _min ? _min : f > _max ? _max : f; + } + + inline float isValid() const { return _valid; } + inline float min() const { return _min; } + inline float max() const { return _max; } + inline float factor() const { return _factor; } + + void set_min(float m, bool force = true) { + if (force || _min == -SGLimitsf::max()) + _min = m; + } + void set_max(float m, bool force = true) { + if (force || _max == SGLimitsf::max()) + _max = m; + } + +private: + bool _valid; + SGConstPropertyNode_ptr _property; + float _factor; + float _offset; + float _min; + float _max; + float _coeff; + float _damped; +}; + + + +class HUD::Item { // An Abstract Base Class (ABC) +public: + Item(HUD *parent, const SGPropertyNode *, float x = 0.0f, float y = 0.0f); + virtual ~Item () {} + virtual void draw() = 0; + virtual bool isEnabled(); + +protected: + + inline Rect get_location() const { return _scrn_pos; } + inline float get_span() const { return _scr_span; } + inline Point get_centroid() const { return _mid_span; } + inline int get_digits() const { return _digits; } + + inline float get_x() const { return _scrn_pos.left; } + inline float get_y() const { return _scrn_pos.top; } + inline float get_width() const { return _scrn_pos.right; } + inline float get_height() const { return _scrn_pos.bottom; } + + inline bool option_vert() const { return _options & VERT; } + inline bool option_left() const { return _options & LEFT; } + inline bool option_right() const { return _options & RIGHT; } + inline bool option_both() const { return (_options & BOTH) == BOTH; } + inline bool option_noticks() const { return _options & NOTICKS; } + inline bool option_notext() const { return _options & NOTEXT; } + inline bool option_top() const { return _options & TOP; } + inline bool option_bottom() const { return _options & BOTTOM; } + + void draw_line( float x1, float y1, float x2, float y2) { + _hud->_line_list.add(LineSegment(x1, y1, x2, y2)); + } + + void draw_stipple_line( float x1, float y1, float x2, float y2) { + _hud->_stipple_line_list.add(LineSegment(x1, y1, x2, y2)); + } + + void draw_text( float x, float y, char *msg, int digit) { + _hud->_text_list.add(HUDText(x, y, msg, digit)); + } + + float text_width(char *str) const { + assert(_hud->_font_renderer); + float r, l; + _hud->_font->getBBox(str, _hud->_font_size, 0, &l, &r, 0, 0); + return r - l; + } + + void draw_circle(float x1, float y1, float r) const { + glBegin(GL_LINE_LOOP); + for (int count = 0; count < 25; count++) { + float cosine = r * cos(count * 2 * SG_PI / 10.0); + float sine = r * sin(count * 2 * SG_PI / 10.0); + glVertex2f(cosine + x1, sine + y1); + } + glEnd(); + } + + HUD *_hud; + string _name; + int _options; + +private: + SGCondition *_condition; + Rect _scrn_pos; // Framing - affects scale dimensions + // and orientation. Vert vs Horz, etc. + float _disp_factor; // Multiply by to get numbers shown on scale. + float _scr_span; // Working values for draw; + Point _mid_span; + int _digits; +}; + + + +class HUD::Label : public Item { +public: + Label(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw(); + +private: + enum Format { + INVALID, + NONE, + INT, + LONG, + FLOAT, + DOUBLE, + STRING, + }; + + enum FontSize { + FONT_SMALL, + FONT_LARGE + }; + + Format check_format(const char *) const; + bool blink(); + + Input _input; + Format _mode; + string _format; + Adjust _halign; + int _fontsize; + int _blink; + bool _box; + + SGCondition *_blink_condition; + double _blink_interval; + double _blink_target; // time for next blink state change + bool _blink_state; +}; + + + +// abstract base class for both moving scale and moving needle (fixed scale) +// indicators. +// +class HUD::Scale : public Item { +public: + Scale(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw ( void ) {} // No-op here. Defined in derived classes. + +protected: + inline unsigned int modulo() const { return _modulo; } + inline float factor() const { return scale_factor; } + inline float range_to_show() const { return _range_shown; } + + Input _input; + unsigned int _major_divs; // major division marker units + unsigned int _minor_divs; // minor division marker units + +private: + float _range_shown; // Width Units. + float scale_factor; // factor => screen units/range values. + unsigned int _modulo; // Roll over point +}; + + +class HUD::Gauge : public Scale { +public: + Gauge(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw(); +}; + + + +// displays the indicated quantity on a scale that moves past the +// pointer. It may be horizontal or vertical. +// +class HUD::Tape : public Scale { +public: + Tape(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw(); + +protected: + void circle(float, float, float); + void fixed(float, float, float, float, float, float); + void zoomed_scale(int, int); + +private: + float val_span; + float half_width_units; + bool draw_tick_bottom; + bool draw_tick_top; + bool draw_tick_right; + bool draw_tick_left; + bool draw_cap_bottom; + bool draw_cap_top; + bool draw_cap_right; + bool draw_cap_left; + float marker_offset; + bool pointer; + string pointer_type; + string tick_type; + string tick_length; + int zoom; +}; + + + +class HUD::Dial : public Scale { +public: + Dial(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw(); + +private: + float _radius; + int _divisions; +}; + + + +class HUD::TurnBankIndicator : public Item { +public: + TurnBankIndicator(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw(); + +private: + Input _bank; + Input _sideslip; + + float _gap_width; + bool _bank_scale; + float _bank_scale_radius; +}; + + + +class HUD::Ladder : public Item { +public: + Ladder(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw(); + +private: + void draw_zenith(float, float, float); + void draw_nadir(float, float, float); + + void draw_text(float x, float y, char *s) { + _locTextList.add(HUDText(x, y, s)); + } + + void draw_line(float x1, float y1, float x2, float y2) { + _locLineList.add(LineSegment(x1, y1, x2, y2)); + } + + void draw_stipple_line(float x1, float y1, float x2, float y2) { + _locStippleLineList.add(LineSegment(x1, y1, x2, y2)); + } + + Input _pitch; + Input _roll; + enum Type { PITCH, CLIMB_DIVE } _type; + unsigned int width_units; + int div_units; + unsigned int minor_div; + unsigned int label_pos; + unsigned int _scr_hole; + float _vmax; + float _vmin; + float _compression; + bool _frl; + bool _target_spot; + bool _velocity_vector; + bool _drift_marker; + bool _alpha_bracket; + bool _energy_marker; + bool _climb_dive_marker; + bool _glide_slope_marker; + float _glide_slope; + bool _energy_worm; + bool _waypoint_marker; + int _zenith; + int _nadir; + int _hat; + + // The Ladder has its own temporary display lists + TextList _locTextList; + LineList _locLineList; + LineList _locStippleLineList; +}; + + + +// responsible for rendering the active runway in the hud (if visible). +// +class HUD::Runway : public Item { +public: + Runway(HUD *parent, const SGPropertyNode *, float x, float y); + virtual void draw(); + +private: + void boundPoint(const sgdVec3& v, sgdVec3& m); + bool boundOutsidePoints(sgdVec3& v, sgdVec3& m); + bool drawLine(const sgdVec3& a1, const sgdVec3& a2, + const sgdVec3& p1, const sgdVec3& p2); + void drawArrow(); + bool get_active_runway(FGRunway& rwy); + void get_rwy_points(sgdVec3 *points); + void setLineWidth(); + + SGPropertyNode_ptr _agl; + sgdVec3 _points3d[6], _points2d[6]; + double _mm[16]; + double _pm[16]; + double _arrow_scale; // scales of runway indication arrow + double _arrow_radius; + double _line_scale; // maximum line scale + double _scale_dist; // distance where to start scaling the lines + double _default_pitch; + double _default_heading; + GLint _view[4]; + FGRunway _runway; + FGViewer* _cockpit_view; + unsigned short _stipple_out; // stipple pattern of the outline of the runway + unsigned short _stipple_center; // stipple pattern of the center line of the runway + bool _draw_arrow; // draw arrow when runway is not visible in HUD + bool _draw_arrow_always; // always draws arrow + Rect _location; + Point _center; +}; + + +#endif // _HUD_HXX diff --git a/src/Instrumentation/HUD/HUD_dial.cxx b/src/Instrumentation/HUD/HUD_dial.cxx new file mode 100644 index 000000000..96b3da924 --- /dev/null +++ b/src/Instrumentation/HUD/HUD_dial.cxx @@ -0,0 +1,93 @@ +// HUD_dial.cxx -- HUD Dial Instrument +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 "HUD.hxx" + + +HUD::Dial::Dial(HUD *hud, const SGPropertyNode *n, float x, float y) : + Scale(hud, n, x, y), + _radius(n->getFloatValue("radius")), + _divisions(n->getIntValue("divisions")) +{ +} + + +void HUD::Dial::draw(void) +{ + if (!_input.isValid()) + return; + + const int BUFSIZE = 80; + char buf[BUFSIZE]; + + Rect scrn_rect = get_location(); + + float x, y; + float i; + y = (float)(scrn_rect.top); + x = (float)(scrn_rect.left); + glEnable(GL_POINT_SMOOTH); + glPointSize(3.0); + + float incr = 360.0 / _divisions; + for (i = 0.0; i < 360.0; i += incr) { + float i1 = i * SGD_DEGREES_TO_RADIANS; + float x1 = x + _radius * cos(i1); + float y1 = y + _radius * sin(i1); + + glBegin(GL_POINTS); + glVertex2f(x1, y1); + glEnd(); + } + glPointSize(1.0); + glDisable(GL_POINT_SMOOTH); + + + float offset = 90.0 * SGD_DEGREES_TO_RADIANS; + float r1 = 10.0; //size of carrot + float theta = _input.getFloatValue(); + + float theta1 = -theta * SGD_DEGREES_TO_RADIANS + offset; + float x1 = x + _radius * cos(theta1); + float y1 = y + _radius * sin(theta1); + float x2 = x1 - r1 * cos(theta1 - 30.0 * SGD_DEGREES_TO_RADIANS); + float y2 = y1 - r1 * sin(theta1 - 30.0 * SGD_DEGREES_TO_RADIANS); + float x3 = x1 - r1 * cos(theta1 + 30.0 * SGD_DEGREES_TO_RADIANS); + float y3 = y1 - r1 * sin(theta1 + 30.0 * SGD_DEGREES_TO_RADIANS); + + // draw carrot + draw_line(x1, y1, x2, y2); + draw_line(x1, y1, x3, y3); + snprintf(buf, BUFSIZE, "%3.1f\n", theta); + + // draw value + int l = abs((int)theta); + if (l) { + if (l < 10) + draw_text(x, y, buf, 0); + else if (l < 100) + draw_text(x - 1.0, y, buf, 0); + else if (l < 360) + draw_text(x - 2.0, y, buf, 0); + } +} + + diff --git a/src/Instrumentation/HUD/HUD_gauge.cxx b/src/Instrumentation/HUD/HUD_gauge.cxx new file mode 100644 index 000000000..a47e17be8 --- /dev/null +++ b/src/Instrumentation/HUD/HUD_gauge.cxx @@ -0,0 +1,291 @@ +// HUD_gauge.cxx -- HUD Gauge Instrument +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 "HUD.hxx" + + +HUD::Gauge::Gauge(HUD *hud, const SGPropertyNode *n, float x, float y) : + Scale(hud, n, x, y) +{ +} + + +// As implemented, draw only correctly draws a horizontal or vertical +// scale. It should contain a variation that permits clock type displays. +// Now is supports "tickless" displays such as control surface indicators. +// This routine should be worked over before using. Current value would be +// fetched and not used if not commented out. Clearly that is intollerable. + +void HUD::Gauge::draw(void) +{ + if (!_input.isValid()) + return; + + float marker_xs, marker_xe; + float marker_ys, marker_ye; + float text_x, text_y; + float width, height, bottom_4; + float lenstr; + int i; + const int BUFSIZE = 80; + char buf[BUFSIZE]; + bool condition; + int disp_val = 0; + float vmin = _input.min(); + float vmax = _input.max(); + Point mid_scr = get_centroid(); + float cur_value = _input.getFloatValue(); + Rect scrn_rect = get_location(); + + width = scrn_rect.left + scrn_rect.right; + height = scrn_rect.top + scrn_rect.bottom; + bottom_4 = scrn_rect.bottom / 4.0; + + // Draw the basic markings for the scale... + if (option_vert()) { // Vertical scale + // Bottom tick bar + draw_line(scrn_rect.left, scrn_rect.top, width, scrn_rect.top); + + // Top tick bar + draw_line( scrn_rect.left, height, width, height); + + marker_xs = scrn_rect.left; + marker_xe = width; + + if (option_left()) { // Read left, so line down right side + draw_line(width, scrn_rect.top, width, height); + marker_xs = marker_xe - scrn_rect.right / 3.0; // Adjust tick + } + + if (option_right()) { // Read right, so down left sides + draw_line(scrn_rect.left, scrn_rect.top, scrn_rect.left, height); + marker_xe = scrn_rect.left + scrn_rect.right / 3.0; // Adjust tick + } + + // At this point marker x_start and x_end values are transposed. + // To keep this from confusing things they are now interchanged. + if (option_both()) { + marker_ye = marker_xs; + marker_xs = marker_xe; + marker_xe = marker_ye; + } + + // Work through from bottom to top of scale. Calculating where to put + // minor and major ticks. + + if (!option_noticks()) { // If not no ticks...:) + // Calculate x marker offsets + int last = (int)vmax + 1; // float_to_int(vmax)+1; + i = (int)vmin; //float_to_int(vmin); + + for (; i < last; i++) { + // Calculate the location of this tick + marker_ys = scrn_rect.top + (i - vmin) * factor()/* +.5f*/; + + // We compute marker_ys even though we don't know if we will use + // either major or minor divisions. Simpler. + + if (_minor_divs) { // Minor tick marks + if (!(i % (int)_minor_divs)) { + if (option_left() && option_right()) { + draw_line(scrn_rect.left, marker_ys, marker_xs - 3, marker_ys); + draw_line(marker_xe + 3, marker_ys, width, marker_ys); + + } else if (option_left()) { + draw_line(marker_xs + 3, marker_ys, marker_xe, marker_ys); + } else { + draw_line(marker_xs, marker_ys, marker_xe - 3, marker_ys); + } + } + } + + // Now we work on the major divisions. Since these are also labeled + // and no labels are drawn otherwise, we label inside this if + // statement. + + if (_major_divs) { // Major tick mark + if (!(i % (int)_major_divs)) { + if (option_left() && option_right()) { + draw_line(scrn_rect.left, marker_ys, marker_xs, marker_ys); + draw_line(marker_xe, marker_ys, width, marker_ys); + } else { + draw_line(marker_xs, marker_ys, marker_xe, marker_ys); + } + + if (!option_notext()) { + disp_val = i; + snprintf(buf, BUFSIZE, "%d", + int(disp_val * _input.factor()/*+.5*/)); /// was data_scaling(), which makes no sense + + lenstr = text_width(buf); + + if (option_left() && option_right()) { + text_x = mid_scr.x - lenstr/2 ; + + } else if (option_left()) { + text_x = marker_xs - lenstr; + } else { + text_x = marker_xe - lenstr; + } + // Now we know where to put the text. + text_y = marker_ys; + draw_text(text_x, text_y, buf, 0); + } + } + } + } + } + + // Now that the scale is drawn, we draw in the pointer(s). Since labels + // have been drawn, text_x and text_y may be recycled. This is used + // with the marker start stops to produce a pointer for each side reading + + text_y = scrn_rect.top + ((cur_value - vmin) * factor() /*+.5f*/); + // text_x = marker_xs - scrn_rect.left; + + if (option_right()) { + glBegin(GL_LINE_STRIP); + glVertex2f(scrn_rect.left, text_y + 5); + glVertex2f(marker_xe, text_y); + glVertex2f(scrn_rect.left, text_y - 5); + glEnd(); + } + if (option_left()) { + glBegin(GL_LINE_STRIP); + glVertex2f(width, text_y + 5); + glVertex2f(marker_xs, text_y); + glVertex2f(width, text_y - 5); + glEnd(); + } + // End if VERTICAL SCALE TYPE + + } else { // Horizontal scale by default + // left tick bar + draw_line(scrn_rect.left, scrn_rect.top, scrn_rect.left, height); + + // right tick bar + draw_line(width, scrn_rect.top, width, height ); + + marker_ys = scrn_rect.top; // Starting point for + marker_ye = height; // tick y location calcs + marker_xs = scrn_rect.left + (cur_value - vmin) * factor() /*+ .5f*/; + + if (option_top()) { + // Bottom box line + draw_line(scrn_rect.left, scrn_rect.top, width, scrn_rect.top); + + marker_ye = scrn_rect.top + scrn_rect.bottom / 2.0; // Tick point adjust + // Bottom arrow + glBegin(GL_LINE_STRIP); + glVertex2f(marker_xs - bottom_4, scrn_rect.top); + glVertex2f(marker_xs, marker_ye); + glVertex2f(marker_xs + bottom_4, scrn_rect.top); + glEnd(); + } + + if (option_bottom()) { + // Top box line + draw_line(scrn_rect.left, height, width, height); + // Tick point adjust + marker_ys = height - scrn_rect.bottom / 2.0; + + // Top arrow + glBegin(GL_LINE_STRIP); + glVertex2f(marker_xs + bottom_4, height); + glVertex2f(marker_xs, marker_ys ); + glVertex2f(marker_xs - bottom_4, height); + glEnd(); + } + + + int last = (int)vmax + 1; //float_to_int(vmax)+1; + i = (int)vmin; //float_to_int(vmin); + for (; i scrn_rect.left) + || ((marker_xs - 5) < (width))) { + + if (option_both()) { + draw_line(marker_xs, scrn_rect.top, marker_xs, marker_ys - 4); + draw_line(marker_xs, marker_ye + 4, marker_xs, height); + + } else if (option_top()) { + draw_line(marker_xs, marker_ys, marker_xs, marker_ye - 4); + } else { + draw_line(marker_xs, marker_ys + 4, marker_xs, marker_ye); + } + } + } + } + + if (_major_divs) { + if (!(i % (int)_major_divs)) { + if (modulo()) { + if (disp_val < 0) { + while (disp_val < 0) + disp_val += modulo(); + } + disp_val = i % (int)modulo(); + } else { + disp_val = i; + } + snprintf(buf, BUFSIZE, "%d", + int(disp_val * _input.factor()/* +.5*/)); // was data_scaling(), which makes no sense + lenstr = text_width(buf); + + // Draw major ticks and text only if far enough from the edge. + if (((marker_xs - 10) > scrn_rect.left) + && ((marker_xs + 10) < width)) { + if (option_both()) { + draw_line(marker_xs, scrn_rect.top, marker_xs, marker_ys); + draw_line(marker_xs, marker_ye, marker_xs, height); + + if (!option_notext()) + draw_text(marker_xs - lenstr, marker_ys + 4, buf, 0); + + } else { + draw_line(marker_xs, marker_ys, marker_xs, marker_ye); + + if (!option_notext()) { + if (option_top()) + draw_text(marker_xs - lenstr, height - 10, buf, 0); + else + draw_text(marker_xs - lenstr, scrn_rect.top, buf, 0); + } + } + } + } + } + } + } + } +} + + diff --git a/src/Instrumentation/HUD/HUD_instrument.cxx b/src/Instrumentation/HUD/HUD_instrument.cxx new file mode 100644 index 000000000..1f17be3f4 --- /dev/null +++ b/src/Instrumentation/HUD/HUD_instrument.cxx @@ -0,0 +1,96 @@ +// HUD_instrument.cxx -- HUD Common Instrument Base +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 +#include +#include "HUD.hxx" + + +HUD::Item::Item(HUD *hud, const SGPropertyNode *n, float x, float y) : + _hud(hud), + _name(n->getStringValue("name", "[unnamed]")), + _options(0), + _condition(0), + _digits(n->getIntValue("digits")) +{ + const SGPropertyNode *node = n->getNode("condition"); + if (node) + _condition = sgReadCondition(globals->get_props(), node); + + _scrn_pos.left = n->getIntValue("x") + x; + _scrn_pos.top = n->getIntValue("y") + y; + _scrn_pos.right = n->getIntValue("width"); + _scrn_pos.bottom = n->getIntValue("height"); + + vector opt = n->getChildren("option"); + for (unsigned int i = 0; i < opt.size(); i++) { + const char *o = opt[i]->getStringValue(); + if (!strcmp(o, "autoticks")) + _options |= AUTOTICKS; + else if (!strcmp(o, "vertical")) + _options |= VERT; + else if (!strcmp(o, "horizontal")) + _options |= HORZ; + else if (!strcmp(o, "top")) + _options |= TOP; + else if (!strcmp(o, "left")) + _options |= LEFT; + else if (!strcmp(o, "bottom")) + _options |= BOTTOM; + else if (!strcmp(o, "right")) + _options |= RIGHT; + else if (!strcmp(o, "both")) + _options |= (LEFT|RIGHT); + else if (!strcmp(o, "noticks")) + _options |= NOTICKS; + else if (!strcmp(o, "arithtic")) + _options |= ARITHTIC; + else if (!strcmp(o, "decitics")) + _options |= DECITICS; + else if (!strcmp(o, "notext")) + _options |= NOTEXT; + else + SG_LOG(SG_INPUT, SG_WARN, "HUD: unsupported option: " << o); + } + + // Set up convenience values for centroid of the box and + // the span values according to orientation + + if (_options & VERT) { + _scr_span = _scrn_pos.bottom; + } else { + _scr_span = _scrn_pos.right; + } + + _mid_span.x = _scrn_pos.left + _scrn_pos.right / 2.0; + _mid_span.y = _scrn_pos.top + _scrn_pos.bottom / 2.0; +} + + +bool HUD::Item::isEnabled() +{ + if (!_condition) + return true; + + return _condition->test(); +} + + diff --git a/src/Instrumentation/HUD/HUD_label.cxx b/src/Instrumentation/HUD/HUD_label.cxx new file mode 100644 index 000000000..7973d0a98 --- /dev/null +++ b/src/Instrumentation/HUD/HUD_label.cxx @@ -0,0 +1,208 @@ +// HUD_label.cxx -- HUD Label +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 +#include "HUD.hxx" + + +HUD::Label::Label(HUD *hud, const SGPropertyNode *n, float x, float y) : + Item(hud, n, x, y), + _input(n->getNode("input", false)), + _fontsize(fgGetInt("/sim/startup/xsize") > 1000 ? FONT_LARGE : FONT_SMALL), // FIXME + _box(n->getBoolValue("box", false)), + _blink_condition(0), + _blink_interval(n->getFloatValue("blinking/interval", -1.0f)), + _blink_target(0.0), + _blink_state(true) +{ + const SGPropertyNode *node = n->getNode("blinking/condition"); + if (node) + _blink_condition = sgReadCondition(globals->get_props(), node); + + const char *halign = n->getStringValue("halign", "center"); + if (!strcmp(halign, "left")) + _halign = LEFT_ALIGN; + else if (!strcmp(halign, "right")) + _halign = RIGHT_ALIGN; + else + _halign = CENTER_ALIGN; + + const char *pre = n->getStringValue("prefix", 0); + const char *post = n->getStringValue("postfix", 0); + const char *fmt = n->getStringValue("format", 0); + + if (pre) + _format = pre; + if (fmt) + _format += fmt; + else + _format += "%s"; + if (post) + _format += post; + + _mode = check_format(_format.c_str()); + if (_mode == INVALID) { + SG_LOG(SG_INPUT, SG_ALERT, "HUD: invalid format '" << _format.c_str() << '\''); + _format = "INVALID"; + _mode = NONE; + } + + blink(); +} + + +void HUD::Label::draw(void) +{ + if (!(_mode == NONE || _input.isValid() && blink())) + return; + + Rect scrn_rect = get_location(); + + if (_box) { + float x = scrn_rect.left; + float y = scrn_rect.top; + float w = scrn_rect.right; + float h = _hud->_font_size; // FIXME + + glPushMatrix(); + glLoadIdentity(); + + glBegin(GL_LINES); + glVertex2f(x - 2.0, y - 2.0); + glVertex2f(x + w + 2.0, y - 2.0); + glVertex2f(x + w + 2.0, y + h + 2.0); + glVertex2f(x - 2.0, y + h + 2.0); + glEnd(); + + glEnable(GL_LINE_STIPPLE); + glLineStipple(1, 0xAAAA); + + glBegin(GL_LINES); + glVertex2f(x + w + 2.0, y - 2.0); + glVertex2f(x + w + 2.0, y + h + 2.0); + glVertex2f(x - 2.0, y + h + 2.0); + glVertex2f(x - 2.0, y - 2.0); + glEnd(); + + glDisable(GL_LINE_STIPPLE); + glPopMatrix(); + } + + const int BUFSIZE = 256; + char buf[BUFSIZE]; + if (_mode == NONE) + snprintf(buf, BUFSIZE, _format.c_str()); + else if (_mode == STRING) + snprintf(buf, BUFSIZE, _format.c_str(), _input.getStringValue()); + else if (_mode == INT) + snprintf(buf, BUFSIZE, _format.c_str(), int(_input.getFloatValue())); + else if (_mode == LONG) + snprintf(buf, BUFSIZE, _format.c_str(), long(_input.getFloatValue())); + else if (_mode == FLOAT) + snprintf(buf, BUFSIZE, _format.c_str(), float(_input.getFloatValue())); + else if (_mode == DOUBLE) // not really supported yet + snprintf(buf, BUFSIZE, _format.c_str(), double(_input.getFloatValue())); + + float posincr; + float lenstr = text_width(buf); + + if (_halign == RIGHT_ALIGN) + posincr = scrn_rect.right - lenstr; + else if (_halign == CENTER_ALIGN) + posincr = get_span() - (lenstr / 2); // FIXME get_span() ? really? + else // LEFT_ALIGN + posincr = 0; + + if (_fontsize == FONT_SMALL) + draw_text(scrn_rect.left + posincr, scrn_rect.top, buf, get_digits()); + else if (_fontsize == FONT_LARGE) + draw_text(scrn_rect.left + posincr, scrn_rect.top, buf, get_digits()); +} + + +// make sure the format matches '[ -+#]?\d*(\.\d*)?(l?[df]|s)' +// +HUD::Label::Format HUD::Label::check_format(const char *f) const +{ + bool l = false; + Format fmt = STRING; + + for (; *f; f++) { + if (*f == '%') { + if (f[1] == '%') + f++; + else + break; + } + } + if (*f++ != '%') + return NONE; + if (*f == ' ' || *f == '+' || *f == '-' || *f == '#') + f++; + while (*f && isdigit(*f)) + f++; + if (*f == '.') { + f++; + while (*f && isdigit(*f)) + f++; + } + if (*f == 'l') + l = true, f++; + + if (*f == 'd') + fmt = l ? LONG : INT; + if (*f == 'f') + fmt = l ? DOUBLE : FLOAT; + else if (*f == 's') { + if (l) + return INVALID; + fmt = STRING; + } else + return INVALID; + + for (++f; *f; f++) { + if (*f == '%') { + if (f[1] == '%') + f++; + else + return INVALID; + } + } + return fmt; +} + + +bool HUD::Label::blink() +{ + if (_blink_interval < 0.0f) + return true; + + if (_blink_condition && !_blink_condition->test()) + return true; + + if (_hud->timer() < _blink_target) + return _blink_state; + + _blink_target = _hud->timer() + _blink_interval; + return _blink_state = !_blink_state; +} + + diff --git a/src/Instrumentation/HUD/HUD_ladder.cxx b/src/Instrumentation/HUD/HUD_ladder.cxx new file mode 100644 index 000000000..3993e7a21 --- /dev/null +++ b/src/Instrumentation/HUD/HUD_ladder.cxx @@ -0,0 +1,792 @@ +// HUD_ladder.cxx -- HUD Ladder Instrument +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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
+#include "HUD.hxx" + + +// FIXME +float get__heading() { return fgGetFloat("/orientation/heading-deg") * M_PI / 180.0; } +float get__throttleval() { return fgGetFloat("/controls/engines/engine/throttle"); } +float get__aoa() { return fgGetFloat("/sim/frame-rate"); } // FIXME +float get__Vx() { return fgGetFloat("/velocities/uBody-fps"); } +float get__Vy() { return fgGetFloat("/velocities/vBody-fps"); } +float get__Vz() { return fgGetFloat("/velocities/wBody-fps"); } +float get__Ax() { return fgGetFloat("/acclerations/pilot/x-accel-fps_sec"); } +float get__Ay() { return fgGetFloat("/acclerations/pilot/y-accel-fps_sec"); } +float get__Az() { return fgGetFloat("/acclerations/pilot/z-accel-fps_sec"); } +#undef ENABLE_SP_FMDS + + +HUD::Ladder::Ladder(HUD *hud, const SGPropertyNode *n, float x, float y) : + Item(hud, n, x, y), + _pitch(n->getNode("pitch-input", false)), + _roll(n->getNode("roll-input", false)), + width_units(int(n->getFloatValue("display-span"))), + div_units(int(fabs(n->getFloatValue("divisions")))), + minor_div(0 /* hud.cxx: static float minor_division = 0 */), + label_pos(n->getIntValue("lbl-pos")), + _scr_hole(n->getIntValue("screen-hole")), + _compression(n->getFloatValue("compression-factor")), + _frl(n->getBoolValue("enable-fuselage-ref-line", false)), + _target_spot(n->getBoolValue("enable-target-spot", false)), + _velocity_vector(n->getBoolValue("enable-velocity-vector", false)), + _drift_marker(n->getBoolValue("enable-drift-marker", false)), + _alpha_bracket(n->getBoolValue("enable-alpha-bracket", false)), + _energy_marker(n->getBoolValue("enable-energy-marker", false)), + _climb_dive_marker(n->getBoolValue("enable-climb-dive-marker", false)), // WTF FIXME + _glide_slope_marker(n->getBoolValue("enable-glide-slope-marker",false)), + _glide_slope(n->getFloatValue("glide-slope", -4.0)), + _energy_worm(n->getBoolValue("enable-energy-marker", false)), + _waypoint_marker(n->getBoolValue("enable-waypoint-marker", false)), + _zenith(n->getIntValue("zenith")), + _nadir(n->getIntValue("nadir")), + _hat(n->getIntValue("hat")) +{ + const char *t = n->getStringValue("type"); + _type = strcmp(t, "climb-dive") ? PITCH : CLIMB_DIVE; + + if (!width_units) + width_units = 45; + + _vmax = width_units / 2; + _vmin = -_vmax; +} + + +void HUD::Ladder::draw(void) +{ + if (!_pitch.isValid() || !_roll.isValid()) + return; + + float x_ini, x_ini2; + float x_end, x_end2; + float y = 0; + int count; + float cosine, sine, xvvr, yvvr, Vxx = 0.0, Vyy = 0.0, Vzz = 0.0; + float up_vel, ground_vel, actslope = 0.0; + float Axx = 0.0, Ayy = 0.0, Azz = 0.0, total_vel = 0.0, pot_slope, t1; + float t2 = 0.0, psi = 0.0, alpha, pla; + float vel_x = 0.0, vel_y = 0.0, drift; + bool pitch_ladder = false; + bool climb_dive_ladder = false; + bool clip_plane = false; + + GLdouble eqn_top[4] = {0.0, -1.0, 0.0, 0.0}; + GLdouble eqn_left[4] = {-1.0, 0.0, 0.0, 100.0}; + GLdouble eqn_right[4] = {1.0, 0.0, 0.0, 100.0}; + + Point centroid = get_centroid(); + Rect box = get_location(); + + float half_span = box.right / 2.0; + float roll_value = _roll.getFloatValue() * SGD_DEGREES_TO_RADIANS; // FIXME rad/deg conversion + alpha = get__aoa(); + pla = get__throttleval(); + +#ifdef ENABLE_SP_FMDS + int lgear, wown, wowm, ilcanclaw, ihook; + ilcanclaw = get__iaux2(); + lgear = get__iaux3(); + wown = get__iaux4(); + wowm = get__iaux5(); + ihook = get__iaux6(); +#endif + float pitch_value = _pitch.getFloatValue(); + + if (_type == CLIMB_DIVE) { + pitch_ladder = false; + climb_dive_ladder = true; + clip_plane = true; + + } else { // _type == PITCH + pitch_ladder = true; + climb_dive_ladder = false; + clip_plane = false; + } + + //************************************************************** + glPushMatrix(); + // define (0, 0) as center of screen + glTranslatef(centroid.x, centroid.y, 0); + + // OBJECT STATIC RETICLE + // TYPE FRL (FUSELAGE REFERENCE LINE) + // ATTRIB - ALWAYS + // Draw the FRL spot and line + if (_frl) { +#define FRL_DIAMOND_SIZE 2.0 + glBegin(GL_LINE_LOOP); + glVertex2f(-FRL_DIAMOND_SIZE, 0.0); + glVertex2f(0.0, FRL_DIAMOND_SIZE); + glVertex2f(FRL_DIAMOND_SIZE, 0.0); + glVertex2f(0.0, -FRL_DIAMOND_SIZE); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2f(0, FRL_DIAMOND_SIZE); + glVertex2f(0, 8.0); + glEnd(); +#undef FRL_DIAMOND_SIZE + } + // TYPE WATERLINE_MARK (W shaped _ _ ) + // \/\/ + + //**************************************************************** + // TYPE TARGET_SPOT + // Draw the target spot. + if (_target_spot) { +#define CENTER_DIAMOND_SIZE 6.0 + glBegin(GL_LINE_LOOP); + glVertex2f(-CENTER_DIAMOND_SIZE, 0.0); + glVertex2f(0.0, CENTER_DIAMOND_SIZE); + glVertex2f(CENTER_DIAMOND_SIZE, 0.0); + glVertex2f(0.0, -CENTER_DIAMOND_SIZE); + glEnd(); +#undef CENTER_DIAMOND_SIZE + } + + //**************************************************************** + //velocity vector reticle - computations + if (_velocity_vector) { + Vxx = get__Vx(); + Vyy = get__Vy(); + Vzz = get__Vz(); + Axx = get__Ax(); + Ayy = get__Ay(); + Azz = get__Az(); + psi = get__heading(); + + if (psi > 180.0) + psi = psi - 360; + + total_vel = sqrt(Vxx * Vxx + Vyy * Vyy + Vzz * Vzz); + ground_vel = sqrt(Vxx * Vxx + Vyy * Vyy); + up_vel = Vzz; + + if (ground_vel < 2.0) { + if (fabs(up_vel) < 2.0) + actslope = 0.0; + else + actslope = (up_vel / fabs(up_vel)) * 90.0; + + } else { + actslope = atan(up_vel / ground_vel) * SGD_RADIANS_TO_DEGREES; + } + + xvvr = (((atan2(Vyy, Vxx) * SGD_RADIANS_TO_DEGREES) - psi) + * (_compression / globals->get_current_view()->get_aspect_ratio())); + drift = ((atan2(Vyy, Vxx) * SGD_RADIANS_TO_DEGREES) - psi); + yvvr = ((actslope - pitch_value) * _compression); + vel_y = ((actslope - pitch_value) * cos(roll_value) + drift * sin(roll_value)) * _compression; + vel_x = (-(actslope - pitch_value) * sin(roll_value) + drift * cos(roll_value)) + * (_compression / globals->get_current_view()->get_aspect_ratio()); + // printf("%f %f %f %f\n",vel_x, vel_y, drift, psi); + + //**************************************************************** + // OBJECT MOVING RETICLE + // TYPE - DRIFT MARKER + // ATTRIB - ALWAYS + // drift marker + if (_drift_marker) { + glBegin(GL_LINE_STRIP); + glVertex2f((xvvr * 25 / 120) - 6, -4); + glVertex2f(xvvr * 25 / 120, 8); + glVertex2f((xvvr * 25 / 120) + 6, -4); + glEnd(); + } + + //**************************************************************** + // Clipping coordinates for ladder to be input from xml file + // Clip hud ladder + if (clip_plane) { + glClipPlane(GL_CLIP_PLANE0, eqn_top); + glEnable(GL_CLIP_PLANE0); + glClipPlane(GL_CLIP_PLANE1, eqn_left); + glEnable(GL_CLIP_PLANE1); + glClipPlane(GL_CLIP_PLANE2, eqn_right); + glEnable(GL_CLIP_PLANE2); + // glScissor(-100,-240, 200, 240); + // glEnable(GL_SCISSOR_TEST); + } + + //**************************************************************** + // OBJECT MOVING RETICLE + // TYPE VELOCITY VECTOR + // ATTRIB - ALWAYS + // velocity vector + glBegin(GL_LINE_LOOP); // Use polygon to approximate a circle + for (count = 0; count < 50; count++) { + cosine = 6 * cos(count * SGD_2PI / 50.0); + sine = 6 * sin(count * SGD_2PI / 50.0); + glVertex2f(cosine + vel_x, sine + vel_y); + } + glEnd(); + + //velocity vector reticle orientation lines + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x - 12, vel_y); + glVertex2f(vel_x - 6, vel_y); + glEnd(); + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x + 12, vel_y); + glVertex2f(vel_x + 6, vel_y); + glEnd(); + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x, vel_y + 12); + glVertex2f(vel_x, vel_y + 6); + glEnd(); + +#ifdef ENABLE_SP_FMDS + // OBJECT MOVING RETICLE + // TYPE LINE + // ATTRIB - ON CONDITION + if (lgear == 1) { + // undercarriage status + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x + 8, vel_y); + glVertex2f(vel_x + 8, vel_y - 4); + glEnd(); + + // OBJECT MOVING RETICLE + // TYPE LINE + // ATTRIB - ON CONDITION + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x - 8, vel_y); + glVertex2f(vel_x - 8, vel_y - 4); + glEnd(); + + // OBJECT MOVING RETICLE + // TYPE LINE + // ATTRIB - ON CONDITION + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x, vel_y - 6); + glVertex2f(vel_x, vel_y - 10); + glEnd(); + } + + // OBJECT MOVING RETICLE + // TYPE V + // ATTRIB - ON CONDITION + if (ihook == 1) { + // arrestor hook status + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x - 4, vel_y - 8); + glVertex2f(vel_x, vel_y - 10); + glVertex2f(vel_x + 4, vel_y - 8); + glEnd(); + } +#endif + } // if _velocity_vector + + + //*************************************************************** + // OBJECT MOVING RETICLE + // TYPE - SQUARE_BRACKET + // ATTRIB - ON CONDITION + // alpha bracket +#ifdef ENABLE_SP_FMDS + if (_alpha_bracket && ihook == 1) { + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x - 20 , vel_y - (16 - alpha) * _compression); + glVertex2f(vel_x - 17, vel_y - (16 - alpha) * _compression); + glVertex2f(vel_x - 17, vel_y - (14 - alpha) * _compression); + glVertex2f(vel_x - 20, vel_y - (14 - alpha) * _compression); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x + 20 , vel_y - (16 - alpha) * _compression); + glVertex2f(vel_x + 17, vel_y - (16 - alpha) * _compression); + glVertex2f(vel_x + 17, vel_y - (14 - alpha) * _compression); + glVertex2f(vel_x + 20, vel_y - (14 - alpha) * _compression); + glEnd(); + } +#endif + //printf("xvr=%f, yvr=%f, Vx=%f, Vy=%f, Vz=%f\n",xvvr, yvvr, Vx, Vy, Vz); + //printf("Ax=%f, Ay=%f, Az=%f\n",Ax, Ay, Az); + + //**************************************************************** + // OBJECT MOVING RETICLE + // TYPE ENERGY_MARKERS + // ATTRIB - ALWAYS + //energy markers - compute potential slope + if (_energy_marker) { + if (total_vel < 5.0) { + t1 = 0; + t2 = 0; + } else { + t1 = up_vel / total_vel; + t2 = asin((Vxx * Axx + Vyy * Ayy + Vzz * Azz) / (9.81 * total_vel)); + } + pot_slope = ((t2 / 3) * SGD_RADIANS_TO_DEGREES) * _compression + vel_y; + // if (pot_slope < (vel_y - 45)) pot_slope = vel_y - 45; + // if (pot_slope > (vel_y + 45)) pot_slope = vel_y + 45; + + //energy markers + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x - 20, pot_slope - 5); + glVertex2f(vel_x - 15, pot_slope); + glVertex2f(vel_x - 20, pot_slope + 5); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x + 20, pot_slope - 5); + glVertex2f(vel_x + 15, pot_slope); + glVertex2f(vel_x + 20, pot_slope + 5); + glEnd(); + + if (pla > (105.0 / 131.0)) { + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x - 24, pot_slope - 5); + glVertex2f(vel_x - 19, pot_slope); + glVertex2f(vel_x - 24, pot_slope + 5); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2f(vel_x + 24, pot_slope - 5); + glVertex2f(vel_x + 19, pot_slope); + glVertex2f(vel_x + 24, pot_slope + 5); + glEnd(); + } + } + + //********************************************************** + // ramp reticle + // OBJECT STATIC RETICLE + // TYPE LINE + // ATTRIB - ON CONDITION +#ifdef ENABLE_SP_FMDS + if (_energy_worm && ilcanclaw == 1) { + glBegin(GL_LINE_STRIP); + glVertex2f(-15, -134); + glVertex2f(15, -134); + glEnd(); + + // OBJECT MOVING RETICLE + // TYPE BOX + // ATTRIB - ON CONDITION + glBegin(GL_LINE_STRIP); + glVertex2f(-6, -134); + glVertex2f(-6, t2 * SGD_RADIANS_TO_DEGREES * 4.0 - 134); + glVertex2f(+6, t2 * SGD_RADIANS_TO_DEGREES * 4.0 - 134); + glVertex2f(6, -134); + glEnd(); + + // OBJECT MOVING RETICLE + // TYPE DIAMOND + // ATTRIB - ON CONDITION + glBegin(GL_LINE_LOOP); + glVertex2f(-6, actslope * 4.0 - 134); + glVertex2f(0, actslope * 4.0 -134 + 3); + glVertex2f(6, actslope * 4.0 - 134); + glVertex2f(0, actslope * 4.0 -134 -3); + glEnd(); + } +#endif + + //************************************************************* + // OBJECT MOVING RETICLE + // TYPE DIAMOND + // ATTRIB - ALWAYS + // Draw the locked velocity vector. + if (_climb_dive_marker) { + glBegin(GL_LINE_LOOP); + glVertex2f(-3.0, 0.0 + vel_y); + glVertex2f(0.0, 6.0 + vel_y); + glVertex2f(3.0, 0.0 + vel_y); + glVertex2f(0.0, -6.0 + vel_y); + glEnd(); + } + + //**************************************************************** + + if (climb_dive_ladder) { // CONFORMAL_HUD + _vmin = pitch_value - (float)width_units; + _vmax = pitch_value + (float)width_units; + glTranslatef(vel_x, vel_y, 0); + + } else { // pitch_ladder - Default Hud + _vmin = pitch_value - (float)width_units * 0.5f; + _vmax = pitch_value + (float)width_units * 0.5f; + } + + glRotatef(roll_value * SGD_RADIANS_TO_DEGREES, 0.0, 0.0, 1.0); + // FRL marker not rotated - this line shifted below + + if (div_units) { + const int BUFSIZE = 8; + char buf[BUFSIZE]; + float label_length; + float label_height; + float left; + float right; + float bot; + float top; + float text_offset = 4.0f; + float zero_offset = 0.0; + + if (climb_dive_ladder) + zero_offset = 50.0f; // horizon line is wider by this much (hard coded ??) + else + zero_offset = 10.0f; + + fntFont *font = _hud->_font_renderer->getFont(); // FIXME + float pointsize = _hud->_font_renderer->getPointSize(); + float italic = _hud->_font_renderer->getSlant(); + + _locTextList.setFont(_hud->_font_renderer); + _locTextList.erase(); + _locLineList.erase(); + _locStippleLineList.erase(); + + int last = int(_vmax) + 1; + int i = int(_vmin); + + if (!_scr_hole) { + x_end = half_span; + + for (; igetBBox(buf, pointsize, italic, &left, &right, &bot, &top); + label_length = right - left; + label_length += text_offset; + label_height = (top - bot) / 2.0f; + + x_ini = -half_span; + + if (i >= 0) { + // Make zero point wider on left + if (i == 0) + x_ini -= zero_offset; + + // Zero or above draw solid lines + draw_line(x_ini, y, x_end, y); + + if (i == 90 && _zenith == 1) + draw_zenith(x_ini, x_end, y); + } else { + // Below zero draw dashed lines. + draw_stipple_line(x_ini, y, x_end, y); + + if (i == -90 && _nadir ==1) + draw_nadir(x_ini, x_end, y); + } + + // Calculate the position of the left text and write it. + draw_text(x_ini - label_length, y - label_height, buf); + draw_text(x_end + text_offset, y - label_height, buf); + } + } + + } else { // if (_scr_hole) + // Draw ladder with space in the middle of the lines + float hole = (float)((_scr_hole) / 2.0f); + + x_end = -half_span + hole; + x_ini2 = half_span - hole; + + for (; i < last; i++) { + if (_type == PITCH) + y = (((float)(i - pitch_value) * _compression) + .5); + else // _type == CLIMB_DIVE + y = (((float)(i - actslope) * _compression) + .5); + + if (!(i % div_units)) { // At integral multiple of div + snprintf(buf, BUFSIZE, "%d", i); + font->getBBox(buf, pointsize, italic, &left, &right, &bot, &top); + label_length = right - left; + label_length += text_offset; + label_height = (top - bot) / 2.0f; + // printf("l %f r %f b %f t %f\n",left, right, bot, top); + + // Start by calculating the points and drawing the + // left side lines. + x_ini = -half_span; + x_end2 = half_span; + + if (i >= 0) { + // Make zero point wider on left + if (i == 0) { + x_ini -= zero_offset; + x_end2 += zero_offset; + } + //draw climb bar vertical lines + if (climb_dive_ladder) { + // Zero or above draw solid lines + draw_line(x_end, y - 5.0, x_end, y); + draw_line(x_ini2, y - 5.0, x_ini2, y); + } + // draw pitch / climb bar + draw_line(x_ini, y, x_end, y); + draw_line(x_ini2, y, x_end2, y); + + if (i == 90 && _zenith == 1) + draw_zenith(x_ini2, x_end, y); + + } else { // i < 0 + // draw dive bar vertical lines + if (climb_dive_ladder) { + draw_line(x_end, y + 5.0, x_end, y); + draw_line(x_ini2, y + 5.0, x_ini2, y); + } + + // draw pitch / dive bars + draw_stipple_line(x_ini, y, x_end, y); + draw_stipple_line(x_ini2, y, x_end2, y); + + if (i == -90 && _nadir == 1) + draw_nadir(x_ini2, x_end, y); + } + + // Now calculate the location of the left side label using + draw_text(x_ini - label_length, y - label_height, buf); + draw_text(x_end2 + text_offset, y - label_height, buf); + } + } + + // OBJECT LADDER MARK + // TYPE LINE + // ATTRIB - ON CONDITION + // draw appraoch glide slope marker +#ifdef ENABLE_SP_FMDS + if (_glide_slope_marker && ihook) { + draw_line(-half_span + 15, (_glide_slope - actslope) * _compression, + -half_span + hole, (_glide_slope - actslope) * _compression); + draw_line(half_span - 15, (_glide_slope - actslope) * _compression, + half_span - hole, (_glide_slope - actslope) * _compression); + } +#endif + } + _locTextList.draw(); + + glLineWidth(0.2); + + _locLineList.draw(); + + glEnable(GL_LINE_STIPPLE); + glLineStipple(1, 0x00FF); + _locStippleLineList.draw(); + glDisable(GL_LINE_STIPPLE); + } + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + // glDisable(GL_SCISSOR_TEST); + glPopMatrix(); + //************************************************************* + + //************************************************************* +#ifdef ENABLE_SP_FMDS + if (_waypoint_marker) { + //waypoint marker computation + float fromwp_lat, towp_lat, fromwp_lon, towp_lon, dist, delx, dely, hyp, theta, brg; + + fromwp_lon = get__longitude() * SGD_DEGREES_TO_RADIANS; + fromwp_lat = get__latitude() * SGD_DEGREES_TO_RADIANS; + towp_lon = get__aux2() * SGD_DEGREES_TO_RADIANS; + towp_lat = get__aux1() * SGD_DEGREES_TO_RADIANS; + + dist = acos(sin(fromwp_lat) * sin(towp_lat) + cos(fromwp_lat) + * cos(towp_lat) * cos(fabs(fromwp_lon - towp_lon))); + delx= towp_lat - fromwp_lat; + dely = towp_lon - fromwp_lon; + hyp = sqrt(pow(delx, 2) + pow(dely, 2)); + + if (hyp != 0) + theta = asin(dely / hyp); + else + theta = 0.0; + + brg = theta * SGD_RADIANS_TO_DEGREES; + if (brg > 360.0) + brg = 0.0; + if (delx < 0) + brg = 180 - brg; + + // {Brg = asin(cos(towp_lat)*sin(fabs(fromwp_lon-towp_lon))/ sin(dist)); + // Brg = Brg * SGD_RADIANS_TO_DEGREES; } + + dist *= SGD_RADIANS_TO_DEGREES * 60.0 * 1852.0; //rad->deg->nm->m + // end waypoint marker computation + + //********************************************************* + // OBJECT MOVING RETICLE + // TYPE ARROW + // waypoint marker + if (fabs(brg-psi) > 10.0) { + glPushMatrix(); + glTranslatef(centroid.x, centroid.y, 0); + glTranslatef(vel_x, vel_y, 0); + glRotatef(brg - psi, 0.0, 0.0, -1.0); + glBegin(GL_LINE_LOOP); + glVertex2f(-2.5, 20.0); + glVertex2f(-2.5, 30.0); + glVertex2f(-5.0, 30.0); + glVertex2f(0.0, 35.0); + glVertex2f(5.0, 30.0); + glVertex2f(2.5, 30.0); + glVertex2f(2.5, 20.0); + glEnd(); + glPopMatrix(); + } + + // waypoint marker on heading scale + if (fabs(brg-psi) < 12.0) { + if (_hat == 0) { + glBegin(GL_LINE_LOOP); + glVertex2f(((brg - psi) * 60 / 25) + 320, 240.0); + glVertex2f(((brg - psi) * 60 / 25) + 326, 240.0 - 4); + glVertex2f(((brg - psi) * 60 / 25) + 323, 240.0 - 4); + glVertex2f(((brg - psi) * 60 / 25) + 323, 240.0 - 8); + glVertex2f(((brg - psi) * 60 / 25) + 317, 240.0 - 8); + glVertex2f(((brg - psi) * 60 / 25) + 317, 240.0 - 4); + glVertex2f(((brg - psi) * 60 / 25) + 314, 240.0 - 4); + glEnd(); + + } else { //if _hat=0 + float x = (brg - psi) * 60 / 25 + 320, y = 240.0, r = 5.0; + float x1, y1; + + glEnable(GL_POINT_SMOOTH); + glBegin(GL_POINTS); + + for (int count = 0; count <= 200; count++) { + float temp = count * 3.142 * 3 / (200.0 * 2.0); + float temp1 = temp - (45.0 * SGD_DEGREES_TO_RADIANS); + x1 = x + r * cos(temp1); + y1 = y + r * sin(temp1); + glVertex2f(x1, y1); + } + + glEnd(); + glDisable(GL_POINT_SMOOTH); + } //_hat=0 + + } //brg<12 + } // if _waypoint_marker +#endif +}//draw + + +/******************************************************************/ +// draws the zenith symbol for highest possible climb angle (i.e. 90 degree climb angle) +// +void HUD::Ladder::draw_zenith(float xfirst, float xlast, float yvalue) +{ + float xcentre = (xfirst + xlast) / 2.0; + float ycentre = yvalue; + + draw_line(xcentre - 9.0, ycentre, xcentre - 3.0, ycentre + 1.3); + draw_line(xcentre - 9.0, ycentre, xcentre - 3.0, ycentre - 1.3); + + draw_line(xcentre + 9.0, ycentre, xcentre + 3.0, ycentre + 1.3); + draw_line(xcentre + 9.0, ycentre, xcentre + 3.0, ycentre - 1.3); + + draw_line(xcentre, ycentre + 9.0, xcentre - 1.3, ycentre + 3.0); + draw_line(xcentre, ycentre + 9.0, xcentre + 1.3, ycentre + 3.0); + + draw_line(xcentre - 3.9, ycentre + 3.9, xcentre - 3.0, ycentre + 1.3); + draw_line(xcentre - 3.9, ycentre + 3.9, xcentre - 1.3, ycentre + 3.0); + + draw_line(xcentre + 3.9, ycentre + 3.9, xcentre + 1.3, ycentre+3.0); + draw_line(xcentre + 3.9, ycentre + 3.9, xcentre + 3.0, ycentre+1.3); + + draw_line(xcentre - 3.9, ycentre - 3.9, xcentre - 3.0, ycentre-1.3); + draw_line(xcentre - 3.9, ycentre - 3.9, xcentre - 1.3, ycentre-2.6); + + draw_line(xcentre + 3.9, ycentre - 3.9, xcentre + 3.0, ycentre-1.3); + draw_line(xcentre + 3.9, ycentre - 3.9, xcentre + 1.3, ycentre-2.6); + + draw_line(xcentre - 1.3, ycentre - 2.6, xcentre, ycentre - 27.0); + draw_line(xcentre + 1.3, ycentre - 2.6, xcentre, ycentre - 27.0); +} + + +// draws the nadir symbol for lowest possible dive angle (i.e. 90 degree dive angle) +// +void HUD::Ladder::draw_nadir(float xfirst, float xlast, float yvalue) +{ + float xcentre = (xfirst + xlast) / 2.0; + float ycentre = yvalue; + + float r = 7.5; + float x1, y1, x2, y2; + + // to draw a circle + float xcent1, xcent2, ycent1, ycent2; + xcent1 = xcentre + r; + ycent1 = ycentre; + + for (int count = 1; count <= 400; count++) { + float temp = count * 2 * 3.142 / 400.0; + xcent2 = xcentre + r * cos(temp); + ycent2 = ycentre + r * sin(temp); + + draw_line(xcent1, ycent1, xcent2, ycent2); + + xcent1 = xcent2; + ycent1 = ycent2; + } + + xcent2 = xcentre + r; + ycent2 = ycentre; + + Item::draw_line(xcent1, ycent1, xcent2, ycent2); //to connect last point to first point + //end circle + + //to draw a line above the circle + draw_line(xcentre, ycentre + 7.5, xcentre, ycentre + 22.5); + + //line in the middle of circle + draw_line(xcentre - 7.5, ycentre, xcentre + 7.5, ycentre); + + float theta = asin (2.5 / 7.5); + float theta1 = asin(5.0 / 7.5); + + x1 = xcentre + r * cos(theta); + y1 = ycentre + 2.5; + x2 = xcentre + r * cos((180.0 * SGD_DEGREES_TO_RADIANS) - theta); + y2 = ycentre + 2.5; + draw_line(x1, y1, x2, y2); + + x1 = xcentre + r * cos(theta1); + y1 = ycentre + 5.0; + x2 = xcentre + r * cos((180.0 * SGD_DEGREES_TO_RADIANS) - theta1); + y2 = ycentre + 5.0; + draw_line(x1, y1, x2, y2); + + x1 = xcentre + r * cos((180.0 * SGD_DEGREES_TO_RADIANS) + theta); + y1 = ycentre - 2.5; + x2 = xcentre + r * cos((360.0 * SGD_DEGREES_TO_RADIANS) - theta); + y2 = ycentre - 2.5; + draw_line(x1, y1, x2, y2); + + x1 = xcentre + r * cos((180.0 * SGD_DEGREES_TO_RADIANS) + theta1); + y1 = ycentre - 5.0; + x2 = xcentre + r * cos((360.0 * SGD_DEGREES_TO_RADIANS) - theta1); + y2 = ycentre - 5.0; + draw_line(x1, y1, x2, y2); +} + + diff --git a/src/Instrumentation/HUD/HUD_runway.cxx b/src/Instrumentation/HUD/HUD_runway.cxx new file mode 100644 index 000000000..74240e627 --- /dev/null +++ b/src/Instrumentation/HUD/HUD_runway.cxx @@ -0,0 +1,443 @@ +// HUD_runway.cxx -- An instrument that renders a virtual runway on the HUD +// +// Written by Aaron Wilson & Phillip Merritt, Nov 2004. +// +// Copyright (C) 2004 Aaron Wilson, Aaron.I.Wilson@nasa.gov +// Copyright (C) 2004 Phillip Merritt, Phillip.M.Merritt@nasa.gov +// +// 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 +#include +#include +#include SG_GLU_H + +#include
+#include +#include +#include +#include +#include
+#include
+#include + +#include "HUD.hxx" + + +HUD::Runway::Runway(HUD *hud, const SGPropertyNode *node, float x, float y) : + Item(hud, node, x, y), + _agl(fgGetNode("/position/altitude-agl-ft", true)), + _arrow_scale(node->getDoubleValue("arrow-scale", 1.0)), + _arrow_radius(node->getDoubleValue("arrow-radius")), + _line_scale(node->getDoubleValue("line-scale", 1.0)), + _scale_dist(node->getDoubleValue("scale-dist-nm")), + _default_pitch(fgGetDouble("/sim/view[0]/config/pitch-pitch-deg", 0.0)), + _default_heading(fgGetDouble("/sim/view[0]/config/pitch-heading-deg", 0.0)), + _cockpit_view(globals->get_viewmgr()->get_view(0)), + _stipple_out(node->getIntValue("outer_stipple", 0xFFFF)), + _stipple_center(node->getIntValue("center-stipple", 0xFFFF)), + _draw_arrow(_arrow_scale > 0 ? true : false), + _draw_arrow_always(_arrow_scale > 0 ? node->getBoolValue("arrow-always") : false) +{ + _view[0] = 0; + _view[1] = 0; + _view[2] = 640; + _view[3] = 480; + + _center.x = _view[2] / 2; + _center.y = _view[3] / 2; + + _location.left = _center.x - (get_width() / 2) + get_x(); + _location.right = _center.x + (get_width() / 2) + get_x(); + _location.bottom = _center.y - (get_height() / 2) + get_y(); + _location.top = _center.y + (get_height() / 2) + get_y(); +} + + +void HUD::Runway::draw() +{ + if (!get_active_runway(_runway)) + return; + + glPushAttrib(GL_LINE_STIPPLE | GL_LINE_STIPPLE_PATTERN | GL_LINE_WIDTH); + float modelView[4][4], projMat[4][4]; + bool anyLines; + //Get the current view + FGViewer* curr_view = globals->get_viewmgr()->get_current_view(); + int curr_view_id = globals->get_viewmgr()->get_current(); + double gpo = curr_view->getGoalPitchOffset_deg(); + double gho = curr_view->getGoalHeadingOffset_deg(); + double po = curr_view->getPitchOffset_deg(); + double ho = curr_view->getHeadingOffset_deg(); + + double yaw = -(_cockpit_view->getHeadingOffset_deg() - _default_heading) * SG_DEGREES_TO_RADIANS; + double pitch = (_cockpit_view->getPitchOffset_deg() - _default_pitch) * SG_DEGREES_TO_RADIANS; + //double roll = fgGetDouble("/sim/view[0]/config/roll-offset-deg",0.0) //TODO: adjust for default roll offset + double sPitch = sin(pitch), cPitch = cos(pitch), + sYaw = sin(yaw), cYaw = cos(yaw); + + //Assuming that the "Cockpit View" is always at position zero!!! + if (curr_view_id != 0) { + globals->get_viewmgr()->set_view(0); + globals->get_viewmgr()->copyToCurrent(); + } + //Set the camera to the cockpit view to get the view of the runway from the cockpit + ssgSetCamera((sgVec4 *)_cockpit_view->get_VIEW()); + get_rwy_points(_points3d); + //Get the current project matrix + ssgGetProjectionMatrix(projMat); +// const sgVec4 *viewMat = globals->get_current_view()->get_VIEW(); + //Get the current model view matrix (cockpit view) + ssgGetModelviewMatrix(modelView); + //Create a rotation matrix to correct for any offsets (other than default offsets) to the model view matrix + sgMat4 xy; //rotation about the Rxy, negate the sin's on Ry + xy[0][0] = cYaw; xy[1][0] = 0.0f; xy[2][0] = -sYaw; xy[3][0] = 0.0f; + xy[0][1] = sPitch*-sYaw; xy[1][1] = cPitch; xy[2][1] = -sPitch*cYaw; xy[3][1] = 0.0f; + xy[0][2] = cPitch*sYaw; xy[1][2] = sPitch; xy[2][2] = cPitch*cYaw; xy[3][2] = 0.0f; + xy[0][3] = 0.0f; xy[1][3] = 0.0f; xy[2][3] = 0.0f; xy[3][3] = 1.0f; + //Re-center the model view + sgPostMultMat4(modelView,xy); + //copy float matrices to double + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + int idx = (i * 4) + j; + _mm[idx] = (double)modelView[i][j]; + _pm[idx] = (double)projMat[i][j]; + } + } + + //Calculate the 2D points via gluProject + int result = GL_TRUE; + for (int i = 0; i < 6; i++) { + result = gluProject(_points3d[i][0], _points3d[i][1], _points3d[i][2], _mm, + _pm, _view, &_points2d[i][0], &_points2d[i][1], &_points2d[i][2]); + } + //set the line width based on our distance from the runway + setLineWidth(); + //Draw the runway lines on the HUD + glEnable(GL_LINE_STIPPLE); + glLineStipple(1, _stipple_out); + anyLines = + drawLine(_points3d[0], _points3d[1], _points2d[0], _points2d[1]) | //draw top + drawLine(_points3d[2], _points3d[1], _points2d[2], _points2d[1]) | //draw right + drawLine(_points3d[2], _points3d[3], _points2d[2], _points2d[3]) | //draw bottom + drawLine(_points3d[3], _points3d[0], _points2d[3], _points2d[0]); //draw left + + glLineStipple(1, _stipple_center); + anyLines |= drawLine(_points3d[5], _points3d[4], _points2d[5], _points2d[4]); //draw center + + //Check to see if arrow needs drawn + if ((!anyLines && _draw_arrow) || _draw_arrow_always) { + drawArrow(); //draw indication arrow + } + + //Restore the current view and any offsets + if (curr_view_id != 0) { + globals->get_viewmgr()->set_view(curr_view_id); + globals->get_viewmgr()->copyToCurrent(); + curr_view->setHeadingOffset_deg(ho); + curr_view->setPitchOffset_deg(po); + curr_view->setGoalHeadingOffset_deg(gho); + curr_view->setGoalPitchOffset_deg(gpo); + } + //Set the camera back to the current view + ssgSetCamera((sgVec4 *)curr_view); + glPopAttrib(); +} + + +bool HUD::Runway::get_active_runway(FGRunway& runway) +{ + FGEnvironment stationweather = + ((FGEnvironmentMgr *)globals->get_subsystem("environment"))->getEnvironment(); + double hdg = stationweather.get_wind_from_heading_deg(); + return globals->get_runways()->search(fgGetString("/sim/presets/airport-id"), int(hdg), &runway); +} + + +void HUD::Runway::get_rwy_points(sgdVec3 *_points3d) +{ + static Point3D center = globals->get_scenery()->get_center(); + + //Get the current tile center + Point3D currentCenter = globals->get_scenery()->get_center(); + Point3D tileCenter = currentCenter; + if (center != currentCenter) //if changing tiles + tileCenter = center; //use last center + + double alt = current_aircraft.fdm_state->get_Runway_altitude() * SG_FEET_TO_METER; + double length = (_runway._length / 2.0) * SG_FEET_TO_METER; + double width = (_runway._width / 2.0) * SG_FEET_TO_METER; + double frontLat, frontLon, backLat, backLon,az, tempLat, tempLon; + + geo_direct_wgs_84(alt, _runway._lat, _runway._lon, _runway._heading, length, &backLat, &backLon, &az); + sgGeodToCart(backLat * SG_DEGREES_TO_RADIANS, backLon * SG_DEGREES_TO_RADIANS, alt, _points3d[4]); + + geo_direct_wgs_84(alt, _runway._lat, _runway._lon, _runway._heading + 180, length, &frontLat, &frontLon, &az); + sgGeodToCart(frontLat * SG_DEGREES_TO_RADIANS, frontLon * SG_DEGREES_TO_RADIANS, alt, _points3d[5]); + + geo_direct_wgs_84(alt, backLat, backLon, _runway._heading + 90, width, &tempLat, &tempLon, &az); + sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[0]); + + geo_direct_wgs_84(alt, backLat, backLon, _runway._heading - 90, width, &tempLat, &tempLon, &az); + sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[1]); + + geo_direct_wgs_84(alt, frontLat, frontLon, _runway._heading - 90, width, &tempLat, &tempLon, &az); + sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[2]); + + geo_direct_wgs_84(alt, frontLat, frontLon, _runway._heading + 90, width, &tempLat, &tempLon, &az); + sgGeodToCart(tempLat * SG_DEGREES_TO_RADIANS, tempLon * SG_DEGREES_TO_RADIANS, alt, _points3d[3]); + + for (int i = 0; i < 6; i++) { + _points3d[i][0] -= tileCenter.x(); + _points3d[i][1] -= tileCenter.y(); + _points3d[i][2] -= tileCenter.z(); + } + center = currentCenter; +} + + +bool HUD::Runway::drawLine(const sgdVec3& a1, const sgdVec3& a2, const sgdVec3& point1, const sgdVec3& point2) +{ + sgdVec3 p1, p2; + sgdCopyVec3(p1, point1); + sgdCopyVec3(p2, point2); + bool p1Inside = (p1[0] >= _location.left && p1[0] <= _location.right + && p1[1] >= _location.bottom && p1[1] <= _location.top); + bool p1Insight = (p1[2] >= 0.0 && p1[2] < 1.0); + bool p1Valid = p1Insight && p1Inside; + bool p2Inside = (p2[0] >= _location.left && p2[0] <= _location.right + && p2[1] >= _location.bottom && p2[1] <= _location.top); + bool p2Insight = (p2[2] >= 0.0 && p2[2] < 1.0); + bool p2Valid = p2Insight && p2Inside; + + if (p1Valid && p2Valid) { //Both project points are valid, draw the line + glBegin(GL_LINES); + glVertex2d(p1[0],p1[1]); + glVertex2d(p2[0],p2[1]); + glEnd(); + + } else if (p1Valid) { //p1 is valid and p2 is not, calculate a new valid point + sgdVec3 vec = {a2[0] - a1[0], a2[1] - a1[1], a2[2] - a1[2]}; + //create the unit vector + sgdScaleVec3(vec, 1.0 / sgdLengthVec3(vec)); + sgdVec3 newPt; + sgdCopyVec3(newPt, a1); + sgdAddVec3(newPt, vec); + if (gluProject(newPt[0], newPt[1], newPt[2], _mm, _pm, _view, &p2[0], &p2[1], &p2[2]) + && (p2[2] > 0 && p2[2] < 1.0)) { + boundPoint(p1, p2); + glBegin(GL_LINES); + glVertex2d(p1[0], p1[1]); + glVertex2d(p2[0], p2[1]); + glEnd(); + } + + } else if (p2Valid) { //p2 is valid and p1 is not, calculate a new valid point + sgdVec3 vec = {a1[0] - a2[0], a1[1] - a2[1], a1[2] - a2[2]}; + //create the unit vector + sgdScaleVec3(vec, 1.0 / sgdLengthVec3(vec)); + sgdVec3 newPt; + sgdCopyVec3(newPt, a2); + sgdAddVec3(newPt, vec); + if (gluProject(newPt[0], newPt[1], newPt[2], _mm, _pm, _view, &p1[0], &p1[1], &p1[2]) + && (p1[2] > 0 && p1[2] < 1.0)) { + boundPoint(p2, p1); + glBegin(GL_LINES); + glVertex2d(p2[0], p2[1]); + glVertex2d(p1[0], p1[1]); + glEnd(); + } + + } else if (p1Insight && p2Insight) { //both points are insight, but not inside + bool v = boundOutsidePoints(p1, p2); + if (v) { + glBegin(GL_LINES); + glVertex2d(p1[0], p1[1]); + glVertex2d(p2[0], p2[1]); + glEnd(); + } + return v; + } + //else both points are not insight, don't draw anything + return (p1Valid && p2Valid); +} + + +void HUD::Runway::boundPoint(const sgdVec3& v, sgdVec3& m) +{ + double y = v[1]; + if (m[1] < v[1]) + y = _location.bottom; + else if (m[1] > v[1]) + y = _location.top; + + if (m[0] == v[0]) { + m[1] = y; + return; //prevent divide by zero + } + + double slope = (m[1] - v[1]) / (m[0] - v[0]); + m[0] = (y - v[1]) / slope + v[0]; + m[1] = y; + + if (m[0] < _location.left) { + m[0] = _location.left; + m[1] = slope * (_location.left - v[0]) + v[1]; + + } else if (m[0] > _location.right) { + m[0] = _location.right; + m[1] = slope * (_location.right - v[0]) + v[1]; + } +} + + +bool HUD::Runway::boundOutsidePoints(sgdVec3& v, sgdVec3& m) +{ + bool pointsInvalid = (v[1] > _location.top && m[1] > _location.top) || + (v[1] < _location.bottom && m[1] < _location.bottom) || + (v[0] > _location.right && m[0] > _location.right) || + (v[0] < _location.left && m[0] < _location.left); + if (pointsInvalid) + return false; + + if (m[0] == v[0]) {//x's are equal, vertical line + if (m[1] > v[1]) { + m[1] = _location.top; + v[1] = _location.bottom; + } else { + v[1] = _location.top; + m[1] = _location.bottom; + } + return true; + } + + if (m[1] == v[1]) { //y's are equal, horizontal line + if (m[0] > v[0]) { + m[0] = _location.right; + v[0] = _location.left; + } else { + v[0] = _location.right; + m[0] = _location.left; + } + return true; + } + + double slope = (m[1] - v[1]) / (m[0] - v[0]); + double b = v[1] - (slope * v[0]); + double y1 = slope * _location.left + b; + double y2 = slope * _location.right + b; + double x1 = (_location.bottom - b) / slope; + double x2 = (_location.top - b) / slope; + int counter = 0; + + if (y1 >= _location.bottom && y1 <= _location.top) { + v[0] = _location.left; + v[1] = y1; + counter++; + } + + if (y2 >= _location.bottom && y2 <= _location.top) { + if (counter > 0) { + m[0] = _location.right; + m[1] = y2; + } else { + v[0] = _location.right; + v[1] = y2; + } + counter++; + } + + if (x1 >= _location.left && x1 <= _location.right) { + if (counter > 0) { + m[0] = x1; + m[1] = _location.bottom; + } else { + v[0] = x1; + v[1] = _location.bottom; + } + counter++; + } + + if (x2 >= _location.left && x2 <= _location.right) { + m[0] = x1; + m[1] = _location.bottom; + counter++; + } + return (counter == 2); +} + + +void HUD::Runway::drawArrow() +{ + Point3D ac(0.0), rwy(0.0); + ac.setlat(current_aircraft.fdm_state->get_Latitude_deg()); + ac.setlon(current_aircraft.fdm_state->get_Longitude_deg()); + rwy.setlat(_runway._lat); + rwy.setlon(_runway._lon); + float theta = GetHeadingFromTo(ac, rwy); + theta -= fgGetDouble("/orientation/heading-deg"); + theta = -theta; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glTranslated((_location.right + _location.left) / 2.0,(_location.top + _location.bottom) / 2.0, 0.0); + glRotated(theta, 0.0, 0.0, 1.0); + glTranslated(0.0, _arrow_radius, 0.0); + glScaled(_arrow_scale, _arrow_scale, 0.0); + + glBegin(GL_TRIANGLES); + glVertex2d(-5.0, 12.5); + glVertex2d(0.0, 25.0); + glVertex2d(5.0, 12.5); + glEnd(); + + glBegin(GL_QUADS); + glVertex2d(-2.5, 0.0); + glVertex2d(-2.5, 12.5); + glVertex2d(2.5, 12.5); + glVertex2d(2.5, 0.0); + glEnd(); + glPopMatrix(); +} + + +void HUD::Runway::setLineWidth() +{ + //Calculate the distance from the runway, A + double course, distance; + calc_gc_course_dist(Point3D(_runway._lon * SGD_DEGREES_TO_RADIANS, + _runway._lat * SGD_DEGREES_TO_RADIANS, 0.0), + Point3D(current_aircraft.fdm_state->get_Longitude(), + current_aircraft.fdm_state->get_Latitude(), 0.0 ), + &course, &distance); + distance *= SG_METER_TO_NM; + //Get altitude above runway, B + double alt_nm = _agl->getDoubleValue(); + + if (_hud->getUnits() == FEET) + alt_nm *= SG_FEET_TO_METER; + + alt_nm *= SG_METER_TO_NM; + + //Calculate distance away from runway, C = v(AČ+BČ) + distance = sqrt(alt_nm * alt_nm + distance*distance); + if (distance < _scale_dist) + glLineWidth(1.0 + ((_line_scale - 1) * ((_scale_dist - distance) / _scale_dist))); + else + glLineWidth(1.0); + +} + + diff --git a/src/Instrumentation/HUD/HUD_scale.cxx b/src/Instrumentation/HUD/HUD_scale.cxx new file mode 100644 index 000000000..67149d1fb --- /dev/null +++ b/src/Instrumentation/HUD/HUD_scale.cxx @@ -0,0 +1,58 @@ +// HUD_scale.cxx -- HUD Common Scale Base (inherited from Gauge/Tape/Dial) +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 "HUD.hxx" + + +//============== Scale class memeber definitions =============== +// +// Notes: +// 1. Scales divide the specified location into half and then +// the half opposite the read direction in half again. A bar is +// then drawn along the second divider. Scale ticks are drawn +// between the middle and quarter section lines (minor division +// markers) or just over the middle line. +// +// 2. This class was not intended to be instanciated. See moving_scale +// and gauge_instr classes. +//============================================================== +HUD::Scale::Scale( HUD *hud, const SGPropertyNode *n, float x, float y) : + Item(hud, n, x, y), + _input(n->getNode("input", false)), + _major_divs(n->getIntValue("major-divisions")), + _minor_divs(n->getIntValue("minor-divisions")), + _modulo(n->getIntValue("modulo")) +{ + if (n->hasValue("display-span")) + _range_shown = n->getFloatValue("display-span"); + else + _range_shown = _input.max() - _input.min(); + + scale_factor = (float)get_span() / _range_shown; + if (_range_shown < 0) + _range_shown = -_range_shown; + +// float temp = (_input.max() - _input.min()) / 100; // FIXME huh? +// if (_range_shown < temp) +// _range_shown = temp; +} + + diff --git a/src/Instrumentation/HUD/HUD_tape.cxx b/src/Instrumentation/HUD/HUD_tape.cxx new file mode 100644 index 000000000..022693ec1 --- /dev/null +++ b/src/Instrumentation/HUD/HUD_tape.cxx @@ -0,0 +1,1074 @@ +// HUD_tape.cxx -- HUD Tape Instrument +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 "HUD.hxx" + + +HUD::Tape::Tape(HUD *hud, const SGPropertyNode *n, float x, float y) : + Scale(hud, n, x, y), + draw_tick_bottom(n->getBoolValue("tick-bottom", false)), + draw_tick_top(n->getBoolValue("tick-top", false)), + draw_tick_right(n->getBoolValue("tick-right", false)), + draw_tick_left(n->getBoolValue("tick-left", false)), + draw_cap_bottom(n->getBoolValue("cap-bottom", false)), + draw_cap_top(n->getBoolValue("cap-top", false)), + draw_cap_right(n->getBoolValue("cap-right", false)), + draw_cap_left(n->getBoolValue("cap-left", false)), + marker_offset(n->getFloatValue("marker-offset", 0.0)), + pointer(n->getBoolValue("enable-pointer", true)), + pointer_type(n->getStringValue("pointer-type")), + tick_type(n->getStringValue("tick-type")), // 'circle' or 'line' + tick_length(n->getStringValue("tick-length")), // for variable length + zoom(n->getIntValue("zoom")) +{ + half_width_units = range_to_show() / 2.0; +} + + +void HUD::Tape::draw(void) // (HUD_scale * pscale) +{ + if (!_input.isValid()) + return; + + float vmin = 0.0, vmax = 0.0; + float marker_xs; + float marker_xe; + float marker_ys; + float marker_ye; + float text_x = 0.0, text_y = 0.0; + int lenstr; + float height, width; + int i, last; + const int BUFSIZE = 80; + char buf[BUFSIZE]; + bool condition; + int disp_val = 0; + int oddtype, k; //odd or even values for ticks + + Point mid_scr = get_centroid(); + float cur_value = _input.getFloatValue(); + + if ((int)_input.max() & 1) + oddtype = 1; //draw ticks at odd values + else + oddtype = 0; //draw ticks at even values + + Rect scrn_rect = get_location(); + + height = scrn_rect.top + scrn_rect.bottom; + width = scrn_rect.left + scrn_rect.right; + + + // was: if (type != "gauge") { ... until end + // if its not explicitly a gauge default to tape + if (pointer) { + if (pointer_type == "moving") { + vmin = _input.min(); + vmax = _input.max(); + + } else { + // default to fixed + vmin = cur_value - half_width_units; // width units == needle travel + vmax = cur_value + half_width_units; // or picture unit span. + text_x = mid_scr.x; + text_y = mid_scr.y; + } + + } else { + vmin = cur_value - half_width_units; // width units == needle travel + vmax = cur_value + half_width_units; // or picture unit span. + text_x = mid_scr.x; + text_y = mid_scr.y; + } + + // Draw the basic markings for the scale... + + if (option_vert()) { // Vertical scale + // Bottom tick bar + if (draw_tick_bottom) + draw_line(scrn_rect.left, scrn_rect.top, width, scrn_rect.top); + + // Top tick bar + if (draw_tick_top) + draw_line(scrn_rect.left, height, width, height); + + marker_xs = scrn_rect.left; // x start + marker_xe = width; // x extent + marker_ye = height; + + // glBegin(GL_LINES); + + // Bottom tick bar + // glVertex2f(marker_xs, scrn_rect.top); + // glVertex2f(marker_xe, scrn_rect.top); + + // Top tick bar + // glVertex2f(marker_xs, marker_ye); + // glVertex2f(marker_xe, marker_ye); + // glEnd(); + + + // We do not use else in the following so that combining the + // two options produces a "caged" display with double + // carrots. The same is done for horizontal card indicators. + + // begin vertical/left + //First draw capping lines and pointers + if (option_left()) { // Calculate x marker offset + + if (draw_cap_right) { + // Cap right side + draw_line(marker_xe, scrn_rect.top, marker_xe, marker_ye); + } + + marker_xs = marker_xe - scrn_rect.right / 3; // Adjust tick xs + + // draw_line(marker_xs, mid_scr.y, + // marker_xe, mid_scr.y + scrn_rect.right / 6); + // draw_line(marker_xs, mid_scr.y, + // marker_xe, mid_scr.y - scrn_rect.right / 6); + + // draw pointer + if (pointer) { + if (pointer_type == "moving") { + if (zoom == 0) { + //Code for Moving Type Pointer + float ycentre, ypoint, xpoint; + float range, wth; + if (cur_value > _input.max()) + cur_value = _input.max(); + if (cur_value < _input.min()) + cur_value = _input.min(); + + if (_input.min() >= 0.0) + ycentre = scrn_rect.top; + else if (_input.max() + _input.min() == 0.0) + ycentre = mid_scr.y; + else if (oddtype == 1) + ycentre = scrn_rect.top + (1.0 - _input.min()) * scrn_rect.bottom + / (_input.max() - _input.min()); + else + ycentre = scrn_rect.top + _input.min() * scrn_rect.bottom + / (_input.max() - _input.min()); + + range = scrn_rect.bottom; + wth = scrn_rect.left + scrn_rect.right; + + if (oddtype == 1) + ypoint = ycentre + ((cur_value - 1.0) * range / val_span); + else + ypoint = ycentre + (cur_value * range / val_span); + + xpoint = wth + marker_offset; + draw_line(xpoint, ycentre, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint - marker_offset, ypoint); + draw_line(xpoint - marker_offset, ypoint, xpoint - 5.0, ypoint + 5.0); + draw_line(xpoint - marker_offset, ypoint, xpoint - 5.0, ypoint - 5.0); + } //zoom=0 + + } else { + // default to fixed + fixed(marker_offset + marker_xe, text_y + scrn_rect.right / 6, + marker_offset + marker_xs, text_y, marker_offset + marker_xe, + text_y - scrn_rect.right / 6); + }//end pointer type + } //if pointer + } //end vertical/left + + // begin vertical/right + //First draw capping lines and pointers + if (option_right()) { // We'll default this for now. + if (draw_cap_left) { + // Cap left side + draw_line(scrn_rect.left, scrn_rect.top, scrn_rect.left, marker_ye); + } //endif cap_left + + marker_xe = scrn_rect.left + scrn_rect.right / 3; // Adjust tick xe + // Indicator carrot + // draw_line(scrn_rect.left, mid_scr.y + scrn_rect.right / 6, + // marker_xe, mid_scr.y); + // draw_line(scrn_rect.left, mid_scr.y - scrn_rect.right / 6, + // marker_xe, mid_scr.y); + + // draw pointer + if (pointer) { + if (pointer_type == "moving") { + if (zoom == 0) { + //type-fixed & zoom=1, behaviour to be defined + // Code for Moving Type Pointer + float ycentre, ypoint, xpoint; + float range; + + if (cur_value > _input.max()) + cur_value = _input.max(); + if (cur_value < _input.min()) + cur_value = _input.min(); + + if (_input.min() >= 0.0) + ycentre = scrn_rect.top; + else if (_input.max() + _input.min() == 0.0) + ycentre = mid_scr.y; + else if (oddtype == 1) + ycentre = scrn_rect.top + (1.0 - _input.min()) * scrn_rect.bottom / (_input.max() - _input.min()); + else + ycentre = scrn_rect.top + _input.min() * scrn_rect.bottom / (_input.max() - _input.min()); + + range = scrn_rect.bottom; + + if (oddtype == 1) + ypoint = ycentre + ((cur_value - 1.0) * range / val_span); + else + ypoint = ycentre + (cur_value * range / val_span); + + xpoint = scrn_rect.left - marker_offset; + draw_line(xpoint, ycentre, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint + marker_offset, ypoint); + draw_line(xpoint + marker_offset, ypoint, xpoint + 5.0, ypoint + 5.0); + draw_line(xpoint + marker_offset, ypoint, xpoint + 5.0, ypoint - 5.0); + } + + } else { + // default to fixed + fixed(-marker_offset + scrn_rect.left, text_y + scrn_rect.right / 6, + -marker_offset + marker_xe, text_y, -marker_offset + scrn_rect.left, + text_y - scrn_rect.right / 6); + } + } //if pointer + } //end vertical/right + + // At this point marker x_start and x_end values are transposed. + // To keep this from confusing things they are now interchanged. + if (option_both()) { + marker_ye = marker_xs; + marker_xs = marker_xe; + marker_xe = marker_ye; + } + + // Work through from bottom to top of scale. Calculating where to put + // minor and major ticks. + + // draw scale or tape + +// last = float_to_int(vmax)+1; +// i = float_to_int(vmin); + last = (int)vmax + 1; // N + i = (int)vmin; // N + + if (zoom == 1) { + zoomed_scale((int)vmin, (int)vmax); + } else { + for (; i < last; i++) { + condition = true; + if (!modulo() && i < _input.min()) + condition = false; + + if (condition) { // Show a tick if necessary + // Calculate the location of this tick + marker_ys = scrn_rect.top + ((i - vmin) * factor()/*+.5f*/); + // marker_ys = scrn_rect.top + (int)((i - vmin) * factor() + .5); + // Block calculation artifact from drawing ticks below min coordinate. + // Calculation here accounts for text height. + + if ((marker_ys < (scrn_rect.top + 4)) + || (marker_ys > (height - 4))) { + // Magic numbers!!! + continue; + } + + if (oddtype == 1) + k = i + 1; //enable ticks at odd values + else + k = i; + + // Minor ticks + if (_minor_divs) { + // if ((i % _minor_divs) == 0) { + if (!(k % (int)_minor_divs)) { + if (((marker_ys - 5) > scrn_rect.top) + && ((marker_ys + 5) < (height))) { + + //vertical/left OR vertical/right + if (option_both()) { + if (tick_type == "line") { + if (tick_length == "variable") { + draw_line(scrn_rect.left, marker_ys, + marker_xs, marker_ys); + draw_line(marker_xe, marker_ys, + width, marker_ys); + } else { + draw_line(scrn_rect.left, marker_ys, + marker_xs, marker_ys); + draw_line(marker_xe, marker_ys, + width, marker_ys); + } + + } else if (tick_type == "circle") { + circle(scrn_rect.left,(float)marker_ys, 3.0); + + } else { + // if neither line nor circle draw default as line + draw_line(scrn_rect.left, marker_ys, + marker_xs, marker_ys); + draw_line(marker_xe, marker_ys, + width, marker_ys); + } + // glBegin(GL_LINES); + // glVertex2f(scrn_rect.left, marker_ys); + // glVertex2f(marker_xs, marker_ys); + // glVertex2f(marker_xe, marker_ys); + // glVertex2f(scrn_rect.left + scrn_rect.right, marker_ys); + // glEnd(); + // anything other than option_both + + } else { + if (option_left()) { + if (tick_type == "line") { + if (tick_length == "variable") { + draw_line(marker_xs + 4, marker_ys, + marker_xe, marker_ys); + } else { + draw_line(marker_xs, marker_ys, + marker_xe, marker_ys); + } + } else if (tick_type == "circle") { + circle((float)marker_xs + 4, (float)marker_ys, 3.0); + + } else { + draw_line(marker_xs + 4, marker_ys, + marker_xe, marker_ys); + } + + } else { + if (tick_type == "line") { + if (tick_length == "variable") { + draw_line(marker_xs, marker_ys, + marker_xe - 4, marker_ys); + } else { + draw_line(marker_xs, marker_ys, + marker_xe, marker_ys); + } + + } else if (tick_type == "circle") { + circle((float)marker_xe - 4, (float)marker_ys, 3.0); + } else { + draw_line(marker_xs, marker_ys, + marker_xe - 4, marker_ys); + } + } + } //end huds both + } + } //end draw minor ticks + } //end minor ticks + + // Major ticks + if (_major_divs) { + if (!(k % (int)_major_divs)) { + + if (modulo()) { + disp_val = i % (int) modulo(); // ????????? + if (disp_val < 0) { + while (disp_val < 0) + disp_val += modulo(); + } + } else { + disp_val = i; + } + +// FIXME what nonsense is this?!? + lenstr = snprintf(buf, BUFSIZE, "%d", int(disp_val * _input.factor()/*+.5*/)); // was data_scaling ... makes no sense at all + // (int)(disp_val * data_scaling() +.5)); + /* if (((marker_ys - 8) > scrn_rect.top) && + ((marker_ys + 8) < (height))){ */ + // option_both + if (option_both()) { + // draw_line(scrn_rect.left, marker_ys, + // marker_xs, marker_ys); + // draw_line(marker_xs, marker_ys, + // scrn_rect.left + scrn_rect.right, + // marker_ys); + if (tick_type == "line") { + glBegin(GL_LINE_STRIP); + glVertex2f(scrn_rect.left, marker_ys); + glVertex2f(marker_xs, marker_ys); + glVertex2f(width, marker_ys); + glEnd(); + + } else if (tick_type == "circle") { + circle(scrn_rect.left, (float)marker_ys, 5.0); + + } else { + glBegin(GL_LINE_STRIP); + glVertex2f(scrn_rect.left, marker_ys); + glVertex2f(marker_xs, marker_ys); + glVertex2f(width, marker_ys); + glEnd(); + } + + if (!option_notext()) + draw_text(marker_xs + 2, marker_ys, buf, 0); + + } else { + /* Changes are made to draw a circle when tick_type="circle" */ + // anything other than option_both + if (tick_type == "line") + draw_line(marker_xs, marker_ys, marker_xe, marker_ys); + else if (tick_type == "circle") + circle((float)marker_xs + 4, (float)marker_ys, 5.0); + else + draw_line(marker_xs, marker_ys, marker_xe, marker_ys); + + if (!option_notext()) { + if (option_left()) { + draw_text(marker_xs - 8 * lenstr - 2, + marker_ys - 4, buf, 0); + } else { + draw_text(marker_xe + 3 * lenstr, + marker_ys - 4, buf, 0); + } //End if option_left + } //End if !option_notext + } //End if huds-both + } // End if draw major ticks + } // End if major ticks + } // End condition + } // End for + } //end of zoom + // End if VERTICAL SCALE TYPE (tape loop yet to be closed) + + } else { + // Horizontal scale by default + // left tick bar + if (draw_tick_left) + draw_line(scrn_rect.left, scrn_rect.top, scrn_rect.left, height); + + // right tick bar + if (draw_tick_right) + draw_line(width, scrn_rect.top, width, height); + + marker_ys = scrn_rect.top; // Starting point for + marker_ye = height; // tick y location calcs + marker_xe = width; + marker_xs = scrn_rect.left + ((cur_value - vmin) * factor() /*+ .5f*/); + + // glBegin(GL_LINES); + // left tick bar + // glVertex2f(scrn_rect.left, scrn_rect.top); + // glVertex2f(scrn_rect.left, marker_ye); + + // right tick bar + // glVertex2f(marker_xe, scrn_rect.top); + // glVertex2f(marker_xe, marker_ye); + // glEnd(); + + if (option_top()) { + // Bottom box line + if (draw_cap_bottom) + draw_line(scrn_rect.left, scrn_rect.top, width, scrn_rect.top); + + // Tick point adjust + marker_ye = scrn_rect.top + scrn_rect.bottom / 2; + // Bottom arrow + // draw_line(mid_scr.x, marker_ye, + // mid_scr.x - scrn_rect.bottom / 4, scrn_rect.top); + // draw_line(mid_scr.x, marker_ye, + // mid_scr.x + scrn_rect.bottom / 4, scrn_rect.top); + // draw pointer + if (pointer) { + if (pointer_type == "moving") { + if (zoom == 0) { + //Code for Moving Type Pointer + // static float xcentre, xpoint, ypoint; + // static int range; + if (cur_value > _input.max()) + cur_value = _input.max(); + if (cur_value < _input.min()) + cur_value = _input.min(); + + float xcentre = mid_scr.x; + float range = scrn_rect.right; + float xpoint = xcentre + (cur_value * range / val_span); + float ypoint = scrn_rect.top - marker_offset; + draw_line(xcentre, ypoint, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint, ypoint + marker_offset); + draw_line(xpoint, ypoint + marker_offset, xpoint + 5.0, ypoint + 5.0); + draw_line(xpoint, ypoint + marker_offset, xpoint - 5.0, ypoint + 5.0); + } + + } else { + //default to fixed + fixed(marker_xs - scrn_rect.bottom / 4, scrn_rect.top, marker_xs, + marker_ye, marker_xs + scrn_rect.bottom / 4, scrn_rect.top); + } + } //if pointer + } //End Horizontal scale/top + + if (option_bottom()) { + // Top box line + if (draw_cap_top) + draw_line(scrn_rect.left, height, width, height); + + // Tick point adjust + marker_ys = height - scrn_rect.bottom / 2; + // Top arrow + // draw_line(mid_scr.x + scrn_rect.bottom / 4, + // scrn_rect.top + scrn_rect.bottom, + // mid_scr.x, marker_ys); + // draw_line(mid_scr.x - scrn_rect.bottom / 4, + // scrn_rect.top + scrn_rect.bottom, + // mid_scr.x , marker_ys); + + // draw pointer + if (pointer) { + if (pointer_type == "moving") { + if (zoom == 0) { + //Code for Moving Type Pointer + // static float xcentre, xpoint, ypoint; + // static int range, hgt; + if (cur_value > _input.max()) + cur_value = _input.max(); + if (cur_value < _input.min()) + cur_value = _input.min(); + + float xcentre = mid_scr.x ; + float range = scrn_rect.right; + float hgt = scrn_rect.top + scrn_rect.bottom; + float xpoint = xcentre + (cur_value * range / val_span); + float ypoint = hgt + marker_offset; + draw_line(xcentre, ypoint, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint, ypoint - marker_offset); + draw_line(xpoint, ypoint - marker_offset, xpoint + 5.0, ypoint - 5.0); + draw_line(xpoint, ypoint - marker_offset, xpoint - 5.0, ypoint - 5.0); + } + } else { + fixed(marker_xs + scrn_rect.bottom / 4, height, marker_xs, marker_ys, + marker_xs - scrn_rect.bottom / 4, height); + } + } //if pointer + } //end horizontal scale bottom + + + if (zoom == 1) { + zoomed_scale((int)vmin,(int)vmax); + } else { + //default to zoom=0 + last = (int)vmax + 1; + i = (int)vmin; + for (; i < last; i++) { + // for (i = (int)vmin; i <= (int)vmax; i++) { + // printf("<*> i = %d\n", i); + condition = true; + if (!modulo() && i < _input.min()) + condition = false; + + // printf("<**> i = %d\n", i); + if (condition) { + // marker_xs = scrn_rect.left + (int)((i - vmin) * factor() + .5); + marker_xs = scrn_rect.left + (((i - vmin) * factor()/*+ .5f*/)); + + if (oddtype == 1) + k = i + 1; //enable ticks at odd values + else + k = i; + + if (_minor_divs) { + // if ((i % (int)_minor_divs) == 0) { + //draw minor ticks + if (!(k % (int)_minor_divs)) { + // draw in ticks only if they aren't too close to the edge. + if (((marker_xs - 5) > scrn_rect.left) + && ((marker_xs + 5)< (scrn_rect.left + scrn_rect.right))) { + + if (option_both()) { + if (tick_length == "variable") { + draw_line(marker_xs, scrn_rect.top, + marker_xs, marker_ys - 4); + draw_line(marker_xs, marker_ye + 4, + marker_xs, height); + } else { + draw_line(marker_xs, scrn_rect.top, + marker_xs, marker_ys); + draw_line(marker_xs, marker_ye, + marker_xs, height); + } + // glBegin(GL_LINES); + // glVertex2f(marker_xs, scrn_rect.top); + // glVertex2f(marker_xs, marker_ys - 4); + // glVertex2f(marker_xs, marker_ye + 4); + // glVertex2f(marker_xs, scrn_rect.top + scrn_rect.bottom); + // glEnd(); + + } else { + if (option_top()) { + //draw minor ticks + if (tick_length == "variable") + draw_line(marker_xs, marker_ys, marker_xs, marker_ye - 4); + else + draw_line(marker_xs, marker_ys, marker_xs, marker_ye); + + } else if (tick_length == "variable") { + draw_line(marker_xs, marker_ys + 4, marker_xs, marker_ye); + } else { + draw_line(marker_xs, marker_ys, marker_xs, marker_ye); + } + } + } + } //end draw minor ticks + } //end minor ticks + + //major ticks + if (_major_divs) { + // printf("i = %d\n", i); + // if ((i % (int)_major_divs)==0) { + // draw major ticks + + if (!(k % (int)_major_divs)) { + if (modulo()) { + disp_val = i % (int) modulo(); // ????????? + if (disp_val < 0) { + while (disp_val<0) + disp_val += modulo(); + } + } else { + disp_val = i; + } + // printf("disp_val = %d\n", disp_val); + // printf("%d\n", (int)(disp_val * (double)data_scaling() + 0.5)); + lenstr = snprintf(buf, BUFSIZE, "%d", + // (int)(disp_val * data_scaling() +.5)); + int(disp_val * _input.factor() /*+.5*/)); // was data_scaling() ... makes no sense at all + + // Draw major ticks and text only if far enough from the edge. + if (((marker_xs - 10)> scrn_rect.left) + && ((marker_xs + 10) < (scrn_rect.left + scrn_rect.right))) { + if (option_both()) { + // draw_line(marker_xs, scrn_rect.top, + // marker_xs, marker_ys); + // draw_line(marker_xs, marker_ye, + // marker_xs, scrn_rect.top + scrn_rect.bottom); + glBegin(GL_LINE_STRIP); + glVertex2f(marker_xs, scrn_rect.top); + glVertex2f(marker_xs, marker_ye); + glVertex2f(marker_xs, height); + glEnd(); + + if (!option_notext()) { + draw_text(marker_xs - 4 * lenstr, + marker_ys + 4, buf, 0); + } + } else { + draw_line(marker_xs, marker_ys, marker_xs, marker_ye); + + if (!option_notext()) { + if (option_top()) { + draw_text(marker_xs - 4 * lenstr, + height - 10, buf, 0); + + } else { + draw_text(marker_xs - 4 * lenstr, + scrn_rect.top, buf, 0); + } + } + } + } + } //end draw major ticks + } //endif major ticks + } //end condition + } //end for + } //end zoom + } //end horizontal/vertical scale +} //draw + + + +void HUD::Tape::circle(float x, float y, float size) +{ + glEnable(GL_POINT_SMOOTH); + glPointSize(size); + + glBegin(GL_POINTS); + glVertex2f(x, y); + glEnd(); + + glPointSize(1.0); + glDisable(GL_POINT_SMOOTH); +} + + +void HUD::Tape::fixed(float x1, float y1, float x2, float y2, float x3, float y3) +{ + glBegin(GL_LINE_STRIP); + glVertex2f(x1, y1); + glVertex2f(x2, y2); + glVertex2f(x3, y3); + glEnd(); +} + + +void HUD::Tape::zoomed_scale(int first, int last) +{ + Point mid_scr = get_centroid(); + Rect scrn_rect = get_location(); + const int BUFSIZE = 80; + char buf[BUFSIZE]; + int data[80]; + + float x, y, w, h, bottom; + float cur_value = _input.getFloatValue(); + if (cur_value > _input.max()) + cur_value = _input.max(); + if (cur_value < _input.min()) + cur_value = _input.min(); + + int a = 0; + + while (first <= last) { + if ((first % (int)_major_divs) == 0) { + data[a] = first; + a++ ; + } + first++; + } + int centre = a / 2; + + if (option_vert()) { + x = scrn_rect.left; + y = scrn_rect.top; + w = scrn_rect.left + scrn_rect.right; + h = scrn_rect.top + scrn_rect.bottom; + bottom = scrn_rect.bottom; + + float xstart, yfirst, ycentre, ysecond; + + float hgt = bottom * 20.0 / 100.0; // 60% of height should be zoomed + yfirst = mid_scr.y - hgt; + ycentre = mid_scr.y; + ysecond = mid_scr.y + hgt; + float range = hgt * 2; + + int i; + float factor = range / 10.0; + + float hgt1 = bottom * 30.0 / 100.0; + int incrs = ((int)val_span - (_major_divs * 2)) / _major_divs ; + int incr = incrs / 2; + float factors = hgt1 / incr; + + // begin + //this is for moving type pointer + static float ycent, ypoint, xpoint; + static float wth; + + ycent = mid_scr.y; + wth = scrn_rect.left + scrn_rect.right; + + if (cur_value <= data[centre + 1]) + if (cur_value > data[centre]) { + ypoint = ycent + ((cur_value - data[centre]) * hgt / _major_divs); + } + + if (cur_value >= data[centre - 1]) + if (cur_value <= data[centre]) { + ypoint = ycent - ((data[centre] - cur_value) * hgt / _major_divs); + } + + if (cur_value < data[centre - 1]) + if (cur_value >= _input.min()) { + float diff = _input.min() - data[centre - 1]; + float diff1 = cur_value - data[centre - 1]; + float val = (diff1 * hgt1) / diff; + + ypoint = ycent - hgt - val; + } + + if (cur_value > data[centre + 1]) + if (cur_value <= _input.max()) { + float diff = _input.max() - data[centre + 1]; + float diff1 = cur_value - data[centre + 1]; + float val = (diff1 * hgt1) / diff; + + ypoint = ycent + hgt + val; + } + + if (option_left()) { + xstart = w; + + draw_line(xstart, ycentre, xstart - 5.0, ycentre); //centre tick + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre] * _input.factor())); // was data_scaling() ... makes not sense at all + + if (!option_notext()) + draw_text(x, ycentre, buf, 0); + + for (i = 1; i < 5; i++) { + yfirst += factor; + ycentre += factor; + circle(xstart - 2.5, yfirst, 3.0); + circle(xstart - 2.5, ycentre, 3.0); + } + + yfirst = mid_scr.y - hgt; + + for (i = 0; i <= incr; i++) { + draw_line(xstart, yfirst, xstart - 5.0, yfirst); + draw_line(xstart, ysecond, xstart - 5.0, ysecond); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre - i - 1] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(x, yfirst, buf, 0); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre + i + 1] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(x, ysecond, buf, 0); + + yfirst -= factors; + ysecond += factors; + + } + + //to draw moving type pointer for left option + //begin + xpoint = wth + 10.0; + + if (pointer_type == "moving") { + draw_line(xpoint, ycent, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint - 10.0, ypoint); + draw_line(xpoint - 10.0, ypoint, xpoint - 5.0, ypoint + 5.0); + draw_line(xpoint - 10.0, ypoint, xpoint - 5.0, ypoint - 5.0); + } + //end + + } else { + //option_right + xstart = (x + w) / 2; + + draw_line(xstart, ycentre, xstart + 5.0, ycentre); //centre tick + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(w, ycentre, buf, 0); + + for (i = 1; i < 5; i++) { + yfirst += factor; + ycentre += factor; + circle(xstart + 2.5, yfirst, 3.0); + circle(xstart + 2.5, ycentre, 3.0); + } + + yfirst = mid_scr.y - hgt; + + for (i = 0; i <= incr; i++) { + draw_line(xstart, yfirst, xstart + 5.0, yfirst); + draw_line(xstart, ysecond, xstart + 5.0, ysecond); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre - i - 1] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(w, yfirst, buf, 0); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre + i + 1] * _input.factor())); + + if (!option_notext()) + draw_text(w, ysecond, buf, 0); + + yfirst -= factors; + ysecond += factors; + + } + + // to draw moving type pointer for right option + //begin + xpoint = scrn_rect.left; + + if (pointer_type == "moving") { + draw_line(xpoint, ycent, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint + 10.0, ypoint); + draw_line(xpoint + 10.0, ypoint, xpoint + 5.0, ypoint + 5.0); + draw_line(xpoint + 10.0, ypoint, xpoint + 5.0, ypoint - 5.0); + } + //end + }//end option_right /left + //end of vertical scale + + } else { + //horizontal scale + x = scrn_rect.left; + y = scrn_rect.top; + w = scrn_rect.left + scrn_rect.right; + h = scrn_rect.top + scrn_rect.bottom; + bottom = scrn_rect.right; + + float ystart, xfirst, xcentre, xsecond; + + float hgt = bottom * 20.0 / 100.0; // 60% of height should be zoomed + xfirst = mid_scr.x - hgt; + xcentre = mid_scr.x; + xsecond = mid_scr.x + hgt; + float range = hgt * 2; + + int i; + float factor = range / 10.0; + + float hgt1 = bottom * 30.0 / 100.0; + int incrs = ((int)val_span - (_major_divs * 2)) / _major_divs ; + int incr = incrs / 2; + float factors = hgt1 / incr; + + + //Code for Moving Type Pointer + //begin + static float xcent, xpoint, ypoint; + + xcent = mid_scr.x; + + if (cur_value <= data[centre + 1]) + if (cur_value > data[centre]) { + xpoint = xcent + ((cur_value - data[centre]) * hgt / _major_divs); + } + + if (cur_value >= data[centre - 1]) + if (cur_value <= data[centre]) { + xpoint = xcent - ((data[centre] - cur_value) * hgt / _major_divs); + } + + if (cur_value < data[centre - 1]) + if (cur_value >= _input.min()) { + float diff = _input.min() - data[centre - 1]; + float diff1 = cur_value - data[centre - 1]; + float val = (diff1 * hgt1) / diff; + + xpoint = xcent - hgt - val; + } + + + if (cur_value > data[centre + 1]) + if (cur_value <= _input.max()) { + float diff = _input.max() - data[centre + 1]; + float diff1 = cur_value - data[centre + 1]; + float val = (diff1 * hgt1) / diff; + + xpoint = xcent + hgt + val; + } + + //end + if (option_top()) { + ystart = h; + draw_line(xcentre, ystart, xcentre, ystart - 5.0); //centre tick + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(xcentre - 10.0, y, buf, 0); + + for (i = 1; i < 5; i++) { + xfirst += factor; + xcentre += factor; + circle(xfirst, ystart - 2.5, 3.0); + circle(xcentre, ystart - 2.5, 3.0); + } + + xfirst = mid_scr.x - hgt; + + for (i = 0; i <= incr; i++) { + draw_line(xfirst, ystart, xfirst, ystart - 5.0); + draw_line(xsecond, ystart, xsecond, ystart - 5.0); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre - i - 1] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(xfirst - 10.0, y, buf, 0); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre + i + 1] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(xsecond - 10.0, y, buf, 0); + + + xfirst -= factors; + xsecond += factors; + } + //this is for moving pointer for top option + //begin + + ypoint = scrn_rect.top + scrn_rect.bottom + 10.0; + + if (pointer_type == "moving") { + draw_line(xcent, ypoint, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint, ypoint - 10.0); + draw_line(xpoint, ypoint - 10.0, xpoint + 5.0, ypoint - 5.0); + draw_line(xpoint, ypoint - 10.0, xpoint - 5.0, ypoint - 5.0); + } + //end of top option + + } else { + //else option_bottom + ystart = (y + h) / 2; + + //draw_line(xstart, yfirst, xstart - 5.0, yfirst); + draw_line(xcentre, ystart, xcentre, ystart + 5.0); //centre tick + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(xcentre - 10.0, h, buf, 0); + + for (i = 1; i < 5; i++) { + xfirst += factor; + xcentre += factor; + circle(xfirst, ystart + 2.5, 3.0); + circle(xcentre, ystart + 2.5, 3.0); + } + + xfirst = mid_scr.x - hgt; + + for (i = 0; i <= incr; i++) { + draw_line(xfirst, ystart, xfirst, ystart + 5.0); + draw_line(xsecond, ystart, xsecond, ystart + 5.0); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre - i - 1] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(xfirst - 10.0, h, buf, 0); + + snprintf(buf, BUFSIZE, "%3.0f\n", (float)(data[centre + i + 1] * _input.factor())); // was data_scaling() ... makes no sense at all + + if (!option_notext()) + draw_text(xsecond - 10.0, h, buf, 0); + + xfirst -= factors; + xsecond += factors; + } + //this is for movimg pointer for bottom option + //begin + + ypoint = scrn_rect.top - 10.0; + if (pointer_type == "moving") { + draw_line(xcent, ypoint, xpoint, ypoint); + draw_line(xpoint, ypoint, xpoint, ypoint + 10.0); + draw_line(xpoint, ypoint + 10.0, xpoint + 5.0, ypoint + 5.0); + draw_line(xpoint, ypoint + 10.0, xpoint - 5.0, ypoint + 5.0); + } + }//end hud_top or hud_bottom + } //end of horizontal/vertical scales +}//end draw + + diff --git a/src/Instrumentation/HUD/HUD_tbi.cxx b/src/Instrumentation/HUD/HUD_tbi.cxx new file mode 100644 index 000000000..7d893efff --- /dev/null +++ b/src/Instrumentation/HUD/HUD_tbi.cxx @@ -0,0 +1,212 @@ +// HUD_tbi.cxx -- HUD Turn-Bank-Indicator Instrument +// +// Written by Michele America, started September 1997. +// +// Copyright (C) 1997 Michele F. America [micheleamerica#geocities:com] +// Copyright (C) 2006 Melchior FRANZ [mfranz#aon:at] +// +// 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 "HUD.hxx" + + +HUD::TurnBankIndicator::TurnBankIndicator(HUD *hud, const SGPropertyNode *n, float x, float y) : + Item(hud, n, x, y), + _bank(n->getNode("bank-input", false)), + _sideslip(n->getNode("sideslip-input", false)), + _gap_width(n->getFloatValue("gap-width", 5)), + _bank_scale(n->getBoolValue("bank-scale", false)), + _bank_scale_radius(n->getFloatValue("bank-scale-radius", 250.0)) +{ + if (!_bank_scale) { + _bank.set_max(30.0, false); + _bank.set_min(-30.0, false); + _sideslip.set_max(20.0, false); + _sideslip.set_min(-20.0, false); + } +} + + +void HUD::TurnBankIndicator::draw(void) +{ + if (!_bank.isValid() || !_sideslip.isValid()) + return; + + float bank = _bank.getFloatValue(); + float sideslip = _sideslip.getFloatValue(); + + float span = get_span(); + Rect My_box = get_location(); + Point centroid = get_centroid(); + + float cen_x = centroid.x; + float cen_y = centroid.y; + + float tee_height = My_box.bottom; + float tee = -tee_height; + float ss_const = 2 * sideslip * span / 40.0; // sideslip angle pixels per deg (width represents 40 deg) + + glPushMatrix(); + glTranslatef(cen_x, cen_y, 0.0); + glRotatef(-bank, 0.0, 0.0, 1.0); + + if (!_bank_scale) { + glBegin(GL_LINES); + + if (!_gap_width) { + glVertex2f(-span, 0.0); + glVertex2f(span, 0.0); + + } else { + glVertex2f(-span, 0.0); + glVertex2f(-_gap_width, 0.0); + glVertex2f(_gap_width, 0.0); + glVertex2f(span, 0.0); + } + + // draw teemarks + glVertex2f(_gap_width, 0.0); + glVertex2f(_gap_width, tee); + glVertex2f(-_gap_width, 0.0); + glVertex2f(-_gap_width, tee); + glEnd(); + + glBegin(GL_LINE_LOOP); + glVertex2f(ss_const, -_gap_width); + glVertex2f(ss_const + _gap_width, 0.0); + glVertex2f(ss_const, _gap_width); + glVertex2f(ss_const - _gap_width, 0.0); + glEnd(); + + + } else { // draw MIL-STD 1878B/4.2.2.4 bank scale + draw_line(cen_x - 1.0, My_box.top, cen_x + 1.0, My_box.top); + draw_line(cen_x - 1.0, My_box.top, cen_x - 1.0, My_box.top + 10.0); + draw_line(cen_x + 1.0, My_box.top, cen_x + 1.0, My_box.top + 10.0); + draw_line(cen_x - 1.0, My_box.top + 10.0, cen_x + 1.0, My_box.top + 10.0); + + float x1, y1, x2, y2, x3, y3, x4, y4, x5, y5; + float xc, yc; + float r = _bank_scale_radius; + float r1 = r - 10.0; + float r2 = r - 5.0; + + xc = My_box.left + My_box.right / 2.0 ; + yc = My_box.top + r; + + // first n last lines + x1 = xc + r * cos(255.0 * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin(255.0 * SGD_DEGREES_TO_RADIANS); + + x2 = xc + r1 * cos(255.0 * SGD_DEGREES_TO_RADIANS); + y2 = yc + r1 * sin(255.0 * SGD_DEGREES_TO_RADIANS); + draw_line(x1, y1, x2, y2); + + x1 = xc + r * cos(285.0 * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin(285.0 * SGD_DEGREES_TO_RADIANS); + + x2 = xc + r1 * cos(285.0 * SGD_DEGREES_TO_RADIANS); + y2 = yc + r1 * sin(285.0 * SGD_DEGREES_TO_RADIANS); + draw_line(x1, y1, x2, y2); + + // second n fifth lines + x1 = xc + r * cos(260.0 * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin(260.0 * SGD_DEGREES_TO_RADIANS); + + x2 = xc + r2 * cos(260.0 * SGD_DEGREES_TO_RADIANS); + y2 = yc + r2 * sin(260.0 * SGD_DEGREES_TO_RADIANS); + draw_line(x1, y1, x2, y2); + + x1 = xc + r * cos(280.0 * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin(280.0 * SGD_DEGREES_TO_RADIANS); + + x2 = xc + r2 * cos(280.0 * SGD_DEGREES_TO_RADIANS); + y2 = yc + r2 * sin(280.0 * SGD_DEGREES_TO_RADIANS); + draw_line(x1, y1, x2, y2); + + // third n fourth lines + x1 = xc + r * cos(265.0 * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin(265.0 * SGD_DEGREES_TO_RADIANS); + + + x2 = xc + r2 * cos(265.0 * SGD_DEGREES_TO_RADIANS); + y2 = yc + r2 * sin(265.0 * SGD_DEGREES_TO_RADIANS); + draw_line(x1, y1, x2, y2); + + x1 = xc + r * cos(275.0 * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin(275.0 * SGD_DEGREES_TO_RADIANS); + + x2 = xc + r2 * cos(275.0 * SGD_DEGREES_TO_RADIANS); + y2 = yc + r2 * sin(275.0 * SGD_DEGREES_TO_RADIANS); + draw_line(x1, y1, x2, y2); + + // draw marker + r = _bank_scale_radius + 5.0; // add gap + + // upper polygon + float valbank = bank * 15.0 / _bank.max(); // total span of TSI is 30 degrees + float valsideslip = sideslip * 15.0 / _sideslip.max(); + + // values 270, 225 and 315 are angles in degrees... + x1 = xc + r * cos((valbank + 270.0) * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin((valbank + 270.0) * SGD_DEGREES_TO_RADIANS); + + x2 = x1 + 6.0 * cos(225 * SGD_DEGREES_TO_RADIANS); + y2 = y1 + 6.0 * sin(225 * SGD_DEGREES_TO_RADIANS); + + x3 = x1 + 6.0 * cos(315 * SGD_DEGREES_TO_RADIANS); + y3 = y1 + 6.0 * sin(315 * SGD_DEGREES_TO_RADIANS); + + draw_line(x1, y1, x2, y2); + draw_line(x2, y2, x3, y3); + draw_line(x3, y3, x1, y1); + + // lower polygon // FIXME this is inefficient and wrong! + x1 = xc + r * cos((valbank + 270.0) * SGD_DEGREES_TO_RADIANS); + y1 = yc + r * sin((valbank + 270.0) * SGD_DEGREES_TO_RADIANS); + + x2 = x1 + 6.0 * cos(225 * SGD_DEGREES_TO_RADIANS); + y2 = y1 + 6.0 * sin(225 * SGD_DEGREES_TO_RADIANS); + + x3 = x1 + 6.0 * cos(315 * SGD_DEGREES_TO_RADIANS); + y3 = y1 + 6.0 * sin(315 * SGD_DEGREES_TO_RADIANS); + + x4 = x1 + 10.0 * cos(225 * SGD_DEGREES_TO_RADIANS); + y4 = y1 + 10.0 * sin(225 * SGD_DEGREES_TO_RADIANS); + + x5 = x1 + 10.0 * cos(315 * SGD_DEGREES_TO_RADIANS); + y5 = y1 + 10.0 * sin(315 * SGD_DEGREES_TO_RADIANS); + + float cosss = cos(valsideslip * SGD_DEGREES_TO_RADIANS); + float sinss = sin(valsideslip * SGD_DEGREES_TO_RADIANS); + + x2 = x2 + cosss; + y2 = y2 + sinss; + x3 = x3 + cosss; + y3 = y3 + sinss; + x4 = x4 + cosss; + y4 = y4 + sinss; + x5 = x5 + cosss; + y5 = y5 + sinss; + + draw_line(x2, y2, x3, y3); + draw_line(x3, y3, x5, y5); + draw_line(x5, y5, x4, y4); + draw_line(x4, y4, x2, y2); + } + glPopMatrix(); +} + +