]> git.mxchange.org Git - simgear.git/commitdiff
PagedLOD for random trees, buildings and objects.
authorStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Fri, 6 Dec 2013 20:20:46 +0000 (20:20 +0000)
committerStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Fri, 6 Dec 2013 20:20:46 +0000 (20:20 +0000)
simgear/scene/tgdb/SGBuildingBin.cxx
simgear/scene/tgdb/SGBuildingBin.hxx
simgear/scene/tgdb/obj.cxx

index 8a29f35b58906036863a8021496d05436d43711e..3149f844a9519fd31672e06aa67b354742d23caa 100644 (file)
@@ -61,13 +61,13 @@ using namespace osg;
 
 namespace simgear
 {
-  
+
 typedef std::map<std::string, osg::observer_ptr<osg::StateSet> > BuildingStateSetMap;
 static BuildingStateSetMap statesetmap;
 
 typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;
 static EffectMap buildingEffectMap;
-    
+
 // Building instance scheme:
 // vertex - local position of vertices, with 0,0,0 being the center front.
 // fog coord - rotation
@@ -88,7 +88,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
     const Geometry* geom = static_cast<const Geometry*>(&drawable);
     const Vec3Array* v = static_cast<const Vec3Array*>(geom->getVertexArray());
     const Vec4Array* pos = static_cast<const Vec4Array*>(geom->getColorArray());
-    
+
     Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList();
     for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(), psend = primSets.end();
          psitr != psend;
@@ -104,132 +104,132 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
             bb.expandBy(pt);
         }
     }
-    return bb;    
+    return bb;
 }
 
   // Set up the building set based on the material definitions
   SGBuildingBin::SGBuildingBin(const SGMaterial *mat) {
-    
-    material_name = mat->get_names()[0];
+
+    material_name = new std::string(mat->get_names()[0]);
     SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name);
-    texture = mat->get_building_texture();
-    lightMap = mat->get_building_lightmap();
+    texture = new std::string(mat->get_building_texture());
+    lightMap = new std::string(mat->get_building_lightmap());
     SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture);
-    
+
     // Generate a random seed for the building generation.
     mt seed;
     mt_init(&seed, unsigned(123));
-    
+
     smallSharedGeometry = new osg::Geometry();
     mediumSharedGeometry = new osg::Geometry();
     largeSharedGeometry = new osg::Geometry();
-    
+
     smallBuildingMaxRadius = std::max(mat->get_building_small_max_depth() * 0.5, mat->get_building_small_max_width() * 0.5);
     mediumBuildingMaxRadius = std::max(mat->get_building_medium_max_depth() * 0.5, mat->get_building_medium_max_width() * 0.5);
     largeBuildingMaxRadius = std::max(mat->get_building_large_max_depth() * 0.5, mat->get_building_large_max_width() * 0.5);
-    
+
     smallBuildingMaxDepth = mat->get_building_small_max_depth();
     mediumBuildingMaxDepth = mat->get_building_medium_max_depth();
-    largeBuildingMaxDepth = mat->get_building_large_max_depth();    
-    
+    largeBuildingMaxDepth = mat->get_building_large_max_depth();
+
     smallBuildingFraction = mat->get_building_small_fraction();
     mediumBuildingFraction = mat->get_building_medium_fraction();
-    
+
     buildingRange = mat->get_building_range();
-    
+
     SG_LOG(SG_TERRAIN, SG_DEBUG, "Building fractions " << smallBuildingFraction << " " << mediumBuildingFraction);
-    
-    
+
+
     // TODO: Reverse this - otherwise we never get any large buildings!
-    BuildingType types[] = { SGBuildingBin::SMALL, SGBuildingBin::MEDIUM, SGBuildingBin::LARGE };    
+    BuildingType types[] = { SGBuildingBin::SMALL, SGBuildingBin::MEDIUM, SGBuildingBin::LARGE };
     BuildingList lists[] = { SGBuildingBin::smallBuildings, SGBuildingBin::mediumBuildings, SGBuildingBin::largeBuildings };
     ref_ptr<Geometry> geometries[] = { smallSharedGeometry, mediumSharedGeometry, largeSharedGeometry };
-    
+
     for (int bt=0; bt < 3; bt++) {
       SGBuildingBin::BuildingType buildingtype = types[bt];
       ref_ptr<Geometry> sharedGeometry = geometries[bt];
       BuildingList buildings = lists[bt];
-        
+
       osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
       osg::ref_ptr<osg::Vec2Array> t = new osg::Vec2Array;
       osg::ref_ptr<osg::Vec3Array> n = new osg::Vec3Array;
-      
+
       v->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
       t->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
       n->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
-      
+
       sharedGeometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
       sharedGeometry->setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
       sharedGeometry->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback);
       sharedGeometry->setUseDisplayList(false);
-      
-      for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) {      
+
+      for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) {
         float width;
         float depth;
         int floors;
         float height;
         bool pitched;
-                                        
+
         if (buildingtype == SGBuildingBin::SMALL) {
           // Small building
           width = mat->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_width() - mat->get_building_small_min_width());
           depth = mat->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_depth() - mat->get_building_small_min_depth());
           floors = SGMisc<double>::round(mat->get_building_small_min_floors() + mt_rand(&seed) * (mat->get_building_small_max_floors() - mat->get_building_small_min_floors()));
           height = floors * (2.8 + mt_rand(&seed));
-          
+
           // Small buildings are never deeper than they are wide.
           if (depth > width) { depth = width; }
-          
+
           pitched = (mt_rand(&seed) < mat->get_building_small_pitch());
         } else if (buildingtype == SGBuildingBin::MEDIUM) {
           width = mat->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_width() - mat->get_building_medium_min_width());
           depth = mat->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_depth() - mat->get_building_medium_min_depth());
           floors = SGMisc<double>::round(mat->get_building_medium_min_floors() + mt_rand(&seed) * (mat->get_building_medium_max_floors() - mat->get_building_medium_min_floors()));
           height = floors * (2.8 + mt_rand(&seed));
-          
+
           while ((height > width) && (floors > mat->get_building_medium_min_floors())) {
             // Ensure that medium buildings aren't taller than they are wide
             floors--;
-            height = floors * (2.8 + mt_rand(&seed));                            
+            height = floors * (2.8 + mt_rand(&seed));
           }
-          
-          pitched = (mt_rand(&seed) < mat->get_building_medium_pitch());         
+
+          pitched = (mt_rand(&seed) < mat->get_building_medium_pitch());
         } else {
           width = mat->get_building_large_min_width() + mt_rand(&seed) * (mat->get_building_large_max_width() - mat->get_building_large_min_width());
           depth = mat->get_building_large_min_depth() + mt_rand(&seed) * (mat->get_building_large_max_depth() - mat->get_building_large_min_depth());
-          floors = SGMisc<double>::round(mat->get_building_large_min_floors() + mt_rand(&seed) * (mat->get_building_large_max_floors() - mat->get_building_large_min_floors())); 
+          floors = SGMisc<double>::round(mat->get_building_large_min_floors() + mt_rand(&seed) * (mat->get_building_large_max_floors() - mat->get_building_large_min_floors()));
           height = floors * (2.8 + mt_rand(&seed));
-          pitched = (mt_rand(&seed) < mat->get_building_large_pitch());                   
+          pitched = (mt_rand(&seed) < mat->get_building_large_pitch());
         }
-        
-        Building building = Building(buildingtype, 
-                                    width, 
-                                    depth, 
-                                    height, 
+
+        Building building = Building(buildingtype,
+                                    width,
+                                    depth,
+                                    height,
                                     floors,
-                                    pitched);                                                            
-        
+                                    pitched);
+
         buildings.push_back(building);
 
         // Now create an OSG Geometry based on the Building
         float cw = 0.5f * building.width;
         float cd = building.depth;
         float ch = building.height;
-        
+
         // 0,0,0 is the bottom center of the front
-        // face, e.g. where the front door would be      
-        
+        // face, e.g. where the front door would be
+
         // BASEMENT
         // This exteds 10m below the main section
-        // Front face        
+        // Front face
         v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom right
         v->push_back( osg::Vec3( 0,  cw, -10) ); // bottom left
         v->push_back( osg::Vec3( 0,  cw,   0) ); // top left
         v->push_back( osg::Vec3( 0, -cw,   0) ); // top right
-        
+
         for (int i=0; i<4; ++i)
           n->push_back( osg::Vec3(1, 0, 0) ); // normal
-        
+
         // Left face
         v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom right
         v->push_back( osg::Vec3(   0, -cw, -10) ); // bottom left
@@ -244,10 +244,10 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
         v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom left
         v->push_back( osg::Vec3( -cd, -cw,   0) ); // top left
         v->push_back( osg::Vec3( -cd,  cw,   0) ); // top right
-        
+
         for (int i=0; i<4; ++i)
           n->push_back( osg::Vec3(-1, 0, 0) ); // normal
-        
+
         // Right face
         v->push_back( osg::Vec3(   0, cw, -10) ); // bottom right
         v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom left
@@ -255,18 +255,18 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
         v->push_back( osg::Vec3(   0, cw,   0) ); // top right
 
         for (int i=0; i<4; ++i)
-          n->push_back( osg::Vec3(0, 1, 0) ); // normal      
-        
+          n->push_back( osg::Vec3(0, 1, 0) ); // normal
+
         // MAIN BODY
