1
0
Fork 0

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.
This commit is contained in:
Torsten Dreyer 2011-04-14 11:11:17 +02:00
parent 1456635c55
commit f44dd24462
22 changed files with 3970 additions and 0 deletions

7
utils/fgpanel/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
fgpanel
*.o
*.obj
.deps
*.Po
Makefile
Makefile.in

View file

@ -0,0 +1,31 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __APPLICATION_PROPERTIES
#define __APPLICATION_PROPERTIES
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include "FGFontCache.hxx"
class ApplicationProperties {
public:
static double getDouble( const char * name, double def = 0.0 );
static SGPath GetRootPath( const char * subDir = NULL );
static SGPropertyNode_ptr Properties;
static std::string root;
static FGFontCache fontCache;
};
#endif

View file

@ -0,0 +1,208 @@
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
using namespace std;
#include <map>
#include <algorithm>
#include "ApplicationProperties.hxx"
#include "FGFontCache.hxx"
////////////////////////////////////////////////////////////////////////
// FGFontCache class.
////////////////////////////////////////////////////////////////////////
//extern puFont FONT_HELVETICA_14;
//extern puFont FONT_SANS_12B;
namespace
{
struct GuiFont
{
const char *name;
puFont *font;
struct Predicate
: public std::unary_function<const GuiFont, bool>
{
Predicate(const char* name_) : name(name_) {}
bool operator() (const GuiFont& f1) const
{
return ::strcmp(f1.name, name) == 0;
}
const char* name;
};
};
const GuiFont guifonts[] = {
{ "default", &PUFONT_HELVETICA_12 },
{ "FIXED_8x13", &PUFONT_8_BY_13 },
{ "FIXED_9x15", &PUFONT_9_BY_15 },
{ "TIMES_10", &PUFONT_TIMES_ROMAN_10 },
{ "TIMES_24", &PUFONT_TIMES_ROMAN_24 },
{ "HELVETICA_10", &PUFONT_HELVETICA_10 },
{ "HELVETICA_12", &PUFONT_HELVETICA_12 },
// { "HELVETICA_14", &FONT_HELVETICA_14 },
{ "HELVETICA_18", &PUFONT_HELVETICA_18 }
// { "SANS_12B", &FONT_SANS_12B }
};
const GuiFont* guifontsEnd = &guifonts[sizeof(guifonts)/ sizeof(guifonts[0])];
}
FGFontCache::FGFontCache() :
_initialized(false)
{
}
FGFontCache::~FGFontCache()
{
PuFontMap::iterator it, end = _puFonts.end();
for (it = _puFonts.begin(); it != end; ++it)
delete it->second;
}
inline bool FGFontCache::FntParamsLess::operator()(const FntParams& f1,
const FntParams& f2) const
{
int comp = f1.name.compare(f2.name);
if (comp < 0)
return true;
else if (comp > 0)
return false;
if (f1.size < f2.size)
return true;
else if (f1.size > f2.size)
return false;
return f1.slant < f2.slant;
}
struct FGFontCache::fnt *
FGFontCache::getfnt(const char *name, float size, float slant)
{
string fontName(name);
FntParams fntParams(fontName, size, slant);
PuFontMap::iterator i = _puFonts.find(fntParams);
if (i != _puFonts.end())
return i->second;
// fntTexFont s are all preloaded into the _texFonts map
TexFontMap::iterator texi = _texFonts.find(fontName);
fntTexFont* texfont = 0;
puFont* pufont = 0;
if (texi != _texFonts.end()) {
texfont = texi->second;
} else {
const GuiFont* guifont = std::find_if(&guifonts[0], guifontsEnd,
GuiFont::Predicate(name));
if (guifont != guifontsEnd) {
pufont = guifont->font;
}
}
fnt* f = new fnt;
if (pufont) {
f->pufont = pufont;
} else if (texfont) {
f->texfont = texfont;
f->pufont = new puFont;
f->pufont->initialize(static_cast<fntFont *>(f->texfont), size, slant);
} else {
f->pufont = guifonts[0].font;
}
_puFonts[fntParams] = f;
return f;
}
puFont *
FGFontCache::get(const char *name, float size, float slant)
{
return getfnt(name, size, slant)->pufont;
}
fntTexFont *
FGFontCache::getTexFont(const char *name, float size, float slant)
{
init();
return getfnt(name, size, slant)->texfont;
}
puFont *
FGFontCache::get(SGPropertyNode *node)
{
if (!node)
return get("Helvetica.txf", 15.0, 0.0);
const char *name = node->getStringValue("name", "Helvetica.txf");
float size = node->getFloatValue("size", 15.0);
float slant = node->getFloatValue("slant", 0.0);
return get(name, size, slant);
}
void FGFontCache::init()
{
if (!_initialized) {
char *envp = ::getenv("FG_FONTS");
if (envp != NULL) {
_path.set(envp);
} else {
_path.set(ApplicationProperties::GetRootPath("Fonts").str());
}
_initialized = true;
}
}
SGPath
FGFontCache::getfntpath(const char *name)
{
init();
SGPath path(_path);
if (name && std::string(name) != "") {
path.append(name);
if (path.exists())
return path;
}
path = SGPath(_path);
path.append("Helvetica.txf");
return path;
}
bool FGFontCache::initializeFonts()
{
static string fontext("txf");
init();
ulDir* fontdir = ulOpenDir(_path.c_str());
if (!fontdir)
return false;
const ulDirEnt *dirEntry;
while ((dirEntry = ulReadDir(fontdir)) != 0) {
SGPath path(_path);
path.append(dirEntry->d_name);
if (path.extension() == fontext) {
fntTexFont* f = new fntTexFont;
if (f->load((char *)path.c_str()))
_texFonts[string(dirEntry->d_name)] = f;
else
delete f;
}
}
ulCloseDir(fontdir);
return true;
}
// end of new_gui.cxx

View file

@ -0,0 +1,86 @@
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __FGFONTCACHE_HXX
#define __FGFONTCACHE_HXX
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include <plib/pu.h>
/**
* A class to keep all fonts available for future use.
* This also assures a font isn't resident more than once.
*/
class FGFontCache {
private:
// The parameters of a request to the cache.
struct FntParams
{
const std::string name;
const float size;
const float slant;
FntParams() : size(0.0f), slant(0.0f) {}
FntParams(const FntParams& rhs)
: name(rhs.name), size(rhs.size), slant(rhs.slant)
{
}
FntParams(const std::string& name_, float size_, float slant_)
: name(name_), size(size_), slant(slant_)
{
}
};
struct FntParamsLess
: public std::binary_function<const FntParams, const FntParams, bool>
{
bool operator() (const FntParams& f1, const FntParams& f2) const;
};
struct fnt {
fnt(puFont *pu = 0) : pufont(pu), texfont(0) {}
~fnt() { if (texfont) { delete pufont; delete texfont; } }
// Font used by plib GUI code
puFont *pufont;
// TXF font
fntTexFont *texfont;
};
// Path to the font directory
SGPath _path;
typedef map<const string, fntTexFont*> TexFontMap;
typedef map<const FntParams, fnt*, FntParamsLess> PuFontMap;
TexFontMap _texFonts;
PuFontMap _puFonts;
bool _initialized;
struct fnt *getfnt(const char *name, float size, float slant);
void init();
public:
FGFontCache();
~FGFontCache();
puFont *get(const char *name, float size=15.0, float slant=0.0);
puFont *get(SGPropertyNode *node);
fntTexFont *getTexFont(const char *name, float size=15.0, float slant=0.0);
SGPath getfntpath(const char *name);
/**
* Preload all the fonts in the FlightGear font directory. It is
* important to load the font textures early, with the proper
* graphics context current, so that no plib (or our own) code
* tries to load a font from disk when there's no current graphics
* context.
*/
bool initializeFonts();
};
#endif

