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:
parent
1456635c55
commit
f44dd24462
22 changed files with 3970 additions and 0 deletions
7
utils/fgpanel/.gitignore
vendored
Normal file
7
utils/fgpanel/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
fgpanel
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
.deps
|
||||||
|
*.Po
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
31
utils/fgpanel/ApplicationProperties.hxx
Normal file
31
utils/fgpanel/ApplicationProperties.hxx
Normal 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
|
208
utils/fgpanel/FGFontCache.cxx
Normal file
208
utils/fgpanel/FGFontCache.cxx
Normal 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
|
||||||
|
|
86
utils/fgpanel/FGFontCache.hxx
Normal file
86
utils/fgpanel/FGFontCache.hxx
Normal 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
|
94
utils/fgpanel/FGGLApplication.cxx
Normal file
94
utils/fgpanel/FGGLApplication.cxx
Normal 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();
|
||||||
|
}
|
48
utils/fgpanel/FGGLApplication.hxx
Normal file
48
utils/fgpanel/FGGLApplication.hxx
Normal 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
|
142
utils/fgpanel/FGPNGTextureLoader.cxx
Normal file
142
utils/fgpanel/FGPNGTextureLoader.cxx
Normal 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;
|
||||||
|
}
|
26
utils/fgpanel/FGPNGTextureLoader.hxx
Normal file
26
utils/fgpanel/FGPNGTextureLoader.hxx
Normal 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
|
279
utils/fgpanel/FGPanelApplication.cxx
Normal file
279
utils/fgpanel/FGPanelApplication.cxx
Normal 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;
|
55
utils/fgpanel/FGPanelApplication.hxx
Normal file
55
utils/fgpanel/FGPanelApplication.hxx
Normal 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
|
154
utils/fgpanel/FGPanelProtocol.cxx
Normal file
154
utils/fgpanel/FGPanelProtocol.cxx
Normal 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();
|
||||||
|
}
|
41
utils/fgpanel/FGPanelProtocol.hxx
Normal file
41
utils/fgpanel/FGPanelProtocol.hxx
Normal 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
|
504
utils/fgpanel/FGRGBTextureLoader.cxx
Normal file
504
utils/fgpanel/FGRGBTextureLoader.cxx
Normal 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;
|
||||||
|
}
|
||||||
|
|
26
utils/fgpanel/FGRGBTextureLoader.hxx
Normal file
26
utils/fgpanel/FGRGBTextureLoader.hxx
Normal 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
|
27
utils/fgpanel/FGTextureLoaderInterface.hxx
Normal file
27
utils/fgpanel/FGTextureLoaderInterface.hxx
Normal 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
20
utils/fgpanel/Makefile.am
Normal 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
148
utils/fgpanel/README
Normal 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
30
utils/fgpanel/main.cxx
Normal 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
962
utils/fgpanel/panel.cxx
Normal 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
451
utils/fgpanel/panel.hxx
Normal 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
591
utils/fgpanel/panel_io.cxx
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
40
utils/fgpanel/panel_io.hxx
Normal file
40
utils/fgpanel/panel_io.hxx
Normal 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
|
Loading…
Reference in a new issue