]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/tgdb/obj.cxx
Replace SG_USE_STD() by using std::
[simgear.git] / simgear / scene / tgdb / obj.cxx
index 89f19fc15a67aef45958943c6972f6d59df886ff..da21d9f16f05f77572b62ed71fae913753693a3f 100644 (file)
 #include <simgear/scene/model/SGOffsetTransform.hxx>
 #include <simgear/scene/util/SGUpdateVisitor.hxx>
 #include <simgear/scene/util/SGNodeMasks.hxx>
-#include <simgear/threads/SGThread.hxx>
-#include <simgear/threads/SGGuard.hxx>
+#include <simgear/scene/util/QuadTreeBuilder.hxx>
 
 #include "SGTexturedTriangleBin.hxx"
 #include "SGLightBin.hxx"
+#include "SGModelBin.hxx"
+#include "TreeBin.hxx"
 #include "SGDirectionalLightBin.hxx"
+#include "GroundLightManager.hxx"
 
+
+#include "userdata.hxx"
 #include "pt_lights.hxx"
 
+using namespace simgear;
+
 typedef std::map<std::string,SGTexturedTriangleBin> SGMaterialTriangleMap;
 typedef std::list<SGLightBin> SGLightListBin;
 typedef std::list<SGDirectionalLightBin> SGDirectionalLightListBin;