View file

@ -0,0 +1,94 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "FGGLApplication.hxx"
#include "GL/gl.h"
#include "GL/glut.h"
#include <iostream>
#include <exception>
#include <stdio.h>
FGGLApplication * FGGLApplication::application = NULL;
FGGLApplication::FGGLApplication( const char * aName, int argc, char ** argv ) :
gameMode(false),
name( aName )
{
if( application != NULL ) {
std::cerr << "Only one instance of FGGLApplication allowed!" << std::endl;
throw std::exception();
}
application = this;
glutInit( &argc, argv );
}
FGGLApplication::~FGGLApplication()
{
}
void FGGLApplication::DisplayCallback()
{
if( application ) application->Display();
}
void FGGLApplication::IdleCallback()
{
if( application ) application->Idle();
}
void FGGLApplication::KeyCallback( unsigned char key, int x, int y )
{
if( application ) application->Key( key, x, y );
}
void FGGLApplication::ReshapeCallback( int width, int height )
{
if( application ) application->Reshape( width, height );
}
void FGGLApplication::Run( int glutMode, bool gameMode, int width, int height, int bpp )
{
glutInitDisplayMode(glutMode);
if( gameMode ) {
width = glutGet(GLUT_SCREEN_WIDTH);
height = glutGet(GLUT_SCREEN_HEIGHT);
char game_mode_str[20];
snprintf(game_mode_str, 20, "%dx%d:%d", width, height, bpp );
glutGameModeString( game_mode_str );
glutEnterGameMode();
this->gameMode = gameMode;
} else {
if( width == -1 )
width = glutGet(GLUT_SCREEN_WIDTH);
if( height == -1 )
height = glutGet(GLUT_SCREEN_HEIGHT);
glutInitDisplayMode(glutMode);
// glutInitWindowSize(width, height);
windowId = glutCreateWindow(name);
}
Init();
glutKeyboardFunc(FGGLApplication::KeyCallback);
glutIdleFunc(FGGLApplication::IdleCallback);
glutDisplayFunc(FGGLApplication::DisplayCallback);
glutReshapeFunc(FGGLApplication::ReshapeCallback);
glutMainLoop();
}

View file

@ -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

View file

@ -0,0 +1,142 @@
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "FGPNGTextureLoader.hxx"
#include <GL/glu.h>
#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
GLuint FGPNGTextureLoader::loadTexture( const string & filename )
{
//header for testing if it is a png
png_byte header[8];
//open file as binary
FILE *fp = fopen(filename.c_str(), "rb");
if (!fp) {
return NOTEXTURE;
}
//read the header
fread(header, 1, 8, fp);
//test if png
int is_png = !png_sig_cmp(header, 0, 8);
if (!is_png) {
fclose(fp);
return NOTEXTURE;
}
//create png struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!png_ptr) {
fclose(fp);
return (NOTEXTURE);
}
//create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
fclose(fp);
return (NOTEXTURE);
}
//create png info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info) {
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
fclose(fp);
return (NOTEXTURE);
}
//png error stuff, not sure libpng man suggests this.
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
fclose(fp);
return (NOTEXTURE);
}
//init png reading
png_init_io(png_ptr, fp);
//let libpng know you already read the first 8 bytes
png_set_sig_bytes(png_ptr, 8);
// read all the info up to the image data
png_read_info(png_ptr, info_ptr);
//variables to pass to get info
int bit_depth, color_type;
png_uint_32 twidth, theight;
// get info about png
png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type,
NULL, NULL, NULL);
// Update the png info struct.
png_read_update_info(png_ptr, info_ptr);
// Row size in bytes.
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
// Allocate the image_data as a big block, to be given to opengl
png_byte *image_data = new png_byte[rowbytes * theight];
if (!image_data) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
fclose(fp);
return NOTEXTURE;
}
//row_pointers is for pointing to image_data for reading the png with libpng
png_bytep *row_pointers = new png_bytep[theight];
if (!row_pointers) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] image_data;
fclose(fp);
return NOTEXTURE;
}
// set the individual row_pointers to point at the correct offsets of image_data
for (png_uint_32 i = 0; i < theight; ++i)
row_pointers[theight - 1 - i] = image_data + i * rowbytes;
//read the png into image_data through row_pointers
png_read_image(png_ptr, row_pointers);
//Now generate the OpenGL texture object
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
gluBuild2DMipmaps( GL_TEXTURE_2D, 4, twidth, theight, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)image_data );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
// glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, twidth, theight, 0,
// GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) image_data);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] image_data;
delete[] row_pointers;
fclose(fp);
return texture;
}

View file

@ -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

View file

