Separate out the SGMaterial::Object{,Group} code into it's own source file.

This commit is contained in:
curt
2003-05-15 15:08:39 +00:00
parent 4b0b49d1ed
commit bb238c4106
5 changed files with 465 additions and 350 deletions

View File

@@ -6,10 +6,12 @@ noinst_HEADERS =
include_HEADERS = \
mat.hxx \
matlib.hxx
matlib.hxx \
matobj.hxx
libsgmaterial_a_SOURCES = \
mat.cxx \
matlib.cxx
matlib.cxx \
matobj.cxx
INCLUDES = -I$(top_srcdir)

View File

@@ -66,187 +66,6 @@ local_file_exists( const string& path ) {
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGMaterial::Object.
////////////////////////////////////////////////////////////////////////
SGMaterial::Object::Object (const SGPropertyNode * node, double range_m)
: _models_loaded(false),
_coverage_m2(node->getDoubleValue("coverage-m2", 1000000)),
_range_m(range_m)
{
// Sanity check
if (_coverage_m2 < 1000) {
SG_LOG(SG_INPUT, SG_ALERT, "Random object coverage " << _coverage_m2
<< " is too small, forcing, to 1000");
_coverage_m2 = 1000;
}
// Note all the model paths
vector <SGPropertyNode_ptr> path_nodes = node->getChildren("path");
for (unsigned int i = 0; i < path_nodes.size(); i++)
_paths.push_back(path_nodes[i]->getStringValue());
// Note the heading type
string hdg = node->getStringValue("heading-type", "fixed");
if (hdg == "fixed") {
_heading_type = HEADING_FIXED;
} else if (hdg == "billboard") {
_heading_type = HEADING_BILLBOARD;
} else if (hdg == "random") {
_heading_type = HEADING_RANDOM;
} else {
_heading_type = HEADING_FIXED;
SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg
<< "; using 'fixed' instead.");
}
// uncomment to preload models
// load_models();
}
SGMaterial::Object::~Object ()
{
for (unsigned int i = 0; i < _models.size(); i++) {
if (_models[i] != 0) {
_models[i]->deRef();
_models[i] = 0;
}
}
}
int
SGMaterial::Object::get_model_count( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
load_models( loader, fg_root, prop_root, sim_time_sec );
return _models.size();
}
inline void
SGMaterial::Object::load_models ( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
// Load model only on demand
if (!_models_loaded) {
for (unsigned int i = 0; i < _paths.size(); i++) {
ssgEntity *entity = loader->load_model( fg_root, _paths[i],
prop_root, sim_time_sec );
if (entity != 0) {
// FIXME: this stuff can be handled
// in the XML wrapper as well (at least,
// the billboarding should be handled
// there).
float ranges[] = {0, _range_m};
ssgRangeSelector * lod = new ssgRangeSelector;
lod->ref();
lod->setRanges(ranges, 2);
if (_heading_type == HEADING_BILLBOARD) {
ssgCutout * cutout = new ssgCutout(false);
cutout->addKid(entity);
lod->addKid(cutout);
} else {
lod->addKid(entity);
}
_models.push_back(lod);
} else {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
}
}
}
_models_loaded = true;
}
ssgEntity *
SGMaterial::Object::get_model( int index,
SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
load_models( loader, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
return _models[index];
}
ssgEntity *
SGMaterial::Object::get_random_model( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
load_models( loader, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
int nModels = _models.size();
int index = int(sg_random() * nModels);
if (index >= nModels)
index = 0;
return _models[index];
}
double
SGMaterial::Object::get_coverage_m2 () const
{
return _coverage_m2;
}
SGMaterial::Object::HeadingType
SGMaterial::Object::get_heading_type () const
{
return _heading_type;
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGMaterial::ObjectGroup.
////////////////////////////////////////////////////////////////////////
SGMaterial::ObjectGroup::ObjectGroup (SGPropertyNode * node)
: _range_m(node->getDoubleValue("range-m", 2000))
{
// Load the object subnodes
vector<SGPropertyNode_ptr> object_nodes =
((SGPropertyNode *)node)->getChildren("object");
for (unsigned int i = 0; i < object_nodes.size(); i++) {
const SGPropertyNode * object_node = object_nodes[i];
if (object_node->hasChild("path"))
_objects.push_back(new Object(object_node, _range_m));
else
SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object");
}
}
SGMaterial::ObjectGroup::~ObjectGroup ()
{
for (unsigned int i = 0; i < _objects.size(); i++) {
delete _objects[i];
_objects[i] = 0;
}
}
double
SGMaterial::ObjectGroup::get_range_m () const
{
return _range_m;
}
int
SGMaterial::ObjectGroup::get_object_count () const
{
return _objects.size();
}
SGMaterial::Object *
SGMaterial::ObjectGroup::get_object (int index) const
{
return _objects[index];
}
////////////////////////////////////////////////////////////////////////
// Constructors and destructor.
@@ -334,7 +153,7 @@ SGMaterial::read_properties( const string &fg_root, const SGPropertyNode * props
vector<SGPropertyNode_ptr> object_group_nodes =
((SGPropertyNode *)props)->getChildren("object-group");
for (unsigned int i = 0; i < object_group_nodes.size(); i++)
object_groups.push_back(new ObjectGroup(object_group_nodes[i]));
object_groups.push_back(new SGMatObjectGroup(object_group_nodes[i]));
}
@@ -429,4 +248,4 @@ void SGMaterial::set_ssg_state( ssgSimpleState *s )
texture_loaded = true;
}
// end of newmat.cxx
// end of mat.cxx

View File

@@ -40,6 +40,8 @@
#include <simgear/props/props.hxx>
#include <simgear/scene/model/loader.hxx>
#include "matobj.hxx"
SG_USING_STD(string);
@@ -56,169 +58,6 @@ class SGMaterial {
public:
//////////////////////////////////////////////////////////////////////
// Inner classes.
//////////////////////////////////////////////////////////////////////
class ObjectGroup;
/**
* A randomly-placeable object.
*
* SGMaterial uses this class to keep track of the model(s) and
* parameters for a single instance of a randomly-placeable object.
* The object can have more than one variant model (i.e. slightly
* different shapes of trees), but they are considered equivalent
* and interchangeable.
*/
class Object
{
public:
/**
* The heading type for a randomly-placed object.
*/
enum HeadingType {
HEADING_FIXED,
HEADING_BILLBOARD,
HEADING_RANDOM
};
/**
* Get the number of variant models available for the object.
*
* @return The number of variant models.
*/
int get_model_count( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
/**
* Get a specific variant model for the object.
*
* @param index The index of the model.
* @return The model.
*/
ssgEntity *get_model( int index,
SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
/**
* Get a randomly-selected variant model for the object.
*
* @return A randomly select model from the variants.
*/
ssgEntity *get_random_model( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
/**
* Get the average number of meters^2 occupied by each instance.
*
* @return The coverage in meters^2.
*/
double get_coverage_m2 () const;
/**
* Get the heading type for the object.
*
* @return The heading type.
*/
HeadingType get_heading_type () const;
protected:
friend class ObjectGroup;
Object (const SGPropertyNode * node, double range_m);
virtual ~Object ();
private:
/**
* Actually load the models.
*
* This class uses lazy loading so that models won't be held
* in memory for materials that are never referenced.
*/
void load_models( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
vector<string> _paths;
mutable vector<ssgEntity *> _models;
mutable bool _models_loaded;
double _coverage_m2;
double _range_m;
HeadingType _heading_type;
};
/**
* A collection of related objects with the same visual range.
*
* Grouping objects with the same range together significantly
* reduces the memory requirements of randomly-placed objects.
* Each SGMaterial instance keeps a (possibly-empty) list of
* object groups for placing randomly on the scenery.
*/
class ObjectGroup
{
public:
virtual ~ObjectGroup ();
/**
* Get the visual range of the object in meters.
*
* @return The visual range.
*/
double get_range_m () const;
/**
* Get the number of objects in the group.
*
* @return The number of objects.
*/
int get_object_count () const;
/**
* Get a specific object.
*
* @param index The object's index, zero-based.
* @return The object selected.
*/
Object * get_object (int index) const;
protected:
friend class SGMaterial;
ObjectGroup (SGPropertyNode * node);
private:
double _range_m;
vector<Object *> _objects;
};
////////////////////////////////////////////////////////////////////
// Public Constructors.
@@ -311,7 +150,7 @@ public:
/**
* Get a randomly-placed object for this material.
*/
virtual ObjectGroup * get_object_group (int index) const {
virtual SGMatObjectGroup * get_object_group (int index) const {
return object_groups[index];
}
@@ -383,7 +222,7 @@ private:
// true if texture loading deferred, and not yet loaded
bool texture_loaded;
vector<ObjectGroup *> object_groups;
vector<SGMatObjectGroup *> object_groups;
// ref count so we can properly delete if we have multiple
// pointers to this record

View File

@@ -0,0 +1,250 @@
// matobj.cxx -- class to handle material properties
//
// Written by Curtis Olson, started May 1998.
//
// Copyright (C) 1998 - 2003 Curtis L. Olson - curt@flightgear.org
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear/compiler.h>
#include <map>
SG_USING_STD(map);
#include <simgear/compiler.h>
#ifdef SG_MATH_EXCEPTION_CLASH
# include <math.h>
#endif
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/scene/model/loader.hxx>
#include "matobj.hxx"
////////////////////////////////////////////////////////////////////////
// Local static functions.
////////////////////////////////////////////////////////////////////////
/**
* Internal method to test whether a file exists.
*
* TODO: this should be moved to a SimGear library of local file
* functions.
*/
static inline bool
local_file_exists( const string& path ) {
sg_gzifstream in( path );
if ( ! in.is_open() ) {
return false;
} else {
return true;
}
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGMatObject.
////////////////////////////////////////////////////////////////////////
SGMatObject::SGMatObject (const SGPropertyNode * node, double range_m)
: _models_loaded(false),
_coverage_m2(node->getDoubleValue("coverage-m2", 1000000)),
_range_m(range_m)
{
// Sanity check
if (_coverage_m2 < 1000) {
SG_LOG(SG_INPUT, SG_ALERT, "Random object coverage " << _coverage_m2
<< " is too small, forcing, to 1000");
_coverage_m2 = 1000;
}
// Note all the model paths
vector <SGPropertyNode_ptr> path_nodes = node->getChildren("path");
for (unsigned int i = 0; i < path_nodes.size(); i++)
_paths.push_back(path_nodes[i]->getStringValue());
// Note the heading type
string hdg = node->getStringValue("heading-type", "fixed");
if (hdg == "fixed") {
_heading_type = HEADING_FIXED;
} else if (hdg == "billboard") {
_heading_type = HEADING_BILLBOARD;
} else if (hdg == "random") {
_heading_type = HEADING_RANDOM;
} else {
_heading_type = HEADING_FIXED;
SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg
<< "; using 'fixed' instead.");
}
// uncomment to preload models
// load_models();
}
SGMatObject::~SGMatObject ()
{
for (unsigned int i = 0; i < _models.size(); i++) {
if (_models[i] != 0) {
_models[i]->deRef();
_models[i] = 0;
}
}
}
int
SGMatObject::get_model_count( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
load_models( loader, fg_root, prop_root, sim_time_sec );
return _models.size();
}
inline void
SGMatObject::load_models ( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
// Load model only on demand
if (!_models_loaded) {
for (unsigned int i = 0; i < _paths.size(); i++) {
ssgEntity *entity = loader->load_model( fg_root, _paths[i],
prop_root, sim_time_sec );
if (entity != 0) {
// FIXME: this stuff can be handled
// in the XML wrapper as well (at least,
// the billboarding should be handled
// there).
float ranges[] = {0, _range_m};
ssgRangeSelector * lod = new ssgRangeSelector;
lod->ref();
lod->setRanges(ranges, 2);
if (_heading_type == HEADING_BILLBOARD) {
ssgCutout * cutout = new ssgCutout(false);
cutout->addKid(entity);
lod->addKid(cutout);
} else {
lod->addKid(entity);
}
_models.push_back(lod);
} else {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
}
}
}
_models_loaded = true;
}
ssgEntity *
SGMatObject::get_model( int index,
SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
load_models( loader, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
return _models[index];
}
ssgEntity *
SGMatObject::get_random_model( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec )
{
load_models( loader, fg_root, prop_root, sim_time_sec ); // comment this out if preloading models
int nModels = _models.size();
int index = int(sg_random() * nModels);
if (index >= nModels)
index = 0;
return _models[index];
}
double
SGMatObject::get_coverage_m2 () const
{
return _coverage_m2;
}
SGMatObject::HeadingType
SGMatObject::get_heading_type () const
{
return _heading_type;
}
////////////////////////////////////////////////////////////////////////
// Implementation of SGMatObjectGroup.
////////////////////////////////////////////////////////////////////////
SGMatObjectGroup::SGMatObjectGroup (SGPropertyNode * node)
: _range_m(node->getDoubleValue("range-m", 2000))
{
// Load the object subnodes
vector<SGPropertyNode_ptr> object_nodes =
((SGPropertyNode *)node)->getChildren("object");
for (unsigned int i = 0; i < object_nodes.size(); i++) {
const SGPropertyNode * object_node = object_nodes[i];
if (object_node->hasChild("path"))
_objects.push_back(new SGMatObject(object_node, _range_m));
else
SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object");
}
}
SGMatObjectGroup::~SGMatObjectGroup ()
{
for (unsigned int i = 0; i < _objects.size(); i++) {
delete _objects[i];
_objects[i] = 0;
}
}
double
SGMatObjectGroup::get_range_m () const
{
return _range_m;
}
int
SGMatObjectGroup::get_object_count () const
{
return _objects.size();
}
SGMatObject *
SGMatObjectGroup::get_object (int index) const
{
return _objects[index];
}
// end of matobj.cxx

View File

@@ -0,0 +1,205 @@
// matobj.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 - 2003 Curtis L. Olson - curt@flightgear.org
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// $Id$
#ifndef _SG_MAT_OBJ_HXX
#define _SG_MAT_OBJ_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h>
#include STL_STRING // Standard C++ string library
#include <plib/sg.h>
#include <plib/ssg.h>
#include <simgear/props/props.hxx>
#include <simgear/scene/model/loader.hxx>
SG_USING_STD(string);
class SGMatObjectGroup;
/**
* A randomly-placeable object.
*
* SGMaterial uses this class to keep track of the model(s) and
* parameters for a single instance of a randomly-placeable object.
* The object can have more than one variant model (i.e. slightly
* different shapes of trees), but they are considered equivalent
* and interchangeable.
*/
class SGMatObject {
public:
/**
* The heading type for a randomly-placed object.
*/
enum HeadingType {
HEADING_FIXED,
HEADING_BILLBOARD,
HEADING_RANDOM
};
/**
* Get the number of variant models available for the object.
*
* @return The number of variant models.
*/
int get_model_count( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
/**
* Get a specific variant model for the object.
*
* @param index The index of the model.
* @return The model.
*/
ssgEntity *get_model( int index,
SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
/**
* Get a randomly-selected variant model for the object.
*
* @return A randomly select model from the variants.
*/
ssgEntity *get_random_model( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
/**
* Get the average number of meters^2 occupied by each instance.
*
* @return The coverage in meters^2.
*/
double get_coverage_m2 () const;
/**
* Get the heading type for the object.
*
* @return The heading type.
*/
HeadingType get_heading_type () const;
protected:
friend class SGMatObjectGroup;
SGMatObject (const SGPropertyNode * node, double range_m);
virtual ~SGMatObject ();
private:
/**
* Actually load the models.
*
* This class uses lazy loading so that models won't be held
* in memory for materials that are never referenced.
*/
void load_models( SGModelLoader *loader,
const string &fg_root,
SGPropertyNode *prop_root,
double sim_time_sec );
vector<string> _paths;
mutable vector<ssgEntity *> _models;
mutable bool _models_loaded;
double _coverage_m2;
double _range_m;
HeadingType _heading_type;
};
/**
* A collection of related objects with the same visual range.
*
* Grouping objects with the same range together significantly
* reduces the memory requirements of randomly-placed objects.
* Each SGMaterial instance keeps a (possibly-empty) list of
* object groups for placing randomly on the scenery.
*/
class SGMatObjectGroup {
public:
virtual ~SGMatObjectGroup ();
/**
* Get the visual range of the object in meters.
*
* @return The visual range.
*/
double get_range_m () const;
/**
* Get the number of objects in the group.
*
* @return The number of objects.
*/
int get_object_count () const;
/**
* Get a specific object.
*
* @param index The object's index, zero-based.
* @return The object selected.
*/
SGMatObject * get_object (int index) const;
protected:
friend class SGMaterial;
SGMatObjectGroup (SGPropertyNode * node);
private:
double _range_m;
vector<SGMatObject *> _objects;
};
#endif // _SG_MAT_OBJ_HXX