@@ -66,12 +72,14 @@ struct SGTileGeometryBin {
   SGMaterialTriangleMap materialTriangleMap;
   SGLightBin tileLights;
   SGLightBin randomTileLights;
+  TreeBin randomForest;
   SGDirectionalLightBin runwayLights;
   SGDirectionalLightBin taxiLights;
   SGDirectionalLightListBin vasiLights;
   SGDirectionalLightListBin rabitLights;
   SGLightListBin odalLights;
   SGDirectionalLightListBin reilLights;
+  SGMatModelBin randomModels;
 
   static SGVec4f
   getMaterialLightColor(const SGMaterial* material)
@@ -123,7 +131,7 @@ struct SGTileGeometryBin {
       std::string materialName = obj.get_pt_materials()[grp];
       SGMaterial* material = matlib->find(materialName);
       SGVec4f color = getMaterialLightColor(material);
-      
+
       if (3 <= materialName.size() && materialName.substr(0, 3) != "RWY") {
         // Just plain lights. Not something for the runway.
         addPointGeometry(tileLights, obj.get_wgs84_nodes(), color,
@@ -392,7 +400,12 @@ struct SGTileGeometryBin {
 
   void computeRandomSurfaceLights(SGMaterialLib* matlib)
   {
-    SGMaterialTriangleMap::const_iterator i;
+    SGMaterialTriangleMap::iterator i;
+        
+    // generate a repeatable random seed
+    mt seed;
+    mt_init(&seed, unsigned(123));
+    
     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
       SGMaterial *mat = matlib->find(i->first);
       if (!mat)
@@ -407,16 +420,13 @@ struct SGTileGeometryBin {
         coverage = 10000;
       }
       
-      // generate a repeatable random seed
-      sg_srandom(unsigned(coverage));
-
       std::vector<SGVec3f> randomPoints;
       i->second.addRandomSurfacePoints(coverage, 3, randomPoints);
       std::vector<SGVec3f>::iterator j;
       for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
-        float zombie = sg_random();
+        float zombie = mt_rand(&seed);
         // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
-        float factor = sg_random();
+        float factor = mt_rand(&seed);
         factor *= factor;
 
         float bright = 1;
@@ -439,202 +449,249 @@ struct SGTileGeometryBin {
     }
   }
 
-  bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
+  void computeRandomForest(SGMaterialLib* matlib)
   {
-    if (!insertPtGeometry(obj, matlib))
-      return false;
-    if (!insertSurfaceGeometry(obj, matlib))
-      return false;
-    return true;
-  }
-};
+    SGMaterialTriangleMap::iterator i;
 
+    // generate a repeatable random seed
+    mt seed;
+    mt_init(&seed, unsigned(586));
 
-class SGTileUpdateCallback : public osg::NodeCallback {
-public:
-  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
-  {
-    assert(dynamic_cast<osg::Switch*>(node));
-    assert(dynamic_cast<SGUpdateVisitor*>(nv));
+    for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+      SGMaterial *mat = matlib->find(i->first);
+      if (!mat)
+        continue;
 
-    osg::Switch* lightSwitch = static_cast<osg::Switch*>(node);
-    SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
+      float coverage = mat->get_tree_coverage();
+      if (coverage <= 0)
+        continue;
 
-    // The current sun angle in degree
-    float sun_angle = updateVisitor->getSunAngleDeg();
+      // 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();
 
-    // vasi is always on
-    lightSwitch->setValue(0, true);
-    if (sun_angle > 85 || updateVisitor->getVisibility() < 5000) {
-      // runway and taxi
-      lightSwitch->setValue(1, true);
-      lightSwitch->setValue(2, true);
-    } else {
-      // runway and taxi
-      lightSwitch->setValue(1, false);
-      lightSwitch->setValue(2, false);
-    }
-    
-    // ground lights
-    if ( sun_angle > 95 )
-      lightSwitch->setValue(5, true);
-    else
-      lightSwitch->setValue(5, false);
-    if ( sun_angle > 92 )
-      lightSwitch->setValue(4, true);
-    else
-      lightSwitch->setValue(4, false);
-    if ( sun_angle > 89 )
-      lightSwitch->setValue(3, true);
-    else
-      lightSwitch->setValue(3, false);
+      std::vector<SGVec3f> randomPoints;
+      i->second.addRandomSurfacePoints(coverage, 0, randomPoints);
+      std::vector<SGVec3f>::iterator j;
+      for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
 
-    traverse(node, nv);
+        // Apply a random scaling factor and texture index.
+        float scale = (mt_rand(&seed) + mt_rand(&seed)) / 2.0f + 0.5f;
+        int v = (int) (mt_rand(&seed) * mat->get_tree_varieties());
+        if (v == mat->get_tree_varieties()) v--;         
+        randomForest.insert(*j, v, scale);
+      }
+    }
   }
-};
 
-class SGRunwayLightFogUpdateCallback : public osg::StateAttribute::Callback {
-public:
-  virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
+  void computeRandomObjects(SGMaterialLib* matlib)
   {
-    assert(dynamic_cast<SGUpdateVisitor*>(nv));
-    assert(dynamic_cast<osg::Fog*>(sa));
-    SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
-    osg::Fog* fog = static_cast<osg::Fog*>(sa);
-    fog->setMode(osg::Fog::EXP2);
-    fog->setColor(updateVisitor->getFogColor().osg());
-    fog->setDensity(updateVisitor->getRunwayFogExp2Density());
+    SGMaterialTriangleMap::iterator i;
+
+    // generate a repeatable random seed
+    mt seed;
+    mt_init(&seed, unsigned(123));
+
+    for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+      SGMaterial *mat = matlib->find(i->first);
+      if (!mat)
+        continue;
+
+      int group_count = mat->get_object_group_count();
+
+      if (group_count > 0)
+      {
+        for (int j = 0; j < group_count; j++)
+        {
+          SGMatModelGroup *object_group =  mat->get_object_group(j);
+          int nObjects = object_group->get_object_count();
+
+          if (nObjects > 0)
+          {
+            // For each of the random models in the group, determine an appropriate
+            // number of random placements and insert them.
+            for (int k = 0; k < nObjects; k++) {
+              SGMatModel * object = object_group->get_object(k);
+
+              std::vector<SGVec3f> randomPoints;
+
+              i->second.addRandomPoints(object->get_coverage_m2(), randomPoints);
+              std::vector<SGVec3f>::iterator l;
+              for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
+                randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed));
+              }
+            }
+          }
+        }
+      }
+    }
   }