@ -0,0 +1,279 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "FGPanelApplication.hxx"
#include <GL/gl.h>
#include <GL/glut.h>
#include <simgear/math/SGMisc.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/structure/exception.hxx>
#include <iostream>
#include "panel_io.hxx"
#include "ApplicationProperties.hxx"
using namespace std;
inline static string ParseArgs( int argc, char ** argv, const string & token )
{
for( int i = 0; i < argc; i++ ) {
string arg = argv[i];
if( arg.find( token ) == 0 )
return arg.substr( token.length() );
}
return "";
}
inline static string ParseArgs( int argc, char ** argv, const char * token )
{
string s = token;
return ParseArgs( argc, argv, s );
}
#include "FGPNGTextureLoader.hxx"
#include "FGRGBTextureLoader.hxx"
static FGPNGTextureLoader pngTextureLoader;
static FGRGBTextureLoader rgbTextureLoader;
FGPanelApplication::FGPanelApplication( int argc, char ** argv ) :
FGGLApplication( "FlightGear Panel", argc, argv )
{
sglog().setLogLevels( SG_ALL, SG_WARN );
FGCroppedTexture::registerTextureLoader( "png", &pngTextureLoader );
FGCroppedTexture::registerTextureLoader( "rgb", &rgbTextureLoader );
string panelFilename;
string fgRoot;
for( int i = 1; i < argc; i++ ) {
panelFilename = ParseArgs( argc, argv, "--panel=" );
fgRoot = ParseArgs( argc, argv, "--fg-root=" );
}
if( fgRoot.length() > 0 )
ApplicationProperties::root = fgRoot;
if( panelFilename.length() == 0 ) {
cerr << "Need a panel filename. Use --panel=path_to_filename" << endl;
throw exception();
}
try {
SGPath tpath = ApplicationProperties::GetRootPath( panelFilename.c_str() );
readProperties( tpath.str(), ApplicationProperties::Properties );
}
catch( sg_io_exception & e ) {
cerr << e.getFormattedMessage() << endl;
throw;
}
for( int i = 1; i < argc; i++ ) {
string arg = argv[i];
if( arg.find( "--prop:" ) == 0 ) {
string s2 = arg.substr( 7 );
unsigned p = s2.find( "=" );
if( p != string::npos ) {
string propertyName = s2.substr( 0, p );
string propertyValue = s2.substr( p+1 );
ApplicationProperties::Properties->getNode( propertyName.c_str(), true )->setValue( propertyValue.c_str() );
}
}
}
SGPropertyNode_ptr n;
if( (n = ApplicationProperties::Properties->getNode( "panel" )) != NULL )
panel = FGReadablePanel::read( n );
protocol = new FGPanelProtocol( ApplicationProperties::Properties->getNode( "communication", true ) );
protocol->init();
}
FGPanelApplication::~FGPanelApplication()
{
}
void FGPanelApplication::Run()
{
int mode = GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE;
int w = panel == NULL ? 0 : panel->getWidth();
int h = panel == NULL ? 0 : panel->getHeight();
if( w == 0 && h == 0 ) {
w = 1024;
h = 768;
} else if( w == 0 ) {
w = h / 0.75;
} else if( h == 0 ) {
h = w * 0.75;
}
bool gameMode = ApplicationProperties::Properties->getNode( "game-mode", true )->getBoolValue();
FGGLApplication::Run( mode, gameMode, w, h );
}
void FGPanelApplication::Init()
{
glAlphaFunc(GL_GREATER, 0.1);
glutSetCursor( GLUT_CURSOR_NONE );
ApplicationProperties::fontCache.initializeFonts();
}
void FGPanelApplication::Reshape( int width, int height )
{
this->width = width;
this->height = height;
glViewport(0, 0, (GLsizei) width, (GLsizei) height);
}
void FGPanelApplication::Idle()
{
double d = glutGet(GLUT_ELAPSED_TIME);
double dt = Sleep();
if( dt == 0 )
return;
if( panel != NULL )
panel->update( dt );
glutSwapBuffers();
if( protocol != NULL )
protocol->update( dt );
static double dsum = 0.0;
static unsigned cnt = 0;
dsum += glutGet(GLUT_ELAPSED_TIME)-d;
cnt++;
if( dsum > 1000.0 ) {
ApplicationProperties::Properties->getNode( "/sim/frame-rate", true )->setDoubleValue(cnt*1000.0/dsum );
dsum = 0.0;
cnt = 0;
}
}
void FGPanelApplication::Key( unsigned char key, int x, int y )
{
switch( key ) {
case 0x1b:
if( gameMode ) glutLeaveGameMode();
else glutDestroyWindow( windowId );
break;
}
}
double FGPanelApplication::Sleep()
{
SGTimeStamp current_time_stamp;
static SGTimeStamp last_time_stamp;
if ( last_time_stamp.get_seconds() == 0 )
last_time_stamp.stamp();
double model_hz = 60;
double throttle_hz = ApplicationProperties::getDouble("/sim/frame-rate-throttle-hz", 0.0);
if ( throttle_hz > 0.0 ) {
// optionally throttle the frame rate (to get consistent frame
// rates or reduce cpu usage.
double frame_us = 1.0e6 / throttle_hz;
// sleep based timing loop.
//
// Calling sleep, even usleep() on linux is less accurate than
// we like, but it does free up the cpu for other tasks during
// the sleep so it is desirable. Because of the way sleep()
// is implemented in consumer operating systems like windows
// and linux, you almost always sleep a little longer than the
// requested amount.
//
// To combat the problem of sleeping too long, we calculate the
// desired wait time and shorten it by 2000us (2ms) to avoid
// [hopefully] over-sleep'ing. The 2ms value was arrived at
// via experimentation. We follow this up at the end with a
// simple busy-wait loop to get the final pause timing exactly
// right.
//
// Assuming we don't oversleep by more than 2000us, this
// should be a reasonable compromise between sleep based
// waiting, and busy waiting.
// sleep() will always overshoot by a bit so undersleep by
// 2000us in the hopes of never oversleeping.
frame_us -= 2000.0;
if ( frame_us < 0.0 ) {
frame_us = 0.0;
}
current_time_stamp.stamp();
/* Convert to ms */
double elapsed_us = (current_time_stamp - last_time_stamp).toUSecs();
if ( elapsed_us < frame_us ) {
double requested_us = frame_us - elapsed_us;
usleep ( (useconds_t)(requested_us ) ) ;
}
// busy wait timing loop.
//
// This yields the most accurate timing. If the previous
// usleep() call is omitted this will peg the cpu
// (which is just fine if FG is the only app you care about.)
current_time_stamp.stamp();
SGTimeStamp next_time_stamp = last_time_stamp;
next_time_stamp += SGTimeStamp::fromSec(1e-6*frame_us);
while ( current_time_stamp < next_time_stamp ) {
current_time_stamp.stamp();
}
} else {
current_time_stamp.stamp();
}
double real_delta_time_sec = double(current_time_stamp.toUSecs() - last_time_stamp.toUSecs()) / 1000000.0;
last_time_stamp = current_time_stamp;
//fprintf(stdout,"\r%4.1lf ", 1/real_delta_time_sec );
//fflush(stdout);
// round the real time down to a multiple of 1/model-hz.
// this way all systems are updated the _same_ amount of dt.
static double reminder = 0.0;
static long global_multi_loop = 0;
real_delta_time_sec += reminder;
global_multi_loop = long(floor(real_delta_time_sec*model_hz));
global_multi_loop = SGMisc<long>::max(0, global_multi_loop);
reminder = real_delta_time_sec - double(global_multi_loop)/double(model_hz);
return double(global_multi_loop)/double(model_hz);
}
double ApplicationProperties::getDouble( const char * name, double def )
{
SGPropertyNode_ptr n = ApplicationProperties::Properties->getNode( name, false );
if( n == NULL ) return def;
return n->getDoubleValue();
}
SGPath ApplicationProperties::GetRootPath( const char * sub )
{
SGPath path( ApplicationProperties::root );
if( sub != NULL )
path.append( sub );
return path;
}
std::string ApplicationProperties::root = ".";
SGPropertyNode_ptr ApplicationProperties::Properties = new SGPropertyNode;
FGFontCache ApplicationProperties::fontCache;

View file

@ -0,0 +1,55 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __FGPANELAPPLICATION_HXX
#define __FGPANELAPPLICATION_HXX
#include "FGGLApplication.hxx"
#include "FGPanelProtocol.hxx"
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/props/props.hxx>
#include <string>
#include "panel.hxx"
class FGPanelApplication : public FGGLApplication {
public:
FGPanelApplication( int argc, char ** argv );
~FGPanelApplication();
void Run();
protected:
virtual void Key( unsigned char key, int x, int y );
virtual void Idle();
// virtual void Display();
virtual void Reshape( int width, int height );
virtual void Init();
double Sleep();
SGSharedPtr<FGPanel> panel;
SGSharedPtr<FGPanelProtocol> protocol;
int width;
int height;
};
#endif

View file

