--- /dev/null
+fgpanel
+*.o
+*.obj
+.deps
+*.Po
+Makefile
+Makefile.in
--- /dev/null
+//
+// 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
--- /dev/null
+//
+// 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
+
--- /dev/null
+// 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
--- /dev/null
+//
+// 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();
+}
--- /dev/null
+//
+// 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
--- /dev/null
+//
+// 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;
+}
--- /dev/null
+// 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
--- /dev/null
+//
+// 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;
--- /dev/null
+//
+// 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
--- /dev/null
+//
+// 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();
+}
--- /dev/null
+//
+// 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
--- /dev/null
+//
+// 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;
+}
+
--- /dev/null
+// 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
--- /dev/null
+//
+// 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
--- /dev/null
+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
--- /dev/null
+=====================================================================
+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>
+
+=====================================================================
--- /dev/null
+//
+// 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;
+ }
+}
--- /dev/null
+// 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
--- /dev/null
+// 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
+
+
+
--- /dev/null
+// 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
+
+
+
--- /dev/null
+// 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