-        // Front face        
+        // Front face
         v->push_back( osg::Vec3( 0, -cw,  0) ); // bottom right
         v->push_back( osg::Vec3( 0,  cw,  0) ); // bottom left
         v->push_back( osg::Vec3( 0,  cw, ch) ); // top left
         v->push_back( osg::Vec3( 0, -cw, ch) ); // top right
-        
+
         for (int i=0; i<4; ++i)
           n->push_back( osg::Vec3(1, 0, 0) ); // normal
-        
+
         // Left face
         v->push_back( osg::Vec3( -cd, -cw,  0) ); // bottom right
         v->push_back( osg::Vec3(   0, -cw,  0) ); // bottom left
@@ -281,10 +281,10 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
         v->push_back( osg::Vec3( -cd, -cw,  0) ); // bottom left
         v->push_back( osg::Vec3( -cd, -cw, ch) ); // top left
         v->push_back( osg::Vec3( -cd,  cw, ch) ); // top right
-        
+
         for (int i=0; i<4; ++i)
           n->push_back( osg::Vec3(-1, 0, 0) ); // normal
-        
+
         // Right face
         v->push_back( osg::Vec3(   0, cw,  0) ); // bottom right
         v->push_back( osg::Vec3( -cd, cw,  0) ); // bottom left
@@ -293,25 +293,25 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
 
         for (int i=0; i<4; ++i)
           n->push_back( osg::Vec3(0, 1, 0) ); // normal
-        
+
         // ROOF
-        if (building.pitched) {      
-          
+        if (building.pitched) {
+
           // Front pitched roof
           v->push_back( osg::Vec3(    0, -cw,   ch) ); // bottom right
           v->push_back( osg::Vec3(    0,  cw,   ch) ); // bottom left
           v->push_back( osg::Vec3(-0.5*cd,  cw, ch+3) ); // top left
           v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
-          
+
           for (int i=0; i<4; ++i)
             n->push_back( osg::Vec3(0.707, 0, 0.707) ); // normal
-          
+
           // Left pitched roof
           v->push_back( osg::Vec3(    -cd, -cw,   ch) ); // bottom right
           v->push_back( osg::Vec3(      0, -cw,   ch) ); // bottom left
           v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
           v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
-          
+
           for (int i=0; i<4; ++i)
             n->push_back( osg::Vec3(0, -1, 0) ); // normal
 
@@ -320,37 +320,37 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           v->push_back( osg::Vec3(    -cd, -cw,   ch) ); // bottom left
           v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
           v->push_back( osg::Vec3(-0.5*cd,  cw, ch+3) ); // top right
-          
+
           for (int i=0; i<4; ++i)
-            n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal      
+            n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal
 
           // Right pitched roof
           v->push_back( osg::Vec3(      0, cw,   ch) ); // bottom right
           v->push_back( osg::Vec3(    -cd, cw,   ch) ); // bottom left
           v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left
           v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right
-          
+
           for (int i=0; i<4; ++i)
             n->push_back( osg::Vec3(0, 1, 0) ); // normal
-        } else {      
-          // If the roof isn't pitched, we still generate the 
+        } else {
+          // If the roof isn't pitched, we still generate the
           // vertices for simplicity later.
-          
+
           // Top of the roof
           v->push_back( osg::Vec3(  0, -cw, ch) ); // bottom right
           v->push_back( osg::Vec3(  0,  cw, ch) ); // bottom left
           v->push_back( osg::Vec3(-cd,  cw, ch) ); // top left
           v->push_back( osg::Vec3(-cd, -cw, ch) ); // top right
-          
+
           for (int i=0; i<4; ++i)
             n->push_back( osg::Vec3(0, 0, 1) ); // normal
-          
+
           // Left non-pitched roof
           v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right
           v->push_back( osg::Vec3(   0, -cw, ch) ); // bottom left
           v->push_back( osg::Vec3(   0, -cw, ch) ); // top left
           v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right
-          
+
           for (int i=0; i<4; ++i)
             n->push_back( osg::Vec3(0, -1, 0) ); // normal
 
@@ -359,25 +359,25 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           v->push_back( osg::Vec3(-cd, -cw, ch) ); // bottom left
           v->push_back( osg::Vec3(-cd, -cw, ch) ); // top left
           v->push_back( osg::Vec3(-cd,  cw, ch) ); // top right
-          
+
           for (int i=0; i<4; ++i)
-            n->push_back( osg::Vec3(1, 0, 0) ); // normal      
+            n->push_back( osg::Vec3(1, 0, 0) ); // normal
 
           // Right pitched roof
           v->push_back( osg::Vec3(  0, cw, ch) ); // bottom right
           v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom left
           v->push_back( osg::Vec3(-cd, cw, ch) ); // top left
           v->push_back( osg::Vec3(  0, cw, ch) ); // top right
-          
+
           for (int i=0; i<4; ++i)
             n->push_back( osg::Vec3(0, 1, 0) ); // normal
         }
-        
+
         // The 1024x1024 texture is split into 32x16 blocks.
         // For a small building, each block is 6m wide and 3m high.
         // For a medium building, each block is 10m wide and 3m high.
         // For a large building, each block is 20m wide and 3m high
-        
+
         if (building.type == SGBuildingBin::SMALL) {
           // Small buildings are represented on the bottom 5 rows of 3 floors
           int row = ((int) (mt_rand(&seed) * 1000)) % 5;
@@ -389,8 +389,8 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           float back_x = 384.0/1024.0 + 32.0 / 1024.0 * round((float) building.depth/ 6.0f);
 
           // BASEMENT - uses the baseline texture
-          for (unsigned int i = 0; i < 16; i++) {          
-            t->push_back( osg::Vec2( left_x, base_y) ); 
+          for (unsigned int i = 0; i < 16; i++) {
+            t->push_back( osg::Vec2( left_x, base_y) );
           }
           // MAIN BODY
           // Front
@@ -398,19 +398,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Left
           t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( front_x, top_y ) ); // top right
-          
+
           // Back (same as front for the moment)
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Right (same as left for the moment)
           t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
@@ -418,32 +418,32 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           t->push_back( osg::Vec2( front_x, top_y ) ); // top right
 
           // ROOF
-          if (building.pitched) { 
+          if (building.pitched) {
             // Use the entire height of the roof texture
-            top_y = base_y + 16.0 * 3.0 / 1024.0;     
+            top_y = base_y + 16.0 * 3.0 / 1024.0;
             left_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f);
             right_x = 512/1024.0;
             front_x = 480.0/1024.0;
             back_x = 512.0/1024.0;
-            
+
             // Front
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-            
+
             // Left
             t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( front_x, top_y ) ); // top right
-            
+
             // Back (same as front for the moment)
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-            
+
             // Right (same as left for the moment)
             t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
@@ -454,31 +454,31 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
             left_x = 640.0/1024.0;
             right_x = 512.0/1024.0;
             // Use the entire height of the roof texture
-            top_y = base_y + 16.0 * 3.0 / 1024.0;    
-            
+            top_y = base_y + 16.0 * 3.0 / 1024.0;
+
             // Flat roofs still have 4 surfaces, so we need to set the textures
-            for (int i=0; i<4; ++i) {      
+            for (int i=0; i<4; ++i) {
               t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
               t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
               t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
               t->push_back( osg::Vec2( right_x, top_y ) ); // top right
             }
           }
-          
+
         }
-        
-        if (building.type == SGBuildingBin::MEDIUM) 
+
+        if (building.type == SGBuildingBin::MEDIUM)
         {
-          int column = ((int) (mt_rand(&seed) * 1000)) % 5;        
+          int column = ((int) (mt_rand(&seed) * 1000)) % 5;
           float base_y = 288 / 1024.0;
           float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
           float left_x = column * 192.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 10.0f);
           float right_x = column * 192.0 /1024.0;
 
           // BASEMENT - uses the baseline texture
-          for (unsigned int i = 0; i < 16; i++) {          
-            t->push_back( osg::Vec2( left_x, base_y) ); 
-          }      
+          for (unsigned int i = 0; i < 16; i++) {
+            t->push_back( osg::Vec2( left_x, base_y) );
+          }
 
           // MAIN BODY
           // Front
@@ -486,19 +486,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Left
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Back (same as front for the moment)
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Right (same as left for the moment)
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
@@ -506,30 +506,30 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
 
           // ROOF
-          if (building.pitched) {      
+          if (building.pitched) {
             base_y = 288.0/1024.0;
             top_y = 576.0/1024.0;
             left_x = 960.0/1024.0;
             right_x = 1.0;
-            
+
             // Front
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-            
+
             // Left
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-              
+
             // Back (same as front for the moment)
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-            
+
             // Right (same as left for the moment)
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
@@ -541,9 +541,9 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
             top_y = 576.0/1024.0;
             left_x = column * 192.0 /1024.0;
             right_x = (column + 1)* 192.0 /1024.0;
-            
+
             // Flat roofs still have 4 surfaces
-            for (int i=0; i<4; ++i) {        
+            for (int i=0; i<4; ++i) {
               t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
               t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
               t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
@@ -554,16 +554,16 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
 
         if (building.type == SGBuildingBin::LARGE)
         {
-          int column = ((int) (mt_rand(&seed) * 1000)) % 8;        
+          int column = ((int) (mt_rand(&seed) * 1000)) % 8;
           float base_y = 576 / 1024.0;
           float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
           float left_x = column * 128.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 20.0f);
-          float right_x = column * 128.0 /1024.0; 
+          float right_x = column * 128.0 /1024.0;
 
           // BASEMENT - uses the baseline texture
-          for (unsigned int i = 0; i < 16; i++) {          
-            t->push_back( osg::Vec2( left_x, base_y) ); 
-          }      
+          for (unsigned int i = 0; i < 16; i++) {
+            t->push_back( osg::Vec2( left_x, base_y) );
+          }
 
           // MAIN BODY
           // Front
@@ -571,19 +571,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Left
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Back (same as front for the moment)
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-          
+
           // Right (same as left for the moment)
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
@@ -591,7 +591,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
 
           // ROOF
-          if (building.pitched) {      
+          if (building.pitched) {
             base_y = 896/1024.0;
             top_y = 1.0;
             // Front
@@ -599,19 +599,19 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-            
+
             // Left
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-              
+
             // Back (same as front for the moment)
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
             t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
             t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-            
+
             // Right (same as left for the moment)
             t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
             t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
@@ -621,9 +621,9 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
             // Flat roof
             base_y = 896/1024.0;
             top_y = 1.0;
-            
+
             // Flat roofs still have 4 surfaces
-            for (int i=0; i<4; ++i) {        
+            for (int i=0; i<4; ++i) {
               t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
               t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
               t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
@@ -632,7 +632,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           }
         }
       }
-      
+
       // Set the vertex, texture and normals.  Colors will be set per-instance
       // later.
       sharedGeometry->setVertexArray(v);
@@ -640,88 +640,88 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
       sharedGeometry->setNormalArray(n);
     }
   }
-  
-  void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) { 
-    
+
+  void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) {
+
     if (type == SGBuildingBin::SMALL) {
-      smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry)); 
+      smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry));
     }
