TREE_LIST STG verb

Add a TREE_LIST STG analogous to the existing BUILDING_LIST verb

  TREE_LIST <filename> <material name> <lon> <lat> <elev>

where <filename> is a file containing a set of trees to generate
using the defined <material name> at the given location.

The referenced <filename> contains lines of the form

X Y Z A B C

Where:
- X,Y,Z are the cartesian coordinates of the tree. +X is East, +Y is North
- A,B,C is optional and represents the normal of the underlying terrain.  Used for shadows.  Defaults to (0,0,1)
This commit is contained in:
Stuart Buchanan
2020-11-17 21:17:04 +00:00
parent 158ac28e60
commit 25d922cf6b
3 changed files with 128 additions and 2 deletions

View File

@@ -52,6 +52,7 @@
#include <simgear/scene/tgdb/obj.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/tgdb/SGBuildingBin.hxx>
#include <simgear/scene/tgdb/TreeBin.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
@@ -64,6 +65,7 @@
#define RAILWAY_ROUGH "OBJECT_RAILWAY_ROUGH"
#define RAILWAY_DETAILED "OBJECT_RAILWAY_DETAILED"
#define BUILDING_LIST "BUILDING_LIST"
#define TREE_LIST "TREE_LIST"
namespace simgear {
@@ -139,6 +141,13 @@ struct ReaderWriterSTG::_ModelBin {
std::string _material_name;
double _lon, _lat, _elev;
};
struct _TreeList {
_TreeList() : _lon(0), _lat(0), _elev(0) { }
std::string _filename;
std::string _material_name;
double _lon, _lat, _elev;
};
class DelayLoadReadFileCallback : public OptionsReadFileCallback {
@@ -258,6 +267,45 @@ struct ReaderWriterSTG::_ModelBin {
}
}
if (!_treeList.empty()) {
SGMaterialLibPtr matlib = _options->getMaterialLib();
if (!matlib) {
SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get materials definition for buildings");
} else {
for (const auto& b : _treeList) {
// Build buildings for each list of buildings
SGGeod geodPos = SGGeod::fromDegM(b._lon, b._lat, 0.0);
SGSharedPtr<SGMaterial> mat = matlib->find(b._material_name, geodPos);
// trying to avoid crash on null material, see:
// https://sentry.io/organizations/flightgear/issues/1867075869
if (!mat) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Tree list specifies unknown material: " << b._filename << " " << b._material_name);
continue;
}
const auto path = SGPath(b._filename);
TreeBin* treeBin = new TreeBin(path, mat);
SGTreeBinList treeList;
treeList.push_back(treeBin);
SG_LOG(SG_TERRAIN, SG_ALERT, "Making trees " << b._material_name);
osg::MatrixTransform* matrixTransform;
matrixTransform = new osg::MatrixTransform(makeZUpFrame(SGGeod::fromDegM(b._lon, b._lat, b._elev)));
matrixTransform->setName("rotateTrees");
matrixTransform->setDataVariance(osg::Object::STATIC);
matrixTransform->addChild(createForest(treeList, osg::Matrix::identity(), _options));
group->addChild(matrixTransform);
std::for_each(treeList.begin(), treeList.end(), [](TreeBin* bb) {
delete bb;
});
}
}
}
return group.release();
}
@@ -265,6 +313,7 @@ struct ReaderWriterSTG::_ModelBin {
std::list<_ObjectStatic> _objectStaticList;
std::list<_Sign> _signList;
std::list<_BuildingList> _buildingList;
std::list<_TreeList> _treeList;
/// The original options to use for this bunch of models
osg::ref_ptr<SGReaderWriterOptions> _options;
@@ -554,7 +603,12 @@ struct ReaderWriterSTG::_ModelBin {
in >> buildinglist._material_name >> buildinglist._lon >> buildinglist._lat >> buildinglist._elev;
checkInsideBucket(absoluteFileName, buildinglist._lon, buildinglist._lat);
_buildingListList.push_back(buildinglist);
//SG_LOG(SG_TERRAIN, SG_ALERT, "Building list: " << buildinglist._filename << " " << buildinglist._material_name << " " << buildinglist._lon << " " << buildinglist._lat);
} else if (token == TREE_LIST) {
_TreeList treelist;
treelist._filename = path.utf8Str();
in >> treelist._material_name >> treelist._lon >> treelist._lat >> treelist._elev;
checkInsideBucket(absoluteFileName, treelist._lon, treelist._lat);
_treeListList.push_back(treelist);
} else {
// Check registered callback for token. Keep lock until callback completed to make sure it will not be
// executed after a thread successfully executed removeSTGObjectHandler()
@@ -640,7 +694,7 @@ struct ReaderWriterSTG::_ModelBin {
i->_elev += elevation(*terrainGroup, SGGeod::fromDeg(i->_lon, i->_lat));
}
if (_objectStaticList.empty() && _signList.empty() && (_buildingListList.size() == 0)) {
if (_objectStaticList.empty() && _signList.empty() && (_buildingListList.size() == 0) && (_treeListList.size() == 0)) {
// The simple case, just return the terrain group
return terrainGroup.release();
} else {
@@ -656,6 +710,7 @@ struct ReaderWriterSTG::_ModelBin {
osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback;
readFileCallback->_objectStaticList = _objectStaticList;
readFileCallback->_buildingList = _buildingListList;
readFileCallback->_treeList = _treeListList;
readFileCallback->_signList = _signList;
readFileCallback->_options = options;
readFileCallback->_bucket = bucket;
@@ -683,6 +738,7 @@ struct ReaderWriterSTG::_ModelBin {
std::list<_ObjectStatic> _objectStaticList;
std::list<_Sign> _signList;
std::list<_BuildingList> _buildingListList;
std::list<_TreeList> _treeListList;
};
ReaderWriterSTG::ReaderWriterSTG()

View File

@@ -39,6 +39,7 @@
#include <osgDB/FileUtils>
#include <simgear/debug/logstream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/Effect.hxx>
@@ -442,5 +443,70 @@ osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform
return mt;
}
TreeBin::TreeBin(const SGMaterial *mat)
{
texture_varieties = mat->get_tree_varieties();
range = mat->get_tree_range();
height = mat->get_tree_height();
width = mat->get_tree_width();
texture = mat->get_tree_texture();
teffect = mat->get_tree_effect();
};
TreeBin::TreeBin(const SGPath& absoluteFileName, const SGMaterial *mat) :
TreeBin(mat)
{
if (!absoluteFileName.exists()) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Tree list file " << absoluteFileName << " does not exist.");
return;
}
sg_gzifstream stream(absoluteFileName);
if (!stream.is_open()) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to open " << absoluteFileName);
return;
}
while (!stream.eof()) {
// read a line. Each line defines a single tree position, and may have
// a comment, starting with #
std::string line;
std::getline(stream, line);
// strip comments
std::string::size_type hash_pos = line.find('#');
if (hash_pos != std::string::npos)
line.resize(hash_pos);
// and process further
std::stringstream in(line);
// Line format is X Y Z A B C
// where:
// X,Y,Z are the cartesian coordinates of the center tree
// A,B,C is the normal of the underlying terrain, defaulting to 0,0,1
float x = 0.0f, y = 0.0f, z = 0.0f, a = 0.0f, b = 0.0f, c = 1.0f;
in >> x >> y >> z;
if (in.bad() || in.fail()) {
SG_LOG(SG_TERRAIN, SG_WARN, "Error parsing tree entry in: " << absoluteFileName << " line: \"" << line << "\"");
continue;
}
// these might fail, so check them after we look at failbit
in >> a >> b >> c;
SGVec3f loc = SGVec3f(x,y,z);
SGVec3f norm = SGVec3f(a,b,c);
SG_LOG(SG_TERRAIN, SG_ALERT, "Adding tree " << x << " " << y << " " << z);
insert(Tree(loc, norm));
}
stream.close();
};
}

View File

@@ -35,6 +35,10 @@ namespace simgear
{
class TreeBin {
public:
TreeBin() = default;
TreeBin(const SGMaterial *mat);
TreeBin(const SGPath& absoluteFileName, const SGMaterial *mat);
~TreeBin() = default;
struct Tree {