@ -0,0 +1,154 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "FGPanelProtocol.hxx"
#include "ApplicationProperties.hxx"
#include <simgear/io/sg_socket.hxx>
#include <simgear/misc/strutils.hxx>
class PropertySetter {
public:
PropertySetter( SGPropertyNode_ptr node ) : _node(node) {}
virtual void setValue( const char * value ) = 0;
protected:
SGPropertyNode_ptr _node;
};
class BoolPropertySetter : public PropertySetter {
public:
BoolPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
virtual void setValue( const char * value ) {
_node->setBoolValue( atoi( value ) != 0 );
}
};
class IntPropertySetter : public PropertySetter {
public:
IntPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
virtual void setValue( const char * value ) {
_node->setIntValue( atol( value ) );
}
};
class FloatPropertySetter : public PropertySetter {
public:
FloatPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
virtual void setValue( const char * value ) {
_node->setFloatValue( strtof( value, NULL ) );
}
};
class DoublePropertySetter : public PropertySetter {
public:
DoublePropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
virtual void setValue( const char * value ) {
_node->setDoubleValue( strtod( value, NULL ) );
}
};
class StringPropertySetter : public PropertySetter {
public:
StringPropertySetter( SGPropertyNode_ptr node ) : PropertySetter(node) {}
virtual void setValue( const char * value ) {
_node->setStringValue( value );
}
};
FGPanelProtocol::FGPanelProtocol( SGPropertyNode_ptr aRoot )
: SGSubsystem(),
root(aRoot),
io(NULL)
{
SGPropertyNode_ptr outputNode = root->getNode( "protocol/generic/output" );
if( outputNode ) {
vector<SGPropertyNode_ptr> chunks = outputNode->getChildren( "chunk" );
for( vector<SGPropertyNode_ptr>::size_type i = 0; i < chunks.size(); i++ ) {
SGPropertyNode_ptr chunk = chunks[i];
SGPropertyNode_ptr nodeNode = chunk->getNode("node", false );
if( nodeNode == NULL )
continue;
SGPropertyNode_ptr node = ApplicationProperties::Properties->getNode( nodeNode->getStringValue(), true );
string type = "";
SGPropertyNode_ptr typeNode = chunk->getNode( "type", false );
if( typeNode != NULL ) type = typeNode->getStringValue();
if( type == "float" ) {
propertySetterVector.push_back( new FloatPropertySetter( node ) );
} else if( type == "double" || type == "fixed" ) {
propertySetterVector.push_back( new DoublePropertySetter( node ) );
} else if( type == "bool" || type == "boolean" ) {
propertySetterVector.push_back( new BoolPropertySetter( node ) );
} else if( type == "string" ) {
propertySetterVector.push_back( new StringPropertySetter( node ) );
} else {
propertySetterVector.push_back( new IntPropertySetter( node ) );
}
}
}
}
FGPanelProtocol::~FGPanelProtocol()
{
for( PropertySetterVector::size_type i = 0; i < propertySetterVector.size(); i++ )
delete propertySetterVector[i];
}
void FGPanelProtocol::update( double dt )
{
char buf[8192];
if( io == NULL )
return;
int length = io->readline( buf, sizeof(buf)-1 );
buf[sizeof(buf)-1] = 0;
if ( length > 0 ) {
vector<string> tokens = simgear::strutils::split( buf, "," );
for( vector<string>::size_type i = 0; i < tokens.size(); i++ ) {
if( i < propertySetterVector.size() )
propertySetterVector[i]->setValue( tokens[i].c_str() );
}
}
}
void FGPanelProtocol::init()
{
SGPropertyNode_ptr listenNode = root->getNode( "listen" );
if( listenNode == NULL ) {
return;
}
string hostname = listenNode->getNode( "host", true )->getStringValue();
string port = listenNode->getNode( "port", true )->getStringValue();
string style = listenNode->getNode( "style", true )->getStringValue();
if( io != NULL )
delete io;
io = new SGSocket( hostname, port, style );
if( !io->open( SG_IO_IN ) ) {
cerr << "can't open socket " << style << ":" << hostname << ":" << port << endl;
}
}
void FGPanelProtocol::reinit()
{
init();
}

View file

@ -0,0 +1,41 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __FGPANELPROTOCOL_HXX
#define __FGPANELPROTOCOL_HXX
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/props/props.hxx>
#include <simgear/io/iochannel.hxx>
class PropertySetter;
typedef vector<PropertySetter*> PropertySetterVector;
class FGPanelProtocol : public SGSubsystem {
public:
FGPanelProtocol( SGPropertyNode_ptr root );
virtual ~FGPanelProtocol();
virtual void init();
virtual void reinit();
virtual void update( double dt );
protected:
private:
SGPropertyNode_ptr root;
SGIOChannel * io;
PropertySetterVector propertySetterVector;
};
#endif

View file

