]> git.mxchange.org Git - flightgear.git/commitdiff
Initial commit of the fgpanel code
authorTorsten Dreyer <Torsten@t3r.de>
Thu, 14 Apr 2011 09:11:17 +0000 (11:11 +0200)
committerTorsten Dreyer <Torsten@t3r.de>
Thu, 14 Apr 2011 09:11:17 +0000 (11:11 +0200)
fgpanel is basically the stripped down 2D-Panel code from
FlightGear. It is designed as a standalone lightweight panel
rendering engine to draw 2d panels on a lowcost computer/graphic card
without 3d acceleration at reasonablel framerates.

Patches for inclusion into the build system will follow.

22 files changed:
utils/fgpanel/.gitignore [new file with mode: 0644]
utils/fgpanel/ApplicationProperties.hxx [new file with mode: 0644]
utils/fgpanel/FGFontCache.cxx [new file with mode: 0644]
utils/fgpanel/FGFontCache.hxx [new file with mode: 0644]
utils/fgpanel/FGGLApplication.cxx [new file with mode: 0644]
utils/fgpanel/FGGLApplication.hxx [new file with mode: 0644]
utils/fgpanel/FGPNGTextureLoader.cxx [new file with mode: 0644]
utils/fgpanel/FGPNGTextureLoader.hxx [new file with mode: 0644]
utils/fgpanel/FGPanelApplication.cxx [new file with mode: 0644]
utils/fgpanel/FGPanelApplication.hxx [new file with mode: 0644]
utils/fgpanel/FGPanelProtocol.cxx [new file with mode: 0644]
utils/fgpanel/FGPanelProtocol.hxx [new file with mode: 0644]
utils/fgpanel/FGRGBTextureLoader.cxx [new file with mode: 0644]
utils/fgpanel/FGRGBTextureLoader.hxx [new file with mode: 0644]
utils/fgpanel/FGTextureLoaderInterface.hxx [new file with mode: 0644]
utils/fgpanel/Makefile.am [new file with mode: 0644]
utils/fgpanel/README [new file with mode: 0644]
utils/fgpanel/main.cxx [new file with mode: 0644]
utils/fgpanel/panel.cxx [new file with mode: 0644]
utils/fgpanel/panel.hxx [new file with mode: 0644]
utils/fgpanel/panel_io.cxx [new file with mode: 0644]
utils/fgpanel/panel_io.hxx [new file with mode: 0644]

