Updates to random forest:

- Bug fix - use multiple textures per tile
- Graduate LoD so tree cover fades in/out.
This commit is contained in:
Stuart Buchanan
2011-07-29 15:22:24 +01:00
parent b47d1ad5fd
commit 38ac7b41ad
3 changed files with 87 additions and 49 deletions
+42 -28
View File
@@ -54,6 +54,7 @@
#include "TreeBin.hxx" #include "TreeBin.hxx"
#define SG_TREE_QUAD_TREE_DEPTH 3 #define SG_TREE_QUAD_TREE_DEPTH 3
#define SG_TREE_FADE_OUT_LEVELS 10
using namespace osg; using namespace osg;
@@ -230,9 +231,15 @@ struct MakeTreesLeaf
LOD* operator() () const LOD* operator() () const
{ {
LOD* result = new LOD; LOD* result = new LOD;
EffectGeode* geode = createTreeGeode(_width, _height, _varieties);
geode->setEffect(_effect.get()); // Create a series of LOD nodes so trees cover decreases slightly
result->addChild(geode, 0, _range); // gradually with distance from _range to 2*_range
for (float i = 0.0; i < SG_TREE_FADE_OUT_LEVELS; i++)
{
EffectGeode* geode = createTreeGeode(_width, _height, _varieties);
geode->setEffect(_effect.get());
result->addChild(geode, 0, _range * (1.0 + i / (SG_TREE_FADE_OUT_LEVELS - 1.0)));
}
return result; return result;
} }
float _range; float _range;
@@ -246,7 +253,7 @@ struct AddTreesLeafObject
{ {
void operator() (LOD* lod, const TreeBin::Tree& tree) const void operator() (LOD* lod, const TreeBin::Tree& tree) const
{ {
Geode* geode = static_cast<Geode*>(lod->getChild(0)); Geode* geode = static_cast<Geode*>(lod->getChild(rand() % SG_TREE_FADE_OUT_LEVELS));
addTreeToLeafGeode(geode, tree.position); addTreeToLeafGeode(geode, tree.position);
} }
}; };
@@ -278,48 +285,55 @@ struct TreeTransformer
// forest into the local Z-up coordinate system we can reuse the // forest into the local Z-up coordinate system we can reuse the
// primitive tree geometry for all the forests of the same type. // primitive tree geometry for all the forests of the same type.
osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform) osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform)
{ {
Matrix transInv = Matrix::inverse(transform); Matrix transInv = Matrix::inverse(transform);
static Matrix ident; static Matrix ident;
// Set up some shared structures. // Set up some shared structures.
ref_ptr<Group> group; ref_ptr<Group> group;
MatrixTransform* mt = new MatrixTransform(transform);
Effect* effect = 0; SGTreeBinList::iterator i;
EffectMap::iterator iter = treeEffectMap.find(forest.texture);
if (iter == treeEffectMap.end()) { for (i = forestList.begin(); i != forestList.end(); ++i) {
SGPropertyNode_ptr effectProp = new SGPropertyNode; TreeBin* forest = *i;
makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
SGPropertyNode* params = makeChild(effectProp, "parameters"); Effect* effect = 0;
// emphasize n = 0 EffectMap::iterator iter = treeEffectMap.find(forest->texture);
params->getChild("texture", 0, true)->getChild("image", 0, true) if (iter == treeEffectMap.end()) {
->setStringValue(forest.texture); SGPropertyNode_ptr effectProp = new SGPropertyNode;
effect = makeEffect(effectProp, true); makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
treeEffectMap.insert(EffectMap::value_type(forest.texture, effect)); SGPropertyNode* params = makeChild(effectProp, "parameters");
} else { // emphasize n = 0
effect = iter->second.get(); params->getChild("texture", 0, true)->getChild("image", 0, true)
} ->setStringValue(forest->texture);
// Now, create a quadtree for the forest. effect = makeEffect(effectProp, true);
{ treeEffectMap.insert(EffectMap::value_type(forest->texture, effect));
} else {
effect = iter->second.get();
}
// Now, create a quadtree for the forest.
ShaderGeometryQuadtree ShaderGeometryQuadtree
quadtree(GetTreeCoord(), AddTreesLeafObject(), quadtree(GetTreeCoord(), AddTreesLeafObject(),
SG_TREE_QUAD_TREE_DEPTH, SG_TREE_QUAD_TREE_DEPTH,
MakeTreesLeaf(forest.range, forest.texture_varieties, MakeTreesLeaf(forest->range, forest->texture_varieties,
forest.width, forest.height, effect)); forest->width, forest->height, effect));
// Transform tree positions from the "geocentric" positions we // Transform tree positions from the "geocentric" positions we
// get from the scenery polys into the local Z-up coordinate // get from the scenery polys into the local Z-up coordinate
// system. // system.
std::vector<TreeBin::Tree> rotatedTrees; std::vector<TreeBin::Tree> rotatedTrees;
rotatedTrees.reserve(forest._trees.size()); rotatedTrees.reserve(forest->_trees.size());
std::transform(forest._trees.begin(), forest._trees.end(), std::transform(forest->_trees.begin(), forest->_trees.end(),
std::back_inserter(rotatedTrees), std::back_inserter(rotatedTrees),
TreeTransformer(transInv)); TreeTransformer(transInv));
quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end()); quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end());
group = quadtree.getRoot(); group = quadtree.getRoot();
for (size_t i = 0; i < group->getNumChildren(); ++i)
mt->addChild(group->getChild(i));
} }
MatrixTransform* mt = new MatrixTransform(transform);
for (size_t i = 0; i < group->getNumChildren(); ++i)
mt->addChild(group->getChild(i));
return mt; return mt;
} }
+4 -1
View File
@@ -62,6 +62,9 @@ public:
TreeList _trees; TreeList _trees;
}; };
osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform);
typedef std::list<TreeBin*> SGTreeBinList;
osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform);
} }
#endif #endif
+38 -17
View File
@@ -40,6 +40,8 @@
#include <osg/StateSet> #include <osg/StateSet>
#include <osg/Switch> #include <osg/Switch>
#include <boost/foreach.hpp>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
#include <simgear/io/sg_binobj.hxx> #include <simgear/io/sg_binobj.hxx>
#include <simgear/math/sg_geodesy.hxx> #include <simgear/math/sg_geodesy.hxx>
@@ -74,7 +76,7 @@ struct SGTileGeometryBin {
SGMaterialTriangleMap materialTriangleMap; SGMaterialTriangleMap materialTriangleMap;
SGLightBin tileLights; SGLightBin tileLights;
SGLightBin randomTileLights; SGLightBin randomTileLights;
TreeBin randomForest; SGTreeBinList randomForest;
SGDirectionalLightBin runwayLights; SGDirectionalLightBin runwayLights;
SGDirectionalLightBin taxiLights; SGDirectionalLightBin taxiLights;
SGDirectionalLightListBin vasiLights; SGDirectionalLightListBin vasiLights;
@@ -460,12 +462,32 @@ struct SGTileGeometryBin {
if (wood_coverage <= 0) if (wood_coverage <= 0)
continue; continue;
// Attributes that don't vary by tree // Attributes that don't vary by tree but do vary by material
randomForest.texture = mat->get_tree_texture(); bool found = false;
randomForest.range = mat->get_tree_range(); TreeBin* bin = NULL;
randomForest.width = mat->get_tree_width();
randomForest.height = mat->get_tree_height(); BOOST_FOREACH(bin, randomForest)
randomForest.texture_varieties = mat->get_tree_varieties(); {
if ((bin->texture == mat->get_tree_texture() ) &&
(bin->texture_varieties == mat->get_tree_varieties()) &&
(bin->range == mat->get_tree_range() ) &&
(bin->width == mat->get_tree_width() ) &&
(bin->height == mat->get_tree_height() ) ) {
found = true;
break;
}
}
if (!found) {
bin = new TreeBin();
bin->texture = mat->get_tree_texture();
SG_LOG(SG_INPUT, SG_DEBUG, "Tree texture " << bin->texture);
bin->range = mat->get_tree_range();
bin->width = mat->get_tree_width();
bin->height = mat->get_tree_height();
bin->texture_varieties = mat->get_tree_varieties();
randomForest.push_back(bin);
}
std::vector<SGVec3f> randomPoints; std::vector<SGVec3f> randomPoints;
i->second.addRandomTreePoints(wood_coverage, i->second.addRandomTreePoints(wood_coverage,
@@ -473,9 +495,9 @@ struct SGTileGeometryBin {
mat->get_wood_size(), mat->get_wood_size(),
randomPoints); randomPoints);
std::vector<SGVec3f>::iterator j; std::vector<SGVec3f>::iterator k;
for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { for (k = randomPoints.begin(); k != randomPoints.end(); ++k) {
randomForest.insert(*j); bin->insert(*k);
} }
} }
} }
@@ -589,7 +611,7 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94); osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
osg::ref_ptr<osg::Group> randomObjects; osg::ref_ptr<osg::Group> randomObjects;
osg::ref_ptr<osg::Group> randomForest; osg::ref_ptr<osg::Group> forestNode;
osg::Group* terrainGroup = new osg::Group; osg::Group* terrainGroup = new osg::Group;
osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib); osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
@@ -640,10 +662,9 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
// Now add some random forest. // Now add some random forest.
tileGeometryBin.computeRandomForest(matlib); tileGeometryBin.computeRandomForest(matlib);
if (tileGeometryBin.randomForest.getNumTrees() > 0) { if (tileGeometryBin.randomForest.size() > 0) {
randomForest = createForest(tileGeometryBin.randomForest, forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity());
osg::Matrix::identity()); forestNode->setName("Random trees");
randomForest->setName("random trees");
} }
} }
} }
@@ -770,14 +791,14 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
transform->addChild(lightLOD); transform->addChild(lightLOD);
} }
if (randomObjects.valid() || randomForest.valid()) { if (randomObjects.valid() || forestNode.valid()) {
// Add a LoD node, so we don't try to display anything when the tile center // Add a LoD node, so we don't try to display anything when the tile center
// is more than 20km away. // is more than 20km away.
osg::LOD* objectLOD = new osg::LOD; osg::LOD* objectLOD = new osg::LOD;
if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000); if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
if (randomForest.valid()) objectLOD->addChild(randomForest.get(), 0, 20000); if (forestNode.valid()) objectLOD->addChild(forestNode.get(), 0, 20000);
unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT; unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
objectLOD->setNodeMask(nodeMask); objectLOD->setNodeMask(nodeMask);