]> git.mxchange.org Git - simgear.git/commitdiff
Improve memory occupancy and load time of random buildings.
authorStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Wed, 12 Sep 2012 21:45:12 +0000 (22:45 +0100)
committerStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Wed, 12 Sep 2012 21:45:12 +0000 (22:45 +0100)
simgear/scene/tgdb/SGBuildingBin.cxx
simgear/scene/tgdb/SGBuildingBin.hxx
simgear/scene/tgdb/obj.cxx

index 180dc48d373c0b2d35cc954be4b77f07603f9023..7d5e4861000458b99ad09535949f9053e3d98eb2 100644 (file)
 #include <osgDB/FileUtils>
 
 #include <simgear/debug/logstream.hxx>
 #include <osgDB/FileUtils>
 
 #include <simgear/debug/logstream.hxx>
+#include <simgear/math/SGLimits.hxx>
+#include <simgear/math/SGMisc.hxx>
 #include <simgear/math/sg_random.h>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/scene/material/Effect.hxx>
 #include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/model/model.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/math/sg_random.h>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/scene/material/Effect.hxx>
 #include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/model/model.hxx>
 #include <simgear/props/props.hxx>
-#include <simgear/scene/util/QuadTreeBuilder.hxx>
-#include <simgear/scene/util/RenderConstants.hxx>
-#include <simgear/scene/util/StateAttributeFactory.hxx>
-#include <simgear/structure/OSGUtils.hxx>
-
 
 #include "ShaderGeometry.hxx"
 #include "SGBuildingBin.hxx"
 
 
 #include "ShaderGeometry.hxx"
 #include "SGBuildingBin.hxx"
 
-#define SG_BUILDING_QUAD_TREE_DEPTH 2
-#define SG_BUILDING_FADE_OUT_LEVELS 4
 
 using namespace osg;
 
 
 using namespace osg;
 
@@ -71,247 +66,332 @@ typedef std::map<std::string, osg::observer_ptr<osg::StateSet> > BuildingStateSe
 static BuildingStateSetMap statesetmap;
 static int numBuildings;
 
 static BuildingStateSetMap statesetmap;
 static int numBuildings;
 
