64028a37ef
I wrote: > I can confirm this. Layers on the 2D panels (but oddly, only the 2D > panels) aren't drawing over the background with the current ATI > drivers. OK, this turns out to be a trivial fix, although I still think it's a driver bug. There are two calls to glPolygonOffset in the panel rendering code (shared by both 2D and 3D panels). One is called per-layer, and sets up a layer-specific offset. The other is called for drawing the background textures, to lift them off of any underlying cockpit geometry. I was using different "factor" values for each, incorrectly. Patch attached. It was affecting only 2D panels because the 3D ones don't use background images. Problem is, by my reading of the specification the bug should have had the effect of pushing the background texture *farther* behind the instruments, instead of pulling it on top of them. Either I'm reading the spec incorrectly or ATI has inverted the sense of the factor argument. Dunno, I'll submit a bug report to them and see what happens.
1153 lines
26 KiB
C++
1153 lines
26 KiB
C++
// panel.cxx - default, 2D single-engine prop instrument panel
|
||
//
|
||
// Written by David Megginson, started January 2000.
|
||
//
|
||
// This program is free software; you can redistribute it and/or
|
||
// modify it under the terms of the GNU General Public License as
|
||
// published by the Free Software Foundation; either version 2 of the
|
||
// License, or (at your option) any later version.
|
||
//
|
||
// This program is distributed in the hope that it will be useful, but
|
||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
// General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU General Public License
|
||
// along with this program; if not, write to the Free Software
|
||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
//
|
||
// $Id$
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
# include <config.h>
|
||
#endif
|
||
|
||
#ifdef HAVE_WINDOWS_H
|
||
# include <windows.h>
|
||
#endif
|
||
|
||
#include <stdio.h> // sprintf
|
||
#include <string.h>
|
||
|
||
#include <plib/ssg.h>
|
||
#include <plib/fnt.h>
|
||
|
||
#include <simgear/debug/logstream.hxx>
|
||
#include <simgear/misc/sg_path.hxx>
|
||
|
||
#include <Main/globals.hxx>
|
||
#include <Main/fg_props.hxx>
|
||
#include <Main/viewmgr.hxx>
|
||
#include <Objects/texload.h>
|
||
#include <Time/light.hxx>
|
||
|
||
#include "hud.hxx"
|
||
#include "panel.hxx"
|
||
|
||
#define WIN_X 0
|
||
#define WIN_Y 0
|
||
#define WIN_W 1024
|
||
#define WIN_H 768
|
||
|
||
// The number of polygon-offset "units" to place between layers. In
|
||
// principle, one is supposed to be enough. In practice, I find that
|
||
// my hardware/driver requires many more.
|
||
#define POFF_UNITS 4
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Local functions.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
/**
|
||
* Calculate the aspect adjustment for the panel.
|
||
*/
|
||
static float
|
||
get_aspect_adjust (int xsize, int ysize)
|
||
{
|
||
float ideal_aspect = float(WIN_W) / float(WIN_H);
|
||
float real_aspect = float(xsize) / float(ysize);
|
||
return (real_aspect / ideal_aspect);
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Global functions.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
bool
|
||
fgPanelVisible ()
|
||
{
|
||
if(current_panel == 0)
|
||
return false;
|
||
if(current_panel->getVisibility() == 0)
|
||
return false;
|
||
if(globals->get_viewmgr()->get_current() != 0)
|
||
return false;
|
||
if(globals->get_current_view()->getHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS != 0)
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGTextureManager.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
map<string,ssgTexture *> FGTextureManager::_textureMap;
|
||
|
||
ssgTexture *
|
||
FGTextureManager::createTexture (const string &relativePath)
|
||
{
|
||
ssgTexture * texture = _textureMap[relativePath];
|
||
if (texture == 0) {
|
||
SG_LOG( SG_COCKPIT, SG_DEBUG,
|
||
"Texture " << relativePath << " does not yet exist" );
|
||
SGPath tpath(globals->get_fg_root());
|
||
tpath.append(relativePath);
|
||
texture = new ssgTexture((char *)tpath.c_str(), false, false);
|
||
_textureMap[relativePath] = texture;
|
||
if (_textureMap[relativePath] == 0)
|
||
SG_LOG( SG_COCKPIT, SG_ALERT, "Texture *still* doesn't exist" );
|
||
SG_LOG( SG_COCKPIT, SG_DEBUG, "Created texture " << relativePath
|
||
<< " handle=" << texture->getHandle() );
|
||
}
|
||
|
||
return texture;
|
||
}
|
||
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGCropped Texture.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
FGCroppedTexture::FGCroppedTexture ()
|
||
: _path(""), _texture(0),
|
||
_minX(0.0), _minY(0.0), _maxX(1.0), _maxY(1.0)
|
||
{
|
||
}
|
||
|
||
|
||
FGCroppedTexture::FGCroppedTexture (const string &path,
|
||
float minX, float minY,
|
||
float maxX, float maxY)
|
||
: _path(path), _texture(0),
|
||
_minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY)
|
||
{
|
||
}
|
||
|
||
|
||
FGCroppedTexture::~FGCroppedTexture ()
|
||
{
|
||
}
|
||
|
||
|
||
ssgTexture *
|
||
FGCroppedTexture::getTexture ()
|
||
{
|
||
if (_texture == 0) {
|
||
_texture = FGTextureManager::createTexture(_path);
|
||
}
|
||
return _texture;
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGPanel.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGPanel * current_panel = NULL;
|
||
static fntRenderer text_renderer;
|
||
static fntTexFont *default_font = 0;
|
||
static fntTexFont *led_font = 0;
|
||
|
||
/**
|
||
* Constructor.
|
||
*/
|
||
FGPanel::FGPanel ()
|
||
: _mouseDown(false),
|
||
_mouseInstrument(0),
|
||
_width(WIN_W), _height(int(WIN_H * 0.5768 + 1)),
|
||
_x_offset(0), _y_offset(0), _view_height(int(WIN_H * 0.4232)),
|
||
_jitter(0.0),
|
||
_xsize_node(fgGetNode("/sim/startup/xsize", true)),
|
||
_ysize_node(fgGetNode("/sim/startup/ysize", true))
|
||
{
|
||
setVisibility(fgPanelVisible());
|
||
}
|
||
|
||
|
||
/**
|
||
* Destructor.
|
||
*/
|
||
FGPanel::~FGPanel ()
|
||
{
|
||
for (instrument_list_type::iterator it = _instruments.begin();
|
||
it != _instruments.end();
|
||
it++) {
|
||
delete *it;
|
||
*it = 0;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Add an instrument to the panel.
|
||
*/
|
||
void
|
||
FGPanel::addInstrument (FGPanelInstrument * instrument)
|
||
{
|
||
_instruments.push_back(instrument);
|
||
}
|
||
|
||
|
||
/**
|
||
* Initialize the panel.
|
||
*/
|
||
void
|
||
FGPanel::init ()
|
||
{
|
||
SGPath base_path;
|
||
char* envp = ::getenv( "FG_FONTS" );
|
||
if ( envp != NULL ) {
|
||
base_path.set( envp );
|
||
} else {
|
||
base_path.set( globals->get_fg_root() );
|
||
base_path.append( "Fonts" );
|
||
}
|
||
|
||
SGPath fntpath;
|
||
|
||
// Install the default font
|
||
fntpath = base_path;
|
||
fntpath.append( "typewriter.txf" );
|
||
default_font = new fntTexFont ;
|
||
default_font -> load ( (char *)fntpath.c_str() ) ;
|
||
|
||
// Install the LED font
|
||
fntpath = base_path;
|
||
fntpath.append( "led.txf" );
|
||
led_font = new fntTexFont ;
|
||
led_font -> load ( (char *)fntpath.c_str() ) ;
|
||
}
|
||
|
||
|
||
/**
|
||
* Bind panel properties.
|
||
*/
|
||
void
|
||
FGPanel::bind ()
|
||
{
|
||
fgSetArchivable("/sim/panel/visibility");
|
||
fgSetArchivable("/sim/panel/x-offset");
|
||
fgSetArchivable("/sim/panel/y-offset");
|
||
fgSetArchivable("/sim/panel/jitter");
|
||
}
|
||
|
||
|
||
/**
|
||
* Unbind panel properties.
|
||
*/
|
||
void
|
||
FGPanel::unbind ()
|
||
{
|
||
}
|
||
|
||
|
||
/**
|
||
* Update the panel.
|
||
*/
|
||
void
|
||
FGPanel::update (double dt)
|
||
{
|
||
// TODO: cache the nodes
|
||
_visibility = fgGetBool("/sim/panel/visibility");
|
||
_x_offset = fgGetInt("/sim/panel/x-offset");
|
||
_y_offset = fgGetInt("/sim/panel/y-offset");
|
||
_jitter = fgGetFloat("/sim/panel/jitter");
|
||
_flipx = fgGetBool("/sim/panel/flip-x");
|
||
|
||
// Do nothing if the panel isn't visible.
|
||
if ( !fgPanelVisible() ) {
|
||
return;
|
||
}
|
||
|
||
updateMouseDelay();
|
||
|
||
// Now, draw the panel
|
||
float aspect_adjust = get_aspect_adjust(_xsize_node->getIntValue(),
|
||
_ysize_node->getIntValue());
|
||
if (aspect_adjust <1.0)
|
||
update(WIN_X, int(WIN_W * aspect_adjust), WIN_Y, WIN_H);
|
||
else
|
||
update(WIN_X, WIN_W, WIN_Y, int(WIN_H / aspect_adjust));
|
||
}
|
||
|
||
/**
|
||
* Handle repeatable mouse events. Called from update() and from
|
||
* fgUpdate3DPanels(). This functionality needs to move into the
|
||
* input subsystem. Counting a tick every two frames is clumsy...
|
||
*/
|
||
void FGPanel::updateMouseDelay()
|
||
{
|
||
if (_mouseDown) {
|
||
_mouseDelay--;
|
||
if (_mouseDelay < 0) {
|
||
_mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
|
||
_mouseDelay = 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
FGPanel::update (GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh)
|
||
{
|
||
// Calculate accelerations
|
||
// and jiggle the panel accordingly
|
||
// The factors and bounds are just
|
||
// initial guesses; using sqrt smooths
|
||
// out the spikes.
|
||
double x_offset = _x_offset;
|
||
double y_offset = _y_offset;
|
||
|
||
#if 0
|
||
if (_jitter != 0.0) {
|
||
double a_x_pilot = current_aircraft.fdm_state->get_A_X_pilot();
|
||
double a_y_pilot = current_aircraft.fdm_state->get_A_Y_pilot();
|
||
double a_z_pilot = current_aircraft.fdm_state->get_A_Z_pilot();
|
||
|
||
double a_zx_pilot = a_z_pilot - a_x_pilot;
|
||
|
||
int x_adjust = int(sqrt(fabs(a_y_pilot) * _jitter)) *
|
||
(a_y_pilot < 0 ? -1 : 1);
|
||
int y_adjust = int(sqrt(fabs(a_zx_pilot) * _jitter)) *
|
||
(a_zx_pilot < 0 ? -1 : 1);
|
||
|
||
// adjustments in screen coordinates
|
||
x_offset += x_adjust;
|
||
y_offset += y_adjust;
|
||
}
|
||
#endif
|
||
|
||
glMatrixMode(GL_PROJECTION);
|
||
glPushMatrix();
|
||
glLoadIdentity();
|
||
if ( _flipx ) {
|
||
gluOrtho2D(winx + winw, winx, winy + winh, winy); /* up side down */
|
||
} else {
|
||
gluOrtho2D(winx, winx + winw, winy, winy + winh); /* right side up */
|
||
}
|
||
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glPushMatrix();
|
||
glLoadIdentity();
|
||
|
||
glTranslated(x_offset, y_offset, 0);
|
||
|
||
draw();
|
||
|
||
glMatrixMode(GL_PROJECTION);
|
||
glPopMatrix();
|
||
glMatrixMode(GL_MODELVIEW);
|
||
glPopMatrix();
|
||
|
||
ssgForceBasicState();
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||
}
|
||
|
||
void
|
||
FGPanel::draw()
|
||
{
|
||
// In 3D mode, it's possible that we are being drawn exactly on top
|
||
// of an existing polygon. Use an offset to prevent z-fighting. In
|
||
// 2D mode, this is a no-op.
|
||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||
glPolygonOffset(-1, -POFF_UNITS);
|
||
|
||
// save some state
|
||
glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT
|
||
| GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT );
|
||
|
||
// Draw the background
|
||
glEnable(GL_TEXTURE_2D);
|
||
glDisable(GL_LIGHTING);
|
||
glEnable(GL_BLEND);
|
||
glEnable(GL_ALPHA_TEST);
|
||
glEnable(GL_COLOR_MATERIAL);
|
||
sgVec4 panel_color;
|
||
sgCopyVec4( panel_color, cur_light_params.scene_diffuse );
|
||
if ( fgGetDouble("/systems/electrical/outputs/instrument-lights") > 1.0 ) {
|
||
if ( panel_color[0] < 0.7 ) panel_color[0] = 0.7;
|
||
if ( panel_color[1] < 0.2 ) panel_color[1] = 0.2;
|
||
if ( panel_color[2] < 0.2 ) panel_color[2] = 0.2;
|
||
}
|
||
glColor4fv( panel_color );
|
||
if (_bg != 0) {
|
||
glBindTexture(GL_TEXTURE_2D, _bg->getHandle());
|
||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||
glBegin(GL_POLYGON);
|
||
glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X, WIN_Y);
|
||
glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + _width, WIN_Y);
|
||
glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + _width, WIN_Y + _height);
|
||
glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X, WIN_Y + _height);
|
||
glEnd();
|
||
} else {
|
||
for (int i = 0; i < 4; i ++) {
|
||
// top row of textures...(1,3,5,7)
|
||
glBindTexture(GL_TEXTURE_2D, _mbg[i*2]->getHandle());
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||
glBegin(GL_POLYGON);
|
||
glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + (_height/2));
|
||
glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + (_height/2));
|
||
glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + _height);
|
||
glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + _height);
|
||
glEnd();
|
||
// bottom row of textures...(2,4,6,8)
|
||
glBindTexture(GL_TEXTURE_2D, _mbg[(i*2)+1]->getHandle());
|
||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||
glBegin(GL_POLYGON);
|
||
glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y);
|
||
glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y);
|
||
glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + (_height/2));
|
||
glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + (_height/2));
|
||
glEnd();
|
||
}
|
||
}
|
||
|
||
// Draw the instruments.
|
||
instrument_list_type::const_iterator current = _instruments.begin();
|
||
instrument_list_type::const_iterator end = _instruments.end();
|
||
|
||
for ( ; current != end; current++) {
|
||
FGPanelInstrument * instr = *current;
|
||
glPushMatrix();
|
||
glTranslated(instr->getXPos(), instr->getYPos(), 0);
|
||
instr->draw();
|
||
glPopMatrix();
|
||
}
|
||
|
||
// Draw yellow "hotspots" if directed to. This is a panel authoring
|
||
// feature; not intended to be high performance or to look good.
|
||
if(fgGetBool("/sim/panel-hotspots")) {
|
||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||
glDisable(GL_DEPTH_TEST);
|
||
glDisable(GL_TEXTURE_2D);
|
||
glColor3f(1, 1, 0);
|
||
|
||
for(int i=0; i<_instruments.size(); i++)
|
||
_instruments[i]->drawHotspots();
|
||
|
||
glPopAttrib();
|
||
}
|
||
|
||
|
||
// restore some original state
|
||
glPopAttrib();
|
||
glPolygonOffset(0, 0);
|
||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||
}
|
||
|
||
/**
|
||
* Set the panel's visibility.
|
||
*/
|
||
void
|
||
FGPanel::setVisibility (bool visibility)
|
||
{
|
||
_visibility = visibility;
|
||
}
|
||
|
||
|
||
/**
|
||
* Return true if the panel is visible.
|
||
*/
|
||
bool
|
||
FGPanel::getVisibility () const
|
||
{
|
||
return _visibility;
|
||
}
|
||
|
||
|
||
/**
|
||
* Set the panel's background texture.
|
||
*/
|
||
void
|
||
FGPanel::setBackground (ssgTexture * texture)
|
||
{
|
||
_bg = texture;
|
||
}
|
||
|
||
/**
|
||
* Set the panel's multiple background textures.
|
||
*/
|
||
void
|
||
FGPanel::setMultiBackground (ssgTexture * texture, int idx)
|
||
{
|
||
_bg = 0;
|
||
_mbg[idx] = texture;
|
||
}
|
||
|
||
/**
|
||
* Set the panel's x-offset.
|
||
*/
|
||
void
|
||
FGPanel::setXOffset (int offset)
|
||
{
|
||
if (offset <= 0 && offset >= -_width + WIN_W)
|
||
_x_offset = offset;
|
||
}
|
||
|
||
|
||
/**
|
||
* Set the panel's y-offset.
|
||
*/
|
||
void
|
||
FGPanel::setYOffset (int offset)
|
||
{
|
||
if (offset <= 0 && offset >= -_height)
|
||
_y_offset = offset;
|
||
}
|
||
|
||
/**
|
||
* Handle a mouse action in panel-local (not screen) coordinates.
|
||
* Used by the 3D panel code in Model/panelnode.cxx, in situations
|
||
* where the panel doesn't control its own screen location.
|
||
*/
|
||
bool
|
||
FGPanel::doLocalMouseAction(int button, int updown, int x, int y)
|
||
{
|
||
// Note a released button and return
|
||
if (updown == 1) {
|
||
_mouseDown = false;
|
||
_mouseInstrument = 0;
|
||
return false;
|
||
}
|
||
|
||
// Search for a matching instrument.
|
||
for (int i = 0; i < (int)_instruments.size(); i++) {
|
||
FGPanelInstrument *inst = _instruments[i];
|
||
int ix = inst->getXPos();
|
||
int iy = inst->getYPos();
|
||
int iw = inst->getWidth() / 2;
|
||
int ih = inst->getHeight() / 2;
|
||
if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) {
|
||
_mouseDown = true;
|
||
_mouseDelay = 20;
|
||
_mouseInstrument = inst;
|
||
_mouseButton = button;
|
||
_mouseX = x - ix;
|
||
_mouseY = y - iy;
|
||
// Always do the action once.
|
||
return _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Perform a mouse action.
|
||
*/
|
||
bool
|
||
FGPanel::doMouseAction (int button, int updown, int x, int y)
|
||
{
|
||
// FIXME: this same code appears in update()
|
||
int xsize = _xsize_node->getIntValue();
|
||
int ysize = _ysize_node->getIntValue();
|
||
float aspect_adjust = get_aspect_adjust(xsize, ysize);
|
||
|
||
// Scale for the real window size.
|
||
if (aspect_adjust < 1.0) {
|
||
x = int(((float)x / xsize) * WIN_W * aspect_adjust);
|
||
y = int(WIN_H - ((float(y) / ysize) * WIN_H));
|
||
} else {
|
||
x = int(((float)x / xsize) * WIN_W);
|
||
y = int((WIN_H - ((float(y) / ysize) * WIN_H)) / aspect_adjust);
|
||
}
|
||
|
||
// Adjust for offsets.
|
||
x -= _x_offset;
|
||
y -= _y_offset;
|
||
|
||
// Having fixed up the coordinates, fall through to the local
|
||
// coordinate handler.
|
||
doLocalMouseAction(button, updown, x, y);
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////.
|
||
// Implementation of FGPanelAction.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGPanelAction::FGPanelAction ()
|
||
{
|
||
}
|
||
|
||
FGPanelAction::FGPanelAction (int button, int x, int y, int w, int h)
|
||
: _button(button), _x(x), _y(y), _w(w), _h(h)
|
||
{
|
||
for (unsigned int i = 0; i < _bindings.size(); i++)
|
||
delete _bindings[i];
|
||
}
|
||
|
||
FGPanelAction::~FGPanelAction ()
|
||
{
|
||
}
|
||
|
||
void
|
||
FGPanelAction::addBinding (FGBinding * binding)
|
||
{
|
||
_bindings.push_back(binding);
|
||
}
|
||
|
||
void
|
||
FGPanelAction::doAction ()
|
||
{
|
||
if (test()) {
|
||
int nBindings = _bindings.size();
|
||
for (int i = 0; i < nBindings; i++) {
|
||
_bindings[i]->fire();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGPanelTransformation.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGPanelTransformation::FGPanelTransformation ()
|
||
: table(0)
|
||
{
|
||
}
|
||
|
||
FGPanelTransformation::~FGPanelTransformation ()
|
||
{
|
||
delete table;
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGPanelInstrument.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
FGPanelInstrument::FGPanelInstrument ()
|
||
{
|
||
setPosition(0, 0);
|
||
setSize(0, 0);
|
||
}
|
||
|
||
FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
|
||
{
|
||
setPosition(x, y);
|
||
setSize(w, h);
|
||
}
|
||
|
||
FGPanelInstrument::~FGPanelInstrument ()
|
||
{
|
||
for (action_list_type::iterator it = _actions.begin();
|
||
it != _actions.end();
|
||
it++) {
|
||
delete *it;
|
||
*it = 0;
|
||
}
|
||
}
|
||
|
||
void
|
||
FGPanelInstrument::drawHotspots()
|
||
{
|
||
for(int i=0; i<_actions.size(); i++) {
|
||
FGPanelAction* a = _actions[i];
|
||
float x1 = getXPos() + a->getX();
|
||
float x2 = x1 + a->getWidth();
|
||
float y1 = getYPos() + a->getY();
|
||
float y2 = y1 + a->getHeight();
|
||
|
||
glBegin(GL_LINE_LOOP);
|
||
glVertex2f(x1, y1);
|
||
glVertex2f(x1, y2);
|
||
glVertex2f(x2, y2);
|
||
glVertex2f(x2, y1);
|
||
glEnd();
|
||
}
|
||
}
|
||
|
||
void
|
||
FGPanelInstrument::setPosition (int x, int y)
|
||
{
|
||
_x = x;
|
||
_y = y;
|
||
}
|
||
|
||
void
|
||
FGPanelInstrument::setSize (int w, int h)
|
||
{
|
||
_w = w;
|
||
_h = h;
|
||
}
|
||
|
||
int
|
||
FGPanelInstrument::getXPos () const
|
||
{
|
||
return _x;
|
||
}
|
||
|
||
int
|
||
FGPanelInstrument::getYPos () const
|
||
{
|
||
return _y;
|
||
}
|
||
|
||
int
|
||
FGPanelInstrument::getWidth () const
|
||
{
|
||
return _w;
|
||
}
|
||
|
||
int
|
||
FGPanelInstrument::getHeight () const
|
||
{
|
||
return _h;
|
||
}
|
||
|
||
void
|
||
FGPanelInstrument::addAction (FGPanelAction * action)
|
||
{
|
||
_actions.push_back(action);
|
||
}
|
||
|
||
// Coordinates relative to centre.
|
||
bool
|
||
FGPanelInstrument::doMouseAction (int button, int x, int y)
|
||
{
|
||
if (test()) {
|
||
action_list_type::iterator it = _actions.begin();
|
||
action_list_type::iterator last = _actions.end();
|
||
for ( ; it != last; it++) {
|
||
if ((*it)->inArea(button, x, y)) {
|
||
(*it)->doAction();
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGLayeredInstrument.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
|
||
: FGPanelInstrument(x, y, w, h)
|
||
{
|
||
}
|
||
|
||
FGLayeredInstrument::~FGLayeredInstrument ()
|
||
{
|
||
for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) {
|
||
delete *it;
|
||
*it = 0;
|
||
}
|
||
}
|
||
|
||
void
|
||
FGLayeredInstrument::draw ()
|
||
{
|
||
if (!test())
|
||
return;
|
||
|
||
for (int i = 0; i < (int)_layers.size(); i++) {
|
||
glPushMatrix();
|
||
glPolygonOffset(-1, -POFF_UNITS*(i+2));
|
||
_layers[i]->draw();
|
||
glPopMatrix();
|
||
}
|
||
}
|
||
|
||
int
|
||
FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
|
||
{
|
||
int n = _layers.size();
|
||
if (layer->getWidth() == -1) {
|
||
layer->setWidth(getWidth());
|
||
}
|
||
if (layer->getHeight() == -1) {
|
||
layer->setHeight(getHeight());
|
||
}
|
||
_layers.push_back(layer);
|
||
return n;
|
||
}
|
||
|
||
int
|
||
FGLayeredInstrument::addLayer (FGCroppedTexture &texture,
|
||
int w, int h)
|
||
{
|
||
return addLayer(new FGTexturedLayer(texture, w, h));
|
||
}
|
||
|
||
void
|
||
FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation)
|
||
{
|
||
int layer = _layers.size() - 1;
|
||
_layers[layer]->addTransformation(transformation);
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGInstrumentLayer.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGInstrumentLayer::FGInstrumentLayer (int w, int h)
|
||
: _w(w),
|
||
_h(h)
|
||
{
|
||
}
|
||
|
||
FGInstrumentLayer::~FGInstrumentLayer ()
|
||
{
|
||
for (transformation_list::iterator it = _transformations.begin();
|
||
it != _transformations.end();
|
||
it++) {
|
||
delete *it;
|
||
*it = 0;
|
||
}
|
||
}
|
||
|
||
void
|
||
FGInstrumentLayer::transform () const
|
||
{
|
||
transformation_list::const_iterator it = _transformations.begin();
|
||
transformation_list::const_iterator last = _transformations.end();
|
||
while (it != last) {
|
||
FGPanelTransformation *t = *it;
|
||
if (t->test()) {
|
||
float val = (t->node == 0 ? 0.0 : t->node->getFloatValue());
|
||
if (val < t->min) {
|
||
val = t->min;
|
||
} else if (val > t->max) {
|
||
val = t->max;
|
||
}
|
||
if(t->table==0) {
|
||
val = val * t->factor + t->offset;
|
||
} else {
|
||
val = t->table->interpolate(val) * t->factor + t->offset;
|
||
}
|
||
|
||
switch (t->type) {
|
||
case FGPanelTransformation::XSHIFT:
|
||
glTranslatef(val, 0.0, 0.0);
|
||
break;
|
||
case FGPanelTransformation::YSHIFT:
|
||
glTranslatef(0.0, val, 0.0);
|
||
break;
|
||
case FGPanelTransformation::ROTATION:
|
||
glRotatef(-val, 0.0, 0.0, 1.0);
|
||
break;
|
||
}
|
||
}
|
||
it++;
|
||
}
|
||
}
|
||
|
||
void
|
||
FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation)
|
||
{
|
||
_transformations.push_back(transformation);
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGGroupLayer.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGGroupLayer::FGGroupLayer ()
|
||
{
|
||
}
|
||
|
||
FGGroupLayer::~FGGroupLayer ()
|
||
{
|
||
for (unsigned int i = 0; i < _layers.size(); i++)
|
||
delete _layers[i];
|
||
}
|
||
|
||
void
|
||
FGGroupLayer::draw ()
|
||
{
|
||
if (test()) {
|
||
int nLayers = _layers.size();
|
||
for (int i = 0; i < nLayers; i++)
|
||
_layers[i]->draw();
|
||
}
|
||
}
|
||
|
||
void
|
||
FGGroupLayer::addLayer (FGInstrumentLayer * layer)
|
||
{
|
||
_layers.push_back(layer);
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGTexturedLayer.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
FGTexturedLayer::FGTexturedLayer (const FGCroppedTexture &texture, int w, int h)
|
||
: FGInstrumentLayer(w, h)
|
||
{
|
||
setTexture(texture);
|
||
}
|
||
|
||
|
||
FGTexturedLayer::~FGTexturedLayer ()
|
||
{
|
||
}
|
||
|
||
|
||
void
|
||
FGTexturedLayer::draw ()
|
||
{
|
||
if (test()) {
|
||
int w2 = _w / 2;
|
||
int h2 = _h / 2;
|
||
|
||
transform();
|
||
glBindTexture(GL_TEXTURE_2D, _texture.getTexture()->getHandle());
|
||
glBegin(GL_POLYGON);
|
||
|
||
// From Curt: turn on the panel
|
||
// lights after sundown.
|
||
sgVec4 panel_color;
|
||
sgCopyVec4( panel_color, cur_light_params.scene_diffuse );
|
||
if ( fgGetDouble("/systems/electrical/outputs/instrument-lights") > 1.0 ) {
|
||
if ( panel_color[0] < 0.7 ) panel_color[0] = 0.7;
|
||
if ( panel_color[1] < 0.2 ) panel_color[1] = 0.2;
|
||
if ( panel_color[2] < 0.2 ) panel_color[2] = 0.2;
|
||
}
|
||
glColor4fv( panel_color );
|
||
|
||
glTexCoord2f(_texture.getMinX(), _texture.getMinY()); glVertex2f(-w2, -h2);
|
||
glTexCoord2f(_texture.getMaxX(), _texture.getMinY()); glVertex2f(w2, -h2);
|
||
glTexCoord2f(_texture.getMaxX(), _texture.getMaxY()); glVertex2f(w2, h2);
|
||
glTexCoord2f(_texture.getMinX(), _texture.getMaxY()); glVertex2f(-w2, h2);
|
||
glEnd();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGTextLayer.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGTextLayer::FGTextLayer (int w, int h)
|
||
: FGInstrumentLayer(w, h), _pointSize(14.0), _font_name("default")
|
||
{
|
||
_then.stamp();
|
||
_color[0] = _color[1] = _color[2] = 0.0;
|
||
_color[3] = 1.0;
|
||
}
|
||
|
||
FGTextLayer::~FGTextLayer ()
|
||
{
|
||
chunk_list::iterator it = _chunks.begin();
|
||
chunk_list::iterator last = _chunks.end();
|
||
for ( ; it != last; it++) {
|
||
delete *it;
|
||
}
|
||
}
|
||
|
||
void
|
||
FGTextLayer::draw ()
|
||
{
|
||
if (test()) {
|
||
glColor4fv(_color);
|
||
transform();
|
||
if ( _font_name == "led" && led_font != 0) {
|
||
text_renderer.setFont(led_font);
|
||
} else {
|
||
text_renderer.setFont(guiFntHandle);
|
||
}
|
||
text_renderer.setPointSize(_pointSize);
|
||
text_renderer.begin();
|
||
text_renderer.start3f(0, 0, 0);
|
||
|
||
_now.stamp();
|
||
long diff = _now - _then;
|
||
|
||
if (diff > 100000 || diff < 0 ) {
|
||
// ( diff < 0 ) is a sanity check and indicates our time stamp
|
||
// difference math probably overflowed. We can handle a max
|
||
// difference of 35.8 minutes since the returned value is in
|
||
// usec. So if the panel is left off longer than that we can
|
||
// over flow the math with it is turned back on. This (diff <
|
||
// 0) catches that situation, get's us out of trouble, and
|
||
// back on track.
|
||
recalc_value();
|
||
_then = _now;
|
||
}
|
||
|
||
// Something is goofy. The code in this file renders only CCW
|
||
// polygons, and I have verified that the font code in plib
|
||
// renders only CCW trianbles. Yet they come out backwards.
|
||
// Something around here or in plib is either changing the winding
|
||
// order or (more likely) pushing a left-handed matrix onto the
|
||
// stack. But I can't find it; get out the chainsaw...
|
||
glFrontFace(GL_CW);
|
||
text_renderer.puts((char *)(_value.c_str()));
|
||
glFrontFace(GL_CCW);
|
||
|
||
text_renderer.end();
|
||
glColor4f(1.0, 1.0, 1.0, 1.0); // FIXME
|
||
}
|
||
}
|
||
|
||
void
|
||
FGTextLayer::addChunk (FGTextLayer::Chunk * chunk)
|
||
{
|
||
_chunks.push_back(chunk);
|
||
}
|
||
|
||
void
|
||
FGTextLayer::setColor (float r, float g, float b)
|
||
{
|
||
_color[0] = r;
|
||
_color[1] = g;
|
||
_color[2] = b;
|
||
_color[3] = 1.0;
|
||
}
|
||
|
||
void
|
||
FGTextLayer::setPointSize (float size)
|
||
{
|
||
_pointSize = size;
|
||
}
|
||
|
||
void
|
||
FGTextLayer::setFontName(const string &name)
|
||
{
|
||
_font_name = name;
|
||
}
|
||
|
||
|
||
void
|
||
FGTextLayer::setFont(fntFont * font)
|
||
{
|
||
text_renderer.setFont(font);
|
||
}
|
||
|
||
|
||
void
|
||
FGTextLayer::recalc_value () const
|
||
{
|
||
_value = "";
|
||
chunk_list::const_iterator it = _chunks.begin();
|
||
chunk_list::const_iterator last = _chunks.end();
|
||
for ( ; it != last; it++) {
|
||
_value += (*it)->getValue();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGTextLayer::Chunk.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGTextLayer::Chunk::Chunk (const string &text, const string &fmt)
|
||
: _type(FGTextLayer::TEXT), _fmt(fmt)
|
||
{
|
||
_text = text;
|
||
if (_fmt.empty())
|
||
_fmt = "%s";
|
||
}
|
||
|
||
FGTextLayer::Chunk::Chunk (ChunkType type, const SGPropertyNode * node,
|
||
const string &fmt, float mult)
|
||
: _type(type), _fmt(fmt), _mult(mult)
|
||
{
|
||
if (_fmt.empty()) {
|
||
if (type == TEXT_VALUE)
|
||
_fmt = "%s";
|
||
else
|
||
_fmt = "%.2f";
|
||
}
|
||
_node = node;
|
||
}
|
||
|
||
const char *
|
||
FGTextLayer::Chunk::getValue () const
|
||
{
|
||
if (test()) {
|
||
_buf[0] = '\0';
|
||
switch (_type) {
|
||
case TEXT:
|
||
sprintf(_buf, _fmt.c_str(), _text.c_str());
|
||
return _buf;
|
||
case TEXT_VALUE:
|
||
sprintf(_buf, _fmt.c_str(), _node->getStringValue());
|
||
break;
|
||
case DOUBLE_VALUE:
|
||
sprintf(_buf, _fmt.c_str(), _node->getFloatValue() * _mult);
|
||
break;
|
||
}
|
||
return _buf;
|
||
} else {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// Implementation of FGSwitchLayer.
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
FGSwitchLayer::FGSwitchLayer (int w, int h, const SGPropertyNode * node,
|
||
FGInstrumentLayer * layer1,
|
||
FGInstrumentLayer * layer2)
|
||
: FGInstrumentLayer(w, h), _node(node), _layer1(layer1), _layer2(layer2)
|
||
{
|
||
}
|
||
|
||
FGSwitchLayer::~FGSwitchLayer ()
|
||
{
|
||
delete _layer1;
|
||
delete _layer2;
|
||
}
|
||
|
||
void
|
||
FGSwitchLayer::draw ()
|
||
{
|
||
if (test()) {
|
||
transform();
|
||
if (_node->getBoolValue()) {
|
||
_layer1->draw();
|
||
} else {
|
||
_layer2->draw();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
// end of panel.cxx
|
||
|
||
|
||
|