@ -0,0 +1,504 @@
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// From the OpenSceneGraph distribution ReaderWriterRGB.cpp
// Reader for sgi's .rgb format.
// specification can be found at http://local.wasp.uwa.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html
#include "FGRGBTextureLoader.hxx"
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <fstream>
typedef struct _rawImageRec
{
unsigned short imagic;
unsigned short type;
unsigned short dim;
unsigned short sizeX, sizeY, sizeZ;
unsigned long min, max;
unsigned long wasteBytes;
char name[80];
unsigned long colorMap;
std::istream *file;
unsigned char *tmp, *tmpR, *tmpG, *tmpB, *tmpA;
unsigned long rleEnd;
GLuint *rowStart;
GLint *rowSize;
GLenum swapFlag;
short bpc;
typedef unsigned char * BytePtr;
bool needsBytesSwapped()
{
union {
int testWord;
char testByte[sizeof(int)];
}endianTest;
endianTest.testWord = 1;
if( endianTest.testByte[0] == 1 )
return true;
else
return false;
}
template <class T>
inline void swapBytes( T &s )
{
if( sizeof( T ) == 1 )
return;
T d = s;
BytePtr sptr = (BytePtr)&s;
BytePtr dptr = &(((BytePtr)&d)[sizeof(T)-1]);
for( unsigned int i = 0; i < sizeof(T); i++ )
*(sptr++) = *(dptr--);
}
void swapBytes()
{
swapBytes( imagic );
swapBytes( type );
swapBytes( dim );
swapBytes( sizeX );
swapBytes( sizeY );
swapBytes( sizeZ );
swapBytes( wasteBytes );
swapBytes( min );
swapBytes( max );
swapBytes( colorMap );
}
} rawImageRec;
static void ConvertShort(unsigned short *array, long length)
{
unsigned long b1, b2;
unsigned char *ptr;
ptr = (unsigned char *)array;
while (length--)
{
b1 = *ptr++;
b2 = *ptr++;
*array++ = (unsigned short) ((b1 << 8) | (b2));
}
}
static void ConvertLong(GLuint *array, long length)
{
unsigned long b1, b2, b3, b4;
unsigned char *ptr;
ptr = (unsigned char *)array;
while (length--)
{
b1 = *ptr++;
b2 = *ptr++;
b3 = *ptr++;
b4 = *ptr++;
*array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
}
}
static void RawImageClose(rawImageRec *raw)
{
if (raw)
{
if (raw->tmp) delete [] raw->tmp;
if (raw->tmpR) delete [] raw->tmpR;
if (raw->tmpG) delete [] raw->tmpG;
if (raw->tmpB) delete [] raw->tmpB;
if (raw->tmpA) delete [] raw->tmpA;
if (raw->rowStart) delete [] raw->rowStart;
if (raw->rowSize) delete [] raw->rowSize;
delete raw;
}
}
static rawImageRec *RawImageOpen(std::istream& fin)
{
union
{
int testWord;
char testByte[4];
} endianTest;
rawImageRec *raw;
int x;
raw = new rawImageRec;
if (raw == NULL)
{
// notify(WARN)<< "Out of memory!"<< std::endl;
return NULL;
}
//Set istream pointer
raw->file = &fin;
endianTest.testWord = 1;
if (endianTest.testByte[0] == 1)
{
raw->swapFlag = GL_TRUE;
}
else
{
raw->swapFlag = GL_FALSE;
}
fin.read((char*)raw,12);
if (!fin.good())
return NULL;
if (raw->swapFlag)
{
ConvertShort(&raw->imagic, 6);
}
raw->tmp = raw->tmpR = raw->tmpG = raw->tmpB = raw->tmpA = 0L;
raw->rowStart = 0;
raw->rowSize = 0;
raw->bpc = (raw->type & 0x00FF);
raw->tmp = new unsigned char [raw->sizeX*256*raw->bpc];
if (raw->tmp == NULL )
{
// notify(FATAL)<< "Out of memory!"<< std::endl;
RawImageClose(raw);
return NULL;
}
if( raw->sizeZ >= 1 )
{
if( (raw->tmpR = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
{
// notify(FATAL)<< "Out of memory!"<< std::endl;
RawImageClose(raw);
return NULL;
}
}
if( raw->sizeZ >= 2 )
{
if( (raw->tmpG = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
{
// notify(FATAL)<< "Out of memory!"<< std::endl;
RawImageClose(raw);
return NULL;
}
}
if( raw->sizeZ >= 3 )
{
if( (raw->tmpB = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
{
// notify(FATAL)<< "Out of memory!"<< std::endl;
RawImageClose(raw);
return NULL;
}
}
if (raw->sizeZ >= 4)
{
if( (raw->tmpA = new unsigned char [raw->sizeX*raw->bpc]) == NULL )
{
// notify(FATAL)<< "Out of memory!"<< std::endl;
RawImageClose(raw);
return NULL;
}
}
if ((raw->type & 0xFF00) == 0x0100)
{
unsigned int ybyz = raw->sizeY * raw->sizeZ;
if ( (raw->rowStart = new GLuint [ybyz]) == NULL )
{
// notify(FATAL)<< "Out of memory!"<< std::endl;
RawImageClose(raw);
return NULL;
}
if ( (raw->rowSize = new GLint [ybyz]) == NULL )
{
// notify(FATAL)<< "Out of memory!"<< std::endl;
RawImageClose(raw);
return NULL;
}
x = ybyz * sizeof(GLuint);
raw->rleEnd = 512 + (2 * x);
fin.seekg(512,std::ios::beg);
fin.read((char*)raw->rowStart,x);
fin.read((char*)raw->rowSize,x);
if (raw->swapFlag)
{
ConvertLong(raw->rowStart, (long) (x/sizeof(GLuint)));
ConvertLong((GLuint *)raw->rowSize, (long) (x/sizeof(GLint)));
}
}
return raw;
}
static void RawImageGetRow(rawImageRec *raw, unsigned char *buf, int y, int z)
{
unsigned char *iPtr, *oPtr;
unsigned short pixel;
int count, done = 0;
unsigned short *tempShort;
if ((raw->type & 0xFF00) == 0x0100)
{
raw->file->seekg((long) raw->rowStart[y+z*raw->sizeY], std::ios::beg);
raw->file->read((char*)raw->tmp, (unsigned int)raw->rowSize[y+z*raw->sizeY]);
iPtr = raw->tmp;
oPtr = buf;
while (!done)
{
if (raw->bpc == 1)
pixel = *iPtr++;
else
{
tempShort = reinterpret_cast<unsigned short*>(iPtr);
pixel = *tempShort;
tempShort++;
iPtr = reinterpret_cast<unsigned char *>(tempShort);
}
if(raw->bpc != 1)
ConvertShort(&pixel, 1);
count = (int)(pixel & 0x7F);
// limit the count value to the remiaing row size
if (oPtr + count*raw->bpc > buf + raw->sizeX*raw->bpc)
{
count = ( (buf + raw->sizeX*raw->bpc) - oPtr ) / raw->bpc;
}
if (count<=0)
{
done = 1;
return;
}
if (pixel & 0x80)
{
while (count--)
{
if(raw->bpc == 1)
*oPtr++ = *iPtr++;
else{
tempShort = reinterpret_cast<unsigned short*>(iPtr);
pixel = *tempShort;
tempShort++;
iPtr = reinterpret_cast<unsigned char *>(tempShort);
ConvertShort(&pixel, 1);
tempShort = reinterpret_cast<unsigned short*>(oPtr);
*tempShort = pixel;
tempShort++;
oPtr = reinterpret_cast<unsigned char *>(tempShort);
}
}
}
else
{
if (raw->bpc == 1)
{
pixel = *iPtr++;
}
else
{
tempShort = reinterpret_cast<unsigned short*>(iPtr);
pixel = *tempShort;
tempShort++;
iPtr = reinterpret_cast<unsigned char *>(tempShort);
}
if(raw->bpc != 1)
ConvertShort(&pixel, 1);
while (count--)
{
if(raw->bpc == 1)
*oPtr++ = pixel;
else
{
tempShort = reinterpret_cast<unsigned short*>(oPtr);
*tempShort = pixel;
tempShort++;
oPtr = reinterpret_cast<unsigned char *>(tempShort);
}
}
}
}
}
else
{
raw->file->seekg(512+(y*raw->sizeX*raw->bpc)+(z*raw->sizeX*raw->sizeY*raw->bpc),std::ios::beg);
raw->file->read((char*)buf, raw->sizeX*raw->bpc);
if(raw->swapFlag && raw->bpc != 1){
ConvertShort(reinterpret_cast<unsigned short*>(buf), raw->sizeX);
}
}
}
static void RawImageGetData(rawImageRec *raw, unsigned char **data )
{
unsigned char *ptr;
int i, j;
unsigned short *tempShort;
// // round the width to a factor 4
// int width = (int)(floorf((float)raw->sizeX/4.0f)*4.0f);
// if (width!=raw->sizeX) width += 4;
// byte aligned.
// osg::notify(osg::INFO)<<"raw->sizeX = "<<raw->sizeX<<std::endl;
// osg::notify(osg::INFO)<<"raw->sizeY = "<<raw->sizeY<<std::endl;
// osg::notify(osg::INFO)<<"raw->sizeZ = "<<raw->sizeZ<<std::endl;
// osg::notify(osg::INFO)<<"raw->bpc = "<<raw->bpc<<std::endl;
*data = new unsigned char [(raw->sizeX)*(raw->sizeY)*(raw->sizeZ)*(raw->bpc)];
ptr = *data;
for (i = 0; i < (int)(raw->sizeY); i++)
{
if( raw->sizeZ >= 1 )
RawImageGetRow(raw, raw->tmpR, i, 0);
if( raw->sizeZ >= 2 )
RawImageGetRow(raw, raw->tmpG, i, 1);
if( raw->sizeZ >= 3 )
RawImageGetRow(raw, raw->tmpB, i, 2);
if( raw->sizeZ >= 4 )
RawImageGetRow(raw, raw->tmpA, i, 3);
for (j = 0; j < (int)(raw->sizeX); j++)
{
if(raw->bpc == 1){
if( raw->sizeZ >= 1 )
*ptr++ = *(raw->tmpR + j);
if( raw->sizeZ >= 2 )
*ptr++ = *(raw->tmpG + j);
if( raw->sizeZ >= 3 )
*ptr++ = *(raw->tmpB + j);
if( raw->sizeZ >= 4 )
*ptr++ = *(raw->tmpA + j);
}else{
if( raw->sizeZ >= 1 )
{
tempShort = reinterpret_cast<unsigned short*>(ptr);
*tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpR) + j);
tempShort++;
ptr = reinterpret_cast<unsigned char *>(tempShort);
}
if( raw->sizeZ >= 2 )
{
tempShort = reinterpret_cast<unsigned short*>(ptr);
*tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpG) + j);
tempShort++;
ptr = reinterpret_cast<unsigned char *>(tempShort);
}
if( raw->sizeZ >= 3 )
{
tempShort = reinterpret_cast<unsigned short*>(ptr);
*tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpB) + j);
tempShort++;
ptr = reinterpret_cast<unsigned char *>(tempShort);
}
if( raw->sizeZ >= 4 )
{
tempShort = reinterpret_cast<unsigned short*>(ptr);
*tempShort = *(reinterpret_cast<unsigned short*>(raw->tmpA) + j);
tempShort++;
ptr = reinterpret_cast<unsigned char *>(tempShort);
}
}
}
// // pad the image width with blanks to bring it up to the rounded width.
// for(;j<width;++j) *ptr++ = 0;
}
}
// supportsExtension("rgb","rgb image format");
// supportsExtension("rgba","rgba image format");
// supportsExtension("sgi","sgi image format");
// supportsExtension("int","int image format");
// supportsExtension("inta","inta image format");
// supportsExtension("bw","bw image format");
GLuint readRGBStream(std::istream& fin)
{
rawImageRec *raw;
if( (raw = RawImageOpen(fin)) == NULL )
{
return 0;
}
int s = raw->sizeX;
int t = raw->sizeY;
// int r = 1;
#if 0
int internalFormat = raw->sizeZ == 3 ? GL_RGB5 :
raw->sizeZ == 4 ? GL_RGB5_A1 : GL_RGB;
#else
// int internalFormat = raw->sizeZ;
#endif
unsigned int pixelFormat =
raw->sizeZ == 1 ? GL_LUMINANCE :
raw->sizeZ == 2 ? GL_LUMINANCE_ALPHA :
raw->sizeZ == 3 ? GL_RGB :
raw->sizeZ == 4 ? GL_RGBA : (GLenum)-1;
GLint component = raw->sizeZ;
unsigned int dataType = raw->bpc == 1 ? GL_UNSIGNED_BYTE :
GL_UNSIGNED_SHORT;
unsigned char *data;
RawImageGetData(raw, &data);
RawImageClose(raw);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
gluBuild2DMipmaps( GL_TEXTURE_2D, component, s, t, pixelFormat, dataType, (GLvoid*)data );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
delete []data;
return texture;
}
GLuint FGRGBTextureLoader::loadTexture( const std::string & filename )
{
GLuint texture = NOTEXTURE;
std::ifstream istream(filename.c_str(), std::ios::in | std::ios::binary );
texture = readRGBStream(istream);
istream.close();
return texture;
}