-void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& building)
+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
+// color - xyz of tree quad origin, replicated 4 times.
+
+struct BuildingBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback
 {
 {
-      // Generate a repeatable random seed
-      mt seed;
-      mt_init(&seed, unsigned(building.position.x()));      
-      
-      // Get or create geometry.
-      osg::ref_ptr<osg::Geometry> geom;
-      osg::ref_ptr<osg::Vec3Array> v;
-      osg::ref_ptr<osg::Vec2Array> t;
-      osg::ref_ptr<osg::Vec4Array> c;
-      osg::ref_ptr<osg::Vec3Array> n;
-      
-      if (geode->getNumDrawables() == 0) {
-        geom = new osg::Geometry;        
-        v = new osg::Vec3Array;
-        t = new osg::Vec2Array;
-        c = new osg::Vec4Array;
-        n = new osg::Vec3Array;
+    BuildingBoundingBoxCallback() {}
+    BuildingBoundingBoxCallback(const BuildingBoundingBoxCallback&, const CopyOp&) {}
+    META_Object(simgear, BuildingBoundingBoxCallback);
+    virtual BoundingBox computeBound(const Drawable&) const;
+};
+
+BoundingBox
+BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
+{
+    BoundingBox bb;
+    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;
+         ++psitr) {
+        DrawArrays* da = static_cast<DrawArrays*>(psitr->get());
+        GLint psFirst = da->getFirst();
+        GLint psEndVert = psFirst + da->getCount();
+        for (GLint i = psFirst;i < psEndVert; ++i) {
+            Vec3 pt = (*v)[i];
+            Matrixd trnsfrm = Matrixd::rotate(- M_PI * 2 * (*pos)[i].a(), Vec3(0.0f, 0.0f, 1.0f));
+            pt = pt * trnsfrm;
+            pt += Vec3((*pos)[i].x(), (*pos)[i].y(), (*pos)[i].z());
+            bb.expandBy(pt);
+        }
+    }
+    return bb;    
+}
+
+  // Set up the building set based on the material definitions
+  SGBuildingBin::SGBuildingBin(const SGMaterial *mat) {
+    
+    material_name = mat->get_names()[0];
+    SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name);
+    texture = mat->get_building_texture();
+    lightMap = 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();    
+    
+    smallBuildingFraction = mat->get_building_small_fraction();
+    mediumBuildingFraction = mat->get_building_medium_fraction();
+    
+    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 };    
+    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];
         
         
-        // Set the color, which is bound overall, and simply white
-        c->push_back( osg::Vec4( 1, 1, 1, 1) );
-        geom->setColorArray(c);
-        geom->setColorBinding(osg::Geometry::BIND_OVERALL);
-
-        geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
-        // Temporary primitive set. Will be over-written later.
-        geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,1));
-        geode->addDrawable(geom);
-      } else {
-        geom = (osg::Geometry*) geode->getDrawable(0);        
-        v = (osg::Vec3Array*) geom->getVertexArray();
-        t = (osg::Vec2Array*) geom->getTexCoordArray(0);
-        c = (osg::Vec4Array*) geom->getColorArray();
-        n = (osg::Vec3Array*) geom->getNormalArray();
-      }
-      
-      // For the moment we'll create a simple box with 5 sides (no need 
-      // for a base).
-      int num_quads = 5;    
+      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;
       
       
-      if (building.pitched) {
-        // If it's a pitched roof, we add another 3 quads (we'll be
-        // removing the flat top).
-        num_quads+=3;        
-      }          
-
-      // Set up the rotation and translation matrix, which we apply to
-      // vertices as they are created as we'll be adding buildings later.
-      osg::Matrix transformMat;
-      transformMat = osg::Matrix::translate(toOsg(building.position));
-      double hdg =  - building.rotation * M_PI * 2;
-      osg::Matrix rotationMat = osg::Matrix::rotate(hdg,
-                                               osg::Vec3d(0.0, 0.0, 1.0));
-      transformMat.preMult(rotationMat);                  
-
-      // Create the vertices
-      float cw = 0.5f * building.width;
-      float cd = building.depth;
-      float ch = building.height;
+      v->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
+      t->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
+      n->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
       
       
-      // 0,0,0 is the bottom center of the front
-      // face, e.g. where the front door would be      
+      sharedGeometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
+      sharedGeometry->setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
+      sharedGeometry->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback);
+      sharedGeometry->setUseDisplayList(false);
       
       
-      // BASEMENT
-      // This exteds 10m below the main section
-      // Front face        
-      v->push_back( osg::Vec3( 0, -cw, -10) * transformMat ); // bottom right
-      v->push_back( osg::Vec3( 0,  cw, -10) * transformMat ); // bottom left
-      v->push_back( osg::Vec3( 0,  cw,   0) * transformMat ); // top left
-      v->push_back( osg::Vec3( 0, -cw,   0) * transformMat ); // top right
-      
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
-      
-      // Left face
-      v->push_back( osg::Vec3( -cd, -cw, -10) * transformMat ); // bottom right
-      v->push_back( osg::Vec3(   0, -cw, -10) * transformMat ); // bottom left
-      v->push_back( osg::Vec3(   0, -cw,   0) * transformMat ); // top left
-      v->push_back( osg::Vec3( -cd, -cw,   0) * transformMat ); // top right
-
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal
-
-      // Back face
-      v->push_back( osg::Vec3( -cd,  cw, -10) * transformMat ); // bottom right
-      v->push_back( osg::Vec3( -cd, -cw, -10) * transformMat ); // bottom left
-      v->push_back( osg::Vec3( -cd, -cw,   0) * transformMat ); // top left
-      v->push_back( osg::Vec3( -cd,  cw,   0) * transformMat ); // top right
-      
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
-      
-      // Right face
-      v->push_back( osg::Vec3(   0, cw, -10) * transformMat ); // bottom right
-      v->push_back( osg::Vec3( -cd, cw, -10) * transformMat ); // bottom left
-      v->push_back( osg::Vec3( -cd, cw,   0) * transformMat ); // top left
-      v->push_back( osg::Vec3(   0, cw,   0) * transformMat ); // top right
-
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal      
-      
-      // MAIN BODY
-      // Front face        
-      v->push_back( osg::Vec3( 0, -cw,  0) * transformMat ); // bottom right
-      v->push_back( osg::Vec3( 0,  cw,  0) * transformMat ); // bottom left
-      v->push_back( osg::Vec3( 0,  cw, ch) * transformMat ); // top left
-      v->push_back( osg::Vec3( 0, -cw, ch) * transformMat ); // top right
-      
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
-      
-      // Left face
-      v->push_back( osg::Vec3( -cd, -cw,  0) * transformMat ); // bottom right
-      v->push_back( osg::Vec3(   0, -cw,  0) * transformMat ); // bottom left
-      v->push_back( osg::Vec3(   0, -cw, ch) * transformMat ); // top left
-      v->push_back( osg::Vec3( -cd, -cw, ch) * transformMat ); // top right
-
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal
-
-      // Back face
-      v->push_back( osg::Vec3( -cd,  cw,  0) * transformMat ); // bottom right
-      v->push_back( osg::Vec3( -cd, -cw,  0) * transformMat ); // bottom left
-      v->push_back( osg::Vec3( -cd, -cw, ch) * transformMat ); // top left
-      v->push_back( osg::Vec3( -cd,  cw, ch) * transformMat ); // top right
-      
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
-      
-      // Right face
-      v->push_back( osg::Vec3(   0, cw,  0) * transformMat ); // bottom right
-      v->push_back( osg::Vec3( -cd, cw,  0) * transformMat ); // bottom left
-      v->push_back( osg::Vec3( -cd, cw, ch) * transformMat ); // top left
-      v->push_back( osg::Vec3(   0, cw, ch) * transformMat ); // top right
-
-      for (int i=0; i<4; ++i)
-        n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal
-      
-      // ROOF
-      if (building.pitched) {      
+      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));                            
+          }
+          
+          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())); 
+          height = floors * (2.8 + mt_rand(&seed));
+          pitched = (mt_rand(&seed) < mat->get_building_large_pitch());                   
+        }
         
         
-        // Front pitched roof
-        v->push_back( osg::Vec3(    0, -cw,   ch) * transformMat ); // bottom right
-        v->push_back( osg::Vec3(    0,  cw,   ch) * transformMat ); // bottom left
-        v->push_back( osg::Vec3(-0.5*cd,  cw, ch+3) * transformMat ); // top left
-        v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) * transformMat ); // top right
+        Building building = Building(buildingtype, 
+                                    width, 
+                                    depth, 
+                                    height, 
+                                    floors,
+                                    pitched);                                                            
         
         
-        for (int i=0; i<4; ++i)
-          n->push_back( osg::Vec3(0.707, 0, 0.707) * rotationMat ); // normal
+        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      
         
         
-        // Left pitched roof
-        v->push_back( osg::Vec3(    -cd, -cw,   ch) * transformMat ); // bottom right
-        v->push_back( osg::Vec3(      0, -cw,   ch) * transformMat ); // bottom left
-        v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) * transformMat ); // top left
-        v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) * transformMat ); // top right
+        // BASEMENT
+        // This exteds 10m below the main section
+        // 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)
         
         for (int i=0; i<4; ++i)
-          n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal
-
-        // Back pitched roof
-        v->push_back( osg::Vec3(    -cd,  cw,   ch) * transformMat ); // bottom right
-        v->push_back( osg::Vec3(    -cd, -cw,   ch) * transformMat ); // bottom left
-        v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) * transformMat ); // top left
-        v->push_back( osg::Vec3(-0.5*cd,  cw, ch+3) * transformMat ); // top right
+          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
+        v->push_back( osg::Vec3(   0, -cw,   0) ); // top left
+        v->push_back( osg::Vec3( -cd, -cw,   0) ); // top right
+
         for (int i=0; i<4; ++i)
         for (int i=0; i<4; ++i)
-          n->push_back( osg::Vec3(-0.707, 0, 0.707) * rotationMat ); // normal      
+          n->push_back( osg::Vec3(0, -1, 0) ); // normal
 
 
-        // Right pitched roof
-        v->push_back( osg::Vec3(      0, cw,   ch) * transformMat ); // bottom right
-        v->push_back( osg::Vec3(    -cd, cw,   ch) * transformMat ); // bottom left
-        v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) * transformMat ); // top left
-        v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) * transformMat ); // top right
+        // Back face
+        v->push_back( osg::Vec3( -cd,  cw, -10) ); // bottom right
+        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)
         
         for (int i=0; i<4; ++i)
-          n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal
-      } else {      
-        // Top face
-        v->push_back( osg::Vec3(   0, -cw, ch) * transformMat ); // bottom right
-        v->push_back( osg::Vec3(   0,  cw, ch) * transformMat ); // bottom left
-        v->push_back( osg::Vec3( -cd,  cw, ch) * transformMat ); // top left
-        v->push_back( osg::Vec3( -cd, -cw, ch) * transformMat ); // top right
+          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
+        v->push_back( osg::Vec3( -cd, cw,   0) ); // top left
+        v->push_back( osg::Vec3(   0, cw,   0) ); // top right
+
         for (int i=0; i<4; ++i)
         for (int i=0; i<4; ++i)
-          n->push_back( osg::Vec3( 0, 0, 1) * rotationMat ); // 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;
-        float base_y = (float) row * 16.0 * 3.0 / 1024.0;
-        float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
-        float left_x = 32.0 / 1024.0 * round((float) building.width / 6.0f);
-        float right_x = 0.0f;
-        float front_x = 384.0/1024.0;
-        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) ); 
-        }
+          n->push_back( osg::Vec3(0, 1, 0) ); // normal      
+        
         // MAIN BODY
         // MAIN BODY