-    
+
     if (type == SGBuildingBin::MEDIUM) {
-      mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry));       
+      mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry));
     }
 
     if (type == SGBuildingBin::LARGE) {
-      largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry));       
-    }    
+      largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry));
+    }
   }
 
   int SGBuildingBin::getNumBuildings() {
-    return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size();    
+    return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size();
   }
-  
-  bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) {    
+
+  bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) {
     BuildingInstanceList::iterator iter;
-    
+
     float r = (radius + smallBuildingMaxRadius) * (radius + smallBuildingMaxRadius);
     for (iter = smallBuildingLocations.begin(); iter != smallBuildingLocations.end(); ++iter) {
       if (iter->getDistSqr(p) < r) {
-        return false;        
-      }      
+        return false;
+      }
     }
-    
+
     r = (radius + mediumBuildingMaxRadius) * (radius + mediumBuildingMaxRadius);
     for (iter = mediumBuildingLocations.begin(); iter != mediumBuildingLocations.end(); ++iter) {
       if (iter->getDistSqr(p) < r) {
-        return false;        
-      }      
+        return false;
+      }
     }
-    
+
     r = (radius + largeBuildingMaxRadius) * (radius + largeBuildingMaxRadius);
     for (iter = largeBuildingLocations.begin(); iter != largeBuildingLocations.end(); ++iter) {
       if (iter->getDistSqr(p) < r) {
-        return false;        
-      }      
+        return false;
+      }
     }
-    
+
     return true;
   }
-  
+
   SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
-    
+
     if (roll < smallBuildingFraction) {
-      return SGBuildingBin::SMALL;      
+      return SGBuildingBin::SMALL;
     }
-    
+
     if (roll < (smallBuildingFraction + mediumBuildingFraction)) {
       return SGBuildingBin::MEDIUM;
     }
-    
-    return SGBuildingBin::LARGE;    
+
+    return SGBuildingBin::LARGE;
   }
 
   float SGBuildingBin::getBuildingMaxRadius(BuildingType type) {
-    
+
     if (type == SGBuildingBin::SMALL) return smallBuildingMaxRadius;
     if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxRadius;
     if (type == SGBuildingBin::LARGE) return largeBuildingMaxRadius;
-    
+
     return 0;
   }
-  
+
   float SGBuildingBin::getBuildingMaxDepth(BuildingType type) {
-    
+
     if (type == SGBuildingBin::SMALL) return smallBuildingMaxDepth;
     if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxDepth;
     if (type == SGBuildingBin::LARGE) return largeBuildingMaxDepth;
-    
+
     return 0;
   }
-    
+
   ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options)
   {
     ref_ptr<Effect> effect;
-    EffectMap::iterator iter = buildingEffectMap.find(texture);
+    EffectMap::iterator iter = buildingEffectMap.find(*texture);
 
     if ((iter == buildingEffectMap.end())||
         (!iter->second.lock(effect)))
@@ -731,27 +731,27 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
       SGPropertyNode* params = makeChild(effectProp, "parameters");
       // Main texture - n=0
       params->getChild("texture", 0, true)->getChild("image", 0, true)
-          ->setStringValue(texture);
+          ->setStringValue(*texture);
 
       // Light map - n=3
       params->getChild("texture", 3, true)->getChild("image", 0, true)
-          ->setStringValue(lightMap);
-          
+          ->setStringValue(*lightMap);
+
       effect = makeEffect(effectProp, true, options);
       if (iter == buildingEffectMap.end())
-          buildingEffectMap.insert(EffectMap::value_type(texture, effect));
+          buildingEffectMap.insert(EffectMap::value_type(*texture, effect));
       else
           iter->second = effect; // update existing, but empty observer
     }
-    
+
     ref_ptr<Group> group = new osg::Group();
-  
-    // Now, create a quadbuilding for the buildings.    
-    
-    BuildingInstanceList locs[] = { smallBuildingLocations, 
-                                    SGBuildingBin::mediumBuildingLocations, 
+
+    // Now, create a quadbuilding for the buildings.
+
+    BuildingInstanceList locs[] = { smallBuildingLocations,
+                                    SGBuildingBin::mediumBuildingLocations,
                                     SGBuildingBin::largeBuildingLocations };
-    
+
     for (int i = 0; i < 3; i++)
     {
       // Create a quad tree.  Only small and medium buildings are faded out.
@@ -759,7 +759,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(),
                    SG_BUILDING_QUAD_TREE_DEPTH,
                    MakeBuildingLeaf(buildingRange, effect, (i != 2)));
-                   
+
       // Transform building positions from the "geocentric" positions we
       // get from the scenery polys into the local Z-up coordinate
       // system.
@@ -773,15 +773,15 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
       for (size_t j = 0; j < quadbuilding.getRoot()->getNumChildren(); ++j)
               group->addChild(quadbuilding.getRoot()->getChild(j));
     }
-      
+
     return group;
   }
-    
+
   // We may end up with a quadtree with many empty leaves. One might say
   // that we should avoid constructing the leaves in the first place,
   // but this node visitor tries to clean up after the fact.
   struct QuadTreeCleaner : public osg::NodeVisitor
-  {      
+  {
       QuadTreeCleaner() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
       {
       }
@@ -815,31 +815,32 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
           }
       }
   };
-    
+
   // This actually returns a MatrixTransform node. If we rotate the whole
   // forest into the local Z-up coordinate system we can reuse the
   // primitive building geometry for all the forests of the same type.