-};
 
-class SGTaxiLightFogUpdateCallback : public osg::StateAttribute::Callback {
-public:
-  virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
+  bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
   {
-    assert(dynamic_cast<SGUpdateVisitor*>(nv));
-    assert(dynamic_cast<osg::Fog*>(sa));
-    SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
-    osg::Fog* fog = static_cast<osg::Fog*>(sa);
-    fog->setMode(osg::Fog::EXP2);
-    fog->setColor(updateVisitor->getFogColor().osg());
-    fog->setDensity(updateVisitor->getTaxiFogExp2Density());
+    if (!insertPtGeometry(obj, matlib))
+      return false;
+    if (!insertSurfaceGeometry(obj, matlib))
+      return false;
+    return true;
   }
 };
 
-class SGGroundLightFogUpdateCallback : public osg::StateAttribute::Callback {
-public:
-  virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv)
-  {
-    assert(dynamic_cast<SGUpdateVisitor*>(nv));
-    assert(dynamic_cast<osg::Fog*>(sa));
-    SGUpdateVisitor* updateVisitor = static_cast<SGUpdateVisitor*>(nv);
-    osg::Fog* fog = static_cast<osg::Fog*>(sa);
-    fog->setMode(osg::Fog::EXP2);
-    fog->setColor(updateVisitor->getFogColor().osg());
-    fog->setDensity(updateVisitor->getGroundLightsFogExp2Density());
-  }
+typedef std::pair<osg::Node*, int> ModelLOD;
+struct MakeQuadLeaf {
+    osg::LOD* operator() () const { return new osg::LOD; }
 };
+struct AddModelLOD {
+    void operator() (osg::LOD* leaf, ModelLOD& mlod) const
+    {
+        leaf->addChild(mlod.first, 0, mlod.second);
+    }
+};
+struct GetModelLODCoord {
+    GetModelLODCoord(const osg::Matrix& transform) : _transform(transform) {}
+    GetModelLODCoord(const GetModelLODCoord& rhs) : _transform(rhs._transform)
+    {}
+    osg::Vec3 operator() (const ModelLOD& mlod) const
+    {
+        return mlod.first->getBound().center() * _transform;
+    }
+    osg::Matrix _transform;
+};
+
+typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
+                        GetModelLODCoord>  RandomObjectsQuadtree;
 
 osg::Node*
-SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects)
+SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects, bool use_random_vegetation)
 {
-  SGBinObject obj;
-  if (!obj.read_bin(path))
+  SGBinObject tile;
+  if (!tile.read_bin(path))
     return false;
 
   SGTileGeometryBin tileGeometryBin;
-  if (!tileGeometryBin.insertBinObj(obj, matlib))
+  if (!tileGeometryBin.insertBinObj(tile, matlib))
     return false;
 
-  SGVec3d center = obj.get_gbs_center2();
+  SGVec3d center = tile.get_gbs_center2();
   SGGeod geodPos = SGGeod::fromCart(center);
   SGQuatd hlOr = SGQuatd::fromLonLat(geodPos);
   SGVec3f up = toVec3f(hlOr.backTransform(SGVec3d(0, 0, -1)));
+  GroundLightManager* lightManager = GroundLightManager::instance();
 
-  osg::Group* vasiLights = new osg::Group;
-  vasiLights->setCullCallback(new SGPointSpriteLightCullCallback(osg::Vec3(1, 0.0001, 0.000001), 6));
-  osg::StateSet* stateSet = vasiLights->getOrCreateStateSet();
-  osg::Fog* fog = new osg::Fog;
-  fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback);
-  stateSet->setAttribute(fog);
-
-  osg::Group* rwyLights = new osg::Group;
-  rwyLights->setCullCallback(new SGPointSpriteLightCullCallback);
-  stateSet = rwyLights->getOrCreateStateSet();
-  fog = new osg::Fog;
-  fog->setUpdateCallback(new SGRunwayLightFogUpdateCallback);
-  stateSet->setAttribute(fog);
-
-  osg::Group* taxiLights = new osg::Group;
-  taxiLights->setCullCallback(new SGPointSpriteLightCullCallback);
-  stateSet = taxiLights->getOrCreateStateSet();
-  fog = new osg::Fog;
-  fog->setUpdateCallback(new SGTaxiLightFogUpdateCallback);
-  stateSet->setAttribute(fog);
-
-  osg::Group* groundLights0 = new osg::Group;
-  stateSet = groundLights0->getOrCreateStateSet();
-  fog = new osg::Fog;
-  fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
-  stateSet->setAttribute(fog);
-
-  osg::Group* groundLights1 = new osg::Group;
-  stateSet = groundLights1->getOrCreateStateSet();
-  fog = new osg::Fog;
-  fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
-  stateSet->setAttribute(fog);
-
-  osg::Group* groundLights2 = new osg::Group;
-  stateSet = groundLights2->getOrCreateStateSet();
-  fog = new osg::Fog;
-  fog->setUpdateCallback(new SGGroundLightFogUpdateCallback);
-  stateSet->setAttribute(fog);
-
-  osg::Switch* lightSwitch = new osg::Switch;
-  lightSwitch->setUpdateCallback(new SGTileUpdateCallback);
-  lightSwitch->addChild(vasiLights, true);
-  lightSwitch->addChild(rwyLights, true);
-  lightSwitch->addChild(taxiLights, true);
-  lightSwitch->addChild(groundLights0, true);
-  lightSwitch->addChild(groundLights1, true);
-  lightSwitch->addChild(groundLights2, true);
-
-  osg::Group* lightGroup = new SGOffsetTransform(0.94);
-  lightGroup->addChild(lightSwitch);
-
-  osg::LOD* lightLOD = new osg::LOD;
-  lightLOD->addChild(lightGroup, 0, 30000);
-  unsigned nodeMask = ~0u;
-  nodeMask &= ~SG_NODEMASK_CASTSHADOW_BIT;
-  nodeMask &= ~SG_NODEMASK_RECIEVESHADOW_BIT;
-  nodeMask &= ~SG_NODEMASK_PICK_BIT;
-  nodeMask &= ~SG_NODEMASK_TERRAIN_BIT;
-  lightLOD->setNodeMask(nodeMask);
-
+  osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
+  osg::ref_ptr<osg::Group> randomObjects;
+  osg::ref_ptr<osg::Group> randomForest;
   osg::Group* terrainGroup = new osg::Group;
+
   osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
   if (node)
     terrainGroup->addChild(node);
 