diff --git a/utils/fgpanel/.gitignore b/utils/fgpanel/.gitignore
new file mode 100644 (file)
index 0000000..fd82bd0
--- /dev/null
@@ -0,0 +1,7 @@
+fgpanel
+*.o
+*.obj
+.deps
+*.Po
+Makefile
+Makefile.in
diff --git a/utils/fgpanel/ApplicationProperties.hxx b/utils/fgpanel/ApplicationProperties.hxx
new file mode 100644 (file)
index 0000000..f5578fe
--- /dev/null
@@ -0,0 +1,31 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 __APPLICATION_PROPERTIES
+#define __APPLICATION_PROPERTIES
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/props/props.hxx>
+#include "FGFontCache.hxx"
+class ApplicationProperties {
+public:
+  static double getDouble( const char * name, double def = 0.0 );
+  static SGPath GetRootPath( const char * subDir = NULL );
+  static SGPropertyNode_ptr Properties;
+  static std::string root;
+  static FGFontCache fontCache;
+};
+#endif
diff --git a/utils/fgpanel/FGFontCache.cxx b/utils/fgpanel/FGFontCache.cxx
new file mode 100644 (file)
index 0000000..129e247
--- /dev/null
@@ -0,0 +1,208 @@
+//
+//  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.
+//
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+using namespace std;
+#include <map>
+#include <algorithm>
+#include "ApplicationProperties.hxx"
+#include "FGFontCache.hxx"
+////////////////////////////////////////////////////////////////////////
+// FGFontCache class.
+////////////////////////////////////////////////////////////////////////
+
+//extern puFont FONT_HELVETICA_14;
+//extern puFont FONT_SANS_12B;
+
+namespace
+{
+struct GuiFont
+{
+    const char *name;
+    puFont *font;
+    struct Predicate
+        : public std::unary_function<const GuiFont, bool>
+    {
+        Predicate(const char* name_) : name(name_) {}
+        bool operator() (const GuiFont& f1) const
+        {
+            return ::strcmp(f1.name, name) == 0;
+        }
+        const char* name;
+    };
+};
+
+const GuiFont guifonts[] = {
+    { "default",      &PUFONT_HELVETICA_12 },
+    { "FIXED_8x13",   &PUFONT_8_BY_13 },
+    { "FIXED_9x15",   &PUFONT_9_BY_15 },
+    { "TIMES_10",     &PUFONT_TIMES_ROMAN_10 },
+    { "TIMES_24",     &PUFONT_TIMES_ROMAN_24 },
+    { "HELVETICA_10", &PUFONT_HELVETICA_10 },
+    { "HELVETICA_12", &PUFONT_HELVETICA_12 },
+//    { "HELVETICA_14", &FONT_HELVETICA_14 },
+    { "HELVETICA_18", &PUFONT_HELVETICA_18 }
+//    { "SANS_12B",     &FONT_SANS_12B }
+};
+
+const GuiFont* guifontsEnd = &guifonts[sizeof(guifonts)/ sizeof(guifonts[0])];
+}
+
+FGFontCache::FGFontCache() :
+    _initialized(false)
+{
+}
+
+FGFontCache::~FGFontCache()
+{
+   PuFontMap::iterator it, end = _puFonts.end();
+   for (it = _puFonts.begin(); it != end; ++it)
+       delete it->second;
+}
+
+inline bool FGFontCache::FntParamsLess::operator()(const FntParams& f1,
+                                                   const FntParams& f2) const
+{
+    int comp = f1.name.compare(f2.name);
+    if (comp < 0)
+        return true;
+    else if (comp > 0)
+        return false;
+    if (f1.size < f2.size)
+        return true;
+    else if (f1.size > f2.size)
+        return false;
+    return f1.slant < f2.slant;
+}
+
+struct FGFontCache::fnt *
+FGFontCache::getfnt(const char *name, float size, float slant)
+{
+    string fontName(name);
+    FntParams fntParams(fontName, size, slant);
+    PuFontMap::iterator i = _puFonts.find(fntParams);
+    if (i != _puFonts.end())
+        return i->second;
+    // fntTexFont s are all preloaded into the _texFonts map
+    TexFontMap::iterator texi = _texFonts.find(fontName);
+    fntTexFont* texfont = 0;
+    puFont* pufont = 0;
+    if (texi != _texFonts.end()) {
+        texfont = texi->second;
+    } else {
+        const GuiFont* guifont = std::find_if(&guifonts[0], guifontsEnd,
+                                              GuiFont::Predicate(name));
+        if (guifont != guifontsEnd) {
+            pufont = guifont->font;
+        }
+    }
+    fnt* f = new fnt;
+    if (pufont) {
+        f->pufont = pufont;
+    } else if (texfont) {
+        f->texfont = texfont;
+        f->pufont = new puFont;
+        f->pufont->initialize(static_cast<fntFont *>(f->texfont), size, slant);
+    } else {
+        f->pufont = guifonts[0].font;
+    }
+    _puFonts[fntParams] = f;
+    return f;
+}
+
+puFont *
+FGFontCache::get(const char *name, float size, float slant)
+{
+    return getfnt(name, size, slant)->pufont;
+}
+
+fntTexFont *
+FGFontCache::getTexFont(const char *name, float size, float slant)
+{
+    init();
+    return getfnt(name, size, slant)->texfont;
+}
+
+puFont *
+FGFontCache::get(SGPropertyNode *node)
+{
+    if (!node)
+        return get("Helvetica.txf", 15.0, 0.0);
+
+    const char *name = node->getStringValue("name", "Helvetica.txf");
+    float size = node->getFloatValue("size", 15.0);
+    float slant = node->getFloatValue("slant", 0.0);
+
+    return get(name, size, slant);
+}
+
+void FGFontCache::init()
+{
+    if (!_initialized) {
+        char *envp = ::getenv("FG_FONTS");
+        if (envp != NULL) {
+            _path.set(envp);
+        } else {
+            _path.set(ApplicationProperties::GetRootPath("Fonts").str());
+        }
+        _initialized = true;
+    }
+}
+
+SGPath
+FGFontCache::getfntpath(const char *name)
+{
+    init();
+    SGPath path(_path);
+    if (name && std::string(name) != "") {
+        path.append(name);
+        if (path.exists())
+            return path;
+    }
+
+    path = SGPath(_path);
+    path.append("Helvetica.txf");
+    
+    return path;
+}
+
+bool FGFontCache::initializeFonts()
+{
+    static string fontext("txf");
+    init();
+    ulDir* fontdir = ulOpenDir(_path.c_str());
+    if (!fontdir)
+        return false;
+    const ulDirEnt *dirEntry;
+    while ((dirEntry = ulReadDir(fontdir)) != 0) {
+        SGPath path(_path);
+        path.append(dirEntry->d_name);
+        if (path.extension() == fontext) {
+            fntTexFont* f = new fntTexFont;
+            if (f->load((char *)path.c_str()))
+                _texFonts[string(dirEntry->d_name)] = f;
+            else
+                delete f;
+        }
+    }
+    ulCloseDir(fontdir);
+    return true;
+}
+
+// end of new_gui.cxx
+
diff --git a/utils/fgpanel/FGFontCache.hxx b/utils/fgpanel/FGFontCache.hxx
new file mode 100644 (file)
index 0000000..81202ee
--- /dev/null
@@ -0,0 +1,86 @@
+//  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 __FGFONTCACHE_HXX
+#define __FGFONTCACHE_HXX
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/props/props.hxx>
+#include <plib/pu.h>
+/**
+ * A class to keep all fonts available for future use.
+ * This also assures a font isn't resident more than once.
+ */
+class FGFontCache {
+private:
+    // The parameters of a request to the cache.
+    struct FntParams
+    {
+        const std::string name;
+        const float size;
+        const float slant;
+        FntParams() : size(0.0f), slant(0.0f) {}
+        FntParams(const FntParams& rhs)
+            : name(rhs.name), size(rhs.size), slant(rhs.slant)
+        {
+        }
+        FntParams(const std::string& name_, float size_, float slant_)
+            : name(name_), size(size_), slant(slant_)
+        {
+        }
+    };
+    struct FntParamsLess
+        : public std::binary_function<const FntParams, const FntParams, bool>
+    {
+        bool operator() (const FntParams& f1, const FntParams& f2) const;
+    };
+    struct fnt {
+        fnt(puFont *pu = 0) : pufont(pu), texfont(0) {}
+        ~fnt() { if (texfont) { delete pufont; delete texfont; } }
+        // Font used by plib GUI code
+        puFont *pufont;
+        // TXF font
+        fntTexFont *texfont;
+    };
+    // Path to the font directory
+    SGPath _path;
+
+    typedef map<const string, fntTexFont*> TexFontMap;
+    typedef map<const FntParams, fnt*, FntParamsLess> PuFontMap;
+    TexFontMap _texFonts;
+    PuFontMap _puFonts;
+
+    bool _initialized;
+    struct fnt *getfnt(const char *name, float size, float slant);
+    void init();
+
+public:
+    FGFontCache();
+    ~FGFontCache();
+
+    puFont *get(const char *name, float size=15.0, float slant=0.0);
+    puFont *get(SGPropertyNode *node);
+
+    fntTexFont *getTexFont(const char *name, float size=15.0, float slant=0.0);
+
+    SGPath getfntpath(const char *name);
+    /**
+     * Preload all the fonts in the FlightGear font directory. It is
+     * important to load the font textures early, with the proper
+     * graphics context current, so that no plib (or our own) code
+     * tries to load a font from disk when there's no current graphics
+     * context.
+     */
+    bool initializeFonts();
+};
+#endif
diff --git a/utils/fgpanel/FGGLApplication.cxx b/utils/fgpanel/FGGLApplication.cxx
new file mode 100644 (file)
index 0000000..5c52943
--- /dev/null
@@ -0,0 +1,94 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 "FGGLApplication.hxx"
+#include "GL/gl.h"
+#include "GL/glut.h"
+
+#include <iostream>
+#include <exception>
+#include <stdio.h>
+
+FGGLApplication * FGGLApplication::application = NULL;
+
+FGGLApplication::FGGLApplication( const char * aName, int argc, char ** argv ) :
+  gameMode(false),
+  name( aName )
+{
+  if( application != NULL ) {
+    std::cerr << "Only one instance of FGGLApplication allowed!" << std::endl;
+    throw std::exception();
+  }
+  application = this;  
+
+  glutInit( &argc, argv );
+}
+
+FGGLApplication::~FGGLApplication()
+{
+}
+
+void FGGLApplication::DisplayCallback()
+{
+  if( application ) application->Display();
+}
+
+void FGGLApplication::IdleCallback()
+{
+  if( application ) application->Idle();
+}
+
+void FGGLApplication::KeyCallback( unsigned char key, int x, int y )
+{
+  if( application ) application->Key( key, x, y );
+}
+
+void FGGLApplication::ReshapeCallback( int width, int height )
+{
+  if( application ) application->Reshape( width, height );
+}
+
+void FGGLApplication::Run( int glutMode, bool gameMode, int width, int height, int bpp )
+{
+  glutInitDisplayMode(glutMode);
+  if( gameMode ) {
+    width = glutGet(GLUT_SCREEN_WIDTH);
+    height = glutGet(GLUT_SCREEN_HEIGHT);
+    char game_mode_str[20];
+    snprintf(game_mode_str, 20, "%dx%d:%d", width, height, bpp );
+    glutGameModeString( game_mode_str );
+    glutEnterGameMode();
+    this->gameMode = gameMode;
+  } else {
+    if( width == -1 ) 
+      width = glutGet(GLUT_SCREEN_WIDTH);
+
+    if( height == -1 )
+      height = glutGet(GLUT_SCREEN_HEIGHT);
+
+    glutInitDisplayMode(glutMode);
+//    glutInitWindowSize(width, height);
+    windowId = glutCreateWindow(name);
+  }
+  Init();
+
+  glutKeyboardFunc(FGGLApplication::KeyCallback);
+  glutIdleFunc(FGGLApplication::IdleCallback);
+  glutDisplayFunc(FGGLApplication::DisplayCallback);
+  glutReshapeFunc(FGGLApplication::ReshapeCallback);
+  glutMainLoop();
+}
diff --git a/utils/fgpanel/FGGLApplication.hxx b/utils/fgpanel/FGGLApplication.hxx
new file mode 100644 (file)
index 0000000..aba699f
--- /dev/null
@@ -0,0 +1,48 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 __FGGLAPPLICATION_HXX
+#define __FGGLAPPLICATION_HXX
+
+class FGGLApplication {
+public:
+  FGGLApplication( const char * name, int argc, char ** argv );
+  virtual ~FGGLApplication();
+  void Run( int glutMode, bool gameMode, int widht=-1, int height=-1, int bpp = 32 );
+protected:
+  virtual void Key( unsigned char key, int x, int y ) {}
+  virtual void Idle() {}
+  virtual void Display() {}
+  virtual void Reshape( int width, int height ) {}
+
+  virtual void Init() {}
+
+  int windowId;
+  bool gameMode;
+
+  const char * name;
+
+  static FGGLApplication * application;
+private:
+  static void KeyCallback( unsigned char key, int x, int y );
+  static void IdleCallback();
+  static void DisplayCallback();
+  static void ReshapeCallback( int width, int height );
+
+};
+
+#endif
diff --git a/utils/fgpanel/FGPNGTextureLoader.cxx b/utils/fgpanel/FGPNGTextureLoader.cxx
new file mode 100644 (file)
index 0000000..236792b
--- /dev/null
@@ -0,0 +1,142 @@
+//
+//  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 "FGPNGTextureLoader.hxx"
+
+#include <GL/glu.h>
+#include <png.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <iostream>
+using namespace std;
+GLuint FGPNGTextureLoader::loadTexture( const string & filename )
+{
+  //header for testing if it is a png
+  png_byte header[8];
+  
+  //open file as binary
+  FILE *fp = fopen(filename.c_str(), "rb");
+  if (!fp) {
+    return NOTEXTURE;
+  }
+  
+  //read the header
+  fread(header, 1, 8, fp);
+  
+  //test if png
+  int is_png = !png_sig_cmp(header, 0, 8);
+  if (!is_png) {
+    fclose(fp);
+    return NOTEXTURE;
+  }
+  
+  //create png struct
+  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
+      NULL, NULL);
+  if (!png_ptr) {
+    fclose(fp);
+    return (NOTEXTURE);
+  }
+  
+  //create png info struct
+  png_infop info_ptr = png_create_info_struct(png_ptr);
+  if (!info_ptr) {
+    png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+    fclose(fp);
+    return (NOTEXTURE);
+  }
+
+  //create png info struct
+  png_infop end_info = png_create_info_struct(png_ptr);
+  if (!end_info) {
+    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+    fclose(fp);
+    return (NOTEXTURE);
+  }
+
+  //png error stuff, not sure libpng man suggests this.
+  if (setjmp(png_jmpbuf(png_ptr))) {
+    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+    fclose(fp);
+    return (NOTEXTURE);
+  }
+
+  //init png reading
+  png_init_io(png_ptr, fp);
+  
+  //let libpng know you already read the first 8 bytes
+  png_set_sig_bytes(png_ptr, 8);
+
+  // read all the info up to the image data
+  png_read_info(png_ptr, info_ptr);
+
+  //variables to pass to get info
+  int bit_depth, color_type;
+  png_uint_32 twidth, theight;
+
+  // get info about png
+  png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type,
+      NULL, NULL, NULL);
+
+  // Update the png info struct.
+  png_read_update_info(png_ptr, info_ptr);
+
+  // Row size in bytes.
+  int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+  // Allocate the image_data as a big block, to be given to opengl
+  png_byte *image_data = new png_byte[rowbytes * theight];
+  if (!image_data) {
+    //clean up memory and close stuff
+    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+    fclose(fp);
+    return NOTEXTURE;
+  }
+
+  //row_pointers is for pointing to image_data for reading the png with libpng
+  png_bytep *row_pointers = new png_bytep[theight];
+  if (!row_pointers) {
+    //clean up memory and close stuff
+    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+    delete[] image_data;
+    fclose(fp);
+    return NOTEXTURE;
+  }
+  // set the individual row_pointers to point at the correct offsets of image_data
+  for (png_uint_32 i = 0; i < theight; ++i)
+    row_pointers[theight - 1 - i] = image_data + i * rowbytes;
+
+  //read the png into image_data through row_pointers
+  png_read_image(png_ptr, row_pointers);
+
+  //Now generate the OpenGL texture object
+  GLuint texture;
+  glGenTextures(1, &texture);
+  glBindTexture(GL_TEXTURE_2D, texture);
+  gluBuild2DMipmaps( GL_TEXTURE_2D, 4, twidth, theight, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)image_data );
+  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+//  glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, twidth, theight, 0,
+//      GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) image_data);
+//  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+  //clean up memory and close stuff
+  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+  delete[] image_data;
+  delete[] row_pointers;
+  fclose(fp);
+  return texture;
+}
diff --git a/utils/fgpanel/FGPNGTextureLoader.hxx b/utils/fgpanel/FGPNGTextureLoader.hxx
new file mode 100644 (file)
index 0000000..e8bbfa4
--- /dev/null
@@ -0,0 +1,26 @@
+//  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 __FGPNGTEXTURELOADER_HXX
+#define __FGPNGTEXTURELOADER_HXX
+
+#include "FGTextureLoaderInterface.hxx"
+
+class FGPNGTextureLoader : public FGTextureLoaderInterface {
+public:
+  virtual GLuint loadTexture( const std::string & filename );
+
+  const static GLuint NOTEXTURE = 0;
+};
+#endif
diff --git a/utils/fgpanel/FGPanelApplication.cxx b/utils/fgpanel/FGPanelApplication.cxx
new file mode 100644 (file)
index 0000000..e01f206
--- /dev/null
@@ -0,0 +1,279 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 "FGPanelApplication.hxx"
+#include <GL/gl.h>
+#include <GL/glut.h>
+
+#include <simgear/math/SGMisc.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/props/props_io.hxx>
+#include <simgear/structure/exception.hxx>
+
+#include <iostream>
+
+#include "panel_io.hxx"
+#include "ApplicationProperties.hxx"
+
+using namespace std;
+
+inline static string ParseArgs( int argc, char ** argv, const string & token )
+{
+  for( int i = 0; i < argc; i++ ) {
+    string arg = argv[i];
+    if( arg.find( token ) == 0 )
+      return arg.substr( token.length() );
+  }
+  return "";
+}
+
+inline static string ParseArgs( int argc, char ** argv, const char * token )
+{
+  string s = token;
+  return ParseArgs( argc, argv, s );
+}
+
+#include "FGPNGTextureLoader.hxx"
+#include "FGRGBTextureLoader.hxx"
+static FGPNGTextureLoader pngTextureLoader;
+static FGRGBTextureLoader rgbTextureLoader;
+
+FGPanelApplication::FGPanelApplication( int argc, char ** argv ) :
+  FGGLApplication( "FlightGear Panel", argc, argv )
+{
+  sglog().setLogLevels( SG_ALL, SG_WARN );
+  FGCroppedTexture::registerTextureLoader( "png", &pngTextureLoader );
+  FGCroppedTexture::registerTextureLoader( "rgb", &rgbTextureLoader );
+
+  string panelFilename;
+  string fgRoot;
+
+  for( int i = 1; i < argc; i++ ) {
+    panelFilename = ParseArgs( argc, argv, "--panel=" );
+    fgRoot        = ParseArgs( argc, argv, "--fg-root=" );
+  }
+
+  if( fgRoot.length() > 0 )
+    ApplicationProperties::root = fgRoot;
+
+  if( panelFilename.length() == 0 ) {
+    cerr << "Need a panel filename. Use --panel=path_to_filename" << endl; 
+    throw exception();
+  }
+
+  try {
+    SGPath tpath = ApplicationProperties::GetRootPath( panelFilename.c_str() );
+    readProperties( tpath.str(), ApplicationProperties::Properties );
+  }
+  catch( sg_io_exception & e ) {
+    cerr << e.getFormattedMessage() << endl;
+    throw;
+  }
+
+  for( int i = 1; i < argc; i++ ) {
+    string arg = argv[i];
+    if( arg.find( "--prop:" ) == 0 ) {
+      string s2 = arg.substr( 7 );
+      unsigned p = s2.find( "=" );
+      if( p != string::npos ) {
+        string propertyName = s2.substr( 0, p );
+        string propertyValue = s2.substr( p+1 );
+        ApplicationProperties::Properties->getNode( propertyName.c_str(), true )->setValue( propertyValue.c_str() );
+      }
+    }
+  }
+
+  SGPropertyNode_ptr n;
+  if( (n = ApplicationProperties::Properties->getNode( "panel" )) != NULL )
+    panel = FGReadablePanel::read( n );
+
+  protocol = new FGPanelProtocol( ApplicationProperties::Properties->getNode( "communication", true ) );
+  protocol->init();
+}
+
+FGPanelApplication::~FGPanelApplication()
+{
+}
+
+void FGPanelApplication::Run()
+{
+  int mode = GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE;
+  int w = panel == NULL ? 0 : panel->getWidth();
+  int h = panel == NULL ? 0 : panel->getHeight();
+  if( w == 0 && h == 0 ) {
+    w = 1024;
+    h = 768;
+  } else if( w == 0 ) {
+    w = h / 0.75;
+  } else if( h == 0 ) { 
+    h = w * 0.75;
+  }
+  
+  bool gameMode = ApplicationProperties::Properties->getNode( "game-mode", true )->getBoolValue();
+  FGGLApplication::Run( mode, gameMode, w, h );
+}
+
+void FGPanelApplication::Init()
+{
+  glAlphaFunc(GL_GREATER, 0.1);
+  glutSetCursor( GLUT_CURSOR_NONE );
+  ApplicationProperties::fontCache.initializeFonts();  
+}
+
+void FGPanelApplication::Reshape( int width, int height )
+{
+  this->width = width;
+  this->height = height;
+  glViewport(0, 0, (GLsizei) width, (GLsizei) height);
+}
+
+void FGPanelApplication::Idle()
+{
+  double d = glutGet(GLUT_ELAPSED_TIME);
+
+  double dt = Sleep();
+  if( dt == 0 )
+    return;
+
+  if( panel != NULL )
+    panel->update( dt );
+
+  glutSwapBuffers();
+
+  if( protocol != NULL )
+    protocol->update( dt );
+
+  static double dsum = 0.0;
+  static unsigned cnt = 0;
+  dsum += glutGet(GLUT_ELAPSED_TIME)-d;
+  cnt++;
+  if( dsum > 1000.0 ) {
+    ApplicationProperties::Properties->getNode( "/sim/frame-rate", true )->setDoubleValue(cnt*1000.0/dsum );
+    dsum = 0.0;
+    cnt = 0;
+  }
+}
+
+void FGPanelApplication::Key( unsigned char key, int x, int y )
+{
+  switch( key ) {
+    case 0x1b:
+      if( gameMode ) glutLeaveGameMode();
+      else           glutDestroyWindow( windowId );
+      break;
+  }
+}
+
+double FGPanelApplication::Sleep()
+{
+  SGTimeStamp current_time_stamp;
+  static SGTimeStamp last_time_stamp;
+
+  if ( last_time_stamp.get_seconds() == 0 )
+    last_time_stamp.stamp();
+
+  double model_hz = 60;
+  double throttle_hz = ApplicationProperties::getDouble("/sim/frame-rate-throttle-hz", 0.0);
+  if ( throttle_hz > 0.0 ) {
+    // optionally throttle the frame rate (to get consistent frame
+    // rates or reduce cpu usage.
+
+    double frame_us = 1.0e6 / throttle_hz;
+
+    // sleep based timing loop.
+    //
+    // Calling sleep, even usleep() on linux is less accurate than
+    // we like, but it does free up the cpu for other tasks during
+    // the sleep so it is desirable.  Because of the way sleep()
+    // is implemented in consumer operating systems like windows
+    // and linux, you almost always sleep a little longer than the
+    // requested amount.
+    //
+    // To combat the problem of sleeping too long, we calculate the
+    // desired wait time and shorten it by 2000us (2ms) to avoid
+    // [hopefully] over-sleep'ing.  The 2ms value was arrived at
+    // via experimentation.  We follow this up at the end with a
+    // simple busy-wait loop to get the final pause timing exactly
+    // right.
+    //
+    // Assuming we don't oversleep by more than 2000us, this
+    // should be a reasonable compromise between sleep based
+    // waiting, and busy waiting.
+
+    // sleep() will always overshoot by a bit so undersleep by
+    // 2000us in the hopes of never oversleeping.
+    frame_us -= 2000.0;
+    if ( frame_us < 0.0 ) {
+      frame_us = 0.0;
+    }
+    current_time_stamp.stamp();
+
+    /* Convert to ms */
+    double elapsed_us = (current_time_stamp - last_time_stamp).toUSecs();
+    if ( elapsed_us < frame_us ) {
+      double requested_us = frame_us - elapsed_us;
+      usleep ( (useconds_t)(requested_us ) ) ;
+    }
+    // busy wait timing loop.
+    //
+    // This yields the most accurate timing.  If the previous
+    // usleep() call is omitted this will peg the cpu
+    // (which is just fine if FG is the only app you care about.)
+    current_time_stamp.stamp();
+    SGTimeStamp next_time_stamp = last_time_stamp;
+    next_time_stamp += SGTimeStamp::fromSec(1e-6*frame_us);
+    while ( current_time_stamp < next_time_stamp ) {
+      current_time_stamp.stamp();
+    }
+
+  } else {
+    current_time_stamp.stamp();
+  }
+
+  double real_delta_time_sec = double(current_time_stamp.toUSecs() - last_time_stamp.toUSecs()) / 1000000.0;
+  last_time_stamp = current_time_stamp;
+//fprintf(stdout,"\r%4.1lf ", 1/real_delta_time_sec );
+//fflush(stdout);
+
+  // round the real time down to a multiple of 1/model-hz.
+  // this way all systems are updated the _same_ amount of dt.
+  static double reminder = 0.0;
+  static long global_multi_loop = 0;
+  real_delta_time_sec += reminder;
+  global_multi_loop = long(floor(real_delta_time_sec*model_hz));
+  global_multi_loop = SGMisc<long>::max(0, global_multi_loop);
+  reminder = real_delta_time_sec - double(global_multi_loop)/double(model_hz);
+  return double(global_multi_loop)/double(model_hz);
+}
+
+double ApplicationProperties::getDouble( const char * name, double def )
+{
+  SGPropertyNode_ptr n = ApplicationProperties::Properties->getNode( name, false );
+  if( n == NULL ) return def;
+  return n->getDoubleValue();
+}
+SGPath ApplicationProperties::GetRootPath( const char * sub )
+{
+  SGPath path( ApplicationProperties::root );
+  if( sub != NULL )
+    path.append( sub );
+  return path;
+}
+
+std::string ApplicationProperties::root = ".";
+SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode;
+FGFontCache ApplicationProperties::fontCache;
diff --git a/utils/fgpanel/FGPanelApplication.hxx b/utils/fgpanel/FGPanelApplication.hxx
new file mode 100644 (file)
index 0000000..f2652ee
--- /dev/null
@@ -0,0 +1,55 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 __FGPANELAPPLICATION_HXX
+#define __FGPANELAPPLICATION_HXX
+
+#include "FGGLApplication.hxx"
+#include "FGPanelProtocol.hxx"
+
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/props/props.hxx>
+
+#include <string>
+
+#include "panel.hxx"
+
+class FGPanelApplication : public FGGLApplication {
+public:
+  FGPanelApplication( int argc, char ** argv );
+  ~FGPanelApplication();
+
+  void Run();
+
+protected:
+  virtual void Key( unsigned char key, int x, int y );
+  virtual void Idle();
+//  virtual void Display();
+  virtual void Reshape( int width, int height );
+
+  virtual void Init();
+
+  double Sleep();
+
+  SGSharedPtr<FGPanel> panel;
+  SGSharedPtr<FGPanelProtocol> protocol;
+
+  int width;
+  int height;
+};
+
+#endif
diff --git a/utils/fgpanel/FGPanelProtocol.cxx b/utils/fgpanel/FGPanelProtocol.cxx
new file mode 100644 (file)
index 0000000..0743724
--- /dev/null
@@ -0,0 +1,154 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 "FGPanelProtocol.hxx"
+#include "ApplicationProperties.hxx"
+#include <simgear/io/sg_socket.hxx>
+#include <simgear/misc/strutils.hxx>
+
+class PropertySetter {
+public:
+  PropertySetter( SGPropertyNode_ptr node ) : _node(node) {}
+  virtual void setValue( const char * value ) = 0;
+protected:
+  SGPropertyNode_ptr _node;
+};
+
+class BoolPropertySetter : public PropertySetter {
+public:
+  BoolPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
+  virtual void setValue( const char * value ) {
+    _node->setBoolValue( atoi( value ) != 0 );
+  }
+};
+
+class IntPropertySetter : public PropertySetter {
+public:
+  IntPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
+  virtual void setValue( const char * value ) {
+    _node->setIntValue( atol( value ) );
+  }
+};
+
+class FloatPropertySetter : public PropertySetter {
+public:
+  FloatPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
+  virtual void setValue( const char * value ) {
+    _node->setFloatValue( strtof( value, NULL ) );
+  }
+};
+
+class DoublePropertySetter : public PropertySetter {
+public:
+  DoublePropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
+  virtual void setValue( const char * value ) {
+    _node->setDoubleValue( strtod( value, NULL ) );
+  }
+};
+
+class StringPropertySetter : public PropertySetter {
+public:
+  StringPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
+  virtual void setValue( const char * value ) {
+    _node->setStringValue( value );
+  }
+};
+
+FGPanelProtocol::FGPanelProtocol( SGPropertyNode_ptr aRoot )
+  : SGSubsystem(),
+   root(aRoot),
+   io(NULL)
+{
+  SGPropertyNode_ptr outputNode = root->getNode( "protocol/generic/output" );
+  if( outputNode ) {
+    vector<SGPropertyNode_ptr> chunks = outputNode->getChildren( "chunk" );
+    for( vector<SGPropertyNode_ptr>::size_type i = 0; i < chunks.size(); i++ ) {
+      SGPropertyNode_ptr chunk = chunks[i];
+
+      SGPropertyNode_ptr nodeNode = chunk->getNode("node", false );
+      if( nodeNode == NULL )
+        continue;
+
+      SGPropertyNode_ptr node = ApplicationProperties::Properties->getNode( nodeNode->getStringValue(), true );
+
+      string type = "";
+      SGPropertyNode_ptr typeNode = chunk->getNode( "type", false );
+      if( typeNode != NULL ) type = typeNode->getStringValue();
+      if( type == "float" ) {
+        propertySetterVector.push_back( new FloatPropertySetter( node ) );
+      } else if( type == "double" || type == "fixed" ) {
+        propertySetterVector.push_back( new DoublePropertySetter( node ) );
+      } else if( type == "bool" || type == "boolean" ) {
+        propertySetterVector.push_back( new BoolPropertySetter( node ) );
+      } else if( type == "string" ) {
+        propertySetterVector.push_back( new StringPropertySetter( node ) );
+      } else {
+        propertySetterVector.push_back( new IntPropertySetter( node ) );
+      }
+    }
+  }
+}
+
+FGPanelProtocol::~FGPanelProtocol()
+{
+  for( PropertySetterVector::size_type i = 0; i < propertySetterVector.size(); i++ )
+    delete propertySetterVector[i];
+}
+
+void FGPanelProtocol::update( double dt )
+{
+  char buf[8192];
+
+  if( io == NULL )
+    return;
+
+  int length = io->readline( buf, sizeof(buf)-1 );
+  buf[sizeof(buf)-1] = 0;
+  if ( length > 0 ) {
+    vector<string> tokens = simgear::strutils::split( buf, "," );
+    for( vector<string>::size_type i = 0; i < tokens.size(); i++ ) {
+      if( i < propertySetterVector.size() )
+        propertySetterVector[i]->setValue( tokens[i].c_str() );
+    }
+  }
+}
+
+void FGPanelProtocol::init()
+{
+  SGPropertyNode_ptr listenNode = root->getNode( "listen" );
+  if( listenNode == NULL ) {
+    return;
+  }
+
+  string hostname = listenNode->getNode( "host", true )->getStringValue();
+  string port = listenNode->getNode( "port", true )->getStringValue();
+  string style = listenNode->getNode( "style", true )->getStringValue();
+
+  if( io != NULL )
+    delete io;
+
+  io = new SGSocket( hostname, port, style );
+
+  if( !io->open( SG_IO_IN ) ) {
+    cerr << "can't open socket " << style << ":" << hostname << ":" << port << endl;
+  }
+}
+
+void FGPanelProtocol::reinit()
+{
+  init();
+}
diff --git a/utils/fgpanel/FGPanelProtocol.hxx b/utils/fgpanel/FGPanelProtocol.hxx
new file mode 100644 (file)
index 0000000..5b08daf
--- /dev/null
@@ -0,0 +1,41 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 __FGPANELPROTOCOL_HXX
+#define __FGPANELPROTOCOL_HXX
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/io/iochannel.hxx>
+class PropertySetter;
+
+typedef vector<PropertySetter*> PropertySetterVector;
+class FGPanelProtocol : public SGSubsystem {
+public:
+  FGPanelProtocol( SGPropertyNode_ptr root );
+  virtual ~FGPanelProtocol();
+  virtual void init();
+  virtual void reinit();
+  virtual void update( double dt );
+
+protected:
+
+private:
+  SGPropertyNode_ptr root;
+  SGIOChannel * io;
+  PropertySetterVector propertySetterVector;
+};
+#endif
diff --git a/utils/fgpanel/FGRGBTextureLoader.cxx b/utils/fgpanel/FGRGBTextureLoader.cxx
new file mode 100644 (file)
index 0000000..faf1369
--- /dev/null
@@ -0,0 +1,504 @@
+//
+//  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.
+//
+// From the OpenSceneGraph distribution ReaderWriterRGB.cpp
+// Reader for sgi's .rgb format.
+// specification can be found at http://local.wasp.uwa.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html
+
+#include "FGRGBTextureLoader.hxx"
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <iostream>
+#include <fstream>
+
+typedef struct _rawImageRec
+{
+    unsigned short imagic;
+    unsigned short type;
+    unsigned short dim;
+    unsigned short sizeX, sizeY, sizeZ;
+    unsigned long min, max;
+    unsigned long wasteBytes;
+    char name[80];
+    unsigned long colorMap;
+    std::istream *file;
+    unsigned char *tmp, *tmpR, *tmpG, *tmpB, *tmpA;
+    unsigned long rleEnd;
+    GLuint *rowStart;
+    GLint *rowSize;
+    GLenum swapFlag;
+    short bpc;
+  
+    typedef unsigned char * BytePtr;
+
+    bool needsBytesSwapped()
+    {
+        union {
+            int testWord;
+            char testByte[sizeof(int)];
+        }endianTest; 
+        endianTest.testWord = 1;
+        if( endianTest.testByte[0] == 1 )
+            return true;
+        else
+            return false;
+    }
+
+    template <class T>
+    inline void swapBytes(  T &s )
+    {
+        if( sizeof( T ) == 1 ) 
+            return;
+
+        T d = s;
+        BytePtr sptr = (BytePtr)&s;
+        BytePtr dptr = &(((BytePtr)&d)[sizeof(T)-1]);
+
+        for( unsigned int i = 0; i < sizeof(T); i++ )
+            *(sptr++) = *(dptr--);
+    }
+
+    void swapBytes()
+    {
+        swapBytes( imagic );
+        swapBytes( type );
+        swapBytes( dim );
+        swapBytes( sizeX );
+        swapBytes( sizeY );
+        swapBytes( sizeZ );
+        swapBytes( wasteBytes );
+        swapBytes( min );
+        swapBytes( max );
+        swapBytes( colorMap );
+    }
+} rawImageRec;
+
+static void ConvertShort(unsigned short *array, long length)
+{
+    unsigned long b1, b2;
+    unsigned char *ptr;
+
+    ptr = (unsigned char *)array;
+    while (length--)
+    {
+        b1 = *ptr++;
+        b2 = *ptr++;
+        *array++ = (unsigned short) ((b1 << 8) | (b2));
+    }
+}
+
+static void ConvertLong(GLuint *array, long length)
+{
+    unsigned long b1, b2, b3, b4;
+    unsigned char *ptr;
+
+    ptr = (unsigned char *)array;
+    while (length--)
+    {
+        b1 = *ptr++;
+        b2 = *ptr++;
+        b3 = *ptr++;
+        b4 = *ptr++;
+        *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
+    }
+}
+
+
+static void RawImageClose(rawImageRec *raw)
+{
+    if (raw)
+    {
+        
+        if (raw->tmp) delete [] raw->tmp;
+        if (raw->tmpR) delete [] raw->tmpR;
+        if (raw->tmpG) delete [] raw->tmpG;
+        if (raw->tmpB) delete [] raw->tmpB;
+        if (raw->tmpA) delete [] raw->tmpA;
+
+        if (raw->rowStart) delete [] raw->rowStart;        
+        if (raw->rowSize) delete [] raw->rowSize;        
+
+        delete raw;
+    }
+}
+
+
+static rawImageRec *RawImageOpen(std::istream& fin)
+{
+    union
+    {
+        int testWord;
+        char testByte[4];
+    } endianTest;
+    rawImageRec *raw;
+    int x;
+
+    raw = new rawImageRec;
+    if (raw == NULL)
+    {
+//        notify(WARN)<< "Out of memory!"<< std::endl;
+        return NULL;
+    }
+
+    //Set istream pointer
+    raw->file = &fin;
+
+    endianTest.testWord = 1;
+    if (endianTest.testByte[0] == 1)
+    {
+        raw->swapFlag = GL_TRUE;
+    }
+    else
+    {
+        raw->swapFlag = GL_FALSE;
+    }
+
+    fin.read((char*)raw,12);
+    if (!fin.good())
+        return NULL;
+
+    if (raw->swapFlag)
+    {
+        ConvertShort(&raw->imagic, 6);
+    }
+
+    raw->tmp = raw->tmpR = raw->tmpG = raw->tmpB = raw->tmpA = 0L;
+    raw->rowStart = 0;
+    raw->rowSize = 0;
+    raw->bpc = (raw->type & 0x00FF);
+
+    raw->tmp = new unsigned char [raw->sizeX*256*raw->bpc];
+    if (raw->tmp == NULL )
+    {
+//        notify(FATAL)<< "Out of memory!"<< std::endl;
+        RawImageClose(raw);
+        return NULL;
+    }
+
+    if( raw->sizeZ >= 1 )
+    {
+        if( (raw->tmpR = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
+        {
+//            notify(FATAL)<< "Out of memory!"<< std::endl;
+            RawImageClose(raw);
+            return NULL;
+        }
+    }
+    if( raw->sizeZ >= 2 )
+    {
+        if( (raw->tmpG = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
+        {
+//            notify(FATAL)<< "Out of memory!"<< std::endl;
+            RawImageClose(raw);
+            return NULL;
+        }
+    }
+    if( raw->sizeZ >= 3 )
+    {
+        if( (raw->tmpB = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
+        {
+//            notify(FATAL)<< "Out of memory!"<< std::endl;
+            RawImageClose(raw);
+            return NULL;
+        }
+    }
+    if (raw->sizeZ >= 4)
+    {
+        if( (raw->tmpA = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
+        {
+//            notify(FATAL)<< "Out of memory!"<< std::endl;
+            RawImageClose(raw);
+            return NULL;
+        }
+    }
+    
+    if ((raw->type & 0xFF00) == 0x0100)
+    {
+        unsigned int ybyz = raw->sizeY * raw->sizeZ;
+        if ( (raw->rowStart = new GLuint [ybyz]) == NULL )
+        {
+//            notify(FATAL)<< "Out of memory!"<< std::endl;
+            RawImageClose(raw);
+            return NULL;
+        }
+
+        if ( (raw->rowSize = new GLint [ybyz]) == NULL )
+        {
+//            notify(FATAL)<< "Out of memory!"<< std::endl;
+            RawImageClose(raw);
+            return NULL;
+        }
+        x = ybyz * sizeof(GLuint);
+        raw->rleEnd = 512 + (2 * x);
+                fin.seekg(512,std::ios::beg);
+        fin.read((char*)raw->rowStart,x);
+        fin.read((char*)raw->rowSize,x);
+        if (raw->swapFlag)
+        {
+            ConvertLong(raw->rowStart, (long) (x/sizeof(GLuint)));
+            ConvertLong((GLuint *)raw->rowSize, (long) (x/sizeof(GLint)));
+        }
+    }
+    return raw;
+}
+
+
+static void RawImageGetRow(rawImageRec *raw, unsigned char *buf, int y, int z)
+{
+    unsigned char *iPtr, *oPtr;
+    unsigned short pixel;
+    int count, done = 0;
+    unsigned short *tempShort;
+
+    if ((raw->type & 0xFF00) == 0x0100)
+    {
+        raw->file->seekg((long) raw->rowStart[y+z*raw->sizeY], std::ios::beg);
+        raw->file->read((char*)raw->tmp, (unsigned int)raw->rowSize[y+z*raw->sizeY]);
+
+        iPtr = raw->tmp;
+        oPtr = buf;
+        while (!done)
+        {
+            if (raw->bpc == 1)
+                pixel = *iPtr++;
+            else
+            {
+                tempShort = reinterpret_cast<unsigned short*>(iPtr);
+                pixel = *tempShort;
+                tempShort++;
+                iPtr = reinterpret_cast<unsigned char *>(tempShort);
+            }
+            
+            if(raw->bpc != 1)
+                ConvertShort(&pixel, 1);
+
+            count = (int)(pixel & 0x7F);
+            
+            // limit the count value to the remiaing row size
+            if (oPtr + count*raw->bpc > buf + raw->sizeX*raw->bpc)
+            {
+                count = ( (buf + raw->sizeX*raw->bpc) - oPtr ) / raw->bpc;
+            }
+                
+            if (count<=0)
+            {
+                done = 1;
+                return;
+            }
+            
+            if (pixel & 0x80)
+            {
+                while (count--)
+                {
+                    if(raw->bpc == 1)
+                        *oPtr++ = *iPtr++;
+                    else{
+                        tempShort = reinterpret_cast<unsigned short*>(iPtr);
+                        pixel = *tempShort;
+                        tempShort++;
+                        iPtr = reinterpret_cast<unsigned char *>(tempShort);
+                        
+                        ConvertShort(&pixel, 1);
+
+                        tempShort = reinterpret_cast<unsigned short*>(oPtr);
+                        *tempShort = pixel;
+                        tempShort++;
+                        oPtr = reinterpret_cast<unsigned char *>(tempShort);
+                    }
+                }
+            }
+            else
+            {
+                if (raw->bpc == 1)
+                {
+                    pixel = *iPtr++;
+                }
+                else
+                {
+                    tempShort = reinterpret_cast<unsigned short*>(iPtr);
+                    pixel = *tempShort;
+                    tempShort++;
+                    iPtr = reinterpret_cast<unsigned char *>(tempShort);
+                }
+                if(raw->bpc != 1)
+                    ConvertShort(&pixel, 1);
+                while (count--)
+                {
+                    if(raw->bpc == 1)
+                        *oPtr++ = pixel;
+                    else
+                    {
+                        tempShort = reinterpret_cast<unsigned short*>(oPtr);
+                        *tempShort = pixel;
+                        tempShort++;
+                        oPtr = reinterpret_cast<unsigned char *>(tempShort);
+                    }
+                }
+            }
+        }
+    }
+    else
+    {
+        raw->file->seekg(512+(y*raw->sizeX*raw->bpc)+(z*raw->sizeX*raw->sizeY*raw->bpc),std::ios::beg);
+        raw->file->read((char*)buf, raw->sizeX*raw->bpc);
+        if(raw->swapFlag && raw->bpc != 1){
+            ConvertShort(reinterpret_cast<unsigned short*>(buf), raw->sizeX);
+        }
+    }
+}
+
+
+static void RawImageGetData(rawImageRec *raw, unsigned char **data )
+{
+    unsigned char *ptr;
+    int i, j;
+    unsigned short *tempShort;
+
+    //     // round the width to a factor 4
+    //     int width = (int)(floorf((float)raw->sizeX/4.0f)*4.0f);
+    //     if (width!=raw->sizeX) width += 4;
+
+    // byte aligned.
+    
+//    osg::notify(osg::INFO)<<"raw->sizeX = "<<raw->sizeX<<std::endl;
+//    osg::notify(osg::INFO)<<"raw->sizeY = "<<raw->sizeY<<std::endl;
+//    osg::notify(osg::INFO)<<"raw->sizeZ = "<<raw->sizeZ<<std::endl;
+//    osg::notify(osg::INFO)<<"raw->bpc = "<<raw->bpc<<std::endl;
+    
+    *data = new unsigned char [(raw->sizeX)*(raw->sizeY)*(raw->sizeZ)*(raw->bpc)];
+
+    ptr = *data;
+    for (i = 0; i < (int)(raw->sizeY); i++)
+    {
+        if( raw->sizeZ >= 1 )
+            RawImageGetRow(raw, raw->tmpR, i, 0);
+        if( raw->sizeZ >= 2 )
+            RawImageGetRow(raw, raw->tmpG, i, 1);
+        if( raw->sizeZ >= 3 )
+            RawImageGetRow(raw, raw->tmpB, i, 2);
+        if( raw->sizeZ >= 4 )
+            RawImageGetRow(raw, raw->tmpA, i, 3);
+        for (j = 0; j < (int)(raw->sizeX); j++)
+        {
+          if(raw->bpc == 1){
+            if( raw->sizeZ >= 1 )
+                *ptr++ = *(raw->tmpR + j);
+            if( raw->sizeZ >= 2 )
+                *ptr++ = *(raw->tmpG + j);
+            if( raw->sizeZ >= 3 )
+                *ptr++ = *(raw->tmpB + j);
+            if( raw->sizeZ >= 4 )
+                *ptr++ = *(raw->tmpA + j);
+          }else{
+            if( raw->sizeZ >= 1 )
+            {
+                tempShort = reinterpret_cast<unsigned short*>(ptr);
+                *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpR) + j);
+                tempShort++;
+                ptr = reinterpret_cast<unsigned char *>(tempShort);
+            }
+            if( raw->sizeZ >= 2 )
+            {
+                tempShort = reinterpret_cast<unsigned short*>(ptr);
+                *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpG) + j);
+                tempShort++;
+                ptr = reinterpret_cast<unsigned char *>(tempShort);
+            }
+            if( raw->sizeZ >= 3 )
+            {
+                tempShort = reinterpret_cast<unsigned short*>(ptr);
+                *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpB) + j);
+                tempShort++;
+                ptr = reinterpret_cast<unsigned char *>(tempShort);
+            }
+            if( raw->sizeZ >= 4 )
+            {
+                tempShort = reinterpret_cast<unsigned short*>(ptr);
+                *tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpA) + j);
+                tempShort++;
+                ptr = reinterpret_cast<unsigned char *>(tempShort);
+            }
+          }
+        }
+        //         // pad the image width with blanks to bring it up to the rounded width.
+        //         for(;j<width;++j) *ptr++ = 0;
+    }
+}
+
+
+//            supportsExtension("rgb","rgb image format");
+//            supportsExtension("rgba","rgba image format");
+//            supportsExtension("sgi","sgi image format");
+//            supportsExtension("int","int image format");
+//            supportsExtension("inta","inta image format");
+//            supportsExtension("bw","bw image format");
+        
+        GLuint readRGBStream(std::istream& fin)
+        {
+            rawImageRec *raw;
+
+            if( (raw = RawImageOpen(fin)) == NULL )
+            {
+                return 0;
+            }
+
+            int s = raw->sizeX;
+            int t = raw->sizeY;
+//            int r = 1;
+
+        #if 0
+            int internalFormat = raw->sizeZ == 3 ? GL_RGB5 :
+            raw->sizeZ == 4 ? GL_RGB5_A1 : GL_RGB;
+        #else
+//            int internalFormat = raw->sizeZ;
+        #endif
+            unsigned int pixelFormat =
+                raw->sizeZ == 1 ? GL_LUMINANCE :
+                raw->sizeZ == 2 ? GL_LUMINANCE_ALPHA :
+                raw->sizeZ == 3 ? GL_RGB :
+                raw->sizeZ == 4 ? GL_RGBA : (GLenum)-1;
+            GLint component = raw->sizeZ;
+
+            unsigned int dataType = raw->bpc == 1 ? GL_UNSIGNED_BYTE :
+              GL_UNSIGNED_SHORT;
+
+            unsigned char *data;
+            RawImageGetData(raw, &data);
+            RawImageClose(raw);
+
+  GLuint texture;
+  glGenTextures(1, &texture);
+  glBindTexture(GL_TEXTURE_2D, texture);
+  gluBuild2DMipmaps( GL_TEXTURE_2D, component, s, t, pixelFormat, dataType, (GLvoid*)data );
+  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
+
+            delete []data;
+            return texture;
+        }
+
+GLuint FGRGBTextureLoader::loadTexture( const std::string & filename )
+{
+  GLuint texture = NOTEXTURE;
+  std::ifstream istream(filename.c_str(), std::ios::in | std::ios::binary );
+  texture = readRGBStream(istream);
+  istream.close();
+  return texture;
+}
+
diff --git a/utils/fgpanel/FGRGBTextureLoader.hxx b/utils/fgpanel/FGRGBTextureLoader.hxx
new file mode 100644 (file)
index 0000000..e3c2766
--- /dev/null
@@ -0,0 +1,26 @@
+//  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 __FGRGBTEXTURELOADER_HXX
+#define __FGRGBTEXTURELOADER_HXX
+
+#include "FGTextureLoaderInterface.hxx"
+
+class FGRGBTextureLoader : public FGTextureLoaderInterface {
+public:
+  virtual GLuint loadTexture( const std::string & filename );
+
+  const static GLuint NOTEXTURE = 0;
+};
+#endif
diff --git a/utils/fgpanel/FGTextureLoaderInterface.hxx b/utils/fgpanel/FGTextureLoaderInterface.hxx
new file mode 100644 (file)
index 0000000..58c15b8
--- /dev/null
@@ -0,0 +1,27 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 __FGTEXTURELOADERINTERFACE_HXX
+#define __FGTEXTURELOADERINTERFACE_HXX
+
+#include <GL/gl.h>
+#include <string>
+class FGTextureLoaderInterface {
+public:
+  virtual GLuint loadTexture( const std::string & filename ) = 0;
+};
+#endif
diff --git a/utils/fgpanel/Makefile.am b/utils/fgpanel/Makefile.am
new file mode 100644 (file)
index 0000000..58804e9
--- /dev/null
@@ -0,0 +1,20 @@
+AM_CXXFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\"
+
+bin_PROGRAMS = fgpanel
+
+fgpanel_SOURCES = main.cxx \
+               FGGLApplication.cxx FGGLApplication.hxx \
+               FGPanelApplication.cxx FGPanelApplication.hxx \
+                FGPNGTextureLoader.cxx FGPNGTextureLoader.hxx FGTextureLoaderInterface.hxx \
+                FGRGBTextureLoader.cxx FGRGBTextureLoader.hxx \
+               FGPanelProtocol.cxx \
+               FGFontCache.cxx \
+               panel.cxx panel.hxx \
+               panel_io.cxx panel_io.hxx
+
+LIBS =
+
+fgpanel_LDADD = \
+       -lGLU -lglut -lsgmath -lsgprops -lsgio -lsgdebug -lsgmisc -lsgstructure -lsgxml -lsgtiming \
+       -lplibpu -lplibfnt -lplibul \
+       -lrt -lpng
diff --git a/utils/fgpanel/README b/utils/fgpanel/README
new file mode 100644 (file)
index 0000000..a1199d2
--- /dev/null
@@ -0,0 +1,148 @@
+=====================================================================
+This is fgpanel - basically the stripped down 2D-Panel code from
+FlightGear. It is designed as a standalone lightweight panel 
+rendering engine to draw 2d panels on a lowcost computer/graphic card
+without 3d acceleration at reasonablel framerates.
+
+=====================================================================
+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.
+
+=====================================================================
+Usage
+start fgpanel with
+fgpanel --fg-root=/path/to/fg/data --panel=Aircraft/MyAircraft/Panels/MyPanel.xml
+with the command args set to
+--fg-root shall point to the directory where your FGDATA lives
+  NOTE: you don't need a full copy of FGDATA, just the panel definition files for
+  your aircraft, e.g.
+  - Aircraft/MyAircraft/Panels/*
+  - Aircraft/Instruments/* (if referenced)
+
+-panel shall point to a panel-configuration file, relative to FGDATA
+
+start flightgear with
+fgfs --generic=socket,out,10,239.24.10.64,5432,udp,../Aircraft/MyAircraft/Panels/SampleProtocol
+
+=====================================================================
+Sample:
+
+Create the sample files within your aicraft directory, preferrable under Panels
+MyPanel.xml
+sample-2d-panel.xml
+SampleProtocol.xml
+
+=====================================================================
+Sample panel configuration file (MyPanel.xml)
+<PropertyList>
+
+  <!-- true: run full-screen, false; run in window -->
+  <game-mode type="bool">false</game-mode>
+
+  <!-- include the panel definitions (2d-panel syntax)-->
+  <panel include="sample-2d-panel.xml"/>
+
+  <!-- compose your property-tree here -->
+  <sim>
+    <panel>
+      <flip-x type="bool">false</flip-x>
+    </panel>
+    <instrument-options>
+      <omit-knobs type="bool">true</omit-knobs>
+    </instrument-options>
+  </sim>
+
+  <!-- network communication settings -->
+  <communication>
+    <listen>
+      <!-- interface to bind to, 
+           leave empty for all interfaces -->
+      <host>239.24.10.64</host> <!-- multicast address! -->
+      <port>5432</port> <!-- tcp port to listen to -->
+      <style>udp</style> <!-- udp or tcp (forget about tcp!) -->
+    </listen>
+
+    <!-- the generic protocol definition 
+         same as used for fgfs --generic=foobar option
+    -->
+    <protocol include="SampleProtocol.xml"/>
+  </communication>
+</PropertyList>
+
+=====================================================================
+Sampe 2d-panel configuration file sample-2d-panel.xml
+To be included from the panel configuration file
+
+<?xml version="1.0"?>
+<PropertyList>
+    <name>Sample Instrument Panel</name>
+    <w>375</w> <!-- screen width: 375mm -->
+    <h>305</h> <!-- screen height: 305mm -->
+    <instruments>
+        <!-- use FlightGear's c172 attitude indicator -->
+        <instrument include="../../Instruments/ati-c172s.xml">
+            <name>Attitude Gyro</name>
+            <x alias="../../../params/col-2"/>
+            <y alias="../../../params/row-1"/>
+            <w>80</w>
+            <h>80</h>
+        </instrument>
+    </instruments>
+</PropertyList>
+
+=====================================================================
+Sample protocol configuration file to drive the AI (SampleProtocol.xml)
+<?xml version="1.0"?>
+
+<PropertyList>
+ <generic>
+
+  <output>
+   <line_separator>newline</line_separator>
+   <var_separator>,</var_separator>
+
+   <chunk>
+    <type>float</type>
+    <format>%.2f</format>
+    <node>/position/altitude-agl-ft</node>
+   </chunk>
+
+   <chunk>
+    <type>float</type>
+    <format>%.2f</format>
+    <node>/instrumentation/attitude-indicator/indicated-roll-deg</node>
+   </chunk>
+
+   <chunk>
+    <type>float</type>
+    <format>%.2f</format>
+    <node>/instrumentation/attitude-indicator/indicated-pitch-deg</node>
+   </chunk>
+
+   <chunk>
+    <type>float</type>
+    <format>%.2f</format>
+    <node>/instrumentation/attitude-indicator/horizon-offset-deg</node>
+   </chunk>
+
+   <chunk>
+    <type>float</type>
+    <format>%.4e</format>
+    <node>/instrumentation/attitude-indicator/spin</node>
+   </chunk>
+  </output>
+ </generic>
+</PropertyList>
+
+=====================================================================
diff --git a/utils/fgpanel/main.cxx b/utils/fgpanel/main.cxx
new file mode 100644 (file)
index 0000000..80215e9
--- /dev/null
@@ -0,0 +1,30 @@
+//
+//  Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
+//
+//  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 "FGPanelApplication.hxx"
+
+int main( int argc, char ** argv )
+{
+  try {
+    FGPanelApplication app(argc,argv);
+    app.Run();
+    return 0;
+  }
+  catch( ... ) {
+    cerr << "Sorry, your program terminated." << endl;
+  }
+}
diff --git a/utils/fgpanel/panel.cxx b/utils/fgpanel/panel.cxx
new file mode 100644 (file)
index 0000000..8904499
--- /dev/null
@@ -0,0 +1,962 @@
+//  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//  $Id: panel.cxx,v 1.44 2006/09/05 20:28:48 curt 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 <simgear/compiler.h>
+
+#include <GL/glut.h>
+
+#include <plib/fnt.h>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
+
+#include "panel.hxx"
+#include "ApplicationProperties.hxx"
+////////////////////////////////////////////////////////////////////////
+// Local functions.
+////////////////////////////////////////////////////////////////////////
+
+class FGDummyTextureLoader : public FGTextureLoaderInterface {
+public:
+  virtual GLuint loadTexture( const string & filename );
+};
+
+GLuint FGDummyTextureLoader::loadTexture( const string & filename )
+{
+  GLuint _texture = 0;
+  glGenTextures( 1, &_texture );
+  glBindTexture( GL_TEXTURE_2D, _texture );
+
+//  glTexEnvi       ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ;
+//  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) ;
+//  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ) ;
+//  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) ;
+//  glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) ;
+
+  GLubyte image[ 2 * 2 * 3 ] ;
+
+  /* Red and white chequerboard */
+  image [ 0 ] = 255 ; image [ 1 ] =  0  ; image [ 2 ] =  0  ;
+  image [ 3 ] = 255 ; image [ 4 ] = 255 ; image [ 5 ] = 255 ;
+  image [ 6 ] = 255 ; image [ 7 ] = 255 ; image [ 8 ] = 255 ;
+  image [ 9 ] = 255 ; image [ 10] =  0  ; image [ 11] =  0  ;
+
+  glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, 2, 2, 0,
+        GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*) image);
+
+  return _texture;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGCropped Texture.
+////////////////////////////////////////////////////////////////////////
+
+GLuint FGCroppedTexture::current_bound_texture = 0;
+map<string,GLuint> FGCroppedTexture::cache;
+map<string,FGTextureLoaderInterface*> FGCroppedTexture::textureLoader;
+static FGDummyTextureLoader dummyTextureLoader;
+
+FGCroppedTexture::FGCroppedTexture (const string &path,
+                                   float minX, float minY,
+                                   float maxX, float maxY)
+  : _path(path),
+    _minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY), _texture(0)
+{
+}
+
+FGCroppedTexture::~FGCroppedTexture ()
+{
+}
+
+void FGCroppedTexture::bind( bool doGLBind )
+{
+  if( _texture == 0 ) {
+    SG_LOG( SG_COCKPIT, SG_DEBUG, "First bind of texture " << _path );
+    if( cache.count(_path) > 0 ) {
+      _texture = cache[_path];
+      SG_LOG( SG_COCKPIT, SG_DEBUG, "Using texture " << _path << " from cache (#" << _texture << ")" );
+    } else {
+      SGPath tpath = ApplicationProperties::GetRootPath(_path.c_str());
+      string extension = tpath.extension();
+      FGTextureLoaderInterface * loader = &dummyTextureLoader;
+      if( textureLoader.count( extension ) == 0 ) {
+        SG_LOG( SG_COCKPIT, SG_ALERT, "Can't handle textures of type " << extension );
+      } else {
+        loader = textureLoader[extension];
+      }
+
+      _texture = loader->loadTexture( tpath.c_str() );
+      SG_LOG( SG_COCKPIT, SG_DEBUG, "Texture " << tpath.c_str() << " loaded from file as #" << _texture );
+      
+      cache[_path] = _texture;
+    }
+  }
+
+  if( !doGLBind || current_bound_texture == _texture )
+    return;
+
+  glBindTexture( GL_TEXTURE_2D, _texture );
+  current_bound_texture = _texture;
+}
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGPanel.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * Constructor.
+ */
+FGPanel::FGPanel ( SGPropertyNode_ptr root)
+  : _root(root),
+    _flipx(root->getNode("/sim/panel/flip-x", true)),
+    _rotate(root->getNode("/sim/panel/rotate-deg", true)),
+    _bg_width(1.0), _bg_height(1.0),
+    initDisplayList(0)
+{
+}
+
+
+/**
+ * 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 ()
+{
+}
+
+
+/**
+ * Bind panel properties.
+ */
+void
+FGPanel::bind ()
+{
+}
+
+
+/**
+ * Unbind panel properties.
+ */
+void
+FGPanel::unbind ()
+{
+}
+
+GLuint FGPanel::getInitDisplayList()
+{
+  if( initDisplayList != 0 ) return initDisplayList;
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  if ( _flipx->getBoolValue() ) {
+    gluOrtho2D( _width, 0, _height, 0 ); /* up side down */
+  } else {
+    gluOrtho2D( 0, _width, 0, _height ); /* right side up */
+  }
+
+  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+  
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+
+  glClear( GL_COLOR_BUFFER_BIT);
+
+  // save some state
+  glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT
+                | GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT | GL_CULL_FACE 
+                | GL_DEPTH_BUFFER_BIT );
+
+  // Draw the background
+  glEnable(GL_TEXTURE_2D);
+
+  glDisable(GL_LIGHTING);
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+  glEnable(GL_ALPHA_TEST);
+  glEnable(GL_COLOR_MATERIAL);
+  glEnable(GL_CULL_FACE);
+  glCullFace(GL_BACK);
+  glDisable(GL_DEPTH_TEST);
+
+  if (_bg != NULL) {
+    _bg->bind();
+//    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glBegin(GL_QUADS);
+    glTexCoord2f(0.0, 0.0); glVertex2f(0, 0);
+    glTexCoord2f(_bg_width, 0.0); glVertex2f(_width, 0);
+    glTexCoord2f(_bg_width, _bg_height); glVertex2f(_width, _height);
+    glTexCoord2f(0.0, _bg_height); glVertex2f(0, _height);
+    glEnd();
+  } else if( _mbg[0] != NULL ) {
+    for (int i = 0; i < 4; i ++) {
+      // top row of textures...(1,3,5,7)
+      _mbg[i*2]->bind();
+//      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+      glBegin(GL_QUADS);
+      glTexCoord2f(0.0, 0.0); glVertex2f(i*_width/4,     _height/2);
+      glTexCoord2f(1.0, 0.0); glVertex2f((i+1)*_width/4, _height/2);
+      glTexCoord2f(1.0, 1.0); glVertex2f((i+1)*_width/4, _height);
+      glTexCoord2f(0.0, 1.0); glVertex2f(i*_width/4,     _height);
+      glEnd();
+      // bottom row of textures...(2,4,6,8)
+      _mbg[i*2+1]->bind();
+//      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+      glBegin(GL_QUADS);
+      glTexCoord2f(0.0, 0.0); glVertex2f( i*_width/4,     0);
+      glTexCoord2f(1.0, 0.0); glVertex2f( (i+1)*_width/4, 0);
+      glTexCoord2f(1.0, 1.0); glVertex2f( (i+1)*_width/4, _height/2);
+      glTexCoord2f(0.0, 1.0); glVertex2f( i*_width/4,     _height/2);
+      glEnd();
+    }
+  } else {
+    float c[4];
+    glGetFloatv( GL_CURRENT_COLOR, c );
+    glColor4f( 0.0, 0.0, 0.0, 1.0 );
+    glBegin(GL_QUADS);
+    glVertex2f(0, 0);
+    glVertex2f(_width, 0);
+    glVertex2f(_width, _height);
+    glVertex2f(0, _height);
+    glEnd();
+    glColor4fv( c );
+  }
+
+
+  return initDisplayList;  
+}
+
+void
+FGPanel::update (double dt)
+{
+  glCallList(getInitDisplayList());
+
+  // Draw the instruments.
+  // Syd Adams: added instrument clipping
+  instrument_list_type::const_iterator current = _instruments.begin();
+  instrument_list_type::const_iterator end = _instruments.end();
+
+  GLdouble blx[4]={1.0,0.0,0.0,0.0};
+  GLdouble bly[4]={0.0,1.0,0.0,0.0};
+  GLdouble urx[4]={-1.0,0.0,0.0,0.0};
+  GLdouble ury[4]={0.0,-1.0,0.0,0.0};
+
+  for ( ; current != end; current++) {
+    FGPanelInstrument * instr = *current;
+    glPushMatrix();
+    glTranslated(instr->getXPos(), instr->getYPos(), 0);
+
+    int ix= instr->getWidth();
+    int iy= instr->getHeight();
+    glPushMatrix();
+    glTranslated(-ix/2,-iy/2,0);
+    glClipPlane(GL_CLIP_PLANE0,blx);
+    glClipPlane(GL_CLIP_PLANE1,bly);
+    glEnable(GL_CLIP_PLANE0);
+    glEnable(GL_CLIP_PLANE1);
+
+    glTranslated(ix,iy,0);
+    glClipPlane(GL_CLIP_PLANE2,urx);
+    glClipPlane(GL_CLIP_PLANE3,ury);
+    glEnable(GL_CLIP_PLANE2);
+    glEnable(GL_CLIP_PLANE3);
+    glPopMatrix();
+    instr->draw();
+
+    glPopMatrix();
+  }
+
+  glDisable(GL_CLIP_PLANE0);
+  glDisable(GL_CLIP_PLANE1);
+  glDisable(GL_CLIP_PLANE2);
+  glDisable(GL_CLIP_PLANE3);
+
+  // restore some original state
+  glPopAttrib();
+}
+
+#if 0
+/**
+ * Update the panel.
+ */
+void
+FGPanel::update (double dt)
+{
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  if ( _flipx->getBoolValue() ) {
+    gluOrtho2D( _width, 0, _height, 0 ); /* up side down */
+  } else {
+    gluOrtho2D( 0, _width, 0, _height ); /* right side up */
+  }
+
+  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+  
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  
+  draw();
+}
+
+void FGPanel::draw()
+{
+  glClear( GL_COLOR_BUFFER_BIT);
+
+  // save some state
+  glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT
+                | GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT | GL_CULL_FACE 
+                | GL_DEPTH_BUFFER_BIT );
+
+  // Draw the background
+  glEnable(GL_TEXTURE_2D);
+
+  glDisable(GL_LIGHTING);
+  glEnable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+  glEnable(GL_ALPHA_TEST);
+  glEnable(GL_COLOR_MATERIAL);
+  glEnable(GL_CULL_FACE);
+  glCullFace(GL_BACK);
+  glDisable(GL_DEPTH_TEST);
+
+  if (_bg != NULL) {
+    _bg->bind();
+//    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+    glBegin(GL_QUADS);
+    glTexCoord2f(0.0, 0.0); glVertex2f(0, 0);
+    glTexCoord2f(_bg_width, 0.0); glVertex2f(_width, 0);
+    glTexCoord2f(_bg_width, _bg_height); glVertex2f(_width, _height);
+    glTexCoord2f(0.0, _bg_height); glVertex2f(0, _height);
+    glEnd();
+  } else if( _mbg[0] != NULL ) {
+    for (int i = 0; i < 4; i ++) {
+      // top row of textures...(1,3,5,7)
+      _mbg[i*2]->bind();
+//      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+      glBegin(GL_QUADS);
+      glTexCoord2f(0.0, 0.0); glVertex2f(i*_width/4,     _height/2);
+      glTexCoord2f(1.0, 0.0); glVertex2f((i+1)*_width/4, _height/2);
+      glTexCoord2f(1.0, 1.0); glVertex2f((i+1)*_width/4, _height);
+      glTexCoord2f(0.0, 1.0); glVertex2f(i*_width/4,     _height);
+      glEnd();
+      // bottom row of textures...(2,4,6,8)
+      _mbg[i*2+1]->bind();
+//      glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+      glBegin(GL_QUADS);
+      glTexCoord2f(0.0, 0.0); glVertex2f( i*_width/4,     0);
+      glTexCoord2f(1.0, 0.0); glVertex2f( (i+1)*_width/4, 0);
+      glTexCoord2f(1.0, 1.0); glVertex2f( (i+1)*_width/4, _height/2);
+      glTexCoord2f(0.0, 1.0); glVertex2f( i*_width/4,     _height/2);
+      glEnd();
+    }
+  } else {
+    float c[4];
+    glGetFloatv( GL_CURRENT_COLOR, c );
+    glColor4f( 0.0, 0.0, 0.0, 1.0 );
+    glBegin(GL_QUADS);
+    glVertex2f(0, 0);
+    glVertex2f(_width, 0);
+    glVertex2f(_width, _height);
+    glVertex2f(0, _height);
+    glEnd();
+    glColor4fv( c );
+  }
+
+  // Draw the instruments.
+  // Syd Adams: added instrument clipping
+  instrument_list_type::const_iterator current = _instruments.begin();
+  instrument_list_type::const_iterator end = _instruments.end();
+
+  GLdouble blx[4]={1.0,0.0,0.0,0.0};
+  GLdouble bly[4]={0.0,1.0,0.0,0.0};
+  GLdouble urx[4]={-1.0,0.0,0.0,0.0};
+  GLdouble ury[4]={0.0,-1.0,0.0,0.0};
+
+  for ( ; current != end; current++) {
+    FGPanelInstrument * instr = *current;
+    glPushMatrix();
+    glTranslated(instr->getXPos(), instr->getYPos(), 0);
+
+    int ix= instr->getWidth();
+    int iy= instr->getHeight();
+    glPushMatrix();
+    glTranslated(-ix/2,-iy/2,0);
+    glClipPlane(GL_CLIP_PLANE0,blx);
+    glClipPlane(GL_CLIP_PLANE1,bly);
+    glEnable(GL_CLIP_PLANE0);
+    glEnable(GL_CLIP_PLANE1);
+
+    glTranslated(ix,iy,0);
+    glClipPlane(GL_CLIP_PLANE2,urx);
+    glClipPlane(GL_CLIP_PLANE3,ury);
+    glEnable(GL_CLIP_PLANE2);
+    glEnable(GL_CLIP_PLANE3);
+    glPopMatrix();
+    instr->draw();
+
+    glPopMatrix();
+  }
+
+  glDisable(GL_CLIP_PLANE0);
+  glDisable(GL_CLIP_PLANE1);
+  glDisable(GL_CLIP_PLANE2);
+  glDisable(GL_CLIP_PLANE3);
+
+  // restore some original state
+  glPopAttrib();
+}
+#endif
+
+/**
+ * Set the panel's background texture.
+ */
+void
+FGPanel::setBackground (FGCroppedTexture_ptr texture)
+{
+  _bg = texture;
+}
+
+/**
+ * Set the panel's multiple background textures.
+ */
+void
+FGPanel::setMultiBackground (FGCroppedTexture_ptr texture, int idx)
+{
+  _bg = 0;
+  _mbg[idx] = texture;
+}
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGPanelTransformation.
+////////////////////////////////////////////////////////////////////////
+
+FGPanelTransformation::FGPanelTransformation ()
+  : table(0)
+{
+}
+
+FGPanelTransformation::~FGPanelTransformation ()
+{
+  delete table;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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 ()
+{
+}
+
+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;
+}
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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();
+    _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_ptr 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);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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 (t->has_mod)
+          val = fmod(val, t->mod);
+      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);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGGroupLayer.
+////////////////////////////////////////////////////////////////////////
+
+FGGroupLayer::FGGroupLayer ()
+{
+}
+
+FGGroupLayer::~FGGroupLayer ()
+{
+  for (unsigned int i = 0; i < _layers.size(); i++)
+    delete _layers[i];
+}
+
+void
+FGGroupLayer::draw ()
+{
+  if (test()) {
+    transform();
+    int nLayers = _layers.size();
+    for (int i = 0; i < nLayers; i++)
+      _layers[i]->draw( );
+  }
+}
+
+void
+FGGroupLayer::addLayer (FGInstrumentLayer * layer)
+{
+  _layers.push_back(layer);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGTexturedLayer.
+////////////////////////////////////////////////////////////////////////
+
+
+FGTexturedLayer::FGTexturedLayer (FGCroppedTexture_ptr texture, int w, int h)
+  : FGInstrumentLayer(w, h),
+    _emissive(false),
+    displayList(0)
+{
+  setTexture(texture);
+}
+
+
+FGTexturedLayer::~FGTexturedLayer ()
+{
+}
+
+GLuint
+FGTexturedLayer::getDisplayList()
+{
+  if( displayList != 0 )
+    return displayList;
+
+  int w2 = _w / 2;
+  int h2 = _h / 2;
+
+  _texture->bind( false );
+  displayList = glGenLists(1);
+  glNewList(displayList,GL_COMPILE_AND_EXECUTE);
+    glBindTexture( GL_TEXTURE_2D, _texture->getTexture() );
+    glBegin(GL_QUADS);
+      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();
+  glEndList();
+
+  return displayList;
+}
+
+void
+FGTexturedLayer::draw ( )
+{
+  if (test()) {
+    transform();
+    glCallList(getDisplayList());
+  }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGTextLayer.
+////////////////////////////////////////////////////////////////////////
+
+fntRenderer FGTextLayer::text_renderer;
+
+FGTextLayer::FGTextLayer (int w, int h)
+  : FGInstrumentLayer(w, h), _pointSize(14.0), _font_name("Helvetica.txf")
+{
+  _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()) {
+    float c[4];
+    glGetFloatv( GL_CURRENT_COLOR, c );
+    glColor4fv(_color);
+    transform();
+
+    text_renderer.setFont(ApplicationProperties::fontCache.getTexFont(_font_name.c_str()));
+
+    text_renderer.setPointSize(_pointSize);
+    text_renderer.begin();
+    text_renderer.start3f(0, 0, 0);
+
+    _now.stamp();
+    long diff = (_now - _then).toUSecs();
+
+    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();
+    glColor4fv( c );
+  }
+}
+
+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 + ".txf";
+}
+
+
+void
+FGTextLayer::setFont(fntFont * font)
+{
+  FGTextLayer::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();
+  }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// 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, float offs,
+                           bool truncation)
+  : _type(type), _fmt(fmt), _mult(mult), _offs(offs), _trunc(truncation)
+{
+  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:
+      double d = _offs + _node->getFloatValue() * _mult;
+      if (_trunc)  d = (d < 0) ? -floor(-d) : floor(d);
+      sprintf(_buf, _fmt.c_str(), d);
+      break;
+    }
+    return _buf;
+  } else {
+    return "";
+  }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGSwitchLayer.
+////////////////////////////////////////////////////////////////////////
+
+FGSwitchLayer::FGSwitchLayer ()
+  : FGGroupLayer()
+{
+}
+
+void
+FGSwitchLayer::draw ()
+{
+  if (test()) {
+    transform();
+    int nLayers = _layers.size();
+    for (int i = 0; i < nLayers; i++) {
+      if (_layers[i]->test()) {
+          _layers[i]->draw();
+          return;
+      }
+    }
+  }
+}
+
+\f
+// end of panel.cxx
diff --git a/utils/fgpanel/panel.hxx b/utils/fgpanel/panel.hxx
new file mode 100644 (file)
index 0000000..44ce96a
--- /dev/null
@@ -0,0 +1,451 @@
+//  panel.hxx - generic support classes for a 2D panel.
+//
+//  Written by David Megginson, started January 2000.
+//  Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License as
+//  published by the Free Software Foundation; either version 2 of the
+//  License, or (at your option) any later version.
+// 
+//  This program is distributed in the hope that it will be useful, but
+//  WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//  $Id$
+
+#ifndef __PANEL_HXX
+#define __PANEL_HXX
+
+#ifndef __cplusplus
+# error This library requires C++
+#endif
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <plib/fnt.h>
+
+#include <simgear/props/condition.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/math/interpolater.hxx>
+#include <simgear/sg_inlines.h>
+#include "FGTextureLoaderInterface.hxx"
+
+class FGPanelInstrument;
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Texture management.
+////////////////////////////////////////////////////////////////////////
+
+class FGCroppedTexture;
+typedef SGSharedPtr<FGCroppedTexture> FGCroppedTexture_ptr;
+/**
+ * Cropped texture (should migrate out into FGFS).
+ *
+ * This structure wraps an SSG texture with cropping information.
+ */
+class FGCroppedTexture : public SGReferenced
+{
+public:
+  FGCroppedTexture (const string &path,
+                 float _minX = 0.0, float _minY = 0.0,
+                 float _maxX = 1.0, float _maxY = 1.0);
+
+  virtual ~FGCroppedTexture ();
+
+  virtual void setPath (const string &path) { _path = path; }
+
+  virtual const string &getPath () const { return _path; }
+
+  virtual void setCrop (float minX, float minY, float maxX, float maxY) {
+    _minX = minX; _minY = minY; _maxX = maxX; _maxY = maxY;
+  }
+
+  static void registerTextureLoader( const string & extension, FGTextureLoaderInterface * loader ) {
+    if( textureLoader.count( extension ) == 0 )
+      textureLoader[extension] = loader;
+  }
+
+  virtual float getMinX () const { return _minX; }
+  virtual float getMinY () const { return _minY; }
+  virtual float getMaxX () const { return _maxX; }
+  virtual float getMaxY () const { return _maxY; }
+  GLuint getTexture() const { return _texture; }
+
+  virtual void bind( bool doGLBind = true );
+
+private:
+  string _path;
+  float _minX, _minY, _maxX, _maxY;
+
+  GLuint _texture;
+  static GLuint current_bound_texture;
+  static map<string,GLuint> cache;
+  static map<string,FGTextureLoaderInterface*> textureLoader;
+};
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Top-level panel.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Instrument panel class.
+ *
+ * The panel is a container that has a background texture and holds
+ * zero or more instruments.  The panel will order the instruments to
+ * redraw themselves when necessary, and will pass mouse clicks on to
+ * the appropriate instruments for processing.
+ */
+class FGPanel : public SGSubsystem
+{
+public:
+
+  FGPanel ( SGPropertyNode_ptr root );
+  virtual ~FGPanel ();
+
+                               // Update the panel (every frame).
+  virtual void init ();
+  virtual void bind ();
+  virtual void unbind ();
+//  virtual void draw ();
+  virtual void update (double dt);
+
+                               // transfer pointer ownership!!!
+  virtual void addInstrument (FGPanelInstrument * instrument);
+
+                               // Background texture.
+  virtual void setBackground (FGCroppedTexture_ptr texture);
+  inline void setBackgroundWidth( double d ) {
+    _bg_width = d;
+  }
+
+  inline void setBackgroundHeight( double d ) {
+    _bg_height = d;
+  }
+
+                               // Background multiple textures.
+  virtual void setMultiBackground (FGCroppedTexture_ptr texture , int idx);
+
+                               // Full width of panel.
+  virtual void setWidth (int width) { _width = width; }
+  virtual int getWidth () const { return _width; }
+
+                               // Full height of panel.
+  virtual void setHeight (int height) { _height = height; }
+  virtual int getHeight () const { return _height; }
+
+private:
+
+  typedef vector<FGPanelInstrument *> instrument_list_type;
+  int _width;
+  int _height;
+
+  SGPropertyNode_ptr _root;
+  SGPropertyNode_ptr _flipx;
+  SGPropertyNode_ptr _rotate;
+
+  FGCroppedTexture_ptr _bg;
+  double _bg_width;
+  double _bg_height;
+  FGCroppedTexture_ptr _mbg[8];
+                               // List of instruments in panel.
+  instrument_list_type _instruments;
+
+  GLuint initDisplayList;
+
+  GLuint getInitDisplayList();
+};
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Transformations.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * A transformation for a layer.
+ */
+class FGPanelTransformation : public SGConditional
+{
+public:
+
+  enum Type {
+    XSHIFT,
+    YSHIFT,
+    ROTATION
+  };
+
+  FGPanelTransformation ();
+  virtual ~FGPanelTransformation ();
+
+  Type type;
+  SGConstPropertyNode_ptr node;
+  float min;
+  float max;
+  bool has_mod;
+  float mod;
+  float factor;
+  float offset;
+  SGInterpTable * table;
+};
+
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Layers
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * A single layer of a multi-layered instrument.
+ *
+ * Each layer can be subject to a series of transformations based
+ * on current FGFS instrument readings: for example, a texture
+ * representing a needle can rotate to show the airspeed.
+ */
+class FGInstrumentLayer : public SGConditional
+{
+public:
+
+  FGInstrumentLayer (int w = -1, int h = -1);
+  virtual ~FGInstrumentLayer ();
+
+  virtual void draw () = 0;
+  virtual void transform () const;
+
+  virtual int getWidth () const { return _w; }
+  virtual int getHeight () const { return _h; }
+  virtual void setWidth (int w) { _w = w; }
+  virtual void setHeight (int h) { _h = h; }
+
+                               // Transfer pointer ownership!!
+                               // DEPRECATED
+  virtual void addTransformation (FGPanelTransformation * transformation);
+
+protected:
+  int _w, _h;
+
+  typedef vector<FGPanelTransformation *> transformation_list;
+  transformation_list _transformations;
+};
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Instruments.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Abstract base class for a panel instrument.
+ *
+ * A panel instrument consists of zero or more actions, associated
+ * with mouse clicks in rectangular areas.  Currently, the only
+ * concrete class derived from this is FGLayeredInstrument, but others
+ * may show up in the future (some complex instruments could be 
+ * entirely hand-coded, for example).
+ */
+class FGPanelInstrument : public SGConditional
+{
+public:
+  FGPanelInstrument ();
+  FGPanelInstrument (int x, int y, int w, int h);
+  virtual ~FGPanelInstrument ();
+
+  virtual void draw () = 0;
+
+  virtual void setPosition(int x, int y);
+  virtual void setSize(int w, int h);
+
+  virtual int getXPos () const;
+  virtual int getYPos () const;
+  virtual int getWidth () const;
+  virtual int getHeight () const;
+
+protected:
+  int _x, _y, _w, _h;
+};
+
+
+/**
+ * An instrument constructed of multiple layers.
+ *
+ * Each individual layer can be rotated or shifted to correspond
+ * to internal FGFS instrument readings.
+ */
+class FGLayeredInstrument : public FGPanelInstrument
+{
+public:
+  FGLayeredInstrument (int x, int y, int w, int h);
+  virtual ~FGLayeredInstrument ();
+
+  virtual void draw ();
+
+                               // Transfer pointer ownership!!
+  virtual int addLayer (FGInstrumentLayer *layer);
+  virtual int addLayer (FGCroppedTexture_ptr texture, int w = -1, int h = -1);
+
+                               // Transfer pointer ownership!!
+  virtual void addTransformation (FGPanelTransformation * transformation);
+
+protected:
+  typedef vector<FGInstrumentLayer *> layer_list;
+  layer_list _layers;
+};
+
+
+/**
+ * An instrument layer containing a group of sublayers.
+ *
+ * This class is useful for gathering together a group of related
+ * layers, either to hold in an external file or to work under
+ * the same condition.
+ */
+class FGGroupLayer : public FGInstrumentLayer
+{
+public:
+  FGGroupLayer ();
+  virtual ~FGGroupLayer ();
+  virtual void draw ();
+                               // transfer pointer ownership
+  virtual void addLayer (FGInstrumentLayer * layer);
+protected:
+  vector<FGInstrumentLayer *> _layers;
+};
+
+
+/**
+ * A textured layer of an instrument.
+ *
+ * This is a layer holding a single texture.  Normally, the texture's
+ * backgound should be transparent so that lower layers and the panel
+ * background can show through.
+ */
+class FGTexturedLayer : public FGInstrumentLayer
+{
+public:
+  FGTexturedLayer (int w = -1, int h = -1) : FGInstrumentLayer(w, h) {}
+  FGTexturedLayer (FGCroppedTexture_ptr texture, int w = -1, int h = -1);
+  virtual ~FGTexturedLayer ();
+
+  virtual void draw ();
+
+  virtual void setTexture (FGCroppedTexture_ptr texture) {
+    _texture = texture;
+  }
+  FGCroppedTexture_ptr getTexture() { return _texture; }
+
+  void setEmissive(bool e) { _emissive = e; }
+
+private:
+  GLuint getDisplayList();
+
+  FGCroppedTexture_ptr _texture;
+  bool _emissive;
+  GLuint displayList;
+};
+
+
+/**
+ * A text layer of an instrument.
+ *
+ * This is a layer holding a string of static and/or generated text.
+ * It is useful for instruments that have text displays, such as
+ * a chronometer, GPS, or NavCom radio.
+ */
+class FGTextLayer : public FGInstrumentLayer
+{
+public:
+  enum ChunkType {
+    TEXT,
+    TEXT_VALUE,
+    DOUBLE_VALUE
+  };
+
+  class Chunk : public SGConditional
+  {
+  public:
+    Chunk (const string &text, const string &fmt = "%s");
+    Chunk (ChunkType type, const SGPropertyNode * node,
+          const string &fmt = "", float mult = 1.0, float offs = 0.0,
+           bool truncation = false);
+
+    const char * getValue () const;
+  private:
+    ChunkType _type;
+    string _text;
+    SGConstPropertyNode_ptr _node;
+    string _fmt;
+    float _mult;
+    float _offs;
+    bool _trunc;
+    mutable char _buf[1024];
+    
+  };
+
+  FGTextLayer (int w = -1, int h = -1);
+  virtual ~FGTextLayer ();
+
+  virtual void draw ();
+
+                               // Transfer pointer!!
+  virtual void addChunk (Chunk * chunk);
+  virtual void setColor (float r, float g, float b);
+  virtual void setPointSize (float size);
+  virtual void setFontName ( const string &name );
+  virtual void setFont (fntFont * font);
+
+private:
+
+  void recalc_value () const;
+
+  typedef vector<Chunk *> chunk_list;
+  chunk_list _chunks;
+  float _color[4];
+
+  float _pointSize;
+  mutable string _font_name;
+  mutable string _value;
+  mutable SGTimeStamp _then;
+  mutable SGTimeStamp _now;
+
+  static fntRenderer text_renderer;
+};
+
+
+/**
+ * A group layer that switches among its children.
+ *
+ * The first layer that passes its condition will be drawn, and
+ * any following layers will be ignored.
+ */
+class FGSwitchLayer : public FGGroupLayer
+{
+public:
+                               // Transfer pointers!!
+  FGSwitchLayer ();
+  virtual void draw ();
+
+};
+
+\f
+#endif // __PANEL_HXX
+
+// end of panel.hxx
+
+
+
diff --git a/utils/fgpanel/panel_io.cxx b/utils/fgpanel/panel_io.cxx
new file mode 100644 (file)
index 0000000..9f5084c
--- /dev/null
@@ -0,0 +1,591 @@
+//  panel_io.cxx - I/O for 2D 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//  $Id: panel_io.cxx,v 1.26 2006/08/10 11:12:39 mfranz Exp $
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#  include <windows.h>
+#endif
+
+#include <string.h>            // for strcmp()
+
+#include <simgear/compiler.h>
+#include <simgear/structure/exception.hxx>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/props/condition.hxx>
+
+#include "panel.hxx"
+#include "panel_io.hxx"
+#include "ApplicationProperties.hxx"
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Read and construct a panel.
+//
+// The panel is specified as a regular property list, and each of the
+// instruments is its own, separate property list (and thus, a separate
+// XML document).  The functions in this section read in the files
+// as property lists, then extract properties to set up the panel
+// itself.
+//
+// A panel contains zero or more instruments.
+//
+// An instrument contains one or more layers and zero or more actions.
+//
+// A layer contains zero or more transformations.
+//
+// Some special types of layers also contain other objects, such as 
+// chunks of text or other layers.
+//
+// There are currently four types of layers:
+//
+// 1. Textured Layer (type="texture"), the default
+// 2. Text Layer (type="text")
+// 3. Switch Layer (type="switch")
+// 4. Built-in Layer (type="built-in", must also specify class)
+//
+// The only built-in layer so far is the ribbon for the magnetic compass
+// (class="compass-ribbon").
+//
+// There are three types of actions:
+//
+// 1. Adjust (type="adjust"), the default
+// 2. Swap (type="swap")
+// 3. Toggle (type="toggle")
+//
+// There are three types of transformations:
+//
+// 1. X shift (type="x-shift"), the default
+// 2. Y shift (type="y-shift")
+// 3. Rotation (type="rotation")
+//
+// Each of these may be associated with a property, so that a needle
+// will rotate with the airspeed, for example, or may have a fixed
+// floating-point value.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Read a cropped texture from the instrument's property list.
+ *
+ * The x1 and y1 properties give the starting position of the texture
+ * (between 0.0 and 1.0), and the the x2 and y2 properties give the
+ * ending position.  For example, to use the bottom-left quarter of a
+ * texture, x1=0.0, y1=0.0, x2=0.5, y2=0.5.
+ */
+static FGCroppedTexture_ptr
+readTexture (const SGPropertyNode * node)
+{
+    return new FGCroppedTexture(node->getStringValue("path"),
+                            node->getFloatValue("x1"),
+                            node->getFloatValue("y1"),
+                            node->getFloatValue("x2", 1.0),
+                            node->getFloatValue("y2", 1.0));
+    SG_LOG(SG_COCKPIT, SG_DEBUG, "Read texture " << node->getName());
+}
+
+/**
+ * Test for a condition in the current node.
+ */
+\f
+////////////////////////////////////////////////////////////////////////
+// Read a condition and use it if necessary.
+////////////////////////////////////////////////////////////////////////
+
+static void
+readConditions (SGConditional *component, const SGPropertyNode *node)
+{
+  const SGPropertyNode * conditionNode = node->getChild("condition");
+  if (conditionNode != 0)
+                               // The top level is implicitly AND
+    component->setCondition(sgReadCondition(ApplicationProperties::Properties,
+                                            conditionNode) );
+  ;
+}
+
+
+/**
+ * Read a transformation from the instrument's property list.
+ *
+ * The panel module uses the transformations to slide or spin needles,
+ * knobs, and other indicators, and to place layers in the correct
+ * positions.  Every layer starts centered exactly on the x,y co-ordinate,
+ * and many layers need to be moved or rotated simply to display the
+ * instrument correctly.
+ *
+ * There are three types of transformations:
+ *
+ * "x-shift" - move the layer horizontally.
+ *
+ * "y-shift" - move the layer vertically.
+ *
+ * "rotation" - rotate the layer.
+ *
+ * Each transformation may have a fixed offset, and may also have
+ * a floating-point property value to add to the offset.  The
+ * floating-point property may be clamped to a minimum and/or
+ * maximum range and scaled (after clamping).
+ *
+ * Note that because of the way OpenGL works, transformations will
+ * appear to be applied backwards.
+ */
+static FGPanelTransformation *
+readTransformation (const SGPropertyNode * node, float w_scale, float h_scale)
+{
+  FGPanelTransformation * t = new FGPanelTransformation;
+
+  string name = node->getName();
+  string type = node->getStringValue("type");
+  string propName = node->getStringValue("property", "");
+  const SGPropertyNode * target = 0;
+
+  if (type.empty()) {
+    SG_LOG( SG_COCKPIT, SG_INFO,
+            "No type supplied for transformation " << name
+            << " assuming \"rotation\"" );
+    type = "rotation";
+  }
+
+  if (!propName.empty())
+    target = ApplicationProperties::Properties->getNode(propName.c_str(), true);
+
+  t->node = target;
+  t->min = node->getFloatValue("min", -9999999);
+  t->max = node->getFloatValue("max", 99999999);
+  t->has_mod = node->hasChild("modulator");
+  if (t->has_mod)
+      t->mod = node->getFloatValue("modulator");
+  t->factor = node->getFloatValue("scale", 1.0);
+  t->offset = node->getFloatValue("offset", 0.0);
+
+
+                               // Check for an interpolation table
+  const SGPropertyNode * trans_table = node->getNode("interpolation");
+  if (trans_table != 0) {
+    SG_LOG( SG_COCKPIT, SG_INFO, "Found interpolation table with "
+            << trans_table->nChildren() << " children" );
+    t->table = new SGInterpTable();
+    for (int i = 0; i < trans_table->nChildren(); i++) {
+      const SGPropertyNode * node = trans_table->getChild(i);
+      if (!strcmp(node->getName(), "entry")) {
+       double ind = node->getDoubleValue("ind", 0.0);
+       double dep = node->getDoubleValue("dep", 0.0);
+       SG_LOG( SG_COCKPIT, SG_INFO, "Adding interpolation entry "
+                << ind << "==>" << dep );
+       t->table->addEntry(ind, dep);
+      } else {
+       SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName()
+                << " in interpolation" );
+      }
+    }
+  } else {
+    t->table = 0;
+  }
+  
+                               // Move the layer horizontally.
+  if (type == "x-shift") {
+    t->type = FGPanelTransformation::XSHIFT;
+//     t->min *= w_scale; //removed by Martin Dressler
+//     t->max *= w_scale; //removed by Martin Dressler
+    t->offset *= w_scale;
+    t->factor *= w_scale; //Added by Martin Dressler
+  } 
+
+                               // Move the layer vertically.
+  else if (type == "y-shift") {
+    t->type = FGPanelTransformation::YSHIFT;
+    //t->min *= h_scale; //removed
+    //t->max *= h_scale; //removed
+    t->offset *= h_scale;
+    t->factor *= h_scale; //Added
+  } 
+
+                               // Rotate the layer.  The rotation
+                               // is in degrees, and does not need
+                               // to scale with the instrument size.
+  else if (type == "rotation") {
+    t->type = FGPanelTransformation::ROTATION;
+  } 
+
+  else {
+    SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized transformation type " << type );
+    delete t;
+    return 0;
+  }
+
+  readConditions(t, node);
+  SG_LOG( SG_COCKPIT, SG_DEBUG, "Read transformation " << name );
+  return t;
+}
+
+
+/**
+ * Read a chunk of text from the instrument's property list.
+ *
+ * A text layer consists of one or more chunks of text.  All chunks
+ * share the same font size and color (and eventually, font), but
+ * each can come from a different source.  There are three types of
+ * text chunks:
+ *
+ * "literal" - a literal text string (the default)
+ *
+ * "text-value" - the current value of a string property
+ *
+ * "number-value" - the current value of a floating-point property.
+ *
+ * All three may also include a printf-style format string.
+ */
+FGTextLayer::Chunk *
+readTextChunk (const SGPropertyNode * node)
+{
+  FGTextLayer::Chunk * chunk;
+  string name = node->getStringValue("name");
+  string type = node->getStringValue("type");
+  string format = node->getStringValue("format");
+
+                               // Default to literal text.
+  if (type.empty()) {
+    SG_LOG( SG_COCKPIT, SG_INFO, "No type provided for text chunk " << name
+            << " assuming \"literal\"");
+    type = "literal";
+  }
+
+                               // A literal text string.
+  if (type == "literal") {
+    string text = node->getStringValue("text");
+    chunk = new FGTextLayer::Chunk(text, format);
+  }
+
+                               // The value of a string property.
+  else if (type == "text-value") {
+    SGPropertyNode * target =
+      ApplicationProperties::Properties->getNode( node->getStringValue("property"), true);
+    chunk = new FGTextLayer::Chunk(FGTextLayer::TEXT_VALUE, target, format);
+  }
+
+                               // The value of a float property.
+  else if (type == "number-value") {
+    string propName = node->getStringValue("property");
+    float scale = node->getFloatValue("scale", 1.0);
+    float offset = node->getFloatValue("offset", 0.0);
+    bool truncation = node->getBoolValue("truncate", false);
+    SGPropertyNode * target = ApplicationProperties::Properties->getNode(propName.c_str(), true);
+    chunk = new FGTextLayer::Chunk(FGTextLayer::DOUBLE_VALUE, target,
+                                  format, scale, offset, truncation);
+  }
+
+                               // Unknown type.
+  else {
+    SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized type " << type
+            << " for text chunk " << name );
+    return 0;
+  }
+
+  readConditions(chunk, node);
+  return chunk;
+}
+
+
+/**
+ * Read a single layer from an instrument's property list.
+ *
+ * Each instrument consists of one or more layers stacked on top
+ * of each other; the lower layers show through only where the upper
+ * layers contain an alpha component.  Each layer can be moved
+ * horizontally and vertically and rotated using transformations.
+ *
+ * This module currently recognizes four kinds of layers:
+ *
+ * "texture" - a layer containing a texture (the default)
+ *
+ * "text" - a layer containing text
+ *
+ * "switch" - a layer that switches between two other layers
+ *   based on the current value of a boolean property.
+ *
+ * "built-in" - a hard-coded layer supported by C++ code in FlightGear.
+ *
+ * Currently, the only built-in layer class is "compass-ribbon".
+ */
+static FGInstrumentLayer *
+readLayer (const SGPropertyNode * node, float w_scale, float h_scale)
+{
+  FGInstrumentLayer * layer = NULL;
+  string name = node->getStringValue("name");
+  string type = node->getStringValue("type");
+  int w = node->getIntValue("w", -1);
+  int h = node->getIntValue("h", -1);
+  bool emissive = node->getBoolValue("emissive", false);
+  if (w != -1)
+    w = int(w * w_scale);
+  if (h != -1)
+    h = int(h * h_scale);
+
+
+  if (type.empty()) {
+    SG_LOG( SG_COCKPIT, SG_INFO,
+            "No type supplied for layer " << name
+            << " assuming \"texture\"" );
+    type = "texture";
+  }
+
+
+                               // A textured instrument layer.
+  if (type == "texture") {
+    FGCroppedTexture_ptr texture = readTexture(node->getNode("texture"));
+    layer = new FGTexturedLayer(texture, w, h);
+    if (emissive) {
+      FGTexturedLayer *tl=(FGTexturedLayer*)layer;
+      tl->setEmissive(true);
+    }
+
+  }
+                               // A group of sublayers.
+  else if (type == "group") {
+    layer = new FGGroupLayer();
+    for (int i = 0; i < node->nChildren(); i++) {
+      const SGPropertyNode * child = node->getChild(i);
+      if (!strcmp(child->getName(), "layer"))
+       ((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale));
+    }
+  }
+
+
+                               // A textual instrument layer.
+  else if (type == "text") {
+    FGTextLayer * tlayer = new FGTextLayer(w, h); // FIXME
+
+                               // Set the text color.
+    float red = node->getFloatValue("color/red", 0.0);
+    float green = node->getFloatValue("color/green", 0.0);
+    float blue = node->getFloatValue("color/blue", 0.0);
+    tlayer->setColor(red, green, blue);
+
+                               // Set the point size.
+    float pointSize = node->getFloatValue("point-size", 10.0) * w_scale;
+    tlayer->setPointSize(pointSize);
+
+                               // Set the font.
+    string fontName = node->getStringValue("font", "Helvetica");
+    tlayer->setFontName(fontName);
+
+    const SGPropertyNode * chunk_group = node->getNode("chunks");
+    if (chunk_group != 0) {
+      int nChunks = chunk_group->nChildren();
+      for (int i = 0; i < nChunks; i++) {
+       const SGPropertyNode * node = chunk_group->getChild(i);
+       if (!strcmp(node->getName(), "chunk")) {
+         FGTextLayer::Chunk * chunk = readTextChunk(node);
+         if (chunk != 0)
+           tlayer->addChunk(chunk);
+       } else {
+         SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName()
+                  << " in chunks" );
+       }
+      }
+      layer = tlayer;
+    }
+  }
+
+                               // A switch instrument layer.
+  else if (type == "switch") {
+    layer = new FGSwitchLayer();
+    for (int i = 0; i < node->nChildren(); i++) {
+      const SGPropertyNode * child = node->getChild(i);
+      if (!strcmp(child->getName(), "layer"))
+       ((FGGroupLayer *)layer)->addLayer(readLayer(child, w_scale, h_scale));
+    }
+  }
+
+                               // An unknown type.
+  else {
+    SG_LOG( SG_COCKPIT, SG_ALERT, "Unrecognized layer type " << type );
+    delete layer;
+    return 0;
+  }
+  
+  //
+  // Get the transformations for each layer.
+  //
+  const SGPropertyNode * trans_group = node->getNode("transformations");
+  if (trans_group != 0) {
+    int nTransformations = trans_group->nChildren();
+    for (int i = 0; i < nTransformations; i++) {
+      const SGPropertyNode * node = trans_group->getChild(i);
+      if (!strcmp(node->getName(), "transformation")) {
+       FGPanelTransformation * t = readTransformation(node, w_scale, h_scale);
+       if (t != 0)
+         layer->addTransformation(t);
+      } else {
+       SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName()
+                << " in transformations" );
+      }
+    }
+  }
+
+  readConditions(layer, node);
+  SG_LOG( SG_COCKPIT, SG_DEBUG, "Read layer " << name );
+  return layer;
+}
+
+
+/**
+ * Read an instrument from a property list.
+ *
+ * The instrument consists of a preferred width and height
+ * (the panel may override these), together with a list of layers
+ * and a list of actions to be performed when the user clicks 
+ * the mouse over the instrument.  All co-ordinates are relative
+ * to the instrument's position, so instruments are fully relocatable;
+ * likewise, co-ordinates for actions and transformations will be
+ * scaled automatically if the instrument is not at its preferred size.
+ */
+static FGPanelInstrument *
+readInstrument (const SGPropertyNode * node)
+{
+  const string name = node->getStringValue("name");
+  int x = node->getIntValue("x", -1);
+  int y = node->getIntValue("y", -1);
+  int real_w = node->getIntValue("w", -1);
+  int real_h = node->getIntValue("h", -1);
+  int w = node->getIntValue("w-base", -1);
+  int h = node->getIntValue("h-base", -1);
+
+  if (x == -1 || y == -1) {
+    SG_LOG( SG_COCKPIT, SG_ALERT,
+            "x and y positions must be specified and > 0" );
+    return 0;
+  }
+
+  float w_scale = 1.0;
+  float h_scale = 1.0;
+  if (real_w != -1) {
+    w_scale = float(real_w) / float(w);
+    w = real_w;
+  }
+  if (real_h != -1) {
+    h_scale = float(real_h) / float(h);
+    h = real_h;
+  }
+
+  SG_LOG( SG_COCKPIT, SG_DEBUG, "Reading instrument " << name );
+
+  FGLayeredInstrument * instrument =
+    new FGLayeredInstrument(x, y, w, h);
+
+  //
+  // Get the layers for the instrument.
+  //
+  const SGPropertyNode * layer_group = node->getNode("layers");
+  if (layer_group != 0) {
+    int nLayers = layer_group->nChildren();
+    for (int i = 0; i < nLayers; i++) {
+      const SGPropertyNode * node = layer_group->getChild(i);
+      if (!strcmp(node->getName(), "layer")) {
+       FGInstrumentLayer * layer = readLayer(node, w_scale, h_scale);
+       if (layer != 0)
+         instrument->addLayer(layer);
+      } else {
+       SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName()
+                << " in layers" );
+      }
+    }
+  }
+
+  readConditions(instrument, node);
+  SG_LOG( SG_COCKPIT, SG_DEBUG, "Done reading instrument " << name );
+  return instrument;
+}
+
+
+/**
+ * Construct the panel from a property tree.
+ */
+SGSharedPtr<FGPanel>
+FGReadablePanel::read(SGPropertyNode_ptr root)
+{
+  SG_LOG( SG_COCKPIT, SG_INFO, "Reading properties for panel " <<
+          root->getStringValue("name", "[Unnamed Panel]") );
+
+  FGPanel * panel = new FGPanel(root);
+  panel->setWidth(root->getIntValue("w", 1024));
+  panel->setHeight(root->getIntValue("h", 443));
+
+  SG_LOG( SG_COCKPIT, SG_INFO, "Size=" << panel->getWidth() << "x" << panel->getHeight() );
+
+  // Assign the background texture, if any, or a bogus chequerboard.
+  //
+  string bgTexture = root->getStringValue("background");
+  if( !bgTexture.empty() )
+    panel->setBackground( new FGCroppedTexture( bgTexture ) );
+  panel->setBackgroundWidth( root->getDoubleValue( "background-width", 1.0 ) );
+  panel->setBackgroundHeight( root->getDoubleValue( "background-height", 1.0 ) );
+  SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << bgTexture );
+
+  //
+  // Get multibackground if any...
+  //
+  for( int i = 0; i < 8; i++ ) {
+    SGPropertyNode * mbgNode = root->getChild( "multibackground", i );
+    string mbgTexture;
+    if( mbgNode != NULL ) mbgTexture = mbgNode->getStringValue();
+    if( mbgTexture.empty() ) {
+      if( i == 0 ) break; // if first texture is missing, ignore the rest
+      else mbgTexture = "FOO"; // if others are missing - set default texture
+    }
+    panel->setMultiBackground( new FGCroppedTexture(mbgTexture), i );
+    SG_LOG( SG_COCKPIT, SG_INFO, "Set multi-background texture" << i << " to " << mbgTexture );
+  }
+  //
+  // Create each instrument.
+  //
+  SG_LOG( SG_COCKPIT, SG_INFO, "Reading panel instruments" );
+  const SGPropertyNode * instrument_group = root->getChild("instruments");
+  if (instrument_group != 0) {
+    int nInstruments = instrument_group->nChildren();
+    for (int i = 0; i < nInstruments; i++) {
+      const SGPropertyNode * node = instrument_group->getChild(i);
+      if (!strcmp(node->getName(), "instrument")) {
+        FGPanelInstrument * instrument = readInstrument(node);
+        if (instrument != 0)
+          panel->addInstrument(instrument);
+      } else {
+        SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName()
+        << " in instruments section" );
+      }
+    }
+  }
+  SG_LOG( SG_COCKPIT, SG_INFO, "Done reading panel instruments" );
+
+
+  //
+  // Return the new panel.
+  //
+  return panel;
+}
+
+// end of panel_io.cxx
+
+
+
diff --git a/utils/fgpanel/panel_io.hxx b/utils/fgpanel/panel_io.hxx
new file mode 100644 (file)
index 0000000..e353ce0
--- /dev/null
@@ -0,0 +1,40 @@
+//  panel_io.cxx - I/O for 2D 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+//  $Id: panel_io.hxx,v 1.6 2006/04/17 13:03:43 mfranz Exp $
+
+#ifndef __PANEL_IO_HXX
+#define __PANEL_IO_HXX
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#  include <windows.h>
+#endif
+
+#include "panel.hxx"
+
+class FGReadablePanel : public FGPanel {
+public:
+  static SGSharedPtr<FGPanel> read(SGPropertyNode_ptr root);
+};
+
+
+#endif // __PANEL_IO_HXX