-  osg::Group* createRandomBuildings(SGBuildingBinList buildings, const osg::Matrix& transform,
+  osg::Group* createRandomBuildings(SGBuildingBinList& buildings, const osg::Matrix& transform,
                            const SGReaderWriterOptions* options)
   {
       Matrix transInv = Matrix::inverse(transform);
       static Matrix ident;
       // Set up some shared structures.
       MatrixTransform* mt = new MatrixTransform(transform);
+      SGBuildingBinList::iterator i;
 
-      SGBuildingBin* bin = NULL;
-      
-      BOOST_FOREACH(bin, buildings)
-      {      
+      for (i = buildings.begin(); i != buildings.end(); ++i) {
+          SGBuildingBin* bin = *i;
           ref_ptr<Group> group = bin->createBuildingsGroup(transInv, options);
-          
-          for (size_t i = 0; i < group->getNumChildren(); ++i)
-            mt->addChild(group->getChild(i));
-            
-          delete bin;  
-      }        
-      
+
+          for (size_t j = 0; j < group->getNumChildren(); ++j) {
+            mt->addChild(group->getChild(j));
+          }
+
+          delete bin;
+      }
+
       buildings.clear();
+
       QuadTreeCleaner cleaner;
       mt->accept(cleaner);
       return mt;
index 43a4fcd5268c993429855205f3c899ca9815cd0a..23d47fcb3d6553e3fa38eb289088001819005805 100644 (file)
@@ -56,9 +56,9 @@ class SGBuildingBin {
 public:
 
   // Number of buildings to auto-generate. Individual
-  // building instances are taken from this set.  
+  // building instances are taken from this set.
   static const unsigned int BUILDING_SET_SIZE = 200;
-  
+
   static const unsigned int QUADS_PER_BUILDING = 12;
   static const unsigned int VERTICES_PER_BUILDING = 4 * QUADS_PER_BUILDING;
   static const unsigned int VERTICES_PER_BUILDING_SET = BUILDING_SET_SIZE * VERTICES_PER_BUILDING;
@@ -66,21 +66,21 @@ public:
   enum BuildingType {
     SMALL = 0,
     MEDIUM,
-    LARGE };      
-    
+    LARGE };
+
 private:
 
   struct Building {
     Building(BuildingType t, float w, float d, float h, int f, bool pitch) :
-      type(t), 
-      width(w), 
-      depth(d), 
-      height(h), 
+      type(t),
+      width(w),
+      depth(d),
+      height(h),
       floors(f),
-      pitched(pitch), 
+      pitched(pitch),
       radius(std::max(d, 0.5f*w))
     { }
-    
+
     BuildingType type;
     float width;
     float depth;
@@ -88,22 +88,22 @@ private:
     int floors;
     bool pitched;
     float radius;
-    
+
     float getFootprint() {
       return radius;
     }
   };
-  
+
   // The set of buildings that are instantiated
   typedef std::vector<Building> BuildingList;
   BuildingList smallBuildings;
   BuildingList mediumBuildings;
   BuildingList largeBuildings;
-  
-  std::string material_name;
-  std::string texture;
-  std::string lightMap;
-  
+
+  std::string* material_name;
+  std::string* texture;
+  std::string* lightMap;
+
   // Fraction of buildings of this type
   float smallBuildingFraction;
   float mediumBuildingFraction;
@@ -112,20 +112,20 @@ private:
   float smallBuildingMaxRadius;
   float mediumBuildingMaxRadius;
   float largeBuildingMaxRadius;
-  
+
   // The maximum depth of each building type
   float smallBuildingMaxDepth;
   float mediumBuildingMaxDepth;
   float largeBuildingMaxDepth;
-  
+
   // Visibility range for buildings
   float buildingRange;
-  
+
   // Shared geometries of the building set
   ref_ptr<Geometry> smallSharedGeometry;
   ref_ptr<Geometry> mediumSharedGeometry;
   ref_ptr<Geometry> largeSharedGeometry;
-    
+
   struct BuildingInstance {
     BuildingInstance(SGVec3f p, float r, const BuildingList* bl, ref_ptr<Geometry> sg) :
       position(p),
@@ -133,26 +133,26 @@ private:
       buildingList(bl),
       sharedGeometry(sg)
     { }
-    
+
     BuildingInstance(SGVec3f p, BuildingInstance b) :
       position(p),
       rotation(b.rotation),
       buildingList(b.buildingList),
       sharedGeometry(b.sharedGeometry)
-    { }    
-    
+    { }
+
     SGVec3f position;
     float rotation;
-    
+
     // References to allow the QuadTreeBuilder to work
     const BuildingList* buildingList;
-    ref_ptr<Geometry> sharedGeometry;    
-    
+    ref_ptr<Geometry> sharedGeometry;
+
     SGVec3f getPosition() { return position; }
     float getRotation() { return rotation; }
-    
+
     float getDistSqr(SGVec3f p) {
-      return distSqr(p, position);      
+      return distSqr(p, position);
     }
 
     const osg::Vec4f getColorValue() {
@@ -161,42 +161,42 @@ private:
   };
 
   // Information for an instance of a building - position and orientation
-  typedef std::vector<BuildingInstance> BuildingInstanceList; 
+  typedef std::vector<BuildingInstance> BuildingInstanceList;
   BuildingInstanceList smallBuildingLocations;
   BuildingInstanceList mediumBuildingLocations;
   BuildingInstanceList largeBuildingLocations;
-  
-public:   
+
+public:
 
   SGBuildingBin(const SGMaterial *mat);
-  
+
   ~SGBuildingBin() {
-    smallBuildings.clear();    
+    smallBuildings.clear();
     mediumBuildings.clear();
     largeBuildings.clear();
     smallBuildingLocations.clear();
     mediumBuildingLocations.clear();
     largeBuildingLocations.clear();
   }
-  
+
   void insert(SGVec3f p, float r, BuildingType type);
   int getNumBuildings();
-  
+
   bool checkMinDist (SGVec3f p, float radius);
-  
-  std::string getMaterialName() { return material_name; }
-  
+
+  std::string* getMaterialName() { return material_name; }
+
   BuildingType getBuildingType(float roll);
-  
+
   float getBuildingMaxRadius(BuildingType);
   float getBuildingMaxDepth(BuildingType);
-  
+
   // Helper classes for creating the quad tree
   struct MakeBuildingLeaf
   {
       MakeBuildingLeaf(float range, Effect* effect, bool fade) :
           _range(range), _effect(effect), _fade_out(fade) {}
-      
+
       MakeBuildingLeaf(const MakeBuildingLeaf& rhs) :
           _range(rhs._range), _effect(rhs._effect), _fade_out(rhs._fade_out)
       {}
@@ -204,30 +204,30 @@ public:
       LOD* operator() () const
       {
           LOD* result = new LOD;
-          
-          if (_fade_out) {            
+
+          if (_fade_out) {
               // Create a series of LOD nodes so buidling cover decreases
               // gradually with distance from _range to 2*_range
               for (float i = 0.0; i < SG_BUILDING_FADE_OUT_LEVELS; i++)
-              {   
+              {
                   EffectGeode* geode = new EffectGeode;
                   geode->setEffect(_effect.get());
-                  result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0)));               
+                  result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0)));
               }
           } else {
               // No fade-out, so all are visible for 2X range
               EffectGeode* geode = new EffectGeode;
               geode->setEffect(_effect.get());
-              result->addChild(geode, 0, 2.0 * _range);               
+              result->addChild(geode, 0, 2.0 * _range);
           }
           return result;
       }
-      
+
       float _range;
       ref_ptr<Effect> _effect;
       bool _fade_out;
   };
-  
+
   struct AddBuildingLeafObject
   {
       Geometry* createNewBuildingGeometryInstance(const BuildingInstance& building) const
@@ -237,32 +237,32 @@ public:
         geom->setColorBinding(Geometry::BIND_PER_VERTEX);
         geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS));
         return geom;
-      }      
-    
+      }
+
       void operator() (LOD* lod, const BuildingInstance& building) const
       {
           Geode* geode = static_cast<Geode*>(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren()));
-          unsigned int numDrawables = geode->getNumDrawables();          
-          
+          unsigned int numDrawables = geode->getNumDrawables();
+
           // Get the last geometry of to be added and check if there is space for
           // another building instance within it.  This is done by checking
           // if the number of Color values matches the number of vertices.
           // The color array is used to store the position of a particular
           // instance.
           Geometry* geom;
-          
+
           if (numDrawables == 0) {
             // Create a new copy of the shared geometry to instantiate
             geom = createNewBuildingGeometryInstance(building);
             geode->addDrawable(geom);
           } else {
-            geom = static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));      
+            geom = static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
           }
-          
+
           // Check if this building is too close to any other others.
           DrawArrays* primSet  = static_cast<DrawArrays*>(geom->getPrimitiveSet(0));
           Vec4Array* posArray = static_cast<Vec4Array*>(geom->getColorArray());
-          
+
           // Now check if this geometry is full.
           if (posArray->size() >= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
             // This particular geometry is full, so we generate another
@@ -272,7 +272,7 @@ public:
             posArray = static_cast<Vec4Array*>(geom->getColorArray());
             SG_LOG(SG_TERRAIN, SG_DEBUG, "Added new geometry to building geod: " << geode->getNumDrawables());
           }
-          
+
           // We now have a geometry with space for this new building.
           // Set the position and rotation
           osg::Vec4f c = osg::Vec4f(toOsg(building.position), building.rotation);
@@ -293,7 +293,7 @@ public:
 
   typedef QuadTreeBuilder<LOD*, BuildingInstance, MakeBuildingLeaf, AddBuildingLeafObject,
                           GetBuildingCoord> BuildingGeometryQuadtree;
-                          
+
   struct BuildingInstanceTransformer
   {
       BuildingInstanceTransformer(Matrix& mat_) : mat(mat_) {}
@@ -304,8 +304,8 @@ public:
       }
       Matrix mat;
   };
-  
-  ref_ptr<Group> createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options);  
+
+  ref_ptr<Group> createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options);
 
 };
 
@@ -313,7 +313,7 @@ public:
 typedef std::list<SGBuildingBin*> SGBuildingBinList;
 
 
-osg::Group* createRandomBuildings(SGBuildingBinList buildinglist, const osg::Matrix& transform,
+osg::Group* createRandomBuildings(SGBuildingBinList& buildinglist, const osg::Matrix& transform,
                          const SGReaderWriterOptions* options);
 }
 #endif
index b388d4e7e801aa555d127a253ed37aca950c3048..5b484060ef4c6e4b7e57ff077305ce237b3a7202 100644 (file)
@@ -37,6 +37,7 @@
 #include <osg/LOD>
 #include <osg/MatrixTransform>
 #include <osg/Point>
+#include <osg/Referenced>
 #include <osg/StateSet>
 #include <osg/Switch>
 
@@ -59,6 +60,7 @@
 #include <simgear/scene/util/SGNodeMasks.hxx>
 #include <simgear/scene/util/QuadTreeBuilder.hxx>
 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
+#include <simgear/scene/util/OptionsReadFileCallback.hxx>
 
 #include "SGTexturedTriangleBin.hxx"
 #include "SGLightBin.hxx"
@@ -77,7 +79,8 @@ typedef std::map<std::string,SGTexturedTriangleBin> SGMaterialTriangleMap;
 typedef std::list<SGLightBin> SGLightListBin;
 typedef std::list<SGDirectionalLightBin> SGDirectionalLightListBin;
 
