b53cd9c59f
+ The panel(s) are now an first-class SSG node inside the aircraft scene graph. There's a little code added to model.cxx to handle the parsing, but most of the changes are inside the new FGPanelNode class (Model/panelnode.[ch]xx). + The old FGPanel source changed a lot, but mostly cosmetically. The virtual-cockpit code moved out into FGPanelNode, and the core rendering has been abstracted into a draw() method that doesn't try to set any OpenGL state. I also replaced the old inter-layer offset code with glPolygonOffset, as calculating the right Z values is hard across the funky modelview matrix I need to use. The older virtual panel code got away with it by disabling depth test, thus the "panel draws on top of yoke" bug. PolygonOffset is really the appropriate solution for this sort of task anyway. + The /sim/virtual-cockpit property is no more. The 2D panels are still specified in the -set.xml file, but 3D panels are part of the model file. + You can have as many 3D panels as you like. Problems: + The mouse support isn't ready yet, so the 3D panels still aren't interactive. Soon to come. + Being part of the same scene graph as the model, the 3D panels now "jitter" in exactly the same way. While this makes the jitter of the attitude gyro less noticeable, it's still *very* noticeable and annoying. I looked hard for this, and am at this point convinced that the problem is with the two orientation computations. We have one in FGLocation that is used by the model code, and one in FGViewer that is used at the top of the scene graph. My suspicion is that they don't agree exactly, so the final orientation matrix is the right answer plus the difference. I did rule out the FDMs though. None of them show more than about 0.0001 degree of orientation change between frames for a stopped aircraft. That's within an order of magnitude of what you'd expect for the orientation change due to the rotation of the earth (which we don't model -- I cite it only as evidence of how small this is); far, far less than one pixel on the screen. [and later] OK, this is fixed by the attached panel.cxx file. What's happened is that the winding order for the text layer's polygons is wrong, so I reverse it before drawing. That's largely a hatchet job to make things work for now, though. We should figure out why the winding order is wrong for only text layers and fix it. I checked the plib sources -- they're definitely doing things CCW, as is all the rest of the panel code. Odd. I'm also not sure why the 2D panel doesn't care (it works in both winding orders). But this will allow you to check in working code, anyway. There's a big comment to this effect in there.
1091 lines
25 KiB
C++
1091 lines
25 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 40
|
||
|
||
#if defined( NONE ) && defined( _MSC_VER )
|
||
# pragma message( "A sloppy coder has defined NONE as a macro!!!" )
|
||
# undef NONE
|
||
#elif defined( NONE )
|
||
# pragma warn A sloppy coder has defined NONE as a macro!!!
|
||
# undef NONE
|
||
#endif
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// 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;
|
||
static fntTexFont *led_font;
|
||
|
||
/**
|
||
* 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");
|
||
|
||
// Do nothing if the panel isn't visible.
|
||
if ( !fgPanelVisible() ) {
|
||
return;
|
||
}
|
||
|
||
// If the mouse is down, do something
|
||
if (_mouseDown) {
|
||
_mouseDelay--;
|
||
if (_mouseDelay < 0) {
|
||
_mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
|
||
_mouseDelay = 2;
|
||
}
|
||
}
|
||
|
||
// 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));
|
||
}
|
||
|
||
|
||
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();
|
||
gluOrtho2D(winx, winx + winw, winy, winy + winh); /* right side up */
|
||
// gluOrtho2D(winx + winw, winx, winy + winh, winy); /* up side down */
|
||
|
||
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(0, -POFF_UNITS);
|
||
|
||
// Draw the background
|
||
glEnable(GL_TEXTURE_2D);
|
||
glDisable(GL_LIGHTING);
|
||
glEnable(GL_BLEND);
|
||
glEnable(GL_ALPHA_TEST);
|
||
glEnable(GL_COLOR_MATERIAL);
|
||
// glColor4f(1.0, 1.0, 1.0, 1.0);
|
||
if ( cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES < 95.0 ) {
|
||
glColor4fv( cur_light_params.scene_diffuse );
|
||
} else {
|
||
glColor4f(0.7, 0.2, 0.2, 1.0);
|
||
}
|
||
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();
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
/**
|
||
* 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);
|
||
|
||
// Note a released button and return
|
||
// cerr << "Doing mouse action\n";
|
||
if (updown == 1) {
|
||
_mouseDown = false;
|
||
_mouseInstrument = 0;
|
||
return false;
|
||
}
|
||
|
||
// 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;
|
||
|
||
// 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;
|
||
}
|
||
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////.
|
||
// 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::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.
|
||
if ( cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES < 95.0 ) {
|
||
glColor4fv( cur_light_params.scene_diffuse );
|
||
} else {
|
||
glColor4f(0.7, 0.2, 0.2, 1.0);
|
||
}
|
||
|
||
|
||
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" ) {
|
||
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
|
||
|
||
|
||
|