////////////////////////////////////////////////////////////////////
SGPath mpath( globals->get_fg_root() );
- mpath.append( "materials" );
+ mpath.append( "materials.xml" );
if ( material_lib.load( mpath.str() ) ) {
} else {
SG_LOG( SG_GENERAL, SG_ALERT, "Error loading material lib!" );
#include <GL/gl.h>
#include <simgear/compiler.h>
+#include <simgear/misc/exception.hxx>
#include <string.h>
#include STL_STRING
}
-static bool local_file_exists( const string& path ) {
- sg_gzifstream in( path );
- if ( ! in.is_open() ) {
- return false;
- } else {
- return true;
- }
-}
-
-
// Load a library of material properties
bool FGMaterialLib::load( const string& mpath ) {
- string material_name;
- sg_gzifstream in( mpath );
- if ( ! in.is_open() ) {
- SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << mpath );
- exit(-1);
- }
-
-#ifndef __MWERKS__
- while ( ! in.eof() ) {
-#else
- char c = '\0';
- while ( in.get(c) && c != '\0' ) {
- in.putback(c);
-#endif
- // printf("%s", line);
-
- // strip leading white space and comments
- in >> skipcomment;
-
- // set to zero to prevent its value accidently being '{'
- // after a failed >> operation.
- char token = 0;
-
- in >> material_name;
-
- if ( material_name == "alias" ) {
- string src_mat, dst_mat;
- in >> dst_mat >> src_mat;
- SG_LOG( SG_GENERAL, SG_INFO, " Material alias: " << dst_mat <<
- " mapped to " << src_mat );
- FGNewMat *m = matlib[src_mat];
- if ( m != NULL ) {
- matlib[dst_mat] = m;
- m->ref();
- } else {
- SG_LOG( SG_GENERAL, SG_ALERT,
- "Bad material alias pointing to nonexistant material" );
- }
- } else {
- in >> token;
-
- if ( token == '{' ) {
- // Read the data into a temporary but stack allocated
- // copy of the structure
- FGNewMat tmp;
- in >> tmp;
-
- // create a pointer to a heap allocated copy of the structure
- FGNewMat *m = new FGNewMat;
- *m = tmp;
- m->ref();
-
- // build the ssgSimpleState
- SGPath tex_path( globals->get_fg_root() );
- tex_path.append( "Textures.high" );
- tex_path.append( m->get_texture_name() );
- if ( ! local_file_exists(tex_path.str())
- || general.get_glMaxTexSize() < 512 ) {
- tex_path = SGPath( globals->get_fg_root() );
- tex_path.append( "Textures" );
- tex_path.append( m->get_texture_name() );
- }
-
- SG_LOG( SG_TERRAIN, SG_INFO, " Loading material "
- << material_name << " (" << tex_path.c_str() << ")");
-
- GLenum shade_model = GL_SMOOTH;
- if ( fgGetBool("/sim/rendering/shading") ) {
- shade_model = GL_SMOOTH;
- } else {
- shade_model = GL_FLAT;
- }
-
- m->set_texture_name( tex_path.str() );
- m->build_ssg_state( shade_model,
- fgGetBool("/sim/rendering/textures"),
- false );
-
-#if EXTRA_DEBUG
- m->dump_info();
-#endif
-
- matlib[material_name] = m;
- }
- }
+ SGPropertyNode materials;
+
+ cout << "Reading materials from " << mpath << endl;
+ try {
+ readProperties(mpath, &materials);
+ } catch (const sg_exception &ex) {
+ SG_LOG(SG_INPUT, SG_ALERT, "Error reading materials: " << ex.getMessage());
+ throw ex;
+ }
+
+ int nMaterials = materials.nChildren();
+ for (int i = 0; i < nMaterials; i++) {
+ const SGPropertyNode * node = materials.getChild(i);
+ if (node->getName() == "material") {
+ FGNewMat * m = new FGNewMat(node);
+
+ vector<const SGPropertyNode *>names = node->getChildren("name");
+ for (int j = 0; j < names.size(); j++) {
+ m->ref();
+ matlib[names[j]->getStringValue()] = m;
+ SG_LOG( SG_TERRAIN, SG_INFO, " Loading material "
+ << names[j]->getStringValue());
+ }
+ } else {
+ SG_LOG(SG_INPUT, SG_ALERT,
+ "Skipping bad material entry " << node->getName());
}
+ }
// hard coded light state
ssgSimpleState *lights = new ssgSimpleState;
lights->disable( GL_ALPHA_TEST );
lights->disable( GL_LIGHTING );
- FGNewMat *m = new FGNewMat;
- m->set_ssg_state( lights );
- matlib["LIGHTS"] = m;
+ matlib["LIGHTS"] = new FGNewMat(lights);
return true;
}
string tex_name = full_path.substr( pos + 1 );
string tex_path = full_path.substr( 0, pos );
- FGNewMat *m = new FGNewMat( mat_name, full_path );
-
SG_LOG( SG_TERRAIN, SG_INFO, " Loading material "
<< mat_name << " (" << full_path << ")");
-#if EXTRA_DEBUG
- m->dump_info();
-#endif
-
- GLenum shade_model = GL_SMOOTH;
- if ( fgGetBool("/sim/rendering/shading") ) {
- shade_model = GL_SMOOTH;
- } else {
- shade_model = GL_FLAT;
- }
-
- m->build_ssg_state( shade_model,
- fgGetBool("/sim/rendering/textures"),
- true );
-
- material_lib.matlib[mat_name] = m;
+ material_lib.matlib[mat_name] = new FGNewMat(full_path);
return true;
}
// Load a library of material properties
bool FGMaterialLib::add_item ( const string &mat_name, ssgSimpleState *state )
{
- FGNewMat *m = new FGNewMat( mat_name );
- m->set_ssg_state( state );
+ FGNewMat *m = new FGNewMat(state);
SG_LOG( SG_TERRAIN, SG_INFO, " Loading material given a premade "
<< "ssgSimpleState = " << mat_name );
-#if EXTRA_DEBUG
- m->dump_info();
-#endif
-
material_lib.matlib[mat_name] = m;
return true;
for ( material_map_iterator it = begin(); it != end(); it++ ) {
const string &key = it->first;
FGNewMat *slot = it->second;
- // SG_LOG( SG_GENERAL, SG_INFO, "slot = " << slot );
- if ( ! slot->get_texture_loaded() ) {
- SG_LOG( SG_GENERAL, SG_INFO, "Loading deferred texture for "
- << key );
-#ifdef PLIB_1_2_X
- slot->get_textured()->
- setTexture( (char *)slot->get_texture_name_c_str(), 0, 0 );
-#else
- slot->get_textured()->
- setTexture( (char *)slot->get_texture_name_c_str(), 0, 0, 1 );
-#endif
- slot->set_texture_loaded( true );
- }
+ if (slot->load_texture())
+ return;
}
}
void set_step (int step);
int get_step ();
- // Load one pending "deferred" texture. Return true if a texture
- // loaded successfully, false if no pending, or error.
+ /**
+ * Load the next deferred texture, if there is any.
+ */
void load_next_deferred();
material_map_iterator begin() { return matlib.begin(); }
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sgstream.hxx>
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+
#include "newmat.hxx"
-// Constructor
-FGNewMat::FGNewMat ( void ) {
- wrapu = wrapv = 1;
- mipmap = 1;
- light_coverage = -1.0;
- refcount = 0;
+static bool
+local_file_exists( const string& path ) {
+ sg_gzifstream in( path );
+ if ( ! in.is_open() ) {
+ return false;
+ } else {
+ return true;
+ }
}
// Constructor
-FGNewMat::FGNewMat ( const string &name )
+
+FGNewMat::FGNewMat ()
+ : texture_path(""),
+ state(0),
+ textured(0),
+ nontextured(0),
+ alpha(false),
+ xsize(0),
+ ysize(0),
+ wrapu(true),
+ wrapv(true),
+ mipmap(true),
+ texture_loaded(false),
+ refcount(0)
+{
+ for (int i = 0; i < 4; i++)
+ ambient[i] = diffuse[i] = specular[i] = emission[i] = 0.0;
+}
+
+FGNewMat::FGNewMat (const SGPropertyNode * props)
{
- FGNewMat( name, name );
+ FGNewMat();
+ read_properties(props);
+ build_ssg_state(false);
}
+FGNewMat::FGNewMat (const string &texture_path)
+{
+ FGNewMat();
+ build_ssg_state(true);
+}
-// Constructor
-FGNewMat::FGNewMat ( const string &mat_name, const string &tex_name )
+FGNewMat::FGNewMat (ssgSimpleState * s)
+{
+ FGNewMat();
+ set_ssg_state(s);
+}
+
+
+bool
+FGNewMat::load_texture ()
{
- material_name = mat_name;
- texture_name = tex_name;
- xsize = ysize = 0;
- wrapu = wrapv = 1;
- mipmap = 1;
- alpha = 0;
- ambient[0] = ambient[1] = ambient[2] = ambient[3] = 1.0;
- diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = 1.0;
- specular[0] = specular[1] = specular[2] = specular[3] = 1.0;
- emission[0] = emission[1] = emission[2] = emission[3] = 1.0;
- light_coverage = -1.0;
- refcount = 0;
+ if (texture_loaded) {
+ return false;
+ } else {
+ SG_LOG( SG_GENERAL, SG_INFO, "Loading deferred texture " << texture_path );
+#ifdef PLIB_1_2_X
+ textured->setTexture((char *)texture_path.c_str(), wrapu, wrapv );
+#else
+ textured->setTexture((char *)texture_path.c_str(), wrapu, wrapv, mipmap );
+#endif
+ texture_loaded = true;
+ return true;
+ }
}
-void FGNewMat::build_ssg_state( GLenum shade_model, bool texture_default,
- bool defer_tex_load )
+void FGNewMat::build_ssg_state( bool defer_tex_load )
{
+ GLenum shade_model =
+ (fgGetBool("/sim/rendering/shading") ? GL_SMOOTH : GL_FLAT);
+ bool texture_default = fgGetBool("/sim/rendering/textures");
+
state = new ssgStateSelector(2);
state->ref();
textured->disable( GL_BLEND );
textured->disable( GL_ALPHA_TEST );
if ( !defer_tex_load ) {
- textured->setTexture( (char *)texture_name.c_str(), wrapu, wrapv );
+ textured->setTexture( (char *)texture_path.c_str(), wrapu, wrapv );
texture_loaded = true;
} else {
texture_loaded = false;
}
- // cout << "wrap u = " << wrapu << " wrapv = " << wrapv << endl;
textured->enable( GL_COLOR_MATERIAL );
textured->setColourMaterial( GL_AMBIENT_AND_DIFFUSE );
textured->setMaterial( GL_EMISSION, 0, 0, 0, 1 );
nontextured->disable( GL_ALPHA_TEST );
nontextured->disable( GL_COLOR_MATERIAL );
- /* cout << "ambient = " << ambient[0] << "," << ambient[1]
- << "," << ambient[2] << endl; */
nontextured->setMaterial ( GL_AMBIENT,
ambient[0], ambient[1],
ambient[2], ambient[3] ) ;
}
-void FGNewMat::dump_info () {
- SG_LOG( SG_TERRAIN, SG_INFO, "{" << endl << " texture = "
- << texture_name );
- SG_LOG( SG_TERRAIN, SG_INFO, " xsize = " << xsize );
- SG_LOG( SG_TERRAIN, SG_INFO, " ysize = " << ysize );
- SG_LOG( SG_TERRAIN, SG_INFO, " ambient = " << ambient[0] << " "
- << ambient[1] <<" "<< ambient[2] <<" "<< ambient[3] );
- SG_LOG( SG_TERRAIN, SG_INFO, " diffuse = " << diffuse[0] << " "
- << diffuse[1] << " " << diffuse[2] << " " << diffuse[3] );
- SG_LOG( SG_TERRAIN, SG_INFO, " specular = " << specular[0] << " "
- << specular[1] << " " << specular[2] << " " << specular[3]);
- SG_LOG( SG_TERRAIN, SG_INFO, " emission = " << emission[0] << " "
- << emission[1] << " " << emission[2] << " " << emission[3]);
- SG_LOG( SG_TERRAIN, SG_INFO, " alpha = " << alpha << endl <<"}" );
-
-}
-
-
// Destructor
FGNewMat::~FGNewMat ( void ) {
}
-istream&
-operator >> ( istream& in, FGNewMat& m )
+void
+FGNewMat::read_properties (const SGPropertyNode * props)
{
- string token;
-
- for (;;) {
- in >> token;
- if ( token == "texture" ) {
- in >> token >> m.texture_name;
- } else if ( token == "xsize" ) {
- in >> token >> m.xsize;
- } else if ( token == "ysize" ) {
- in >> token >> m.ysize;
- } else if ( token == "wrapu" ) {
- in >> token >> m.wrapu;
- } else if ( token == "wrapv" ) {
- in >> token >> m.wrapv;
- } else if ( token == "mipmap" ) {
- in >> token >> m.mipmap;
- } else if ( token == "ambient" ) {
- in >> token >> m.ambient[0] >> m.ambient[1]
- >> m.ambient[2] >> m.ambient[3];
- } else if ( token == "diffuse" ) {
- in >> token >> m.diffuse[0] >> m.diffuse[1]
- >> m.diffuse[2] >> m.diffuse[3];
- } else if ( token == "specular" ) {
- in >> token >> m.specular[0] >> m.specular[1]
- >> m.specular[2] >> m.specular[3];
- } else if ( token == "emission" ) {
- in >> token >> m.emission[0] >> m.emission[1]
- >> m.emission[2] >> m.emission[3];
- } else if ( token == "alpha" ) {
- in >> token >> token;
- if ( token == "yes" ) {
- m.alpha = 1;
- } else if ( token == "no" ) {
- m.alpha = 0;
- } else {
- SG_LOG( SG_TERRAIN, SG_INFO, "Bad alpha value " << token );
- }
- } else if ( token == "light-coverage" ) {
- in >> token >> m.light_coverage;
- } else if ( token[0] == '}' ) {
- break;
- }
- }
-
- return in;
+ // Get the path to the texture
+ string tname = props->getStringValue("texture", "unknown.rgb");
+ SGPath tpath(globals->get_fg_root());
+ tpath.append("Textures.high");
+ tpath.append(tname);
+ if (!local_file_exists(tpath.str())) {
+ tpath = SGPath(globals->get_fg_root());
+ tpath.append("Textures");
+ tpath.append(tname);
+ }
+ texture_path = tpath.str();
+
+ xsize = props->getDoubleValue("xsize", 0.0);
+ ysize = props->getDoubleValue("ysize", 0.0);
+ wrapu = props->getBoolValue("wrapu", true);
+ wrapv = props->getBoolValue("wrapv", true);
+ mipmap = props->getBoolValue("mipmap", true);
+ light_coverage = props->getDoubleValue("light-coverage");
+
+ ambient[0] = props->getDoubleValue("ambient/r", 0.0);
+ ambient[1] = props->getDoubleValue("ambient/g", 0.0);
+ ambient[2] = props->getDoubleValue("ambient/b", 0.0);
+ ambient[3] = props->getDoubleValue("ambient/a", 0.0);
+
+ diffuse[0] = props->getDoubleValue("diffuse/r", 0.0);
+ diffuse[1] = props->getDoubleValue("diffuse/g", 0.0);
+ diffuse[2] = props->getDoubleValue("diffuse/b", 0.0);
+ diffuse[3] = props->getDoubleValue("diffuse/a", 0.0);
+
+ specular[0] = props->getDoubleValue("specular/r", 0.0);
+ specular[1] = props->getDoubleValue("specular/g", 0.0);
+ specular[2] = props->getDoubleValue("specular/b", 0.0);
+ specular[3] = props->getDoubleValue("specular/a", 0.0);
+
+ emission[0] = props->getDoubleValue("emissive/r", 0.0);
+ emission[1] = props->getDoubleValue("emissive/g", 0.0);
+ emission[2] = props->getDoubleValue("emissive/b", 0.0);
+ emission[3] = props->getDoubleValue("emissive/a", 0.0);
}
-// newmat.hxx -- class to handle material properties
+// newmat.hxx -- a material in the scene graph.
+// TODO: this class needs to be renamed.
//
// Written by Curtis Olson, started May 1998.
+// Overhauled by David Megginson, December 2001
//
// Copyright (C) 1998 - 2000 Curtis L. Olson - curt@flightgear.org
//
#ifndef _NEWMAT_HXX
#define _NEWMAT_HXX
-
#ifndef __cplusplus
# error This library requires C++
#endif
#include <plib/ssg.h>
#include <simgear/compiler.h>
+#include <simgear/misc/props.hxx>
#include <GL/glut.h>
SG_USING_STD(string);
-// MSVC++ 6.0 kuldge - Need forward declaration of friends.
-class FGNewMat;
-istream& operator >> ( istream& in, FGNewMat& m );
-
-// Material property class
+/**
+ * A material in the scene graph.
+ *
+ * A material represents information about a single surface type
+ * in the 3D scene graph, including texture, colour, lighting,
+ * tiling, and so on; most of the materials in FlightGear are
+ * defined in the $FG_ROOT/materials.xml file, and can be changed
+ * at runtime.
+ */
class FGNewMat {
-private:
+public:
- // names
- string material_name;
- string texture_name;
+\f
+ ////////////////////////////////////////////////////////////////////
+ // Public Constructors.
+ ////////////////////////////////////////////////////////////////////
- // pointers to ssg states
- ssgStateSelector *state;
- ssgSimpleState *textured;
- ssgSimpleState *nontextured;
+ /**
+ * Construct a material from a set of properties.
+ *
+ * @param props A property node containing subnodes with the
+ * state information for the material. This node is usually
+ * loaded from the $FG_ROOT/materials.xml file.
+ */
+ FGNewMat (const SGPropertyNode * props);
- // alpha texture?
- int alpha;
- // texture size
- double xsize, ysize;
+ /**
+ * Construct a material from an absolute texture path.
+ *
+ * @param texture_path A string containing an absolute path
+ * to a texture file (usually RGB).
+ */
+ FGNewMat (const string &texture_path);
- // wrap texture?
- int wrapu, wrapv;
- // use mipmapping?
- int mipmap;
+ /**
+ * Construct a material around an existing SSG state.
+ *
+ * This constructor allows the application to create a custom,
+ * low-level state for the scene graph and wrap a material around
+ * it. Note: the pointer ownership is transferred to the material.
+ *
+ * @param s The SSG state for this material.
+ */
+ FGNewMat (ssgSimpleState * s);
- // coverage of night lighting. This number is specifically the
- // amount of area coverage we give a single light. The size of a
- // triangle is divided by this number and that is the number of
- // lights assigned to that triangle. Lower numbers mean more
- // dense light ocverage.
- double light_coverage;
+ /**
+ * Destructor.
+ */
+ virtual ~FGNewMat ( void );
- // material properties
- sgVec4 ambient, diffuse, specular, emission;
- // true if texture loading deferred, and not yet loaded
- bool texture_loaded;
+\f
+ ////////////////////////////////////////////////////////////////////
+ // Public methods.
+ ////////////////////////////////////////////////////////////////////
- // ref count so we can properly delete if we have multiple
- // pointers to this record
- int refcount;
+ /**
+ * Force the texture to load if it hasn't already.
+ *
+ * @return true if the texture loaded, false if it was loaded
+ * already.
+ */
+ virtual bool load_texture ();
-public:
- // Constructor
- FGNewMat ( void );
- FGNewMat ( const string& name );
- FGNewMat ( const string &mat_name, const string &tex_name );
+ /**
+ * Get the textured state.
+ */
+ virtual inline ssgSimpleState *get_textured () { return textured; }
- // Destructor
- ~FGNewMat ( void );
- friend istream& operator >> ( istream& in, FGNewMat& m );
+ /**
+ * Get the xsize of the texture, in meters.
+ */
+ virtual inline double get_xsize() const { return xsize; }
- // void load_texture( const string& root );
- void build_ssg_state( GLenum shade_model, bool texture_default,
- bool defer_tex_load = false );
- void set_ssg_state( ssgSimpleState *s );
- inline string get_material_name() const { return material_name; }
- inline void set_material_name( const string& n ) { material_name = n; }
+ /**
+ * Get the ysize of the texture, in meters.
+ */
+ virtual inline double get_ysize() const { return ysize; }
- inline string get_texture_name() const { return texture_name; }
- inline const char *get_texture_name_c_str() const {
- return texture_name.c_str();
- }
- inline void set_texture_name( const string& n ) { texture_name = n; }
- inline ssgSimpleState *get_textured() { return textured; }
+ /**
+ * Get the light coverage.
+ *
+ * A smaller number means more generated night lighting.
+ *
+ * @return The area (m^2?) covered by each light.
+ */
+ virtual inline double get_light_coverage () const { return light_coverage; }
- inline double get_xsize() const { return xsize; }
- inline double get_ysize() const { return ysize; }
- inline void set_xsize( double x ) { xsize = x; }
- inline void set_ysize( double y ) { ysize = y; }
- inline float *get_ambient() { return ambient; }
- inline float *get_diffuse() { return diffuse; }
- inline float *get_specular() { return specular; }
- inline float *get_emission() { return emission; }
- inline void set_ambient( sgVec4 a ) { sgCopyVec4( ambient, a ); }
- inline void set_diffuse( sgVec4 d ) { sgCopyVec4( diffuse, d ); }
- inline void set_specular( sgVec4 s ) { sgCopyVec4( specular, s ); }
- inline void set_emission( sgVec4 e ) { sgCopyVec4( emission, e ); }
+ /**
+ * Get the current state.
+ */
+ virtual inline ssgStateSelector *get_state() const { return state; }
- inline bool get_texture_loaded() const { return texture_loaded; }
- inline void set_texture_loaded( bool val ) { texture_loaded = val; }
- inline double get_light_coverage () const { return light_coverage; }
- inline void set_light_coverage (double coverage) {
- light_coverage = coverage;
- }
+ /**
+ * Add a reference to the texture.
+ */
+ virtual inline void ref() { refcount++; }
- inline ssgStateSelector *get_state() const { return state; }
- inline void ref() { refcount++; }
- inline void deRef() { refcount--; }
- inline int getRef() const { return refcount; }
+ /**
+ * Remove a reference from the texture.
+ */
+ virtual inline void deRef() { refcount--; }
- void dump_info();
-};
+ /**
+ * Get the number of references to the texture.
+ */
+ virtual inline int getRef() const { return refcount; }
-#endif // _NEWMAT_HXX
+private:
+
+\f
+ ////////////////////////////////////////////////////////////////////
+ // Internal state.
+ ////////////////////////////////////////////////////////////////////
+
+ // names
+ string texture_path;
+
+ // pointers to ssg states
+ ssgStateSelector *state;
+ ssgSimpleState *textured;
+ ssgSimpleState *nontextured;
+
+ // alpha texture?
+ bool alpha;
+
+ // texture size
+ double xsize, ysize;
+
+ // wrap texture?
+ bool wrapu, wrapv;
+ // use mipmapping?
+ int mipmap;
+
+ // coverage of night lighting.
+ double light_coverage;
+
+ // material properties
+ sgVec4 ambient, diffuse, specular, emission;
+
+ // true if texture loading deferred, and not yet loaded
+ bool texture_loaded;
+
+ // ref count so we can properly delete if we have multiple
+ // pointers to this record
+ int refcount;
+
+
+\f
+ ////////////////////////////////////////////////////////////////////
+ // Internal constructors and methods.
+ ////////////////////////////////////////////////////////////////////
+
+ FGNewMat ();
+ FGNewMat (const FGNewMat &mat); // unimplemented
+
+ void read_properties (const SGPropertyNode * props);
+ void build_ssg_state(bool defer_tex_load = false);
+ void set_ssg_state( ssgSimpleState *s );
+
+};
+
+#endif // _NEWMAT_HXX