-        // 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
+        // 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
         
         
-        // 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
+        for (int i=0; i<4; ++i)
+          n->push_back( osg::Vec3(1, 0, 0) ); // normal
         
         
-        // 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
+        // Left face
+        v->push_back( osg::Vec3( -cd, -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( -cd, -cw, ch) ); // top right
+
+        for (int i=0; i<4; ++i)
+          n->push_back( osg::Vec3(0, -1, 0) ); // normal
+
+        // Back face
+        v->push_back( osg::Vec3( -cd,  cw,  0) ); // bottom right
+        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 (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
-        t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
-        t->push_back( osg::Vec2( front_x, top_y ) ); // top right
+        // Right face
+        v->push_back( osg::Vec3(   0, cw,  0) ); // bottom right
+        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(   0, cw, ch) ); // top right
 
 
+        for (int i=0; i<4; ++i)
+          n->push_back( osg::Vec3(0, 1, 0) ); // normal
+        
         // ROOF
         // ROOF
-        if (building.pitched) { 
-          // Use the entire height of the roof texture
-          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;
+        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
+
+          // Back pitched roof
+          v->push_back( osg::Vec3(    -cd,  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.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 
+          // 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
+
+          // Back pitched roof
+          v->push_back( osg::Vec3(-cd,  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(-cd,  cw, ch) ); // top right
+          
+          for (int i=0; i<4; ++i)
+            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;
+          float base_y = (float) row * 16.0 * 3.0 / 1024.0;
+          float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
+          float left_x = 32.0 / 1024.0 * round((float) building.width / 6.0f);
+          float right_x = 0.0f;
+          float front_x = 384.0/1024.0;
+          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) ); 
+          }
+          // MAIN BODY
           // Front
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           // Front
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
@@ -335,66 +415,71 @@ void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& buildin
           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
           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
-        } else {
-          // Flat roof
-          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;    
+
+          // ROOF
+          if (building.pitched) { 
+            // Use the entire height of the roof texture
+            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
+            t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
+            t->push_back( osg::Vec2( front_x, top_y ) ); // top right
+          } else {
+            // Flat roof
+            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;    
+            
+            // Flat roofs still have 4 surfaces, so we need to set the textures
+            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
+            }
+          }
           
           
-          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) 
-      {
-        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) ); 
-        }      
-
-        // MAIN BODY
-        // 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
-        t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
-        t->push_back( osg::Vec2( right_x, top_y ) ); // top right
-
-        // ROOF
-        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;
-          
+        if (building.type == SGBuildingBin::MEDIUM) 
+        {
+          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) ); 
+          }      
+
+          // MAIN BODY
           // Front
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           // Front
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
@@ -406,7 +491,7 @@ void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& buildin
           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
           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
           // 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
@@ -418,62 +503,68 @@ void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& buildin
           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
           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
-        } else {
-          // Flat roof
-          base_y = 416/1024.0;
-          top_y = 576.0/1024.0;
-          left_x = column * 192.0 /1024.0;
-          right_x = (column + 1)* 192.0 /1024.0;
-          
-          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::LARGE)
-      {
-        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; 
-
-        // BASEMENT - uses the baseline texture
-        for (unsigned int i = 0; i < 16; i++) {          
-          t->push_back( osg::Vec2( left_x, base_y) ); 
-        }      
 
 
-        // MAIN BODY
-        // 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
-        t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
-        t->push_back( osg::Vec2( right_x, top_y ) ); // top right
+          // ROOF
+          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
+            t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
+            t->push_back( osg::Vec2( right_x, top_y ) ); // top right
+          } else {
+            // Flat roof
+            base_y = 416/1024.0;
+            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) {        
+              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
+            }
+          }
+        }
 
 
-        // ROOF
-        if (building.pitched) {      
-          base_y = 896/1024.0;
-          top_y = 1.0;
+        if (building.type == SGBuildingBin::LARGE)
+        {
+          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; 
+
+          // BASEMENT - uses the baseline texture
+          for (unsigned int i = 0; i < 16; i++) {          
+            t->push_back( osg::Vec2( left_x, base_y) ); 
+          }      
+
+          // MAIN BODY
           // Front
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
           // Front
           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
@@ -485,7 +576,7 @@ void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& buildin
           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
           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
           // 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
@@ -497,163 +588,259 @@ void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& buildin
           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
           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
-        } else {
-          // Flat roof
-          base_y = 896/1024.0;
-          top_y = 1.0;
-          
-          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
-        }
 
 
+          // ROOF
+          if (building.pitched) {      
+            base_y = 896/1024.0;
+            top_y = 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
+            t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
+            t->push_back( osg::Vec2( right_x, top_y ) ); // top right
+          } else {
+            // Flat roof
+            base_y = 896/1024.0;
+            top_y = 1.0;
+            
+            // Flat roofs still have 4 surfaces
+            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
+            }
+          }
+        }
       }
       }
-
-      // Set the vertex, texture and normals back.
-      geom->setVertexArray(v);
-      geom->setTexCoordArray(0, t);
-      geom->setNormalArray(n);
       
       
-      geom->setPrimitiveSet(0, new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,v->size()));
-      geode->setDrawable(0, geom);      
-}
-
-typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;
+      // Set the vertex, texture and normals.  Colors will be set per-instance
+      // later.
+      sharedGeometry->setVertexArray(v);
+      sharedGeometry->setTexCoordArray(0, t);
+      sharedGeometry->setNormalArray(n);
+    }
+  }
+  
+  void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) { 
+    
+    if (type == SGBuildingBin::SMALL) {
+      smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry)); 
+    }
+    
+    if (type == SGBuildingBin::MEDIUM) {
+      mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry));       
+    }
 
 
-static EffectMap buildingEffectMap;
+    if (type == SGBuildingBin::LARGE) {
+      largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry));       
+    }    
+  }
 
 
-// Helper classes for creating the quad tree
-namespace
-{
-struct MakeBuildingLeaf
-{
-    MakeBuildingLeaf(float range, Effect* effect) :
-        _range(range), _effect(effect) {}
+  int SGBuildingBin::getNumBuildings() {
+    return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size();    
+  }
+  
+  bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) {    
+    BuildingInstanceList::iterator iter;
     
     
-    MakeBuildingLeaf(const MakeBuildingLeaf& rhs) :
-        _range(rhs._range), _effect(rhs._effect)
-    {}
-
-    LOD* operator() () const
-    {
-        LOD* result = new LOD;
-        
-        // Create a series of LOD nodes so trees cover decreases slightly
-        // gradually with distance from _range to 2*_range
-        for (float i = 0.0; i < SG_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)));               
-        }
-        return result;
+    float r = (radius + smallBuildingMaxRadius) * (radius + smallBuildingMaxRadius);
+    for (iter = smallBuildingLocations.begin(); iter != smallBuildingLocations.end(); ++iter) {
+      if (iter->getDistSqr(p) < r) {
+        return false;        
+      }      
     }
     
     }
     
