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

View File

@ -54,6 +54,7 @@
#include "TreeBin.hxx"
#define SG_TREE_QUAD_TREE_DEPTH 3
#define SG_TREE_FADE_OUT_LEVELS 10
using namespace osg;
@ -230,9 +231,15 @@ struct MakeTreesLeaf
LOD* operator() () const
{
LOD* result = new LOD;
EffectGeode* geode = createTreeGeode(_width, _height, _varieties);
geode->setEffect(_effect.get());
result->addChild(geode, 0, _range);
// Create a series of LOD nodes so trees cover decreases slightly
// 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;
}
float _range;
@ -246,7 +253,7 @@ struct AddTreesLeafObject
{
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);
}
};
@ -278,48 +285,55 @@ struct TreeTransformer
// forest into the local Z-up coordinate system we can reuse the
// 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);
static Matrix ident;
// Set up some shared structures.
ref_ptr<Group> group;
MatrixTransform* mt = new MatrixTransform(transform);
Effect* effect = 0;
EffectMap::iterator iter = treeEffectMap.find(forest.texture);
if (iter == treeEffectMap.end()) {
SGPropertyNode_ptr effectProp = new SGPropertyNode;
makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
SGPropertyNode* params = makeChild(effectProp, "parameters");
// emphasize n = 0
params->getChild("texture", 0, true)->getChild("image", 0, true)
->setStringValue(forest.texture);
effect = makeEffect(effectProp, true);
treeEffectMap.insert(EffectMap::value_type(forest.texture, effect));
} else {
effect = iter->second.get();
}
// Now, create a quadtree for the forest.
{
SGTreeBinList::iterator i;
for (i = forestList.begin(); i != forestList.end(); ++i) {
TreeBin* forest = *i;
Effect* effect = 0;
EffectMap::iterator iter = treeEffectMap.find(forest->texture);
if (iter == treeEffectMap.end()) {
SGPropertyNode_ptr effectProp = new SGPropertyNode;
makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
SGPropertyNode* params = makeChild(effectProp, "parameters");
// emphasize n = 0
params->getChild("texture", 0, true)->getChild("image", 0, true)
->setStringValue(forest->texture);
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
quadtree(GetTreeCoord(), AddTreesLeafObject(),
SG_TREE_QUAD_TREE_DEPTH,
MakeTreesLeaf(forest.range, forest.texture_varieties,
forest.width, forest.height, effect));
MakeTreesLeaf(forest->range, forest->texture_varieties,
forest->width, forest->height, effect));
// Transform tree positions from the "geocentric" positions we
// get from the scenery polys into the local Z-up coordinate
// system.
std::vector<TreeBin::Tree> rotatedTrees;
rotatedTrees.reserve(forest._trees.size());
std::transform(forest._trees.begin(), forest._trees.end(),
rotatedTrees.reserve(forest->_trees.size());
std::transform(forest->_trees.begin(), forest->_trees.end(),
std::back_inserter(rotatedTrees),
TreeTransformer(transInv));
quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end());
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;
}

View File

@ -49,7 +49,7 @@ public:
float height;
float width;
std::string texture;
void insert(const Tree& t)
{ _trees.push_back(t); }
void insert(const SGVec3f& p, int t, float s)
@ -62,6 +62,9 @@ public:
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

View File

@ -40,6 +40,8 @@
#include <osg/StateSet>
#include <osg/Switch>
#include <boost/foreach.hpp>
#include <simgear/debug/logstream.hxx>
#include <simgear/io/sg_binobj.hxx>
#include <simgear/math/sg_geodesy.hxx>
@ -74,7 +76,7 @@ struct SGTileGeometryBin {
SGMaterialTriangleMap materialTriangleMap;
SGLightBin tileLights;
SGLightBin randomTileLights;
TreeBin randomForest;
SGTreeBinList randomForest;
SGDirectionalLightBin runwayLights;
SGDirectionalLightBin taxiLights;
SGDirectionalLightListBin vasiLights;
@ -459,13 +461,33 @@ struct SGTileGeometryBin {
float wood_coverage = mat->get_wood_coverage();
if (wood_coverage <= 0)
continue;
// Attributes that don't vary by tree
randomForest.texture = mat->get_tree_texture();
randomForest.range = mat->get_tree_range();
randomForest.width = mat->get_tree_width();
randomForest.height = mat->get_tree_height();
randomForest.texture_varieties = mat->get_tree_varieties();
// Attributes that don't vary by tree but do vary by material
bool found = false;
TreeBin* bin = NULL;
BOOST_FOREACH(bin, randomForest)
{
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;
i->second.addRandomTreePoints(wood_coverage,
@ -473,9 +495,9 @@ struct SGTileGeometryBin {
mat->get_wood_size(),
randomPoints);
std::vector<SGVec3f>::iterator j;
for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
randomForest.insert(*j);
std::vector<SGVec3f>::iterator k;
for (k = randomPoints.begin(); k != randomPoints.end(); ++k) {
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> randomObjects;
osg::ref_ptr<osg::Group> randomForest;
osg::ref_ptr<osg::Group> forestNode;
osg::Group* terrainGroup = new osg::Group;
osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
@ -639,11 +661,10 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
if (use_random_vegetation && matlib) {
// Now add some random forest.
tileGeometryBin.computeRandomForest(matlib);
if (tileGeometryBin.randomForest.getNumTrees() > 0) {
randomForest = createForest(tileGeometryBin.randomForest,
osg::Matrix::identity());
randomForest->setName("random trees");
if (tileGeometryBin.randomForest.size() > 0) {
forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity());
forestNode->setName("Random trees");
}
}
}
@ -770,14 +791,14 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
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
// is more than 20km away.
osg::LOD* objectLOD = new osg::LOD;
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;
objectLOD->setNodeMask(nodeMask);