-struct SGTileGeometryBin {
+class SGTileGeometryBin : public osg::Referenced {
+public:
   SGMaterialTriangleMap materialTriangleMap;
   SGLightBin tileLights;
   SGLightBin randomTileLights;
@@ -281,7 +284,7 @@ struct SGTileGeometryBin {
         triangles.insert(v0, v1, v2);
     }
   }
-  
+
   static void
   addFanGeometry(SGTexturedTriangleBin& triangles,
                  const std::vector<SGVec3d>& vertices,
@@ -418,11 +421,11 @@ struct SGTileGeometryBin {
   void computeRandomSurfaceLights(SGMaterialLib* matlib)
   {
     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)
@@ -436,7 +439,7 @@ struct SGTileGeometryBin {
                << coverage << ", pushing up to 10000");
         coverage = 10000;
       }
-      
+
       std::vector<SGVec3f> randomPoints;
       i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints);
       std::vector<SGVec3f>::iterator j;
@@ -465,50 +468,47 @@ struct SGTileGeometryBin {
       }
     }
   }
-  
+
   void computeRandomObjectsAndBuildings(
-    SGMaterialLib* matlib, 
-    float building_density, 
-    bool use_random_objects, 
+    SGMaterialLib* matlib,
+    float building_density,
+    bool use_random_objects,
     bool use_random_buildings)
   {
     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);
       SGTexturedTriangleBin triangleBin = i->second;
-      
+
       if (!mat)
         continue;
 
       osg::Texture2D* object_mask  = mat->get_object_mask(triangleBin);
+      osg::Image* img;
+      if (object_mask != NULL) {
+          img = object_mask->getImage();
+      }
+
       int   group_count            = mat->get_object_group_count();
-      float building_coverage      = mat->get_building_coverage();      
+      float building_coverage      = mat->get_building_coverage();
       float cos_zero_density_angle = mat->get_cos_object_zero_density_slope_angle();
       float cos_max_density_angle  = mat->get_cos_object_max_density_slope_angle();
-      
-      bool found = false;
-      SGBuildingBin* bin = NULL;
-      
-      if (building_coverage > 0) {      
-        BOOST_FOREACH(bin, randomBuildings)
-        {
-          if (bin->getMaterialName() == mat->get_names()[0]) {
-              found = true;
-              break;
-          }
-        }
-        
-        if (!found) {
-          bin = new SGBuildingBin(mat);
-          randomBuildings.push_back(bin);
-        }       
-      }            
-      
+
+      if (building_coverage == 0)
+         continue;
+
+      SGBuildingBin* bin;
+
+      if (building_coverage > 0) {
+        bin = new SGBuildingBin(mat);
+        randomBuildings.push_back(bin);
+      }
+
       unsigned num = i->second.getNumTriangles();
       int random_dropped = 0;
       int mask_dropped = 0;
@@ -517,7 +517,7 @@ struct SGTileGeometryBin {
 
       for (unsigned i = 0; i < num; ++i) {
         SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i);
-        
+
         SGVec3f vorigin = triangleBin.getVertex(triangleRef[0]).vertex;
         SGVec3f v0 = triangleBin.getVertex(triangleRef[1]).vertex - vorigin;
         SGVec3f v1 = triangleBin.getVertex(triangleRef[2]).vertex - vorigin;
@@ -525,49 +525,49 @@ struct SGTileGeometryBin {
         SGVec2f t0 = triangleBin.getVertex(triangleRef[1]).texCoord - torigin;
         SGVec2f t1 = triangleBin.getVertex(triangleRef[2]).texCoord - torigin;
         SGVec3f normal = cross(v0, v1);
-        
+
         // Ensure the slope isn't too steep by checking the
         // cos of the angle between the slope normal and the
         // vertical (conveniently the z-component of the normalized
-        // normal) and values passed in.                   
+        // normal) and values passed in.
         float cos = normalize(normal).z();
         float slope_density = 1.0;
         if (cos < cos_zero_density_angle) continue; // Too steep for any objects
         if (cos < cos_max_density_angle) {
-          slope_density = 
-            (cos - cos_zero_density_angle) / 
+          slope_density =
+            (cos - cos_zero_density_angle) /
             (cos_max_density_angle - cos_zero_density_angle);
         }
-                
+
         // Containers to hold the random buildings and objects generated
         // for this triangle for collision detection purposes.
         std::vector< std::pair< SGVec3f, float> > triangleObjectsList;
         std::vector< std::pair< SGVec3f, float> > triangleBuildingList;
-        
+
         // Compute the area
         float area = 0.5f*length(normal);
         if (area <= SGLimitsf::min())
           continue;
 
-        // Generate any random objects          
+        // Generate any random objects
         if (use_random_objects && (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) continue;
-              
+
             // 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);
-              
+
               // Determine the number of objecst to place, taking into account
               // the slope density factor.
               double n = slope_density * area / object->get_coverage_m2();
-              
+
               // Use the zombie door method to determine fractional object placement.
               n = n + mt_rand(&seed);
 
@@ -582,85 +582,83 @@ struct SGTileGeometryBin {
 
                 SGVec3f randomPoint = vorigin + a*v0 + b*v1;
                 float rotation = static_cast<float>(mt_rand(&seed));
-                
+
                 // Check that the point is sufficiently far from
                 // the edge of the triangle by measuring the distance
-                // from the three lines that make up the triangle.                          
+                // from the three lines that make up the triangle.
                 float spacing = object->get_spacing_m();
-                
-                SGVec3f p = randomPoint - vorigin;          
+
+                SGVec3f p = randomPoint - vorigin;
                 float edges[] = { length(cross(p     , p - v0)) / length(v0),
                                   length(cross(p - v0, p - v1)) / length(v1 - v0),
                                   length(cross(p - v1, p     )) / length(v1)      };
-                float edge_dist = *std::min_element(edges, edges + 3);   
-                
-                if (edge_dist < spacing) { 
+                float edge_dist = *std::min_element(edges, edges + 3);
+
+                if (edge_dist < spacing) {
                   n -= 1.0;
-                  continue; 
-                }                
-                
+                  continue;
+                }
+
                 if (object_mask != NULL) {
                   SGVec2f texCoord = torigin + a*t0 + b*t1;
-                  
+
                   // Check this random point against the object mask
-                  // blue (for buildings) channel. 
-                  osg::Image* img = object_mask->getImage();            
+                  // blue (for buildings) channel.
+                  osg::Image* img = object_mask->getImage();
                   unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
                   unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
-                  
-                  if (mt_rand(&seed) > img->getColor(x, y).b()) { 
+
+                  if (mt_rand(&seed) > img->getColor(x, y).b()) {
                     // Failed object mask check
                     n -= 1.0;
-                    continue;                    
+                    continue;
                   }
-                  
+
                   rotation = img->getColor(x,y).r();
                 }
-                
+
                 bool close = false;
 
                 // Check it isn't too close to any other random objects in the triangle
                 std::vector<std::pair<SGVec3f, float> >::iterator l;
                 for (l = triangleObjectsList.begin(); l != triangleObjectsList.end(); ++l) {
-                  float min_dist2 = (l->second + object->get_spacing_m()) * 
+                  float min_dist2 = (l->second + object->get_spacing_m()) *
                                     (l->second + object->get_spacing_m());
-                  
+
                   if (distSqr(l->first, randomPoint) > min_dist2) {
                     close = true;
                     continue;
                   }
                 }
-                
+
                 if (!close) {
                     triangleObjectsList.push_back(std::make_pair(randomPoint, object->get_spacing_m()));
-                    randomModels.insert(randomPoint, 
-                                        object, 
-                                        (int)object->get_randomized_range_m(&seed), 
+                    randomModels.insert(randomPoint,
+                                        object,
+                                        (int)object->get_randomized_range_m(&seed),
                                         rotation);
                 }
-                
-                n -= 1.0;
               }
             }
           }
-        }        
-        
+        }
+
         // Random objects now generated.  Now generate the random buildings (if any);
         if (use_random_buildings && (building_coverage > 0) && (building_density > 0)) {
-          
+
           // Calculate the number of buildings, taking into account building density (which is linear)
-          // and the slope density factor.  
+          // and the slope density factor.
           double num = building_density * building_density * slope_density * area / building_coverage;
-          
+
           // For partial units of area, use a zombie door method to
           // create the proper random chance of an object being created
           // for this triangle.
           num = num + mt_rand(&seed);
 
           if (num < 1.0f) {
-            continue;          
+            continue;
           }
-          
+
           // Cosine of the angle between the two vectors.
           float cosine = (dot(v0, v1) / (length(v0) * length(v1)));
 
@@ -668,137 +666,133 @@ struct SGTileGeometryBin {
           // coverage will result.
           float stepv0 = (sqrtf(building_coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine);
           float stepv1 = (sqrtf(building_coverage) / building_density) / length(v1);
-          
+
           stepv0 = std::min(stepv0, 1.0f);
           stepv1 = std::min(stepv1, 1.0f);
-          
+
           // Start at a random point. a will be immediately incremented below.
-          float a = -mt_rand(&seed) * stepv0;  
+          float a = -mt_rand(&seed) * stepv0;
           float b = mt_rand(&seed) * stepv1;
 
           // Place an object each unit of area
           while (num > 1.0) {
+            num -= 1.0;
 
-            // Set the next location to place a building          
+            // Set the next location to place a building
             a += stepv0;
-            
+
             if ((a + b) > 1.0f) {
               // Reached the end of the scan-line on v0. Reset and increment
               // scan-line on v1
               a = mt_rand(&seed) * stepv0;
               b += stepv1;
             }
-            
+
             if (b > 1.0f) {
-              // In a degenerate case of a single point, we might be outside the 
+              // In a degenerate case of a single point, we might be outside the
               // scanline.  Note that we need to still ensure that a+b < 1.
               b = mt_rand(&seed) * stepv1 * (1.0f - a);
             }
-            
+
             if ((a + b) > 1.0f ) {
               // Truly degenerate case - simply choose a random point guaranteed
               // to fulfil the constraing of a+b < 1.
               a = mt_rand(&seed);
               b = mt_rand(&seed) * (1.0f - a);
             }
-            
+
             SGVec3f randomPoint = vorigin + a*v0 + b*v1;
             float rotation = mt_rand(&seed);
-            
+
             if (object_mask != NULL) {
               SGVec2f texCoord = torigin + a*t0 + b*t1;
-              osg::Image* img = object_mask->getImage();            
+              osg::Image* img = object_mask->getImage();
               int x = (int) (img->s() * texCoord.x()) % img->s();
               int y = (int) (img->t() * texCoord.y()) % img->t();
-              
+
               // In some degenerate cases x or y can be < 1, in which case the mod operand fails
               while (x < 0) x += img->s();
               while (y < 0) y += img->t();
 
-              if (mt_rand(&seed) < img->getColor(x, y).b()) {  
+              if (mt_rand(&seed) < img->getColor(x, y).b()) {
                 // Object passes mask. Rotation is taken from the red channel
                 rotation = img->getColor(x,y).r();
               } else {
                 // Fails mask test - try again.
                 mask_dropped++;
-                num -= 1.0;
                 continue;
-              }  
+              }
             }
 
             // Check building isn't too close to the triangle edge.
             float type_roll = mt_rand(&seed);
             SGBuildingBin::BuildingType buildingtype = bin->getBuildingType(type_roll);
             float radius = bin->getBuildingMaxRadius(buildingtype);
-                      
-            // Determine the actual center of the building, by shifting from the 
+
+            // Determine the actual center of the building, by shifting from the
             // center of the front face to the true center.
             osg::Matrix rotationMat = osg::Matrix::rotate(- rotation * M_PI * 2,
                                                  osg::Vec3f(0.0, 0.0, 1.0));
             SGVec3f buildingCenter = randomPoint + toSG(osg::Vec3f(-0.5 * bin->getBuildingMaxDepth(buildingtype), 0.0, 0.0) * rotationMat);
 
-            SGVec3f p = buildingCenter - vorigin;          
+            SGVec3f p = buildingCenter - vorigin;
             float edges[] = { length(cross(p     , p - v0)) / length(v0),
                               length(cross(p - v0, p - v1)) / length(v1 - v0),
                               length(cross(p - v1, p     )) / length(v1)      };
-            float edge_dist = *std::min_element(edges, edges + 3);   
-            
-            if (edge_dist < radius) { 
-              num -= 1.0;
+            float edge_dist = *std::min_element(edges, edges + 3);
+
+            if (edge_dist < radius) {
               triangle_dropped++;
-              continue; 
+              continue;
             }
-                      
-            // Check building isn't too close to random objects and other buildings.          
+
+            // Check building isn't too close to random objects and other buildings.
             bool close = false;
             std::vector<std::pair<SGVec3f, float> >::iterator iter;
-            
+
             for (iter = triangleBuildingList.begin(); iter != triangleBuildingList.end(); ++iter) {
               float min_dist = iter->second + radius;
               if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
                 close = true;
                 continue;
-              }            
+              }
             }
-            
+
             if (close) {
-              num -= 1.0;
               building_dropped++;
-              continue;            
+              continue;
             }
-            
+
             for (iter = triangleObjectsList.begin(); iter != triangleObjectsList.end(); ++iter) {
               float min_dist = iter->second + radius;
               if (distSqr(iter->first, buildingCenter) < min_dist * min_dist) {
                 close = true;
                 continue;
-              }            
+              }
             }
-            
+
             if (close) {
-              num -= 1.0;
               random_dropped++;
-              continue;            
-            }          
+              continue;
+            }
 
-            std::pair<SGVec3f, float> pt = std::make_pair(buildingCenter, radius);              
+            std::pair<SGVec3f, float> pt = std::make_pair(buildingCenter, radius);
             triangleBuildingList.push_back(pt);
             bin->insert(randomPoint, rotation, buildingtype);
-            num -= 1.0;
           }
         }