-    float _range;
-    ref_ptr<Effect> _effect;
-};
-
-struct AddBuildingLeafObject
-{
-    void operator() (LOD* lod, const SGBuildingBin::Building& building) const
-    {
-        Geode* geode = static_cast<Geode*>(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren()));
-        addBuildingToLeafGeode(geode, building);
+    r = (radius + mediumBuildingMaxRadius) * (radius + mediumBuildingMaxRadius);
+    for (iter = mediumBuildingLocations.begin(); iter != mediumBuildingLocations.end(); ++iter) {
+      if (iter->getDistSqr(p) < r) {
+        return false;        
+      }      
     }
     }
-};
-
-struct GetBuildingCoord
-{
-    Vec3 operator() (const SGBuildingBin::Building& building) const
-    {
-        return toOsg(building.position);
+    
+    r = (radius + largeBuildingMaxRadius) * (radius + largeBuildingMaxRadius);
+    for (iter = largeBuildingLocations.begin(); iter != largeBuildingLocations.end(); ++iter) {
+      if (iter->getDistSqr(p) < r) {
+        return false;        
+      }      
     }
     }
-};
+    
+    return true;
+  }
+  
+  SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
+    
+    if (roll < smallBuildingFraction) {
+      return SGBuildingBin::SMALL;      
+    }
+    
+    if (roll < (smallBuildingFraction + mediumBuildingFraction)) {
+      return SGBuildingBin::MEDIUM;
+    }
+    
+    return SGBuildingBin::LARGE;    
+  }
 
 
-typedef QuadTreeBuilder<LOD*, SGBuildingBin::Building, MakeBuildingLeaf, AddBuildingLeafObject,
-                        GetBuildingCoord> BuildingGeometryQuadtree;
-}
+  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);
 
 
-struct BuildingTransformer
-{
-    BuildingTransformer(Matrix& mat_) : mat(mat_) {}
-    SGBuildingBin::Building operator()(const SGBuildingBin::Building& building) const
+    if ((iter == buildingEffectMap.end())||
+        (!iter->second.lock(effect)))
     {
     {
-        Vec3 pos = toOsg(building.position);
-        return SGBuildingBin::Building(toSG(pos * mat), building);
+      SGPropertyNode_ptr effectProp = new SGPropertyNode;
+      makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
+      SGPropertyNode* params = makeChild(effectProp, "parameters");
+      // Main texture - n=0
+      params->getChild("texture", 0, true)->getChild("image", 0, true)
+          ->setStringValue(texture);
+
+      // Light map - n=3
+      params->getChild("texture", 3, true)->getChild("image", 0, true)
+          ->setStringValue(lightMap);
+          
+      effect = makeEffect(effectProp, true, options);
+      if (iter == buildingEffectMap.end())
+          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, 
+                                    SGBuildingBin::largeBuildingLocations };
+    
+    for (int i = 0; i < 3; i++)
+    {
+      BuildingGeometryQuadtree
+          quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(),
+                   SG_BUILDING_QUAD_TREE_DEPTH,
+                   MakeBuildingLeaf(20000.0, effect));
+                   
+      // Transform building positions from the "geocentric" positions we
+      // get from the scenery polys into the local Z-up coordinate
+      // system.
+      std::vector<BuildingInstance> rotatedBuildings;
+      rotatedBuildings.reserve(locs[i].size());
+      std::transform(locs[i].begin(), locs[i].end(),
+                     std::back_inserter(rotatedBuildings),
+                     BuildingInstanceTransformer(transInv));
+      quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end());
+
+      for (size_t i = 0; i < quadbuilding.getRoot()->getNumChildren(); ++i)
+              group->addChild(quadbuilding.getRoot()->getChild(i));
     }
     }