+  if (use_random_objects || use_random_vegetation) {
+
+    // Simple matrix for used for flipping models that have been oriented
+    // with the center of the tile but upside down.
+    static const osg::Matrix flip(1,  0,  0, 0,
+                                  0, -1,  0, 0,
+                                  0,  0, -1, 0,
+                                  0,  0,  0, 1);     
+    // Determine an rotation matrix for the models to place them 
+    // perpendicular to the earth's surface. We use the same matrix, 
+    // based on the centre of the tile, as the small angular differences  
+    // between different points on the tile aren't worth worrying about 
+    // for random objects. We also need to flip the orientation 180 degrees
+    osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg());
+    // The inverse goes from world coordinates to Z up tile coordinates.
+    osg::Matrix world2Tile(osg::Matrix(hlOr.osg().conj()) * flip);
+
+    if (use_random_objects) {
+      tileGeometryBin.computeRandomObjects(matlib);
+    
+      if (tileGeometryBin.randomModels.getNumModels() > 0) {
+        // Generate a repeatable random seed
+        mt seed;
+        mt_init(&seed, unsigned(123));
+
+        std::vector<ModelLOD> models;
+        for (unsigned int i = 0;
+             i < tileGeometryBin.randomModels.getNumModels(); i++) {
+          SGMatModelBin::MatModel obj
+            = tileGeometryBin.randomModels.getMatModel(i);
+          osg::Node* node = sgGetRandomModel(obj.model);
+        
+          // Create a matrix to place the object in the correct
+          // location, and then apply the rotation matrix created
+          // above, with an additional random heading rotation if appropriate.
+          osg::Matrix transformMat(mAtt);
+          transformMat.postMult(osg::Matrix::translate(obj.position.osg()));
+          if (obj.model->get_heading_type() == SGMatModel::HEADING_RANDOM) {
+            // Rotate the object around the z axis.
+            double hdg = mt_rand(&seed) * M_PI * 2;
+            transformMat.preMult(osg::Matrix::rotate(hdg,
+                                                     osg::Vec3d(0.0, 0.0, 1.0)));
+          }
+          osg::MatrixTransform* position =
+            new osg::MatrixTransform(transformMat);
+          position->addChild(node);
+          models.push_back(ModelLOD(position, obj.lod));
+        }
+        RandomObjectsQuadtree quadtree((GetModelLODCoord(world2Tile)),
+                                       (AddModelLOD()));
+        quadtree.buildQuadTree(models.begin(), models.end());
+        randomObjects = quadtree.getRoot();
+        randomObjects->setName("random objects");
+      }
+    }
+
+    if (use_random_vegetation) {
+      // Now add some random forest.
+      tileGeometryBin.computeRandomForest(matlib);
+
+      if (tileGeometryBin.randomForest.getNumTrees() > 0) {
+        randomForest = createForest(tileGeometryBin.randomForest, mAtt);
+        randomForest->setName("random trees");
+      }
+    } 
+  }
+
   if (calc_lights) {
     // FIXME: ugly, has a side effect
     tileGeometryBin.computeRandomSurfaceLights(matlib);
 
-    osg::Geode* geode = new osg::Geode;
-    groundLights0->addChild(geode);
-    geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.tileLights));
-    geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 4, -0.3f));
-
-    geode = new osg::Geode;
-    groundLights1->addChild(geode);
-    geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 2, -0.15f));
-
-    geode = new osg::Geode;
-    groundLights2->addChild(geode);
-    geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights));
+    if (tileGeometryBin.tileLights.getNumLights() > 0
+        || tileGeometryBin.randomTileLights.getNumLights() > 0) {
+      osg::Group* groundLights0 = new osg::Group;
+      groundLights0->setStateSet(lightManager->getGroundLightStateSet());
+      groundLights0->setNodeMask(GROUNDLIGHTS0_BIT);
+      osg::Geode* geode = new osg::Geode;
+      geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.tileLights));
+      geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 4, -0.3f));
+      groundLights0->addChild(geode);
+      lightGroup->addChild(groundLights0);
+    }
+    if (tileGeometryBin.randomTileLights.getNumLights() > 0) {
+      osg::Group* groundLights1 = new osg::Group;
+      groundLights1->setStateSet(lightManager->getGroundLightStateSet());
+      groundLights1->setNodeMask(GROUNDLIGHTS1_BIT);
+      osg::Group* groundLights2 = new osg::Group;
+      groundLights2->setStateSet(lightManager->getGroundLightStateSet());
+      groundLights2->setNodeMask(GROUNDLIGHTS2_BIT);
+      osg::Geode* geode = new osg::Geode;
+      geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights, 2, -0.15f));
+      groundLights1->addChild(geode);
+      lightGroup->addChild(groundLights1);
+      geode = new osg::Geode;
+      geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.randomTileLights));
+      groundLights2->addChild(geode);
+      lightGroup->addChild(groundLights2);
+    }
   }
 