-        
+
         triangleObjectsList.clear();
-        triangleBuildingList.clear();        
+        triangleBuildingList.clear();
       }
-      
+
       SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << ((bin) ? bin->getNumBuildings() : 0));
       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to mask: " << mask_dropped);
       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to random object: " << random_dropped);
       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to other buildings: " << building_dropped);
     }
   }
-  
+
   void computeRandomForest(SGMaterialLib* matlib, float vegetation_density)
   {
     SGMaterialTriangleMap::iterator i;
@@ -816,11 +810,11 @@ struct SGTileGeometryBin {
       float wood_coverage = mat->get_wood_coverage();
       if ((wood_coverage <= 0) || (vegetation_density <= 0))
         continue;
-              
+
       // 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()  ) &&
@@ -832,7 +826,7 @@ struct SGTileGeometryBin {
             break;
         }
       }
-      
+
       if (!found) {
         bin = new TreeBin();
         bin->texture = mat->get_tree_texture();
@@ -851,7 +845,7 @@ struct SGTileGeometryBin {
                                     mat->get_cos_tree_max_density_slope_angle(),
                                     mat->get_cos_tree_zero_density_slope_angle(),
                                     randomPoints);
-      
+
       std::vector<SGVec3f>::iterator k;
       for (k = randomPoints.begin(); k != randomPoints.end(); ++k) {
         bin->insert(*k);
@@ -892,293 +886,366 @@ struct GetModelLODCoord {
 typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
                         GetModelLODCoord>  RandomObjectsQuadtree;
 
-osg::Node*
-SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options)
-{
-  SGBinObject tile;
-  if (!tile.read_bin(path))
-    return NULL;
-
-  SGMaterialLib* matlib = 0;
-  bool use_random_objects = false;
-  bool use_random_vegetation = false;
-  bool use_random_buildings = false;
-  float vegetation_density = 1.0f;  
-  float building_density = 1.0f;
-  if (options) {
-    matlib = options->getMaterialLib();
-    SGPropertyNode* propertyNode = options->getPropertyNode().get();
-    if (propertyNode) {
-        use_random_objects
-            = propertyNode->getBoolValue("/sim/rendering/random-objects",
-                                         use_random_objects);
-        use_random_vegetation
-            = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
-                                         use_random_vegetation);        
-        vegetation_density
-            = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
-                                          vegetation_density);
-        use_random_buildings
-            = propertyNode->getBoolValue("/sim/rendering/random-buildings",
-                                         use_random_buildings);        
-        building_density
-            = propertyNode->getFloatValue("/sim/rendering/building-density",
-                                          building_density);
+class RandomObjectCallback : public OptionsReadFileCallback {
+public:
+    virtual osgDB::ReaderWriter::ReadResult
+    readNode(const std::string&, const osgDB::Options*)
+    {
+        osg::ref_ptr<osg::Group> group = new osg::Group;
+        group->setName("Random Object and Lighting Group");
+        group->setDataVariance(osg::Object::STATIC);
+
+        osg::LOD* lightLOD = generateLightingTileObjects();
+        if (lightLOD)
+          group->addChild(lightLOD);
+
+        osg::LOD* objectLOD = generateRandomTileObjects();
+        if (objectLOD)
+          group->addChild(objectLOD);
+
+        return group.release();
     }
-  }
 
-  SGVec3d center = tile.get_gbs_center();
-  SGGeod geodPos = SGGeod::fromCart(center);
-  SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
-
-  // rotate the tiles so that the bounding boxes get nearly axis aligned.
-  // this will help the collision tree's bounding boxes a bit ...
-  std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
-  for (unsigned i = 0; i < nodes.size(); ++i)
-    nodes[i] = hlOr.transform(nodes[i]);
-  tile.set_wgs84_nodes(nodes);
-
-  SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
-  std::vector<SGVec3f> normals = tile.get_normals();
-  for (unsigned i = 0; i < normals.size(); ++i)
-    normals[i] = hlOrf.transform(normals[i]);
-  tile.set_normals(normals);
-
-  SGTileGeometryBin tileGeometryBin;
-  if (!tileGeometryBin.insertBinObj(tile, matlib))
-    return NULL;
-
-  SGVec3f up(0, 0, 1);
-  GroundLightManager* lightManager = GroundLightManager::instance();
-
-  osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
-  osg::ref_ptr<osg::Group> randomObjects;
-  osg::ref_ptr<osg::Group> forestNode;
-  osg::ref_ptr<osg::Group> buildingNode;  
-  osg::Group* terrainGroup = new osg::Group;
-  terrainGroup->setName("BTGTerrainGroup");
-      
-  osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
-  if (node)
-    terrainGroup->addChild(node);
-    
-  if (matlib && (use_random_objects || use_random_buildings)) {
-    tileGeometryBin.computeRandomObjectsAndBuildings(matlib, 
-                                                     building_density, 
-                                                     use_random_objects, 
-                                                     use_random_buildings);    
-  }
+    // Generate all the lighting objects for the tile.
+    osg::LOD* generateLightingTileObjects()
+    {
+      SGMaterialLib* matlib;
 
-  if (tileGeometryBin.randomModels.getNumModels() > 0) {
-    // Generate a repeatable random seed
-    mt seed;
-    mt_init(&seed, unsigned(123));
+      if (_options)
+        matlib = _options->getMaterialLib();
 
-    std::vector<ModelLOD> models;
-    for (unsigned int i = 0;
-         i < tileGeometryBin.randomModels.getNumModels(); i++) {
-      SGMatModelBin::MatModel obj
-        = tileGeometryBin.randomModels.getMatModel(i);          
-
-      SGPropertyNode* root = options->getPropertyNode()->getRootNode();
-      osg::Node* node = obj.model->get_random_model(root, &seed);
-    
-      // Create a matrix to place the object in the correct
-      // location, and then apply the rotation matrix created
-      // above, with an additional random (or taken from
-      // the object mask) heading rotation if appropriate.
-      osg::Matrix transformMat;
-      transformMat = osg::Matrix::translate(toOsg(obj.position));
-      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)));
+      // FIXME: ugly, has a side effect
+      if (matlib)
+        _tileGeometryBin->computeRandomSurfaceLights(matlib);
+
+      GroundLightManager* lightManager = GroundLightManager::instance();
+      osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
+      SGVec3f up(0, 0, 1);
+
+      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 (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) {
-        // Rotate the object around the z axis.
-        double hdg =  - obj.rotation * M_PI * 2;
-        transformMat.preMult(osg::Matrix::rotate(hdg,
-                                                 osg::Vec3d(0.0, 0.0, 1.0)));
+      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);
       }
-      
-      osg::MatrixTransform* position =
-        new osg::MatrixTransform(transformMat);
-      position->setName("positionRandomeModel");
-      position->addChild(node);
-      models.push_back(ModelLOD(position, obj.lod));
-    }
-    RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
-    quadtree.buildQuadTree(models.begin(), models.end());
-    randomObjects = quadtree.getRoot();
-    randomObjects->setName("Random objects");
-  }
 
-  if (! tileGeometryBin.randomBuildings.empty()) {
-    buildingNode = createRandomBuildings(tileGeometryBin.randomBuildings, osg::Matrix::identity(),
-                                options);                                
-    buildingNode->setName("Random buildings");
-  }
+      if (!_tileGeometryBin->vasiLights.empty()) {
+        EffectGeode* vasiGeode = new EffectGeode;
+        Effect* vasiEffect
+            = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, true);
+        vasiGeode->setEffect(vasiEffect);
+        SGVec4f red(1, 0, 0, 1);
+        SGMaterial* mat = 0;
+        if (matlib)
+          mat = matlib->find("RWY_RED_LIGHTS");
+        if (mat)
+          red = mat->get_light_color();
+        SGVec4f white(1, 1, 1, 1);
+        mat = 0;
+        if (matlib)
+          mat = matlib->find("RWY_WHITE_LIGHTS");
+        if (mat)
+          white = mat->get_light_color();
+        SGDirectionalLightListBin::const_iterator i;
+        for (i = _tileGeometryBin->vasiLights.begin();
+             i != _tileGeometryBin->vasiLights.end(); ++i) {
+          vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
+        }
+        vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
+        lightGroup->addChild(vasiGeode);
+      }
 