-    Matrix mat;
-};
-
-
-
-// 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,
-                         const SGReaderWriterOptions* options)
-{
-    Matrix transInv = Matrix::inverse(transform);
-    static Matrix ident;
-    // Set up some shared structures.
-    MatrixTransform* mt = new MatrixTransform(transform);
-
-    SGBuildingBin* bin = NULL;
-      
-    BOOST_FOREACH(bin, buildings)
-    {      
-        numBuildings = numBuildings + bin->getNumBuildings();
-        SG_LOG(SG_TERRAIN, SG_DEBUG, "Total random buildings generated: " << numBuildings);
-      
-        ref_ptr<Effect> effect;
-        EffectMap::iterator iter = buildingEffectMap.find(bin->texture);
-
-        if ((iter == buildingEffectMap.end())||
-            (!iter->second.lock(effect)))
-        {
-            SGPropertyNode_ptr effectProp = new SGPropertyNode;
-            makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
-            SGPropertyNode* params = makeChild(effectProp, "parameters");
-            // Main texture - n=0
-            params->getChild("texture", 0, true)->getChild("image", 0, true)
-                ->setStringValue(bin->texture);
-
-            // Light map - n=3
-            params->getChild("texture", 3, true)->getChild("image", 0, true)
-                ->setStringValue(bin->lightMap);
-                
-            effect = makeEffect(effectProp, true, options);
-            if (iter == buildingEffectMap.end())
-                buildingEffectMap.insert(EffectMap::value_type(bin->texture, effect));
-            else
-                iter->second = effect; // update existing, but empty observer
-        }
       
       
-        // Now, create a quadbuilding for the buildings.            
-        BuildingGeometryQuadtree
-            quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(),
-                     SG_BUILDING_QUAD_TREE_DEPTH,
-                     MakeBuildingLeaf(20000.0f, effect)); // FIXME - tie to property
-                     
-        // Transform building positions from the "geocentric" positions we
-        // get from the scenery polys into the local Z-up coordinate
-        // system.
-        std::vector<SGBuildingBin::Building> rotatedBuildings;
-        rotatedBuildings.reserve(bin->buildings.size());
-        std::transform(bin->buildings.begin(), bin->buildings.end(),
-                       std::back_inserter(rotatedBuildings),
-                       BuildingTransformer(transInv));
-        quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end());
-        
-        ref_ptr<Group> group = quadbuilding.getRoot();        
-        mt->addChild(group);  
-        delete bin;  
-    }        
+    return group;
+  }
     
     
-    buildings.clear();
+  // 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)
+      {
+      }
+      void apply(LOD& lod)
+      {
+          for (int i  = lod.getNumChildren() - 1; i >= 0; --i) {
+              EffectGeode* geode = dynamic_cast<EffectGeode*>(lod.getChild(i));
+              if (!geode)
+                  continue;
+              bool geodeEmpty = true;
+              if (geode->getNumDrawables() > 1) {
+                SG_LOG(SG_TERRAIN, SG_DEBUG, "Building LOD Drawables: " << geode->getNumDrawables());
+              }
+
+              for (unsigned j = 0; j < geode->getNumDrawables(); ++j) {
+                  const Geometry* geom = dynamic_cast<Geometry*>(geode->getDrawable(j));
+                  if (!geom) {
+                      geodeEmpty = false;
+                      break;
+                  }
+                  for (unsigned k = 0; k < geom->getNumPrimitiveSets(); k++) {
+                      const PrimitiveSet* ps = geom->getPrimitiveSet(k);
+                      if (ps->getNumIndices() > 0) {
+                          geodeEmpty = false;
+                          break;
+                      }
+                  }
+              }
+              if (geodeEmpty)
+                  lod.removeChildren(i, 1);
+          }
+      }
+  };
     
     
-    return mt;
-}
-
+  // 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,
+                           const SGReaderWriterOptions* options)
+  {
+      Matrix transInv = Matrix::inverse(transform);
+      static Matrix ident;
+      // Set up some shared structures.
+      MatrixTransform* mt = new MatrixTransform(transform);
+
+      SGBuildingBin* bin = NULL;
+        
+      BOOST_FOREACH(bin, buildings)
+      {      
+          numBuildings = numBuildings + bin->getNumBuildings();
+          ref_ptr<Group> group = bin->createBuildingsGroup(transInv, options);
+          
+          for (size_t i = 0; i < group->getNumChildren(); ++i)
+            mt->addChild(group->getChild(i));
+            
+          delete bin;  
+      }        
+      
+      buildings.clear();
+      QuadTreeCleaner cleaner;
+      mt->accept(cleaner);
+      return mt;
+  }
 }
 }
index 1fc64cbd72d4d9781ee59a4e9e60ea241160c528..a6d01f342b84a42a42da18f5f56b78caa842bb10 100644 (file)
 #include <vector>
 #include <string>
 
 #include <vector>
 #include <string>
 
+#include <osg/Geode>
 #include <osg/Geometry>
 #include <osg/Geometry>
-#include <osg/Group>
+#include <osg/Math>
+#include <osg/MatrixTransform>
 #include <osg/Matrix>
 #include <osg/Matrix>
+#include <osg/ShadeModel>
+#include <osg/Material>
+#include <osg/CullFace>
+
 
 #include <simgear/scene/util/OsgMath.hxx>
 
 #include <simgear/scene/util/OsgMath.hxx>
