From f44dd244624adce06601fbb916fc792334a3fe52 Mon Sep 17 00:00:00 2001 From: Torsten Dreyer Date: Thu, 14 Apr 2011 11:11:17 +0200 Subject: [PATCH] Initial commit of the fgpanel code fgpanel is basically the stripped down 2D-Panel code from FlightGear. It is designed as a standalone lightweight panel rendering engine to draw 2d panels on a lowcost computer/graphic card without 3d acceleration at reasonablel framerates. Patches for inclusion into the build system will follow. --- utils/fgpanel/.gitignore | 7 + utils/fgpanel/ApplicationProperties.hxx | 31 + utils/fgpanel/FGFontCache.cxx | 208 +++++ utils/fgpanel/FGFontCache.hxx | 86 ++ utils/fgpanel/FGGLApplication.cxx | 94 ++ utils/fgpanel/FGGLApplication.hxx | 48 + utils/fgpanel/FGPNGTextureLoader.cxx | 142 +++ utils/fgpanel/FGPNGTextureLoader.hxx | 26 + utils/fgpanel/FGPanelApplication.cxx | 279 ++++++ utils/fgpanel/FGPanelApplication.hxx | 55 ++ utils/fgpanel/FGPanelProtocol.cxx | 154 ++++ utils/fgpanel/FGPanelProtocol.hxx | 41 + utils/fgpanel/FGRGBTextureLoader.cxx | 504 +++++++++++ utils/fgpanel/FGRGBTextureLoader.hxx | 26 + utils/fgpanel/FGTextureLoaderInterface.hxx | 27 + utils/fgpanel/Makefile.am | 20 + utils/fgpanel/README | 148 ++++ utils/fgpanel/main.cxx | 30 + utils/fgpanel/panel.cxx | 962 +++++++++++++++++++++ utils/fgpanel/panel.hxx | 451 ++++++++++ utils/fgpanel/panel_io.cxx | 591 +++++++++++++ utils/fgpanel/panel_io.hxx | 40 + 22 files changed, 3970 insertions(+) create mode 100644 utils/fgpanel/.gitignore create mode 100644 utils/fgpanel/ApplicationProperties.hxx create mode 100644 utils/fgpanel/FGFontCache.cxx create mode 100644 utils/fgpanel/FGFontCache.hxx create mode 100644 utils/fgpanel/FGGLApplication.cxx create mode 100644 utils/fgpanel/FGGLApplication.hxx create mode 100644 utils/fgpanel/FGPNGTextureLoader.cxx create mode 100644 utils/fgpanel/FGPNGTextureLoader.hxx create mode 100644 utils/fgpanel/FGPanelApplication.cxx create mode 100644 utils/fgpanel/FGPanelApplication.hxx create mode 100644 utils/fgpanel/FGPanelProtocol.cxx create mode 100644 utils/fgpanel/FGPanelProtocol.hxx create mode 100644 utils/fgpanel/FGRGBTextureLoader.cxx create mode 100644 utils/fgpanel/FGRGBTextureLoader.hxx create mode 100644 utils/fgpanel/FGTextureLoaderInterface.hxx create mode 100644 utils/fgpanel/Makefile.am create mode 100644 utils/fgpanel/README create mode 100644 utils/fgpanel/main.cxx create mode 100644 utils/fgpanel/panel.cxx create mode 100644 utils/fgpanel/panel.hxx create mode 100644 utils/fgpanel/panel_io.cxx create mode 100644 utils/fgpanel/panel_io.hxx diff --git a/utils/fgpanel/.gitignore b/utils/fgpanel/.gitignore new file mode 100644 index 000000000..fd82bd006 --- /dev/null +++ b/utils/fgpanel/.gitignore @@ -0,0 +1,7 @@ +fgpanel +*.o +*.obj +.deps +*.Po +Makefile +Makefile.in diff --git a/utils/fgpanel/ApplicationProperties.hxx b/utils/fgpanel/ApplicationProperties.hxx new file mode 100644 index 000000000..f5578feb2 --- /dev/null +++ b/utils/fgpanel/ApplicationProperties.hxx @@ -0,0 +1,31 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __APPLICATION_PROPERTIES +#define __APPLICATION_PROPERTIES +#include +#include +#include "FGFontCache.hxx" +class ApplicationProperties { +public: + static double getDouble( const char * name, double def = 0.0 ); + static SGPath GetRootPath( const char * subDir = NULL ); + static SGPropertyNode_ptr Properties; + static std::string root; + static FGFontCache fontCache; +}; +#endif diff --git a/utils/fgpanel/FGFontCache.cxx b/utils/fgpanel/FGFontCache.cxx new file mode 100644 index 000000000..129e24743 --- /dev/null +++ b/utils/fgpanel/FGFontCache.cxx @@ -0,0 +1,208 @@ +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifdef HAVE_CONFIG_H +# include +#endif + +using namespace std; +#include +#include +#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 + { + 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(f->texfont), size, slant); + } else { + f->pufont = guifonts[0].font; + } + _puFonts[fntParams] = f; + return f; +} + +puFont * +FGFontCache::get(const char *name, float size, float slant) +{ + return getfnt(name, size, slant)->pufont; +} + +fntTexFont * +FGFontCache::getTexFont(const char *name, float size, float slant) +{ + init(); + return getfnt(name, size, slant)->texfont; +} + +puFont * +FGFontCache::get(SGPropertyNode *node) +{ + if (!node) + return get("Helvetica.txf", 15.0, 0.0); + + const char *name = node->getStringValue("name", "Helvetica.txf"); + float size = node->getFloatValue("size", 15.0); + float slant = node->getFloatValue("slant", 0.0); + + return get(name, size, slant); +} + +void FGFontCache::init() +{ + if (!_initialized) { + char *envp = ::getenv("FG_FONTS"); + if (envp != NULL) { + _path.set(envp); + } else { + _path.set(ApplicationProperties::GetRootPath("Fonts").str()); + } + _initialized = true; + } +} + +SGPath +FGFontCache::getfntpath(const char *name) +{ + init(); + SGPath path(_path); + if (name && std::string(name) != "") { + path.append(name); + if (path.exists()) + return path; + } + + path = SGPath(_path); + path.append("Helvetica.txf"); + + return path; +} + +bool FGFontCache::initializeFonts() +{ + static string fontext("txf"); + init(); + ulDir* fontdir = ulOpenDir(_path.c_str()); + if (!fontdir) + return false; + const ulDirEnt *dirEntry; + while ((dirEntry = ulReadDir(fontdir)) != 0) { + SGPath path(_path); + path.append(dirEntry->d_name); + if (path.extension() == fontext) { + fntTexFont* f = new fntTexFont; + if (f->load((char *)path.c_str())) + _texFonts[string(dirEntry->d_name)] = f; + else + delete f; + } + } + ulCloseDir(fontdir); + return true; +} + +// end of new_gui.cxx + diff --git a/utils/fgpanel/FGFontCache.hxx b/utils/fgpanel/FGFontCache.hxx new file mode 100644 index 000000000..81202eee4 --- /dev/null +++ b/utils/fgpanel/FGFontCache.hxx @@ -0,0 +1,86 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGFONTCACHE_HXX +#define __FGFONTCACHE_HXX +#include +#include +#include +/** + * 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 + { + 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 TexFontMap; + typedef map PuFontMap; + TexFontMap _texFonts; + PuFontMap _puFonts; + + bool _initialized; + struct fnt *getfnt(const char *name, float size, float slant); + void init(); + +public: + FGFontCache(); + ~FGFontCache(); + + puFont *get(const char *name, float size=15.0, float slant=0.0); + puFont *get(SGPropertyNode *node); + + fntTexFont *getTexFont(const char *name, float size=15.0, float slant=0.0); + + SGPath getfntpath(const char *name); + /** + * Preload all the fonts in the FlightGear font directory. It is + * important to load the font textures early, with the proper + * graphics context current, so that no plib (or our own) code + * tries to load a font from disk when there's no current graphics + * context. + */ + bool initializeFonts(); +}; +#endif diff --git a/utils/fgpanel/FGGLApplication.cxx b/utils/fgpanel/FGGLApplication.cxx new file mode 100644 index 000000000..5c52943b2 --- /dev/null +++ b/utils/fgpanel/FGGLApplication.cxx @@ -0,0 +1,94 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGGLApplication.hxx" +#include "GL/gl.h" +#include "GL/glut.h" + +#include +#include +#include + +FGGLApplication * FGGLApplication::application = NULL; + +FGGLApplication::FGGLApplication( const char * aName, int argc, char ** argv ) : + gameMode(false), + name( aName ) +{ + if( application != NULL ) { + std::cerr << "Only one instance of FGGLApplication allowed!" << std::endl; + throw std::exception(); + } + application = this; + + glutInit( &argc, argv ); +} + +FGGLApplication::~FGGLApplication() +{ +} + +void FGGLApplication::DisplayCallback() +{ + if( application ) application->Display(); +} + +void FGGLApplication::IdleCallback() +{ + if( application ) application->Idle(); +} + +void FGGLApplication::KeyCallback( unsigned char key, int x, int y ) +{ + if( application ) application->Key( key, x, y ); +} + +void FGGLApplication::ReshapeCallback( int width, int height ) +{ + if( application ) application->Reshape( width, height ); +} + +void FGGLApplication::Run( int glutMode, bool gameMode, int width, int height, int bpp ) +{ + glutInitDisplayMode(glutMode); + if( gameMode ) { + width = glutGet(GLUT_SCREEN_WIDTH); + height = glutGet(GLUT_SCREEN_HEIGHT); + char game_mode_str[20]; + snprintf(game_mode_str, 20, "%dx%d:%d", width, height, bpp ); + glutGameModeString( game_mode_str ); + glutEnterGameMode(); + this->gameMode = gameMode; + } else { + if( width == -1 ) + width = glutGet(GLUT_SCREEN_WIDTH); + + if( height == -1 ) + height = glutGet(GLUT_SCREEN_HEIGHT); + + glutInitDisplayMode(glutMode); +// glutInitWindowSize(width, height); + windowId = glutCreateWindow(name); + } + Init(); + + glutKeyboardFunc(FGGLApplication::KeyCallback); + glutIdleFunc(FGGLApplication::IdleCallback); + glutDisplayFunc(FGGLApplication::DisplayCallback); + glutReshapeFunc(FGGLApplication::ReshapeCallback); + glutMainLoop(); +} diff --git a/utils/fgpanel/FGGLApplication.hxx b/utils/fgpanel/FGGLApplication.hxx new file mode 100644 index 000000000..aba699fa8 --- /dev/null +++ b/utils/fgpanel/FGGLApplication.hxx @@ -0,0 +1,48 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGGLAPPLICATION_HXX +#define __FGGLAPPLICATION_HXX + +class FGGLApplication { +public: + FGGLApplication( const char * name, int argc, char ** argv ); + virtual ~FGGLApplication(); + void Run( int glutMode, bool gameMode, int widht=-1, int height=-1, int bpp = 32 ); +protected: + virtual void Key( unsigned char key, int x, int y ) {} + virtual void Idle() {} + virtual void Display() {} + virtual void Reshape( int width, int height ) {} + + virtual void Init() {} + + int windowId; + bool gameMode; + + const char * name; + + static FGGLApplication * application; +private: + static void KeyCallback( unsigned char key, int x, int y ); + static void IdleCallback(); + static void DisplayCallback(); + static void ReshapeCallback( int width, int height ); + +}; + +#endif diff --git a/utils/fgpanel/FGPNGTextureLoader.cxx b/utils/fgpanel/FGPNGTextureLoader.cxx new file mode 100644 index 000000000..236792b57 --- /dev/null +++ b/utils/fgpanel/FGPNGTextureLoader.cxx @@ -0,0 +1,142 @@ +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPNGTextureLoader.hxx" + +#include +#include +#include +#include + +#include +using namespace std; +GLuint FGPNGTextureLoader::loadTexture( const string & filename ) +{ + //header for testing if it is a png + png_byte header[8]; + + //open file as binary + FILE *fp = fopen(filename.c_str(), "rb"); + if (!fp) { + return NOTEXTURE; + } + + //read the header + fread(header, 1, 8, fp); + + //test if png + int is_png = !png_sig_cmp(header, 0, 8); + if (!is_png) { + fclose(fp); + return NOTEXTURE; + } + + //create png struct + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (!png_ptr) { + fclose(fp); + return (NOTEXTURE); + } + + //create png info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); + fclose(fp); + return (NOTEXTURE); + } + + //create png info struct + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + fclose(fp); + return (NOTEXTURE); + } + + //png error stuff, not sure libpng man suggests this. + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return (NOTEXTURE); + } + + //init png reading + png_init_io(png_ptr, fp); + + //let libpng know you already read the first 8 bytes + png_set_sig_bytes(png_ptr, 8); + + // read all the info up to the image data + png_read_info(png_ptr, info_ptr); + + //variables to pass to get info + int bit_depth, color_type; + png_uint_32 twidth, theight; + + // get info about png + png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type, + NULL, NULL, NULL); + + // Update the png info struct. + png_read_update_info(png_ptr, info_ptr); + + // Row size in bytes. + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + // Allocate the image_data as a big block, to be given to opengl + png_byte *image_data = new png_byte[rowbytes * theight]; + if (!image_data) { + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return NOTEXTURE; + } + + //row_pointers is for pointing to image_data for reading the png with libpng + png_bytep *row_pointers = new png_bytep[theight]; + if (!row_pointers) { + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete[] image_data; + fclose(fp); + return NOTEXTURE; + } + // set the individual row_pointers to point at the correct offsets of image_data + for (png_uint_32 i = 0; i < theight; ++i) + row_pointers[theight - 1 - i] = image_data + i * rowbytes; + + //read the png into image_data through row_pointers + png_read_image(png_ptr, row_pointers); + + //Now generate the OpenGL texture object + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + gluBuild2DMipmaps( GL_TEXTURE_2D, 4, twidth, theight, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)image_data ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR ); +// glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, twidth, theight, 0, +// GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) image_data); +// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete[] image_data; + delete[] row_pointers; + fclose(fp); + return texture; +} diff --git a/utils/fgpanel/FGPNGTextureLoader.hxx b/utils/fgpanel/FGPNGTextureLoader.hxx new file mode 100644 index 000000000..e8bbfa48c --- /dev/null +++ b/utils/fgpanel/FGPNGTextureLoader.hxx @@ -0,0 +1,26 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGPNGTEXTURELOADER_HXX +#define __FGPNGTEXTURELOADER_HXX + +#include "FGTextureLoaderInterface.hxx" + +class FGPNGTextureLoader : public FGTextureLoaderInterface { +public: + virtual GLuint loadTexture( const std::string & filename ); + + const static GLuint NOTEXTURE = 0; +}; +#endif diff --git a/utils/fgpanel/FGPanelApplication.cxx b/utils/fgpanel/FGPanelApplication.cxx new file mode 100644 index 000000000..e01f20695 --- /dev/null +++ b/utils/fgpanel/FGPanelApplication.cxx @@ -0,0 +1,279 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPanelApplication.hxx" +#include +#include + +#include +#include +#include +#include + +#include + +#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::max(0, global_multi_loop); + reminder = real_delta_time_sec - double(global_multi_loop)/double(model_hz); + return double(global_multi_loop)/double(model_hz); +} + +double ApplicationProperties::getDouble( const char * name, double def ) +{ + SGPropertyNode_ptr n = ApplicationProperties::Properties->getNode( name, false ); + if( n == NULL ) return def; + return n->getDoubleValue(); +} +SGPath ApplicationProperties::GetRootPath( const char * sub ) +{ + SGPath path( ApplicationProperties::root ); + if( sub != NULL ) + path.append( sub ); + return path; +} + +std::string ApplicationProperties::root = "."; +SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode; +FGFontCache ApplicationProperties::fontCache; diff --git a/utils/fgpanel/FGPanelApplication.hxx b/utils/fgpanel/FGPanelApplication.hxx new file mode 100644 index 000000000..f2652ee3a --- /dev/null +++ b/utils/fgpanel/FGPanelApplication.hxx @@ -0,0 +1,55 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGPANELAPPLICATION_HXX +#define __FGPANELAPPLICATION_HXX + +#include "FGGLApplication.hxx" +#include "FGPanelProtocol.hxx" + +#include +#include + +#include + +#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 panel; + SGSharedPtr protocol; + + int width; + int height; +}; + +#endif diff --git a/utils/fgpanel/FGPanelProtocol.cxx b/utils/fgpanel/FGPanelProtocol.cxx new file mode 100644 index 000000000..0743724c8 --- /dev/null +++ b/utils/fgpanel/FGPanelProtocol.cxx @@ -0,0 +1,154 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPanelProtocol.hxx" +#include "ApplicationProperties.hxx" +#include +#include + +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 chunks = outputNode->getChildren( "chunk" ); + for( vector::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 tokens = simgear::strutils::split( buf, "," ); + for( vector::size_type i = 0; i < tokens.size(); i++ ) { + if( i < propertySetterVector.size() ) + propertySetterVector[i]->setValue( tokens[i].c_str() ); + } + } +} + +void FGPanelProtocol::init() +{ + SGPropertyNode_ptr listenNode = root->getNode( "listen" ); + if( listenNode == NULL ) { + return; + } + + string hostname = listenNode->getNode( "host", true )->getStringValue(); + string port = listenNode->getNode( "port", true )->getStringValue(); + string style = listenNode->getNode( "style", true )->getStringValue(); + + if( io != NULL ) + delete io; + + io = new SGSocket( hostname, port, style ); + + if( !io->open( SG_IO_IN ) ) { + cerr << "can't open socket " << style << ":" << hostname << ":" << port << endl; + } +} + +void FGPanelProtocol::reinit() +{ + init(); +} diff --git a/utils/fgpanel/FGPanelProtocol.hxx b/utils/fgpanel/FGPanelProtocol.hxx new file mode 100644 index 000000000..5b08daf7f --- /dev/null +++ b/utils/fgpanel/FGPanelProtocol.hxx @@ -0,0 +1,41 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGPANELPROTOCOL_HXX +#define __FGPANELPROTOCOL_HXX +#include +#include +#include +class PropertySetter; + +typedef vector PropertySetterVector; +class FGPanelProtocol : public SGSubsystem { +public: + FGPanelProtocol( SGPropertyNode_ptr root ); + virtual ~FGPanelProtocol(); + virtual void init(); + virtual void reinit(); + virtual void update( double dt ); + +protected: + +private: + SGPropertyNode_ptr root; + SGIOChannel * io; + PropertySetterVector propertySetterVector; +}; +#endif diff --git a/utils/fgpanel/FGRGBTextureLoader.cxx b/utils/fgpanel/FGRGBTextureLoader.cxx new file mode 100644 index 000000000..faf136942 --- /dev/null +++ b/utils/fgpanel/FGRGBTextureLoader.cxx @@ -0,0 +1,504 @@ +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// From the OpenSceneGraph distribution ReaderWriterRGB.cpp +// Reader for sgi's .rgb format. +// specification can be found at http://local.wasp.uwa.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html + +#include "FGRGBTextureLoader.hxx" +#include +#include + +#include +#include +#include +#include +#include + +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 + 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(iPtr); + pixel = *tempShort; + tempShort++; + iPtr = reinterpret_cast(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(iPtr); + pixel = *tempShort; + tempShort++; + iPtr = reinterpret_cast(tempShort); + + ConvertShort(&pixel, 1); + + tempShort = reinterpret_cast(oPtr); + *tempShort = pixel; + tempShort++; + oPtr = reinterpret_cast(tempShort); + } + } + } + else + { + if (raw->bpc == 1) + { + pixel = *iPtr++; + } + else + { + tempShort = reinterpret_cast(iPtr); + pixel = *tempShort; + tempShort++; + iPtr = reinterpret_cast(tempShort); + } + if(raw->bpc != 1) + ConvertShort(&pixel, 1); + while (count--) + { + if(raw->bpc == 1) + *oPtr++ = pixel; + else + { + tempShort = reinterpret_cast(oPtr); + *tempShort = pixel; + tempShort++; + oPtr = reinterpret_cast(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(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 = "<sizeX<sizeY = "<sizeY<sizeZ = "<sizeZ<bpc = "<bpc<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(ptr); + *tempShort = *(reinterpret_cast(raw->tmpR) + j); + tempShort++; + ptr = reinterpret_cast(tempShort); + } + if( raw->sizeZ >= 2 ) + { + tempShort = reinterpret_cast(ptr); + *tempShort = *(reinterpret_cast(raw->tmpG) + j); + tempShort++; + ptr = reinterpret_cast(tempShort); + } + if( raw->sizeZ >= 3 ) + { + tempShort = reinterpret_cast(ptr); + *tempShort = *(reinterpret_cast(raw->tmpB) + j); + tempShort++; + ptr = reinterpret_cast(tempShort); + } + if( raw->sizeZ >= 4 ) + { + tempShort = reinterpret_cast(ptr); + *tempShort = *(reinterpret_cast(raw->tmpA) + j); + tempShort++; + ptr = reinterpret_cast(tempShort); + } + } + } + // // pad the image width with blanks to bring it up to the rounded width. + // for(;jsizeX; + int t = raw->sizeY; +// int r = 1; + + #if 0 + int internalFormat = raw->sizeZ == 3 ? GL_RGB5 : + raw->sizeZ == 4 ? GL_RGB5_A1 : GL_RGB; + #else +// int internalFormat = raw->sizeZ; + #endif + unsigned int pixelFormat = + raw->sizeZ == 1 ? GL_LUMINANCE : + raw->sizeZ == 2 ? GL_LUMINANCE_ALPHA : + raw->sizeZ == 3 ? GL_RGB : + raw->sizeZ == 4 ? GL_RGBA : (GLenum)-1; + GLint component = raw->sizeZ; + + unsigned int dataType = raw->bpc == 1 ? GL_UNSIGNED_BYTE : + GL_UNSIGNED_SHORT; + + unsigned char *data; + RawImageGetData(raw, &data); + RawImageClose(raw); + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + gluBuild2DMipmaps( GL_TEXTURE_2D, component, s, t, pixelFormat, dataType, (GLvoid*)data ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR ); + + delete []data; + return texture; + } + +GLuint FGRGBTextureLoader::loadTexture( const std::string & filename ) +{ + GLuint texture = NOTEXTURE; + std::ifstream istream(filename.c_str(), std::ios::in | std::ios::binary ); + texture = readRGBStream(istream); + istream.close(); + return texture; +} + diff --git a/utils/fgpanel/FGRGBTextureLoader.hxx b/utils/fgpanel/FGRGBTextureLoader.hxx new file mode 100644 index 000000000..e3c27664b --- /dev/null +++ b/utils/fgpanel/FGRGBTextureLoader.hxx @@ -0,0 +1,26 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGRGBTEXTURELOADER_HXX +#define __FGRGBTEXTURELOADER_HXX + +#include "FGTextureLoaderInterface.hxx" + +class FGRGBTextureLoader : public FGTextureLoaderInterface { +public: + virtual GLuint loadTexture( const std::string & filename ); + + const static GLuint NOTEXTURE = 0; +}; +#endif diff --git a/utils/fgpanel/FGTextureLoaderInterface.hxx b/utils/fgpanel/FGTextureLoaderInterface.hxx new file mode 100644 index 000000000..58c15b853 --- /dev/null +++ b/utils/fgpanel/FGTextureLoaderInterface.hxx @@ -0,0 +1,27 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#ifndef __FGTEXTURELOADERINTERFACE_HXX +#define __FGTEXTURELOADERINTERFACE_HXX + +#include +#include +class FGTextureLoaderInterface { +public: + virtual GLuint loadTexture( const std::string & filename ) = 0; +}; +#endif diff --git a/utils/fgpanel/Makefile.am b/utils/fgpanel/Makefile.am new file mode 100644 index 000000000..58804e9c5 --- /dev/null +++ b/utils/fgpanel/Makefile.am @@ -0,0 +1,20 @@ +AM_CXXFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" + +bin_PROGRAMS = fgpanel + +fgpanel_SOURCES = main.cxx \ + FGGLApplication.cxx FGGLApplication.hxx \ + FGPanelApplication.cxx FGPanelApplication.hxx \ + FGPNGTextureLoader.cxx FGPNGTextureLoader.hxx FGTextureLoaderInterface.hxx \ + FGRGBTextureLoader.cxx FGRGBTextureLoader.hxx \ + FGPanelProtocol.cxx \ + FGFontCache.cxx \ + panel.cxx panel.hxx \ + panel_io.cxx panel_io.hxx + +LIBS = + +fgpanel_LDADD = \ + -lGLU -lglut -lsgmath -lsgprops -lsgio -lsgdebug -lsgmisc -lsgstructure -lsgxml -lsgtiming \ + -lplibpu -lplibfnt -lplibul \ + -lrt -lpng diff --git a/utils/fgpanel/README b/utils/fgpanel/README new file mode 100644 index 000000000..a1199d2c9 --- /dev/null +++ b/utils/fgpanel/README @@ -0,0 +1,148 @@ +===================================================================== +This is fgpanel - basically the stripped down 2D-Panel code from +FlightGear. It is designed as a standalone lightweight panel +rendering engine to draw 2d panels on a lowcost computer/graphic card +without 3d acceleration at reasonablel framerates. + +===================================================================== +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +===================================================================== +Usage +start fgpanel with +fgpanel --fg-root=/path/to/fg/data --panel=Aircraft/MyAircraft/Panels/MyPanel.xml +with the command args set to +--fg-root shall point to the directory where your FGDATA lives + NOTE: you don't need a full copy of FGDATA, just the panel definition files for + your aircraft, e.g. + - Aircraft/MyAircraft/Panels/* + - Aircraft/Instruments/* (if referenced) + +-panel shall point to a panel-configuration file, relative to FGDATA + +start flightgear with +fgfs --generic=socket,out,10,239.24.10.64,5432,udp,../Aircraft/MyAircraft/Panels/SampleProtocol + +===================================================================== +Sample: + +Create the sample files within your aicraft directory, preferrable under Panels +MyPanel.xml +sample-2d-panel.xml +SampleProtocol.xml + +===================================================================== +Sample panel configuration file (MyPanel.xml) + + + + false + + + + + + + + false + + + true + + + + + + + + 239.24.10.64 + 5432 + + + + + + + + +===================================================================== +Sampe 2d-panel configuration file sample-2d-panel.xml +To be included from the panel configuration file + + + + Sample Instrument Panel + 375 + 305 + + + + Attitude Gyro + + + 80 + 80 + + + + +===================================================================== +Sample protocol configuration file to drive the AI (SampleProtocol.xml) + + + + + + + newline + , + + + float + %.2f + /position/altitude-agl-ft + + + + float + %.2f + /instrumentation/attitude-indicator/indicated-roll-deg + + + + float + %.2f + /instrumentation/attitude-indicator/indicated-pitch-deg + + + + float + %.2f + /instrumentation/attitude-indicator/horizon-offset-deg + + + + float + %.4e + /instrumentation/attitude-indicator/spin + + + + + +===================================================================== diff --git a/utils/fgpanel/main.cxx b/utils/fgpanel/main.cxx new file mode 100644 index 000000000..80215e94a --- /dev/null +++ b/utils/fgpanel/main.cxx @@ -0,0 +1,30 @@ +// +// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +#include "FGPanelApplication.hxx" + +int main( int argc, char ** argv ) +{ + try { + FGPanelApplication app(argc,argv); + app.Run(); + return 0; + } + catch( ... ) { + cerr << "Sorry, your program terminated." << endl; + } +} diff --git a/utils/fgpanel/panel.cxx b/utils/fgpanel/panel.cxx new file mode 100644 index 000000000..89044997f --- /dev/null +++ b/utils/fgpanel/panel.cxx @@ -0,0 +1,962 @@ +// panel.cxx - default, 2D single-engine prop instrument panel +// +// Written by David Megginson, started January 2000. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: panel.cxx,v 1.44 2006/09/05 20:28:48 curt Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include // sprintf +#include + +#include + +#include + +#include + +#include +#include + +#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 FGCroppedTexture::cache; +map 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; +} + + +//////////////////////////////////////////////////////////////////////// +// 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; +} + + + +//////////////////////////////////////////////////////////////////////// +// 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; +} + + +//////////////////////////////////////////////////////////////////////// +// 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); +} + + + +//////////////////////////////////////////////////////////////////////// +// 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); +} + + + +//////////////////////////////////////////////////////////////////////// +// 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); +} + + + +//////////////////////////////////////////////////////////////////////// +// 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()); + } +} + + + +//////////////////////////////////////////////////////////////////////// +// 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(); + } +} + + + +//////////////////////////////////////////////////////////////////////// +// 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 ""; + } +} + + + +//////////////////////////////////////////////////////////////////////// +// 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; + } + } + } +} + + +// end of panel.cxx diff --git a/utils/fgpanel/panel.hxx b/utils/fgpanel/panel.hxx new file mode 100644 index 000000000..44ce96a05 --- /dev/null +++ b/utils/fgpanel/panel.hxx @@ -0,0 +1,451 @@ +// panel.hxx - generic support classes for a 2D panel. +// +// Written by David Megginson, started January 2000. +// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009 +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef __PANEL_HXX +#define __PANEL_HXX + +#ifndef __cplusplus +# error This library requires C++ +#endif + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include +#include +#include +#include "FGTextureLoaderInterface.hxx" + +class FGPanelInstrument; + + +//////////////////////////////////////////////////////////////////////// +// Texture management. +//////////////////////////////////////////////////////////////////////// + +class FGCroppedTexture; +typedef SGSharedPtr 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 cache; + static map textureLoader; +}; + + + +//////////////////////////////////////////////////////////////////////// +// 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 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(); +}; + + + +//////////////////////////////////////////////////////////////////////// +// 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; +}; + + + + +//////////////////////////////////////////////////////////////////////// +// 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 transformation_list; + transformation_list _transformations; +}; + + + +//////////////////////////////////////////////////////////////////////// +// 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 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 _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_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 (); + +}; + + +#endif // __PANEL_HXX + +// end of panel.hxx + + + diff --git a/utils/fgpanel/panel_io.cxx b/utils/fgpanel/panel_io.cxx new file mode 100644 index 000000000..9f5084cd3 --- /dev/null +++ b/utils/fgpanel/panel_io.cxx @@ -0,0 +1,591 @@ +// panel_io.cxx - I/O for 2D panel. +// +// Written by David Megginson, started January 2000. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: panel_io.cxx,v 1.26 2006/08/10 11:12:39 mfranz Exp $ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include // for strcmp() + +#include +#include +#include +#include +#include +#include + +#include "panel.hxx" +#include "panel_io.hxx" +#include "ApplicationProperties.hxx" + + +//////////////////////////////////////////////////////////////////////// +// 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. + */ + +//////////////////////////////////////////////////////////////////////// +// 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 +FGReadablePanel::read(SGPropertyNode_ptr root) +{ + SG_LOG( SG_COCKPIT, SG_INFO, "Reading properties for panel " << + root->getStringValue("name", "[Unnamed Panel]") ); + + FGPanel * panel = new FGPanel(root); + panel->setWidth(root->getIntValue("w", 1024)); + panel->setHeight(root->getIntValue("h", 443)); + + SG_LOG( SG_COCKPIT, SG_INFO, "Size=" << panel->getWidth() << "x" << panel->getHeight() ); + + // Assign the background texture, if any, or a bogus chequerboard. + // + string bgTexture = root->getStringValue("background"); + if( !bgTexture.empty() ) + panel->setBackground( new FGCroppedTexture( bgTexture ) ); + panel->setBackgroundWidth( root->getDoubleValue( "background-width", 1.0 ) ); + panel->setBackgroundHeight( root->getDoubleValue( "background-height", 1.0 ) ); + SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << bgTexture ); + + // + // Get multibackground if any... + // + for( int i = 0; i < 8; i++ ) { + SGPropertyNode * mbgNode = root->getChild( "multibackground", i ); + string mbgTexture; + if( mbgNode != NULL ) mbgTexture = mbgNode->getStringValue(); + if( mbgTexture.empty() ) { + if( i == 0 ) break; // if first texture is missing, ignore the rest + else mbgTexture = "FOO"; // if others are missing - set default texture + } + panel->setMultiBackground( new FGCroppedTexture(mbgTexture), i ); + SG_LOG( SG_COCKPIT, SG_INFO, "Set multi-background texture" << i << " to " << mbgTexture ); + } + // + // Create each instrument. + // + SG_LOG( SG_COCKPIT, SG_INFO, "Reading panel instruments" ); + const SGPropertyNode * instrument_group = root->getChild("instruments"); + if (instrument_group != 0) { + int nInstruments = instrument_group->nChildren(); + for (int i = 0; i < nInstruments; i++) { + const SGPropertyNode * node = instrument_group->getChild(i); + if (!strcmp(node->getName(), "instrument")) { + FGPanelInstrument * instrument = readInstrument(node); + if (instrument != 0) + panel->addInstrument(instrument); + } else { + SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + << " in instruments section" ); + } + } + } + SG_LOG( SG_COCKPIT, SG_INFO, "Done reading panel instruments" ); + + + // + // Return the new panel. + // + return panel; +} + +// end of panel_io.cxx + + + diff --git a/utils/fgpanel/panel_io.hxx b/utils/fgpanel/panel_io.hxx new file mode 100644 index 000000000..e353ce04e --- /dev/null +++ b/utils/fgpanel/panel_io.hxx @@ -0,0 +1,40 @@ +// panel_io.cxx - I/O for 2D panel. +// +// Written by David Megginson, started January 2000. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id: panel_io.hxx,v 1.6 2006/04/17 13:03:43 mfranz Exp $ + +#ifndef __PANEL_IO_HXX +#define __PANEL_IO_HXX + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include "panel.hxx" + +class FGReadablePanel : public FGPanel { +public: + static SGSharedPtr read(SGPropertyNode_ptr root); +}; + + +#endif // __PANEL_IO_HXX