View file

@ -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

View file

@ -0,0 +1,27 @@
//
// Written and (c) Torsten Dreyer - Torsten(at)t3r_dot_de
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __FGTEXTURELOADERINTERFACE_HXX
#define __FGTEXTURELOADERINTERFACE_HXX
#include <GL/gl.h>
#include <string>
class FGTextureLoaderInterface {
public:
virtual GLuint loadTexture( const std::string & filename ) = 0;
};
#endif

20
utils/fgpanel/Makefile.am Normal file
View file

@ -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

148
utils/fgpanel/README Normal file
View file

@ -0,0 +1,148 @@
=====================================================================
This is fgpanel - basically the stripped down 2D-Panel code from
FlightGear. It is designed as a standalone lightweight panel
rendering engine to draw 2d panels on a lowcost computer/graphic card
without 3d acceleration at reasonablel framerates.
=====================================================================
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
=====================================================================
Usage
start fgpanel with
fgpanel --fg-root=/path/to/fg/data --panel=Aircraft/MyAircraft/Panels/MyPanel.xml
with the command args set to
--fg-root shall point to the directory where your FGDATA lives
NOTE: you don't need a full copy of FGDATA, just the panel definition files for
your aircraft, e.g.
- Aircraft/MyAircraft/Panels/*
- Aircraft/Instruments/* (if referenced)
-panel shall point to a panel-configuration file, relative to FGDATA
start flightgear with
fgfs --generic=socket,out,10,239.24.10.64,5432,udp,../Aircraft/MyAircraft/Panels/SampleProtocol
=====================================================================
Sample:
Create the sample files within your aicraft directory, preferrable under Panels
MyPanel.xml
sample-2d-panel.xml
SampleProtocol.xml
=====================================================================
Sample panel configuration file (MyPanel.xml)
<PropertyList>
<!-- true: run full-screen, false; run in window -->
<game-mode type="bool">false</game-mode>
<!-- include the panel definitions (2d-panel syntax)-->
<panel include="sample-2d-panel.xml"/>
<!-- compose your property-tree here -->
<sim>
<panel>
<flip-x type="bool">false</flip-x>
</panel>
<instrument-options>
<omit-knobs type="bool">true</omit-knobs>
</instrument-options>
</sim>
<!-- network communication settings -->
<communication>
<listen>
<!-- interface to bind to,
leave empty for all interfaces -->
<host>239.24.10.64</host> <!-- multicast address! -->
<port>5432</port> <!-- tcp port to listen to -->
<style>udp</style> <!-- udp or tcp (forget about tcp!) -->
</listen>
<!-- the generic protocol definition
same as used for fgfs --generic=foobar option
-->
<protocol include="SampleProtocol.xml"/>
</communication>
</PropertyList>
=====================================================================
Sampe 2d-panel configuration file sample-2d-panel.xml
To be included from the panel configuration file
<?xml version="1.0"?>
<PropertyList>
<name>Sample Instrument Panel</name>
<w>375</w> <!-- screen width: 375mm -->
<h>305</h> <!-- screen height: 305mm -->
<instruments>
<!-- use FlightGear's c172 attitude indicator -->
<instrument include="../../Instruments/ati-c172s.xml">
<name>Attitude Gyro</name>
<x alias="../../../params/col-2"/>
<y alias="../../../params/row-1"/>
<w>80</w>
<h>80</h>
</instrument>
</instruments>
</PropertyList>
=====================================================================
Sample protocol configuration file to drive the AI (SampleProtocol.xml)
<?xml version="1.0"?>
<PropertyList>
<generic>
<output>
<line_separator>newline</line_separator>
<var_separator>,</var_separator>
<chunk>
<type>float</type>
<format>%.2f</format>
<node>/position/altitude-agl-ft</node>
</chunk>
<chunk>
<type>float</type>
<format>%.2f</format>
<node>/instrumentation/attitude-indicator/indicated-roll-deg</node>
</chunk>
<chunk>
<type>float</type>
<format>%.2f</format>
<node>/instrumentation/attitude-indicator/indicated-pitch-deg</node>
</chunk>
<chunk>
<type>float</type>
<format>%.2f</format>
<node>/instrumentation/attitude-indicator/horizon-offset-deg</node>
</chunk>
<chunk>
<type>float</type>
<format>%.4e</format>
<node>/instrumentation/attitude-indicator/spin</node>
</chunk>
</output>
</generic>
</PropertyList>
=====================================================================

30
utils/fgpanel/main.cxx Normal file
View file

@ -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;
}
}

962
utils/fgpanel/panel.cxx Normal file
View file

@ -0,0 +1,962 @@
// panel.cxx - default, 2D single-engine prop instrument panel
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id: panel.cxx,v 1.44 2006/09/05 20:28:48 curt Exp $
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#include <stdio.h> // sprintf
#include <string.h>
#include <simgear/compiler.h>
#include <GL/glut.h>
#include <plib/fnt.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include "panel.hxx"
#include "ApplicationProperties.hxx"
////////////////////////////////////////////////////////////////////////
// Local functions.
////////////////////////////////////////////////////////////////////////
class FGDummyTextureLoader : public FGTextureLoaderInterface {
public:
virtual GLuint loadTexture( const string & filename );
};
GLuint FGDummyTextureLoader::loadTexture( const string & filename )
{
GLuint _texture = 0;
glGenTextures( 1, &_texture );
glBindTexture( GL_TEXTURE_2D, _texture );
// glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ;
// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) ;
// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ) ;
// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) ;
// glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) ;
GLubyte image[ 2 * 2 * 3 ] ;
/* Red and white chequerboard */
image [ 0 ] = 255 ; image [ 1 ] = 0 ; image [ 2 ] = 0 ;
image [ 3 ] = 255 ; image [ 4 ] = 255 ; image [ 5 ] = 255 ;
image [ 6 ] = 255 ; image [ 7 ] = 255 ; image [ 8 ] = 255 ;
image [ 9 ] = 255 ; image [ 10] = 0 ; image [ 11] = 0 ;
glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, 2, 2, 0,
GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*) image);
return _texture;
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGCropped Texture.
////////////////////////////////////////////////////////////////////////
GLuint FGCroppedTexture::current_bound_texture = 0;
map<string,GLuint> FGCroppedTexture::cache;
map<string,FGTextureLoaderInterface*> FGCroppedTexture::textureLoader;
static FGDummyTextureLoader dummyTextureLoader;
FGCroppedTexture::FGCroppedTexture (const string &path,
float minX, float minY,
float maxX, float maxY)
: _path(path),
_minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY), _texture(0)
{
}
FGCroppedTexture::~FGCroppedTexture ()
{
}
void FGCroppedTexture::bind( bool doGLBind )
{
if( _texture == 0 ) {
SG_LOG( SG_COCKPIT, SG_DEBUG, "First bind of texture " << _path );
if( cache.count(_path) > 0 ) {
_texture = cache[_path];
SG_LOG( SG_COCKPIT, SG_DEBUG, "Using texture " << _path << " from cache (#" << _texture << ")" );
} else {
SGPath tpath = ApplicationProperties::GetRootPath(_path.c_str());
string extension = tpath.extension();
FGTextureLoaderInterface * loader = &dummyTextureLoader;
if( textureLoader.count( extension ) == 0 ) {
SG_LOG( SG_COCKPIT, SG_ALERT, "Can't handle textures of type " << extension );
} else {
loader = textureLoader[extension];
}
_texture = loader->loadTexture( tpath.c_str() );
SG_LOG( SG_COCKPIT, SG_DEBUG, "Texture " << tpath.c_str() << " loaded from file as #" << _texture );
cache[_path] = _texture;
}
}
if( !doGLBind || current_bound_texture == _texture )
return;
glBindTexture( GL_TEXTURE_2D, _texture );
current_bound_texture = _texture;
}
////////////////////////////////////////////////////////////////////////
// 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