+#include <simgear/scene/material/mat.hxx>
+
+#include <simgear/scene/util/QuadTreeBuilder.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
+#include <simgear/scene/util/StateAttributeFactory.hxx>
+#include <simgear/structure/OSGUtils.hxx>
+
+#define SG_BUILDING_QUAD_TREE_DEPTH 2
+#define SG_BUILDING_FADE_OUT_LEVELS 4
+
+using namespace osg;
 
 namespace simgear
 {
 class SGBuildingBin {
 public:
 
 
 namespace simgear
 {
 class SGBuildingBin {
 public:
 
+  // Number of buildings to auto-generate. Individual
+  // 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;
+
   enum BuildingType {
     SMALL = 0,
     MEDIUM,
     LARGE };      
   enum BuildingType {
     SMALL = 0,
     MEDIUM,
     LARGE };      
+    
+private:
 
   struct Building {
 
   struct Building {
-    Building(BuildingType t, const SGVec3f& p, float w, float d, float h, int f, float rot, bool pitch) :
+    Building(BuildingType t, float w, float d, float h, int f, bool pitch) :
       type(t), 
       type(t), 
-      position(p), 
       width(w), 
       depth(d), 
       height(h), 
       floors(f),
       width(w), 
       depth(d), 
       height(h), 
       floors(f),
-      rotation(rot), 
       pitched(pitch), 
       radius(std::max(d, 0.5f*w))
     { }
       pitched(pitch), 
       radius(std::max(d, 0.5f*w))
     { }
-    Building(const SGVec3f& p, Building b) :
-      type(b.type), 
-      position(p), 
-      width(b.width), 
-      depth(b.depth), 
-      height(b.height),
-      floors(b.floors),
-      rotation(b.rotation), 
-      pitched(b.pitched),
-      radius(std::max(b.depth, 0.5f*b.width))
-    { }  
     
     BuildingType type;
     
     BuildingType type;
-    SGVec3f position;
     float width;
     float depth;
     float height;
     int floors;
     float width;
     float depth;
     float height;
     int floors;
-    float rotation;
     bool pitched;
     float radius;
     
     bool pitched;
     float radius;
     
@@ -82,33 +94,214 @@ public:
     }
   };
   
     }
   };
   
+  // The set of buildings that are instantiated
   typedef std::vector<Building> BuildingList;
   typedef std::vector<Building> BuildingList;
-  BuildingList buildings;
+  BuildingList smallBuildings;
+  BuildingList mediumBuildings;
+  BuildingList largeBuildings;
   
   
+  std::string material_name;
   std::string texture;
   std::string lightMap;
   std::string texture;
   std::string lightMap;
+  
+  // Fraction of buildings of this type
+  float smallBuildingFraction;
+  float mediumBuildingFraction;
 
 
-  void insert(const Building& model)
-  { 
-    buildings.push_back(model);   
-  }
+  // The maximum radius of each building type
+  float smallBuildingMaxRadius;
+  float mediumBuildingMaxRadius;
+  float largeBuildingMaxRadius;
+  
+  // The maximum depth of each building type
+  float smallBuildingMaxDepth;
+  float mediumBuildingMaxDepth;
+  float largeBuildingMaxDepth;
   
   
-  void insert(BuildingType t, const SGVec3f& p, float w, float d, float h, int f, float rot, bool pitch)
-  { insert(Building(t, p, w, d, h, f, rot, pitch)); }
+  // 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),
+      rotation(r),
+      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;    
+    
+    SGVec3f getPosition() { return position; }
+    float getRotation() { return rotation; }
+    
+    float getDistSqr(SGVec3f p) {
+      return distSqr(p, position);      
+    }
 
 
-  unsigned getNumBuildings() const
-  { return buildings.size(); }
-  const Building& getBuilding(unsigned i) const
-  { return buildings[i]; }  
+    const osg::Vec4f getColorValue() {
+      return osg::Vec4f(toOsg(position), rotation);
+    }
+  };
+
+  // Information for an instance of a building - position and orientation
+  typedef std::vector<BuildingInstance> BuildingInstanceList; 
+  BuildingInstanceList smallBuildingLocations;
+  BuildingInstanceList mediumBuildingLocations;
+  BuildingInstanceList largeBuildingLocations;
+  
+public:   
+
+  SGBuildingBin(const SGMaterial *mat);
   
   ~SGBuildingBin() {
   
   ~SGBuildingBin() {
-    buildings.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; }
+  
+  BuildingType getBuildingType(float roll);
+  
+  float getBuildingMaxRadius(BuildingType);
+  float getBuildingMaxDepth(BuildingType);
+  
+  // Helper classes for creating the quad tree
+  struct MakeBuildingLeaf
+  {
+      MakeBuildingLeaf(float range, Effect* effect) :
+          _range(range), _effect(effect) {}
+      
+      MakeBuildingLeaf(const MakeBuildingLeaf& rhs) :
+          _range(rhs._range), _effect(rhs._effect)
+      {}
+
+      LOD* operator() () const
+      {
+          LOD* result = new LOD;
+          
+          // Create a series of LOD nodes so trees cover decreases slightly
+          // gradually with distance from _range to 2*_range
+          for (float i = 0.0; i < SG_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)));               
+          }
+          return result;
+      }
+      
+      float _range;
+      ref_ptr<Effect> _effect;
+  };
+  
+  struct AddBuildingLeafObject
+  {
+      Geometry* createNewBuildingGeometryInstance(const BuildingInstance& building) const
+      {
+        Geometry* geom = simgear::clone(building.sharedGeometry.get(), CopyOp::SHALLOW_COPY);
+        geom->setColorArray(new Vec4Array);
+        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();          
+          
+          // 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));      
+          }
+          
+          // 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
+            // by taking a shallow copy of the shared Geomety.
+            geom = createNewBuildingGeometryInstance(building);
+            geode->addDrawable(geom);
+            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);
+          posArray->insert(posArray->end(), VERTICES_PER_BUILDING, c);
+          size_t numVerts = posArray->size();
+          primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(0));
+          primSet->setCount(numVerts);
+      }
+  };
+
+  struct GetBuildingCoord
+  {
+      Vec3 operator() (const BuildingInstance& building) const
+      {
+          return toOsg(building.position);
+      }
+  };
+
+  typedef QuadTreeBuilder<LOD*, BuildingInstance, MakeBuildingLeaf, AddBuildingLeafObject,
+                          GetBuildingCoord> BuildingGeometryQuadtree;
+                          
+  struct BuildingInstanceTransformer
+  {
+      BuildingInstanceTransformer(Matrix& mat_) : mat(mat_) {}
+      BuildingInstance operator()(const BuildingInstance& buildingInstance) const
+      {
+          Vec3 pos = toOsg(buildingInstance.position) * mat;
+          return BuildingInstance(toSG(pos), buildingInstance);
+      }
+      Matrix mat;
+  };
+  
+  ref_ptr<Group> createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options);  
+
 };
 
 // List of buildings
 typedef std::list<SGBuildingBin*> SGBuildingBinList;
 
 };
 
 // List of buildings
 typedef std::list<SGBuildingBin*> SGBuildingBinList;
 
+
 osg::Group* createRandomBuildings(SGBuildingBinList buildinglist, const osg::Matrix& transform,
                          const SGReaderWriterOptions* options);
 }
 osg::Group* createRandomBuildings(SGBuildingBinList buildinglist, const osg::Matrix& transform,
                          const SGReaderWriterOptions* options);
 }
index 3699b556fd786ad0e0af3688b4f6146e57b2369f..418ba2c3c1bc53f92f986aeab21b9f909a8c4b69 100644 (file)
@@ -457,7 +457,11 @@ struct SGTileGeometryBin {
     }
   }
   
     }
   }
   