-  if (use_random_vegetation && matlib) {
-    // Now add some random forest.
-    tileGeometryBin.computeRandomForest(matlib, vegetation_density);
-    
-    if (! tileGeometryBin.randomForest.empty()) {
-      forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(),
-                                options);
-      forestNode->setName("Random trees");
-    }
-  }  
-
-  // FIXME: ugly, has a side effect
-  if (matlib)
-    tileGeometryBin.computeRandomSurfaceLights(matlib);
-
-  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);
-  }
+      Effect* runwayEffect = 0;
+      if (_tileGeometryBin->runwayLights.getNumLights() > 0
+          || !_tileGeometryBin->rabitLights.empty()
+          || !_tileGeometryBin->reilLights.empty()
+          || !_tileGeometryBin->odalLights.empty()
+          || _tileGeometryBin->taxiLights.getNumLights() > 0)
+          runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true);
+      if (_tileGeometryBin->runwayLights.getNumLights() > 0
+          || !_tileGeometryBin->rabitLights.empty()
+          || !_tileGeometryBin->reilLights.empty()
+          || !_tileGeometryBin->odalLights.empty()
+          || !_tileGeometryBin->holdshortLights.empty()
+          || !_tileGeometryBin->guardLights.empty()) {
+        osg::Group* rwyLights = new osg::Group;
+        rwyLights->setStateSet(lightManager->getRunwayLightStateSet());
+        rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
+        if (_tileGeometryBin->runwayLights.getNumLights() != 0) {
+          EffectGeode* geode = new EffectGeode;
+          geode->setEffect(runwayEffect);
+          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));
+        }
+        for (i = _tileGeometryBin->reilLights.begin();
+             i != _tileGeometryBin->reilLights.end(); ++i) {
+          rwyLights->addChild(SGLightFactory::getSequenced(*i));
+        }
+        for (i = _tileGeometryBin->holdshortLights.begin();
+             i != _tileGeometryBin->holdshortLights.end(); ++i) {
+          rwyLights->addChild(SGLightFactory::getHoldShort(*i));
+        }
+        for (i = _tileGeometryBin->guardLights.begin();
+             i != _tileGeometryBin->guardLights.end(); ++i) {
+          rwyLights->addChild(SGLightFactory::getGuard(*i));
+        }
+        SGLightListBin::const_iterator j;
+        for (j = _tileGeometryBin->odalLights.begin();
+             j != _tileGeometryBin->odalLights.end(); ++j) {
+          rwyLights->addChild(SGLightFactory::getOdal(*j));
+        }
+        lightGroup->addChild(rwyLights);
+      }
 
-  if (!tileGeometryBin.vasiLights.empty()) {
-    EffectGeode* vasiGeode = new EffectGeode;
-    Effect* vasiEffect
-        = getLightEffect(24, osg::Vec3(1, 0.0001, 0.000001), 1, 24, true);
-    vasiGeode->setEffect(vasiEffect);
-    SGVec4f red(1, 0, 0, 1);
-    SGMaterial* mat = 0;
-    if (matlib)
-      mat = matlib->find("RWY_RED_LIGHTS");
-    if (mat)
-      red = mat->get_light_color();
-    SGVec4f white(1, 1, 1, 1);
-    mat = 0;
-    if (matlib)
-      mat = matlib->find("RWY_WHITE_LIGHTS");
-    if (mat)
-      white = mat->get_light_color();
-    SGDirectionalLightListBin::const_iterator i;
-    for (i = tileGeometryBin.vasiLights.begin();
-         i != tileGeometryBin.vasiLights.end(); ++i) {
-      vasiGeode->addDrawable(SGLightFactory::getVasi(up, *i, red, white));
-    }
-    vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
-    lightGroup->addChild(vasiGeode);
-  }
-  
-  Effect* runwayEffect = 0;
-  if (tileGeometryBin.runwayLights.getNumLights() > 0
-      || !tileGeometryBin.rabitLights.empty()
-      || !tileGeometryBin.reilLights.empty()
-      || !tileGeometryBin.odalLights.empty()
-      || tileGeometryBin.taxiLights.getNumLights() > 0)
-      runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true);
-  if (tileGeometryBin.runwayLights.getNumLights() > 0
-      || !tileGeometryBin.rabitLights.empty()
-      || !tileGeometryBin.reilLights.empty()
-      || !tileGeometryBin.odalLights.empty()
-      || !tileGeometryBin.holdshortLights.empty()
-      || !tileGeometryBin.guardLights.empty()) {
-    osg::Group* rwyLights = new osg::Group;
-    rwyLights->setStateSet(lightManager->getRunwayLightStateSet());
-    rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
-    if (tileGeometryBin.runwayLights.getNumLights() != 0) {
-      EffectGeode* geode = new EffectGeode;
-      geode->setEffect(runwayEffect);
-      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));
-    }
-    for (i = tileGeometryBin.reilLights.begin();
-         i != tileGeometryBin.reilLights.end(); ++i) {
-      rwyLights->addChild(SGLightFactory::getSequenced(*i));
-    }
-    for (i = tileGeometryBin.holdshortLights.begin();
-         i != tileGeometryBin.holdshortLights.end(); ++i) {
-      rwyLights->addChild(SGLightFactory::getHoldShort(*i));
-    }
-    for (i = tileGeometryBin.guardLights.begin();
-         i != tileGeometryBin.guardLights.end(); ++i) {
-      rwyLights->addChild(SGLightFactory::getGuard(*i));
+      if (_tileGeometryBin->taxiLights.getNumLights() > 0) {
+        osg::Group* taxiLights = new osg::Group;
+        taxiLights->setStateSet(lightManager->getTaxiLightStateSet());
+        taxiLights->setNodeMask(RUNWAYLIGHTS_BIT);
+        EffectGeode* geode = new EffectGeode;
+        geode->setEffect(runwayEffect);
+        geode->addDrawable(SGLightFactory::getLights(_tileGeometryBin->taxiLights));
+        taxiLights->addChild(geode);
+        lightGroup->addChild(taxiLights);
+      }
+
+      osg::LOD* lightLOD = NULL;
+
+      if (lightGroup->getNumChildren() > 0) {
+        lightLOD = new osg::LOD;
+        lightLOD->addChild(lightGroup.get(), 0, 60000);
+        // VASI is always on, so doesn't use light bits.
+        lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT);
+      }
+
+      return lightLOD;
     }
