diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx index 66c26add..56c5d415 100644 --- a/simgear/scene/tgdb/TreeBin.cxx +++ b/simgear/scene/tgdb/TreeBin.cxx @@ -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(lod->getChild(0)); + Geode* geode = static_cast(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; + 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 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; } diff --git a/simgear/scene/tgdb/TreeBin.hxx b/simgear/scene/tgdb/TreeBin.hxx index bb542c23..a9c42198 100644 --- a/simgear/scene/tgdb/TreeBin.hxx +++ b/simgear/scene/tgdb/TreeBin.hxx @@ -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 SGTreeBinList; + +osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform); } #endif diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index 4481b830..36011d72 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -40,6 +40,8 @@ #include #include +#include + #include #include #include @@ -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 randomPoints; i->second.addRandomTreePoints(wood_coverage, @@ -473,9 +495,9 @@ struct SGTileGeometryBin { mat->get_wood_size(), randomPoints); - std::vector::iterator j; - for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { - randomForest.insert(*j); + std::vector::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 lightGroup = new SGOffsetTransform(0.94); osg::ref_ptr randomObjects; - osg::ref_ptr randomForest; + osg::ref_ptr 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);