-  void computeRandomBuildings(SGMaterialLib* matlib, float building_density)
+  void computeRandomObjectsAndBuildings(
+    SGMaterialLib* matlib, 
+    float building_density, 
+    bool use_random_objects, 
+    bool use_random_buildings)
   {
     SGMaterialTriangleMap::iterator i;
         
   {
     SGMaterialTriangleMap::iterator i;
         
@@ -474,45 +478,34 @@ struct SGTileGeometryBin {
 
       osg::Texture2D* object_mask = mat->get_object_mask(triangleBin);
 
 
       osg::Texture2D* object_mask = mat->get_object_mask(triangleBin);
 
-      float coverage = mat->get_building_coverage();
+      int group_count = mat->get_object_group_count();
+      float building_coverage = mat->get_building_coverage();      
       
       
-      // Minimum spacing needs to include the maximum footprint of a building.
-      // As the 0,0,0 point is the center of the front of the building, we need
-      // to consider the full depth, but only half the possible width.          
-      float min_spacing = mat->get_building_spacing();
-
-      if (coverage <= 0)
-        continue;        
-        
       bool found = false;
       SGBuildingBin* bin = NULL;
       
       bool found = false;
       SGBuildingBin* bin = NULL;
       
-      BOOST_FOREACH(bin, randomBuildings)
-      {
-        if (bin->texture == mat->get_building_texture()) {
-            found = true;
-            break;
+      if (building_coverage > 0) {      
+        BOOST_FOREACH(bin, randomBuildings)
+        {
+          if (bin->getMaterialName() == mat->get_names()[0]) {
+              found = true;
+              break;
+          }
         }
         }
-      }
-      
-      if (!found) {
-        bin = new SGBuildingBin();
-        bin->texture = mat->get_building_texture();
-        bin->lightMap = mat->get_building_lightmap();
-        SG_LOG(SG_INPUT, SG_DEBUG, "Building texture " << bin->texture);
-        randomBuildings.push_back(bin);
-      }       
-      
-      std::vector<std::pair<SGVec3f, float> > randomPoints;
+        
+        if (!found) {
+          bin = new SGBuildingBin(mat);
+          randomBuildings.push_back(bin);
+        }       
+      }            
       
       unsigned num = i->second.getNumTriangles();
       
       unsigned num = i->second.getNumTriangles();
-      int triangle_dropped = 0;
-      int building_dropped = 0;
       int random_dropped = 0;
       int mask_dropped = 0;
       int random_dropped = 0;
       int mask_dropped = 0;
+      int building_dropped = 0;
+      int triangle_dropped = 0;
 
       for (unsigned i = 0; i < num; ++i) {
 
       for (unsigned i = 0; i < num; ++i) {
-        SGBuildingBin::BuildingList triangle_buildings;
         SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i);
         
         SGVec3f vorigin = triangleBin.getVertex(triangleRef[0]).vertex;
         SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i);
         
         SGVec3f vorigin = triangleBin.getVertex(triangleRef[0]).vertex;
@@ -523,227 +516,256 @@ struct SGTileGeometryBin {
         SGVec2f t1 = triangleBin.getVertex(triangleRef[2]).texCoord - torigin;
         SGVec3f normal = cross(v0, v1);
         
         SGVec2f t1 = triangleBin.getVertex(triangleRef[2]).texCoord - torigin;
         SGVec3f normal = cross(v0, v1);
         
+        // 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;
 
         // Compute the area
         float area = 0.5f*length(normal);
         if (area <= SGLimitsf::min())
           continue;
 
-        // for partial units of area, use a zombie door method to
-        // create the proper random chance of an object being created
-        // for this triangle.
-        double num = area / coverage + mt_rand(&seed);
-        if (num < 1.0f) {
-          continue;          
-        }
-                
-        // Apply density, which is linear, while we're dealing in areas
-        num = num * building_density * building_density;
-        
-        // Cosine of the angle between the two vectors.
-        float cosine = (dot(v0, v1) / (length(v0) * length(v1)));
-
-        // Determine a grid spacing in each vector such that the correct
-        // coverage will result.
-        float stepv0 = (sqrtf(coverage) / building_density) / length(v0) / sqrtf(1 - cosine * cosine);
-        float stepv1 = (sqrtf(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 b = mt_rand(&seed) * stepv1;
+        // 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);
+              
+              // Use the zombie door method to determine fractional object placement.
+              double n = area / object->get_coverage_m2() + mt_rand(&seed);
+
+              // place an object each unit of area
+              while ( n > 1.0 ) {
+                float a = mt_rand(&seed);
+                float b = mt_rand(&seed);
+                if ( a + b > 1 ) {
+                  a = 1 - a;
+                  b = 1 - b;
+                }
 
 
-        // Place an object each unit of area
-        while (num > 1.0) {
+                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.                          
+                float spacing = object->get_spacing_m();
+                
+                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) { 
+                  n -= 1.0;
+                  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();            
+                  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()) { 
+                    // Failed object mask check
+                    n -= 1.0;
+                    continue;                    
+                  }
+                  
+                  rotation = img->getColor(x,y).r();
+                }
+                
+                bool close = false;
 
 
-          // 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 
-            // scanline.  Note that we need to still ensure that a+b < 1.
-            b = mt_rand(&seed) * stepv1 * (1.0f - a);
+                // 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()) * 
+                                    (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), 
+                                        rotation);
+                }
+                
+                n -= 1.0;
+              }
+            }
           }
           }
-          
-          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);
+        }        
+        
+        // Random objects now generated.  Now generate the random buildings (if any);
+        if (use_random_buildings && (building_coverage > 0)) {
+          // For partial units of area, use a zombie door method to
+          // create the proper random chance of an object being created
+          // for this triangle.
+          double num = area / building_coverage + mt_rand(&seed);
+          if (num < 1.0f) {
+            continue;          
           }
           }
+                  
+          // Apply density, which is linear, while we're dealing in areas
+          num = num * building_density * building_density;
           
           
-          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();            
-            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()) {  
-              // 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;
-            }  
-          }
+          // Cosine of the angle between the two vectors.
+          float cosine = (dot(v0, v1) / (length(v0) * length(v1)));
+
+          // Determine a grid spacing in each vector such that the correct
+          // 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);
           
           
-          // Now create the building, so we have an idea of its footprint
-          // and therefore appropriate spacing.
-          SGBuildingBin::BuildingType buildingtype;
-          float width;
-          float depth;
-          int floors;
-          float height;
-          bool pitched;
-                                  
-          // Determine the building type, and hence dimensions.
-          float type = mt_rand(&seed);
+          stepv0 = std::min(stepv0, 1.0f);
+          stepv1 = std::min(stepv1, 1.0f);
           
           
-          if (type < mat->get_building_small_fraction()) {
-            // Small building
-            buildingtype = SGBuildingBin::SMALL;
-            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));
+          // Start at a random point. a will be immediately incremented below.
+          float a = -mt_rand(&seed) * stepv0;  
+          float b = mt_rand(&seed) * stepv1;
+
+          // Place an object each unit of area
+          while (num > 1.0) {
+
+            // Set the next location to place a building          
+            a += stepv0;
             
             
-            // Small buildings are never deeper than they are wide.
-            if (depth > width) { depth = width; }
+            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;
+            }
             
             
-            pitched = (mt_rand(&seed) < mat->get_building_small_pitch());
-          } else if (type < (mat->get_building_small_fraction() + mat->get_building_medium_fraction())) {
-            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));
+            if (b > 1.0f) {
+              // 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);
+            }
             
             
-            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));                            
+            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);
             }
             
             }
             