-    SGLightListBin::const_iterator j;
-    for (j = tileGeometryBin.odalLights.begin();
-         j != tileGeometryBin.odalLights.end(); ++j) {
-      rwyLights->addChild(SGLightFactory::getOdal(*j));
+
+    // Generate all the random forest, objects and buildings for the tile
+    osg::LOD* generateRandomTileObjects()
+    {
+      SGMaterialLib* matlib;
+      bool use_random_objects = false;
+      bool use_random_vegetation = false;
+      bool use_random_buildings = false;
+      float vegetation_density = 1.0f;
+      float building_density = 1.0f;
+
+      osg::ref_ptr<osg::Group> randomObjects;
+      osg::ref_ptr<osg::Group> forestNode;
+      osg::ref_ptr<osg::Group> buildingNode;
+
+      if (_options) {
+        matlib = _options->getMaterialLib();
+        SGPropertyNode* propertyNode = _options->getPropertyNode().get();
+        if (propertyNode) {
+            use_random_objects
+                = propertyNode->getBoolValue("/sim/rendering/random-objects",
+                                             use_random_objects);
+            use_random_vegetation
+                = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
+                                             use_random_vegetation);
+            vegetation_density
+                = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
+                                              vegetation_density);
+            use_random_buildings
+                = propertyNode->getBoolValue("/sim/rendering/random-buildings",
+                                             use_random_buildings);
+            building_density
+                = propertyNode->getFloatValue("/sim/rendering/building-density",
+                                              building_density);
+        }
+      }
+
+
+
+      if (matlib && (use_random_objects || use_random_buildings)) {
+        _tileGeometryBin->computeRandomObjectsAndBuildings(matlib,
+                                                         building_density,
+                                                         use_random_objects,
+                                                         use_random_buildings);
+      }
+
+
+      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);
+
+          SGPropertyNode* root = _options->getPropertyNode()->getRootNode();
+          osg::Node* node = obj.model->get_random_model(root, &seed);
+
+          // Create a matrix to place the object in the correct
+          // location, and then apply the rotation matrix created
+          // above, with an additional random (or taken from
+          // the object mask) heading rotation if appropriate.
+          osg::Matrix transformMat;
+          transformMat = osg::Matrix::translate(toOsg(obj.position));
+          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)));
+          }
+
+          if (obj.model->get_heading_type() == SGMatModel::HEADING_MASK) {
+            // Rotate the object around the z axis.
+            double hdg =  - obj.rotation * 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->setName("positionRandomModel");
+          position->addChild(node);
+          models.push_back(ModelLOD(position, obj.lod));
+        }
+        RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
+        quadtree.buildQuadTree(models.begin(), models.end());
+        randomObjects = quadtree.getRoot();
+        randomObjects->setName("Random objects");
+      }
+
+      if (! _tileGeometryBin->randomBuildings.empty()) {
+        buildingNode = createRandomBuildings(_tileGeometryBin->randomBuildings, osg::Matrix::identity(),
+                                    _options);
+        buildingNode->setName("Random buildings");
+        _tileGeometryBin->randomBuildings.clear();
+      }
+
+      if (use_random_vegetation && matlib) {
+        // Now add some random forest.
+        _tileGeometryBin->computeRandomForest(matlib, vegetation_density);
+
+        if (! _tileGeometryBin->randomForest.empty()) {
+          forestNode = createForest(_tileGeometryBin->randomForest, osg::Matrix::identity(),
+                                    _options);
+          forestNode->setName("Random trees");
+        }
+      }
+
+      osg::LOD* objectLOD = NULL;
+
+      if (randomObjects.valid() ||  forestNode.valid() || buildingNode.valid()) {
+        objectLOD = new osg::LOD;
+
+        if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
+        if (forestNode.valid())  objectLOD->addChild(forestNode.get(), 0, 20000);
+        if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000);
+
+        unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
+        objectLOD->setNodeMask(nodeMask);
+      }
+
+      return objectLOD;
     }
-    lightGroup->addChild(rwyLights);
-  }
 
-  if (tileGeometryBin.taxiLights.getNumLights() > 0) {
-    osg::Group* taxiLights = new osg::Group;
-    taxiLights->setStateSet(lightManager->getTaxiLightStateSet());
-    taxiLights->setNodeMask(RUNWAYLIGHTS_BIT);
-    EffectGeode* geode = new EffectGeode;
-    geode->setEffect(runwayEffect);
-    geode->addDrawable(SGLightFactory::getLights(tileGeometryBin.taxiLights));
-    taxiLights->addChild(geode);
-    lightGroup->addChild(taxiLights);
-  }
+    /// The original options to use for this bunch of models
+    osg::ref_ptr<SGReaderWriterOptions> _options;
+    osg::ref_ptr<SGTileGeometryBin> _tileGeometryBin;
+};
 
-  // The toplevel transform for that tile.
-  osg::MatrixTransform* transform = new osg::MatrixTransform;
-  transform->setName(path);
-  transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
-                       osg::Matrix::translate(toOsg(center)));
-  transform->addChild(terrainGroup);
-  if (lightGroup->getNumChildren() > 0) {
-    osg::LOD* lightLOD = new osg::LOD;
-    lightLOD->addChild(lightGroup.get(), 0, 60000);
-    // VASI is always on, so doesn't use light bits.
-    lightLOD->setNodeMask(LIGHTS_BITS | MODEL_BIT | PERMANENTLIGHT_BIT);
-    transform->addChild(lightLOD);
-  }
-  
-  if (randomObjects.valid() || forestNode.valid() || buildingNode.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 (forestNode.valid())  objectLOD->addChild(forestNode.get(), 0, 20000);
-    if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000);
-    
-    unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
-    objectLOD->setNodeMask(nodeMask);
-    transform->addChild(objectLOD);
-  }
-  transform->setNodeMask( ~simgear::MODELLIGHT_BIT );
-  
-  return transform;
+osg::Node*
+SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options)
+{
+    SGBinObject tile;
+    if (!tile.read_bin(path))
+      return NULL;
+
+    SGMaterialLib* matlib = 0;
+
+    if (options) {
+      matlib = options->getMaterialLib();
+    }
+
+    SGVec3d center = tile.get_gbs_center();
+    SGGeod geodPos = SGGeod::fromCart(center);
+    SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
+
+    // rotate the tiles so that the bounding boxes get nearly axis aligned.
+    // this will help the collision tree's bounding boxes a bit ...
+    std::vector<SGVec3d> nodes = tile.get_wgs84_nodes();
+    for (unsigned i = 0; i < nodes.size(); ++i)
+      nodes[i] = hlOr.transform(nodes[i]);
+    tile.set_wgs84_nodes(nodes);
+
+    SGQuatf hlOrf(hlOr[0], hlOr[1], hlOr[2], hlOr[3]);
+    std::vector<SGVec3f> normals = tile.get_normals();
+    for (unsigned i = 0; i < normals.size(); ++i)
+      normals[i] = hlOrf.transform(normals[i]);
+    tile.set_normals(normals);
+
+    osg::ref_ptr<SGTileGeometryBin> tileGeometryBin = new SGTileGeometryBin;
+
+    if (!tileGeometryBin->insertBinObj(tile, matlib))
+      return NULL;
+
+    osg::Group* terrainGroup = new osg::Group;
+    terrainGroup->setName("BTGTerrainGroup");
+
+    osg::Node* node = tileGeometryBin->getSurfaceGeometry(matlib);
+    if (node)
+      terrainGroup->addChild(node);
+
+    // The toplevel transform for that tile.
+    osg::MatrixTransform* transform = new osg::MatrixTransform;
+    transform->setName(path);
+    transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
+                         osg::Matrix::translate(toOsg(center)));
+    transform->addChild(terrainGroup);
+
+    // PagedLOD for the random objects so we don't need to generate
+    // them all on tile loading.
+    osg::PagedLOD* pagedLOD = new osg::PagedLOD;
+    pagedLOD->setCenterMode(osg::PagedLOD::USE_BOUNDING_SPHERE_CENTER);
+    pagedLOD->setName("pagedObjectLOD");
+
+    // we just need to know about the read file callback that itself holds the data
+    osg::ref_ptr<RandomObjectCallback> randomObjectCallback = new RandomObjectCallback;
+    randomObjectCallback->_options = SGReaderWriterOptions::copyOrCreate(options);
+    randomObjectCallback->_tileGeometryBin = tileGeometryBin;
+
+    osg::ref_ptr<osgDB::Options> callbackOptions = new osgDB::Options;
+    callbackOptions->setReadFileCallback(randomObjectCallback.get());
+    pagedLOD->setDatabaseOptions(callbackOptions.get());
+
+    pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy name - use the stored data in the read file callback");
+    pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 35000);
+    transform->addChild(pagedLOD);
+    transform->setNodeMask( ~simgear::MODELLIGHT_BIT );
+
+    return transform;
 }