451
utils/fgpanel/panel.hxx Normal file
View file

@ -0,0 +1,451 @@
// panel.hxx - generic support classes for a 2D panel.
//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef __PANEL_HXX
#define __PANEL_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <plib/fnt.h>
#include <simgear/props/condition.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/math/interpolater.hxx>
#include <simgear/sg_inlines.h>
#include "FGTextureLoaderInterface.hxx"
class FGPanelInstrument;
////////////////////////////////////////////////////////////////////////
// Texture management.
////////////////////////////////////////////////////////////////////////
class FGCroppedTexture;
typedef SGSharedPtr<FGCroppedTexture> FGCroppedTexture_ptr;
/**
* Cropped texture (should migrate out into FGFS).
*
* This structure wraps an SSG texture with cropping information.
*/
class FGCroppedTexture : public SGReferenced
{
public:
FGCroppedTexture (const string &path,
float _minX = 0.0, float _minY = 0.0,
float _maxX = 1.0, float _maxY = 1.0);
virtual ~FGCroppedTexture ();
virtual void setPath (const string &path) { _path = path; }
virtual const string &getPath () const { return _path; }
virtual void setCrop (float minX, float minY, float maxX, float maxY) {
_minX = minX; _minY = minY; _maxX = maxX; _maxY = maxY;
}
static void registerTextureLoader( const string & extension, FGTextureLoaderInterface * loader ) {
if( textureLoader.count( extension ) == 0 )
textureLoader[extension] = loader;
}
virtual float getMinX () const { return _minX; }
virtual float getMinY () const { return _minY; }
virtual float getMaxX () const { return _maxX; }
virtual float getMaxY () const { return _maxY; }
GLuint getTexture() const { return _texture; }
virtual void bind( bool doGLBind = true );
private:
string _path;
float _minX, _minY, _maxX, _maxY;
GLuint _texture;
static GLuint current_bound_texture;
static map<string,GLuint> cache;
static map<string,FGTextureLoaderInterface*> textureLoader;
};
////////////////////////////////////////////////////////////////////////
// Top-level panel.
////////////////////////////////////////////////////////////////////////
/**
* Instrument panel class.
*
* The panel is a container that has a background texture and holds
* zero or more instruments. The panel will order the instruments to
* redraw themselves when necessary, and will pass mouse clicks on to
* the appropriate instruments for processing.
*/
class FGPanel : public SGSubsystem
{
public:
FGPanel ( SGPropertyNode_ptr root );
virtual ~FGPanel ();
// Update the panel (every frame).
virtual void init ();
virtual void bind ();
virtual void unbind ();
// virtual void draw ();
virtual void update (double dt);
// transfer pointer ownership!!!
virtual void addInstrument (FGPanelInstrument * instrument);
// Background texture.
virtual void setBackground (FGCroppedTexture_ptr texture);
inline void setBackgroundWidth( double d ) {
_bg_width = d;
}
inline void setBackgroundHeight( double d ) {
_bg_height = d;
}
// Background multiple textures.
virtual void setMultiBackground (FGCroppedTexture_ptr texture , int idx);
// Full width of panel.
virtual void setWidth (int width) { _width = width; }
virtual int getWidth () const { return _width; }
// Full height of panel.
virtual void setHeight (int height) { _height = height; }
virtual int getHeight () const { return _height; }
private:
typedef vector<FGPanelInstrument *> instrument_list_type;
int _width;
int _height;
SGPropertyNode_ptr _root;
SGPropertyNode_ptr _flipx;
SGPropertyNode_ptr _rotate;
FGCroppedTexture_ptr _bg;
double _bg_width;
double _bg_height;
FGCroppedTexture_ptr _mbg[8];
// List of instruments in panel.
instrument_list_type _instruments;
GLuint initDisplayList;
GLuint getInitDisplayList();
};
////////////////////////////////////////////////////////////////////////
// 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<FGPanelTransformation *> 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<FGInstrumentLayer *> layer_list;
layer_list _layers;
};
/**
* An instrument layer containing a group of sublayers.
*
* This class is useful for gathering together a group of related
* layers, either to hold in an external file or to work under
* the same condition.
*/
class FGGroupLayer : public FGInstrumentLayer
{
public:
FGGroupLayer ();
virtual ~FGGroupLayer ();
virtual void draw ();
// transfer pointer ownership
virtual void addLayer (FGInstrumentLayer * layer);
protected:
vector<FGInstrumentLayer *> _layers;
};
/**
* A textured layer of an instrument.
*
* This is a layer holding a single texture. Normally, the texture's
* backgound should be transparent so that lower layers and the panel
* background can show through.
*/
class FGTexturedLayer : public FGInstrumentLayer
{
public:
FGTexturedLayer (int w = -1, int h = -1) : FGInstrumentLayer(w, h) {}
FGTexturedLayer (FGCroppedTexture_ptr texture, int w = -1, int h = -1);
virtual ~FGTexturedLayer ();
virtual void draw ();
virtual void setTexture (FGCroppedTexture_ptr texture) {
_texture = texture;
}
FGCroppedTexture_ptr getTexture() { return _texture; }
void setEmissive(bool e) { _emissive = e; }
private:
GLuint getDisplayList();
FGCroppedTexture_ptr _texture;
bool _emissive;
GLuint displayList;
};
/**
* A text layer of an instrument.
*
* This is a layer holding a string of static and/or generated text.
* It is useful for instruments that have text displays, such as
* a chronometer, GPS, or NavCom radio.
*/
class FGTextLayer : public FGInstrumentLayer
{
public:
enum ChunkType {
TEXT,
TEXT_VALUE,
DOUBLE_VALUE
};
class Chunk : public SGConditional
{
public:
Chunk (const string &text, const string &fmt = "%s");
Chunk (ChunkType type, const SGPropertyNode * node,
const string &fmt = "", float mult = 1.0, float offs = 0.0,
bool truncation = false);
const char * getValue () const;
private:
ChunkType _type;
string _text;
SGConstPropertyNode_ptr _node;
string _fmt;
float _mult;
float _offs;
bool _trunc;
mutable char _buf[1024];
};
FGTextLayer (int w = -1, int h = -1);
virtual ~FGTextLayer ();
virtual void draw ();
// Transfer pointer!!
virtual void addChunk (Chunk * chunk);
virtual void setColor (float r, float g, float b);
virtual void setPointSize (float size);
virtual void setFontName ( const string &name );
virtual void setFont (fntFont * font);
private:
void recalc_value () const;
typedef vector<Chunk *> chunk_list;
chunk_list _chunks;
float _color[4];
float _pointSize;
mutable string _font_name;
mutable string _value;
mutable SGTimeStamp _then;
mutable SGTimeStamp _now;
static fntRenderer text_renderer;
};
/**
* A group layer that switches among its children.
*
* The first layer that passes its condition will be drawn, and
* any following layers will be ignored.
*/
class FGSwitchLayer : public FGGroupLayer
{
public:
// Transfer pointers!!
FGSwitchLayer ();
virtual void draw ();
};
#endif // __PANEL_HXX
// end of panel.hxx