-            pitched = (mt_rand(&seed) < mat->get_building_medium_pitch());         
-          } else {
-            buildingtype = SGBuildingBin::LARGE;
-            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())); 
-            height = floors * (2.8 + mt_rand(&seed));
-            pitched = (mt_rand(&seed) < mat->get_building_large_pitch());                   
-          }
-          
-          // Determine an appropriate minimum spacing for the object.  Note that the
-          // origin of the building model is the center of the front face, hence we
-          // consider the full depth.  We choose _not_ to use the diagonal distance
-          // to one of the rear corners, as we assume that terrain masking will
-          // make the buildings place in some sort of grid.
-          float radius = std::max(depth, 0.5f*width);
-
-          // 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.        
-          SGVec3f p = randomPoint - vorigin;
-          
-          if (((length(cross(p     , p - v0)) / length(v0))      < radius) ||
-              ((length(cross(p - v0, p - v1)) / length(v1 - v0)) < radius) ||
-              ((length(cross(p - v1, p     )) / length(v1))      < radius)   )
-          {
-            triangle_dropped++;
-            num -= 1.0;
-            continue;
-          }
+            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();            
+              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()) {  
+                // 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 against the generic random objects.  TODO - make this more efficient by
-          // masking ahead of time objects outside of the triangle.
-          bool too_close = false;
-          for (unsigned int i = 0; i < randomModels.getNumModels(); ++i) {
-            float min_dist = randomModels.getMatModel(i).model->get_spacing_m() + radius + min_spacing;
-            min_dist = min_dist * min_dist;
+            // 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 
+            // 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;          
+            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 (distSqr(randomModels.getMatModel(i).position, randomPoint) < min_dist) {
-              too_close = true;
-              random_dropped++;
-              continue;
-            }          
-          }
-          
-          if (too_close) {
-            // Too close to a random model - drop and try again
-            num -= 1.0;
-            continue;            
-          }
-          
-          SGBuildingBin::BuildingList::iterator l;       
-          
-          // Check that the building is sufficiently far from any other building within the triangle.
-          for (l = triangle_buildings.begin(); l != triangle_buildings.end(); ++l) {
+            if (edge_dist < radius) { 
+              num -= 1.0;
+              triangle_dropped++;
+              continue; 
+            }
+                      
+            // Check building isn't too close to random objects and other buildings.          
+            bool close = false;
+            std::vector<std::pair<SGVec3f, float> >::iterator iter;
             
             
-            float min_dist = l->radius + radius + min_spacing;
-            min_dist = min_dist * min_dist;
+            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 (distSqr(randomPoint, l->position) < min_dist) {
+            if (close) {
+              num -= 1.0;
               building_dropped++;
               building_dropped++;
-              too_close = true;
-              continue;
+              continue;            
             }
             }
-          }
-          
-          if (too_close) {
-            // Too close to another building - drop and try again
+            
+            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;            
+            }          
+
+            std::pair<SGVec3f, float> pt = std::make_pair(buildingCenter, radius);              
+            triangleBuildingList.push_back(pt);
+            bin->insert(randomPoint, rotation, buildingtype);
             num -= 1.0;
             num -= 1.0;
-            continue;            
           }
           }
-          
-          // If we've passed all of the above tests we have a valid
-          // building, so create it!          
-          SGBuildingBin::Building building = 
-                    SGBuildingBin::Building(buildingtype, 
-                                            randomPoint, 
-                                            width, 
-                                            depth, 
-                                            height, 
-                                            floors,
-                                            rotation,
-                                            pitched);                                                            
-          triangle_buildings.push_back(building);
-
-          num -= 1.0;
-        }        
-        
-        // Add the buildings from this triangle to the overall list.
-        SGBuildingBin::BuildingList::iterator l;  
-
-        for (l = triangle_buildings.begin(); l != triangle_buildings.end(); ++l) {
-          bin->insert(*l);
         }
         
         }
         
-        triangle_buildings.clear();
+        triangleObjectsList.clear();
+        triangleBuildingList.clear();        
       }
       
       SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << bin->getNumBuildings());
       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to mask: " << mask_dropped);
       }
       
       SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << bin->getNumBuildings());
       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to mask: " << mask_dropped);
-      SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to triangle edge: " << triangle_dropped);
       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to random object: " << random_dropped);
       SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to random object: " << random_dropped);
-      SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to other building: " << building_dropped);
+      SG_LOG(SG_TERRAIN, SG_DEBUG, "  Dropped due to other buildings: " << building_dropped);
     }
   }
   
     }
   }
   
@@ -805,69 +827,6 @@ struct SGTileGeometryBin {
     }
   }
 
     }
   }
 
-  void computeRandomObjects(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)
-        continue;
-
-      int group_count = mat->get_object_group_count();
-
-      if (group_count > 0)
-      {
-        for (int j = 0; j < group_count; j++)
-        {
-          SGMatModelGroup *object_group =  mat->get_object_group(j);
-          int nObjects = object_group->get_object_count();
-
-          if (nObjects > 0)
-          {
-            // For each of the random models in the group, determine an appropriate
-            // number of random placements and insert them.
-            for (int k = 0; k < nObjects; k++) {
-              SGMatModel * object = object_group->get_object(k);
-
-              std::vector<std::pair<SGVec3f, float> > randomPoints;
-
-              i->second.addRandomPoints(object->get_coverage_m2(), 
-                                        object->get_spacing_m(),
-                                        mat->get_object_mask(i->second), 
-                                        randomPoints);
-                                        
-              std::vector<std::pair<SGVec3f, float> >::iterator l;
-              for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
-                // Only add the model if it is sufficiently far from the
-                // other models
-                bool close = false;                
-                
-                for (unsigned i = 0; i < randomModels.getNumModels(); i++) {
-                  float spacing = randomModels.getMatModel(i).model->get_spacing_m() + object->get_spacing_m();
-                  spacing = spacing * spacing;
-                  
-                  if (distSqr(randomModels.getMatModel(i).position, l->first) < spacing) {
-                    close = true;                
-                    continue;
-                  }              
-                }            
-                
-                if (!close) { 
-                  randomModels.insert(l->first, object, (int)object->get_randomized_range_m(&seed), l->second);
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
   {
     if (!insertPtGeometry(obj, matlib))
   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
   {
     if (!insertPtGeometry(obj, matlib))
@@ -969,54 +928,63 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
   osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
   if (node)
     terrainGroup->addChild(node);
   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);    
+  }
 
 
-  if (use_random_objects && matlib) {
-    tileGeometryBin.computeRandomObjects(matlib);
-
-    if (tileGeometryBin.randomModels.getNumModels() > 0) {
-      // Generate a repeatable random seed
-      mt seed;
-      mt_init(&seed, unsigned(123));
+  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);          
+    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)));
-        }
+      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->addChild(node);
-        models.push_back(ModelLOD(position, obj.lod));
+      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)));
       }
       }
-      RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
-      quadtree.buildQuadTree(models.begin(), models.end());
-      randomObjects = quadtree.getRoot();
-      randomObjects->setName("random objects");
+      
+      osg::MatrixTransform* position =
+        new osg::MatrixTransform(transformMat);
+      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.size() > 0) {
+    buildingNode = createRandomBuildings(tileGeometryBin.randomBuildings, osg::Matrix::identity(),
+                                options);                                
+    buildingNode->setName("Random buildings");
   }
 
   if (use_random_vegetation && matlib) {
   }
 
   if (use_random_vegetation && matlib) {
@@ -1028,15 +996,6 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
                                 options);
       forestNode->setName("Random trees");
     }
                                 options);
       forestNode->setName("Random trees");
     }
-  } 
-
-  if (use_random_buildings && matlib) {
-    tileGeometryBin.computeRandomBuildings(matlib, building_density);
-    if (tileGeometryBin.randomBuildings.size() > 0) {
-      buildingNode = createRandomBuildings(tileGeometryBin.randomBuildings, osg::Matrix::identity(),
-                                  options);                                
-      buildingNode->setName("Random buildings");
-    }
   }  
 
   // FIXME: ugly, has a side effect
   }  
 
   // FIXME: ugly, has a side effect