I wrote:
> David Megginson wrote:
> > Speaking of taunting, do you have any ideas about the problem I
> > mentioned earlier -- that no text shows up on the radios in your new
> > 3D panel patch?  It's the only thing stopping me from committing it.
>
> None yet, I need to get home and try it.  Nothing looks suspicious; I
> thought perhaps plib was overriding the glPolygonOffset that I'm
> using, but it doesn't seem to be.

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.

Andy

-- 
Andrew J. Ross                NextBus Information Systems
Senior Software Engineer      Emeryville, CA
[EMAIL PROTECTED]              http://www.nextbus.com
"Men go crazy in conflagrations.  They only get better one by one."
 - Sting (misquoted)
//  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: panel.cxx,v 1.72 2002/05/11 14:28:51 david Exp $

#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



Reply via email to