591
utils/fgpanel/panel_io.cxx Normal file
View file

@ -0,0 +1,591 @@
// panel_io.cxx - I/O for 2D panel.
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id: panel_io.cxx,v 1.26 2006/08/10 11:12:39 mfranz Exp $
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#include <string.h> // for strcmp()
#include <simgear/compiler.h>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/condition.hxx>
#include "panel.hxx"
#include "panel_io.hxx"
#include "ApplicationProperties.hxx"
////////////////////////////////////////////////////////////////////////
// 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<FGPanel>
FGReadablePanel::read(SGPropertyNode_ptr root)
{
SG_LOG( SG_COCKPIT, SG_INFO, "Reading properties for panel " <<
root->getStringValue("name", "[Unnamed Panel]") );
FGPanel * panel = new FGPanel(root);
panel->setWidth(root->getIntValue("w", 1024));
panel->setHeight(root->getIntValue("h", 443));
SG_LOG( SG_COCKPIT, SG_INFO, "Size=" << panel->getWidth() << "x" << panel->getHeight() );
// Assign the background texture, if any, or a bogus chequerboard.
//
string bgTexture = root->getStringValue("background");
if( !bgTexture.empty() )
panel->setBackground( new FGCroppedTexture( bgTexture ) );
panel->setBackgroundWidth( root->getDoubleValue( "background-width", 1.0 ) );
panel->setBackgroundHeight( root->getDoubleValue( "background-height", 1.0 ) );
SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << bgTexture );
//
// Get multibackground if any...
//
for( int i = 0; i < 8; i++ ) {
SGPropertyNode * mbgNode = root->getChild( "multibackground", i );
string mbgTexture;
if( mbgNode != NULL ) mbgTexture = mbgNode->getStringValue();
if( mbgTexture.empty() ) {
if( i == 0 ) break; // if first texture is missing, ignore the rest
else mbgTexture = "FOO"; // if others are missing - set default texture
}
panel->setMultiBackground( new FGCroppedTexture(mbgTexture), i );
SG_LOG( SG_COCKPIT, SG_INFO, "Set multi-background texture" << i << " to " << mbgTexture );
}
//
// Create each instrument.
//
SG_LOG( SG_COCKPIT, SG_INFO, "Reading panel instruments" );
const SGPropertyNode * instrument_group = root->getChild("instruments");
if (instrument_group != 0) {
int nInstruments = instrument_group->nChildren();
for (int i = 0; i < nInstruments; i++) {
const SGPropertyNode * node = instrument_group->getChild(i);
if (!strcmp(node->getName(), "instrument")) {
FGPanelInstrument * instrument = readInstrument(node);
if (instrument != 0)
panel->addInstrument(instrument);
} else {
SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName()
<< " in instruments section" );
}
}
}
SG_LOG( SG_COCKPIT, SG_INFO, "Done reading panel instruments" );
//
// Return the new panel.
//
return panel;
}
// end of panel_io.cxx

View file

@ -0,0 +1,40 @@
// panel_io.cxx - I/O for 2D panel.
//
// Written by David Megginson, started January 2000.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id: panel_io.hxx,v 1.6 2006/04/17 13:03:43 mfranz Exp $
#ifndef __PANEL_IO_HXX
#define __PANEL_IO_HXX
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
# include <windows.h>
#endif
#include "panel.hxx"
class FGReadablePanel : public FGPanel {
public:
static SGSharedPtr<FGPanel> read(SGPropertyNode_ptr root);
};
#endif // __PANEL_IO_HXX