-  {
+  if (!tileGeometryBin.vasiLights.empty()) {
+    osg::Geode* vasiGeode = new osg::Geode;
     SGVec4f red(1, 0, 0, 1);
     SGMaterial* mat = matlib->find("RWY_RED_LIGHTS");
     if (mat)
@@ -643,18 +700,32 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
     mat = matlib->find("RWY_WHITE_LIGHTS");
     if (mat)
       white = mat->get_light_color();
-    osg::Geode* geode;
-    geode = new osg::Geode;
-    vasiLights->addChild(geode);
+
     SGDirectionalLightListBin::const_iterator i;
     for (i = tileGeometryBin.vasiLights.begin();
          i != tileGeometryBin.vasiLights.end(); ++i) {
-      geode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
+      vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
     }
-    
-    geode = new osg::Geode;
-    rwyLights->addChild(geode);
-    geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.runwayLights));
+    vasiGeode->setCullCallback(new SGPointSpriteLightCullCallback(osg::Vec3(1, 0.0001, 0.000001), 6));
+    vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
+    lightGroup->addChild(vasiGeode);
+  }
+
+  if (tileGeometryBin.runwayLights.getNumLights() > 0
+      || !tileGeometryBin.rabitLights.empty()
+      || !tileGeometryBin.reilLights.empty()
+      || !tileGeometryBin.odalLights.empty()) {
+    osg::Group* rwyLights = new osg::Group;
+    rwyLights->setCullCallback(new SGPointSpriteLightCullCallback);
+    rwyLights->setStateSet(lightManager->getRunwayLightStateSet());
+    rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
+    if (tileGeometryBin.runwayLights.getNumLights() != 0) {
+      osg::Geode* geode = new osg::Geode;
+      geode->addDrawable(SGLightFactory::getLights(tileGeometryBin
+                                                   .runwayLights));
+      rwyLights->addChild(geode);
+    }
+    SGDirectionalLightListBin::const_iterator i;
     for (i = tileGeometryBin.rabitLights.begin();
          i != tileGeometryBin.rabitLights.end(); ++i) {
       rwyLights->addChild(SGLightFactory::getSequenced(*i));
@@ -663,16 +734,23 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
          i != tileGeometryBin.reilLights.end(); ++i) {
       rwyLights->addChild(SGLightFactory::getSequenced(*i));
     }
-
     SGLightListBin::const_iterator j;
     for (j = tileGeometryBin.odalLights.begin();
          j != tileGeometryBin.odalLights.end(); ++j) {
       rwyLights->addChild(SGLightFactory::getOdal(*j));
     }
+    lightGroup->addChild(rwyLights);
+  }
 
-    geode = new osg::Geode;
-    taxiLights->addChild(geode);
+  if (tileGeometryBin.taxiLights.getNumLights() > 0) {
+    osg::Group* taxiLights = new osg::Group;
+    taxiLights->setCullCallback(new SGPointSpriteLightCullCallback);
+    taxiLights->setStateSet(lightManager->getTaxiLightStateSet());
+    taxiLights->setNodeMask(RUNWAYLIGHTS_BIT);
+    osg::Geode* geode = new osg::Geode;
     geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.taxiLights));
+    taxiLights->addChild(geode);
+    lightGroup->addChild(taxiLights);
   }
 
   // The toplevel transform for that tile.
@@ -680,7 +758,27 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
   transform->setName(path);
   transform->setMatrix(osg::Matrix::translate(center.osg()));
   transform->addChild(terrainGroup);
-  transform->addChild(lightLOD);
-
+  if (lightGroup->getNumChildren() > 0) {
+    osg::LOD* lightLOD = new osg::LOD;
+    lightLOD->addChild(lightGroup.get(), 0, 30000);
+    // VASI is always on, so doesn't use light bits.
+    lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT); 
+    transform->addChild(lightLOD);
+  }
+  
+  if (randomObjects.valid() || randomForest.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);
+    
+    unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
+    objectLOD->setNodeMask(nodeMask);
+    transform->addChild(objectLOD);
+  }
+  
   return transform;
 }