]> git.mxchange.org Git - simgear.git/commitdiff
Random buildings - initial commit.
authorStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Tue, 24 Apr 2012 21:00:35 +0000 (22:00 +0100)
committerStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Tue, 24 Apr 2012 21:00:35 +0000 (22:00 +0100)
simgear/scene/material/mat.cxx
simgear/scene/material/mat.hxx
simgear/scene/tgdb/CMakeLists.txt
simgear/scene/tgdb/SGBuildingBin.cxx [new file with mode: 0644]
simgear/scene/tgdb/SGBuildingBin.hxx [new file with mode: 0644]
simgear/scene/tgdb/obj.cxx

index ec5164bd5c17711e770c12c9d30302875b2e1f5c..2082fd47e83d0485acd51ccad8d439fb51325e51 100644 (file)
@@ -264,6 +264,49 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
     wrapv = props->getBoolValue("wrapv", true);
     mipmap = props->getBoolValue("mipmap", true);
     light_coverage = props->getDoubleValue("light-coverage", 0.0);
+    
+    // Building properties
+    building_coverage = props->getDoubleValue("building-coverage", 0.0);
+    building_spacing = props->getDoubleValue("building-spacing-m", 5.0);
+    
+    string bt = props->getStringValue("building-texture", "Textures/buildings.png");
+    building_texture = SGModelLib::findDataFile(bt, options);    
+    
+    if (building_texture.empty()) {
+        SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt);
+    }    
+        
+    building_small_ratio = props->getDoubleValue("building-small-ratio", 0.8);
+    building_medium_ratio = props->getDoubleValue("building-medium-ratio", 0.15);
+    building_large_ratio =  props->getDoubleValue("building-large-ratio", 0.05);
+    
+    building_small_pitch =  props->getDoubleValue("building-small-pitch", 0.8);
+    building_medium_pitch =  props->getDoubleValue("building-medium-pitch", 0.2);
+    building_large_pitch =  props->getDoubleValue("building-large-pitch", 0.1);
+
+    building_small_min_floors = props->getIntValue("building-small-min-floors", 1);
+    building_small_max_floors = props->getIntValue("building-small-max-floors", 3);
+    building_medium_min_floors = props->getIntValue("building-medium-min-floors", 3);
+    building_medium_max_floors = props->getIntValue("building-medium-max-floors", 8);
+    building_large_min_floors = props->getIntValue("building-large-min-floors", 5);
+    building_large_max_floors = props->getIntValue("building-large-max-floors", 20);
+    
+    building_small_min_width = props->getFloatValue("building-small-min-width-m", 15.0);
+    building_small_max_width = props->getFloatValue("building-small-max-width-m", 60.0);
+    building_small_min_depth = props->getFloatValue("building-small-min-depth-m", 10.0);
+    building_small_max_depth = props->getFloatValue("building-small-max-depth-m", 20.0);
+    
+    building_medium_min_width = props->getFloatValue("building-medium-min-width-m", 25.0);
+    building_medium_max_width = props->getFloatValue("building-medium-max-width-m", 50.0);
+    building_medium_min_depth = props->getFloatValue("building-medium-min-depth-m", 20.0);
+    building_medium_max_depth = props->getFloatValue("building-medium-max-depth-m", 50.0);
+    
+    building_large_min_width = props->getFloatValue("building-large-min-width-m", 50.0);
+    building_large_max_width = props->getFloatValue("building-large-max-width-m", 75.0);
+    building_large_min_depth = props->getFloatValue("building-large-min-depth-m", 50.0);
+    building_large_max_depth = props->getFloatValue("building-large-max-depth-m", 75.0);
+        
+    // Random vegetation properties
     wood_coverage = props->getDoubleValue("wood-coverage", 0.0);
     tree_height = props->getDoubleValue("tree-height-m", 0.0);
     tree_width = props->getDoubleValue("tree-width-m", 0.0);
@@ -358,6 +401,7 @@ SGMaterial::init ()
 
     mipmap = true;
     light_coverage = 0.0;
+    building_coverage = 0.0;
 
     solid = true;
     friction_factor = 1;
index 292df681c03b0a27484b97cc42eea7f5bd72ccf6..f607f7fb32a4805428e0ab27ca9fdc07c6dc5b69 100644 (file)
@@ -145,6 +145,67 @@ public:
    * @return The area (m^2) covered by each light.
    */
   inline double get_light_coverage () const { return light_coverage; }
+  
+  /**
+   * Get the building coverage.
+   *
+   * A smaller number means more generated buildings.
+   *
+   * @return The area (m^2) covered by each light.
+   */
+  inline double get_building_coverage () const { return building_coverage; }
+
+  /**
+   * Get the building spacing.
+   *
+   * This is the minimum spacing between buildings
+   *
+   * @return The minimum distance between buildings
+   */
+  inline double get_building_spacing () const { return building_spacing; }
+
+  /**
+   * Get the building texture.
+   *
+   * This is the texture used for auto-generated buildings.
+   *
+   * @return The texture for auto-generated buildings.
+   */
+  inline std::string get_building_texture () const { return building_texture; }
+  
+  // Ratio of the 3 random building sizes
+  inline double get_building_small_fraction () const { return building_small_ratio / (building_small_ratio + building_medium_ratio + building_large_ratio); }
+  inline double get_building_medium_fraction () const { return building_medium_ratio / (building_small_ratio + building_medium_ratio + building_large_ratio); }
+  inline double get_building_large_fraction () const { return building_large_ratio / (building_small_ratio + building_medium_ratio + building_large_ratio); }
+  
+  // Proportion of buildings with pitched roofs
+  inline double get_building_small_pitch () const { return building_small_pitch; }
+  inline double get_building_medium_pitch () const { return building_medium_pitch; }
+  inline double get_building_large_pitch () const { return building_large_pitch; }
+
+  // Min/Max number of floors for each size
+  inline int get_building_small_min_floors () const { return  building_small_min_floors; }
+  inline int get_building_small_max_floors () const { return  building_small_max_floors; }
+  inline int get_building_medium_min_floors () const { return building_medium_min_floors; }
+  inline int get_building_medium_max_floors () const { return building_medium_max_floors; }
+  inline int get_building_large_min_floors () const { return building_large_min_floors; }
+  inline int get_building_large_max_floors () const { return building_large_max_floors; }
+  
+  // Minimum width and depth for each size
+  inline double get_building_small_min_width () const { return building_small_min_width; }
+  inline double get_building_small_max_width () const { return building_small_max_width; }
+  inline double get_building_small_min_depth () const { return building_small_min_depth; }
+  inline double get_building_small_max_depth () const { return building_small_max_depth; }
+  
+  inline double get_building_medium_min_width () const { return building_medium_min_width; }
+  inline double get_building_medium_max_width () const { return building_medium_max_width; }
+  inline double get_building_medium_min_depth () const { return building_medium_min_depth; }
+  inline double get_building_medium_max_depth () const { return building_medium_max_depth; }
+  
+  inline double get_building_large_min_width () const { return building_large_min_width; }
+  inline double get_building_large_max_width () const { return building_large_max_width; }
+  inline double get_building_large_min_depth () const { return building_large_min_depth; }
+  inline double get_building_large_max_depth () const { return building_large_max_depth; }
 
   /**
    * Get the wood coverage.
@@ -317,6 +378,49 @@ private:
   // coverage of night lighting.
   double light_coverage;
   
+  // coverage of buildings
+  double building_coverage;
+  
+  // building spacing
+  double building_spacing;
+  
+  // building texture
+  std::string building_texture;
+
+  // Ratio of the 3 random building sizes
+  double building_small_ratio;
+  double building_medium_ratio;
+  double building_large_ratio;
+  
+  // Proportion of buildings with pitched roofs
+  double building_small_pitch;
+  double building_medium_pitch;
+  double building_large_pitch;
+
+  // Min/Max number of floors for each size
+  int building_small_min_floors; 
+  int building_small_max_floors;
+  int building_medium_min_floors;
+  int building_medium_max_floors;
+  int building_large_min_floors;
+  int building_large_max_floors;
+  
+  // Minimum width and depth for each size
+  double building_small_min_width;
+  double building_small_max_width;
+  double building_small_min_depth;
+  double building_small_max_depth;
+  
+  double building_medium_min_width;
+  double building_medium_max_width;
+  double building_medium_min_depth;
+  double building_medium_max_depth;
+  
+  double building_large_min_width;
+  double building_large_max_width;
+  double building_large_min_depth;
+  double building_large_max_depth;
+  
   // coverage of woods
   double wood_coverage;
 
index 63cce41d00f3ae9ccf2c95ddbf68cbb4ac90ac50..f340727d5217a57cc7c64ee78eb863fb2ac3a406 100644 (file)
@@ -25,6 +25,7 @@ set(SOURCES
     GroundLightManager.cxx
     ReaderWriterSPT.cxx
     ReaderWriterSTG.cxx
+    SGBuildingBin.cxx
     SGOceanTile.cxx
     SGReaderWriterBTG.cxx
     SGVasiDrawable.cxx
diff --git a/simgear/scene/tgdb/SGBuildingBin.cxx b/simgear/scene/tgdb/SGBuildingBin.cxx
new file mode 100644 (file)
index 0000000..51694ba
--- /dev/null
@@ -0,0 +1,636 @@
+/* -*-c++-*-
+ *
+ * Copyright (C) 2012 Stuart Buchanan
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <map>
+
+#include <boost/foreach.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Math>
+#include <osg/MatrixTransform>
+#include <osg/Matrix>
+#include <osg/ShadeModel>
+#include <osg/Material>
+#include <osg/CullFace>
+
+#include <osgDB/ReadFile>
+#include <osgDB/FileUtils>
+
+#include <simgear/debug/logstream.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"
+
+#define SG_BUILDING_QUAD_TREE_DEPTH 3
+#define SG_BUILDING_FADE_OUT_LEVELS 10
+
+using namespace osg;
+
+namespace simgear
+{
+  
+typedef std::map<std::string, osg::observer_ptr<osg::StateSet> > BuildingStateSetMap;
+static BuildingStateSetMap statesetmap;
+
+void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& building)
+{
+      // 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::Vec3Array* v = new osg::Vec3Array;
+      osg::Vec2Array* t = new osg::Vec2Array;
+      
+      // Color and Normal will be per QUAD
+      osg::Vec4Array* c = new osg::Vec4Array;
+      osg::Vec3Array* n = new osg::Vec3Array;            
+      
+      if (geode->getNumDrawables() == 0) {
+        geom = new osg::Geometry;        
+        v = new osg::Vec3Array;
+        t = new osg::Vec2Array;
+        c = new osg::Vec4Array;
+        n = new osg::Vec3Array;
+        
+        // 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_PRIMITIVE);
+        // 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;    
+      
+      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;
+      
+      // 0,0,0 is the bottom center of the front
+      // face, e.g. where the front door would be
+      
+      
+      // 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
+      
+      n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
+      
+      // Left 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
+
+      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
+      
+      n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
+      
+      // Right 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
+
+      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
+      
+      n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
+      
+      // Left 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
+
+      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
+      
+      n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
+      
+      // Right 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
+
+      n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal
+      
+      // ROOF
+      if (building.pitched) {      
+        
+        // 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
+        n->push_back( osg::Vec3(-0.707, 0, 0.707) * rotationMat ); // normal
+        
+        // Left 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
+        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(0.707, 0, 0.707) * rotationMat ); // normal      
+
+        // Right 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
+        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( 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 = 0.0f;
+        float right_x = 32.0 / 1024.0 * round((float) building.width / 6.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
+        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
+
+        // 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;
+          right_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f);
+          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 = 512.0/1024.0;
+          right_x = 640.0/1024.0;
+          // Use the entire height of the roof texture
+          top_y = base_y + 16.0 * 3.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::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;
+        float right_x = left_x + 32.0 / 1024.0 * round((float) building.width / 10.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
+        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;
+          right_x = left_x + 32.0 / 1024.0 * 6.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;
+        float right_x = left_x + 32.0 / 1024.0 * round((float) building.width / 20.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
+        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 = 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;
+          
+          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);      
+}
+
+// Helper classes for creating the quad tree
+namespace
+{
+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++)
+        {        
+            //osg::ref_ptr<EffectGeode> geode = new EffectGeode();
+            //geode->setEffect(_effect);
+            osg::ref_ptr<osg::Geode>  geode = new osg::Geode();          
+            result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0)));
+        }
+        return result;
+    }
+    
+    float _range;
+    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);
+    }
+};
+
+struct GetBuildingCoord
+{
+    Vec3 operator() (const SGBuildingBin::Building& building) const
+    {
+        return toOsg(building.position);
+    }
+};
+
+typedef QuadTreeBuilder<LOD*, SGBuildingBin::Building, MakeBuildingLeaf, AddBuildingLeafObject,
+                        GetBuildingCoord> BuildingGeometryQuadtree;
+}
+
+struct BuildingTransformer
+{
+    BuildingTransformer(Matrix& mat_) : mat(mat_) {}
+    SGBuildingBin::Building operator()(const SGBuildingBin::Building& building) const
+    {
+        Vec3 pos = toOsg(building.position);
+        return SGBuildingBin::Building(toSG(pos * mat), building);
+    }
+    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);
+    Effect* effect = makeEffect("Effects/model-default", true); 
+
+    SGBuildingBin* bin = NULL;
+      
+    BOOST_FOREACH(bin, buildings)
+    {      
+        // 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();
+        
+        // Set up the stateset for this building bin and the texture to use.
+        osg::StateSet* stateSet = group->getOrCreateStateSet();
+        const std::string texturename = bin->texture;
+        osg::Texture2D* texture = SGLoadTexture2D(texturename);
+        texture->setWrap(osg::Texture2D::WRAP_S,  osg::Texture2D::CLAMP_TO_EDGE);
+        texture->setWrap(osg::Texture2D::WRAP_T,  osg::Texture2D::CLAMP_TO_EDGE);
+        stateSet->setTextureAttributeAndModes(0, texture);
+                
+        osg::ShadeModel* shadeModel = new osg::ShadeModel;
+        shadeModel->setMode(osg::ShadeModel::FLAT);
+        stateSet->setAttributeAndModes(shadeModel);
+        stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
+        stateSet->setMode(GL_FOG, osg::StateAttribute::ON);
+        stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
+        stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
+        stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF);
+        stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
+        stateSet->setAttribute(new osg::CullFace(osg::CullFace::BACK));
+
+        osg::Material* material = new osg::Material;
+        material->setAmbient(osg::Material::FRONT, osg::Vec4(0.3,0.3,0.3,1.0));
+        material->setDiffuse(osg::Material::FRONT, osg::Vec4(1.0,1.0,1.0,1.0));
+        material->setSpecular(osg::Material::FRONT, osg::Vec4(0,0,0,1.0));
+        material->setShininess(osg::Material::FRONT, 0.0);
+        material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
+        stateSet->setAttribute(material);
+        mt->addChild(group);        
+    }
+    
+    return mt;
+}
+
+}
diff --git a/simgear/scene/tgdb/SGBuildingBin.hxx b/simgear/scene/tgdb/SGBuildingBin.hxx
new file mode 100644 (file)
index 0000000..9c6d261
--- /dev/null
@@ -0,0 +1,110 @@
+/* -*-c++-*-
+ *
+ * Copyright (C) 2011 Stuart Buchanan
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SG_BUILDING_BIN_HXX
+#define SG_BUILDING_BIN_HXX
+
+#include <math.h>
+
+#include <vector>
+#include <string>
+
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/Matrix>
+
+#include <simgear/scene/util/OsgMath.hxx>
+
+namespace simgear
+{
+class SGBuildingBin {
+public:
+
+  enum BuildingType {
+    SMALL = 0,
+    MEDIUM,
+    LARGE };      
+
+  struct Building {
+    Building(BuildingType t, const SGVec3f& p, float w, float d, float h, int f, float rot, bool pitch) :
+      type(t), 
+      position(p), 
+      width(w), 
+      depth(d), 
+      height(h), 
+      floors(f),
+      rotation(rot), 
+      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;
+    SGVec3f position;
+    float width;
+    float depth;
+    float height;
+    int floors;
+    float rotation;
+    bool pitched;
+    float radius;
+    
+    float getFootprint() {
+      return radius;
+    }
+  };
+  
+  typedef std::vector<Building> BuildingList;
+  BuildingList buildings;
+  
+  std::string texture;
+
+  void insert(const Building& model)
+  { 
+    buildings.push_back(model);   
+  }
+  
+  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)); }
+
+  unsigned getNumBuildings() const
+  { return buildings.size(); }
+  const Building& getBuilding(unsigned i) const
+  { return buildings[i]; }  
+};
+
+// List of buildings
+typedef std::list<SGBuildingBin*> SGBuildingBinList;
+
+osg::Group* createRandomBuildings(SGBuildingBinList buildinglist, const osg::Matrix& transform,
+                         const SGReaderWriterOptions* options);
+}
+#endif
index c899d8ae9378f430d744adca8f19e3ef0c30cb97..cb29aa74a4849c14a3967eab2ce9d1adedbb2886 100644 (file)
@@ -60,6 +60,7 @@
 #include "SGTexturedTriangleBin.hxx"
 #include "SGLightBin.hxx"
 #include "SGModelBin.hxx"
+#include "SGBuildingBin.hxx"
 #include "TreeBin.hxx"
 #include "SGDirectionalLightBin.hxx"
 #include "GroundLightManager.hxx"
@@ -85,6 +86,7 @@ struct SGTileGeometryBin {
   SGLightListBin odalLights;
   SGDirectionalLightListBin reilLights;
   SGMatModelBin randomModels;
+  SGBuildingBinList randomBuildings;
 
   static SGVec4f
   getMaterialLightColor(const SGMaterial* material)
@@ -445,7 +447,242 @@ struct SGTileGeometryBin {
       }
     }
   }
+  
+  void computeRandomBuildings(SGMaterialLib* matlib, float building_density)
+  {
+    SGMaterialTriangleMap::iterator i;
+        
+    // generate a repeatable random seed
+    mt seed;
+    mt_init(&seed, unsigned(123));
+    
+    for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+      SGMaterial *mat = matlib->find(i->first);
+      SGTexturedTriangleBin triangleBin = i->second;
+      
+      if (!mat)
+        continue;
+
+      osg::Texture2D* object_mask = mat->get_object_mask(triangleBin);
+
+      float 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;
+      
+      BOOST_FOREACH(bin, randomBuildings)
+      {
+        if (bin->texture == mat->get_building_texture()) {
+            found = true;
+            break;
+        }
+      }
+      
+      if (!found) {
+        bin = new SGBuildingBin();
+        bin->texture = mat->get_building_texture();
+        SG_LOG(SG_INPUT, SG_DEBUG, "Building texture " << bin->texture);
+        randomBuildings.push_back(bin);
+      }       
+      
+      std::vector<std::pair<SGVec3f, float> > randomPoints;
+      
+      unsigned num = i->second.getNumTriangles();
+      int triangle_dropped = 0;
+      int building_dropped = 0;
+      int random_dropped = 0;
+
+      for (unsigned i = 0; i < num; ++i) {
+        SGBuildingBin::BuildingList triangle_buildings;
+        SGTexturedTriangleBin::triangle_ref triangleRef = triangleBin.getTriangleRef(i);
+        
+        SGVec3f v0 = triangleBin.getVertex(triangleRef[0]).vertex;
+        SGVec3f v1 = triangleBin.getVertex(triangleRef[1]).vertex;
+        SGVec3f v2 = triangleBin.getVertex(triangleRef[2]).vertex;
+        SGVec2f t0 = triangleBin.getVertex(triangleRef[0]).texCoord;
+        SGVec2f t1 = triangleBin.getVertex(triangleRef[1]).texCoord;
+        SGVec2f t2 = triangleBin.getVertex(triangleRef[2]).texCoord;
+        SGVec3f normal = cross(v1 - v0, v2 - v0);
+        
+        // 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);
+        
+        // Apply density.
+        num = num * building_density;
+
+        // place an object each unit of area
+        while ( num > 1.0 ) {
+          float a = mt_rand(&seed);
+          float b = mt_rand(&seed);
+          if ( a + b > 1 ) {
+            a = 1 - a;
+            b = 1 - b;
+          }
+          
+          float c = 1 - a - b;
+          SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
+          float rotation = mt_rand(&seed);
+          
+          if (object_mask != NULL) {
+            SGVec2f texCoord = a*t0 + b*t1 + c*t2;
+            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()) {  
+              // Object passes mask. Rotation is taken from the red channel
+              rotation = img->getColor(x,y).r();
+            } else {
+              // Fails mask test - try again.
+              num -= 1.0;
+              continue;
+            }  
+          }
+          
+          // 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);
+          
+          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 = 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));
+            
+            if (depth > width) { depth = width; }
+            
+            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 = 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));
+            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 = 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.        
+          if (((length(cross(randomPoint - v0, randomPoint - v1)) / length(v1 - v0)) < radius) ||
+              ((length(cross(randomPoint - v1, randomPoint - v2)) / length(v2 - v1)) < radius) ||
+              ((length(cross(randomPoint - v2, randomPoint - v0)) / length(v0 - v2)) < radius)   )
+          {
+            triangle_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;
+            
+            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) {
+            
+            float min_dist = l->radius + radius + min_spacing;
+            min_dist = min_dist * min_dist;
+            
+            if (distSqr(randomPoint, l->position) < min_dist) {
+              building_dropped++;
+              too_close = true;
+              continue;
+            }
+          }
+          
+          if (too_close) {
+            // Too close to another building - drop and try again
+            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);
+        }
+      }
+
+      SG_LOG(SG_INPUT, SG_DEBUG, "Random Buildings: " << bin->getNumBuildings());
+      SG_LOG(SG_INPUT, SG_DEBUG, "  Dropped due to triangle edge: " << triangle_dropped);
+      SG_LOG(SG_INPUT, SG_DEBUG, "  Dropped due to random object: " << random_dropped);
+      SG_LOG(SG_INPUT, SG_DEBUG, "  Dropped due to other building: " << building_dropped);
+    }
+  }
+  
   void computeRandomForest(SGMaterialLib* matlib, float vegetation_density)
   {
     SGMaterialTriangleMap::iterator i;
@@ -538,9 +775,9 @@ struct SGTileGeometryBin {
                                         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;                
@@ -551,8 +788,10 @@ struct SGTileGeometryBin {
                   
                   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);
                 }
@@ -563,7 +802,6 @@ struct SGTileGeometryBin {
       }
     }
   }
-  
 
   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
   {
@@ -608,7 +846,9 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
   SGMaterialLib* matlib = 0;
   bool use_random_objects = false;
   bool use_random_vegetation = false;
-  float vegetation_density = 1.0f;
+  bool use_random_buildings = false;
+  float vegetation_density = 1.0f;  
+  float building_density = 1.0f;
   if (options) {
     matlib = options->getMaterialLib();
     SGPropertyNode* propertyNode = options->getPropertyNode().get();
@@ -618,10 +858,16 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
                                          use_random_objects);
         use_random_vegetation
             = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
-                                         use_random_vegetation);
+                                         use_random_vegetation);        
         vegetation_density
             = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
                                           vegetation_density);
+        use_random_buildings
+            = propertyNode->getBoolValue("/sim/rendering/random-buildings",
+                                         use_random_buildings);        
+        building_density
+            = propertyNode->getFloatValue("/sim/rendering/building-density",
+                                          building_density);
     }
   }
 
@@ -652,75 +898,82 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
   osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
   osg::ref_ptr<osg::Group> randomObjects;
   osg::ref_ptr<osg::Group> forestNode;
+  osg::ref_ptr<osg::Group> buildingNode;  
   osg::Group* terrainGroup = new osg::Group;
 
   osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
   if (node)
     terrainGroup->addChild(node);
 
-  if (use_random_objects || use_random_vegetation) {
-    if (use_random_objects) {
-      if (matlib)
-        tileGeometryBin.computeRandomObjects(matlib);
-    
-      if (tileGeometryBin.randomModels.getNumModels() > 0) {
-        // Generate a repeatable random seed
-        mt seed;
-        mt_init(&seed, unsigned(123));
-
-        std::vector<ModelLOD> models;
-        for (unsigned int i = 0;
-             i < tileGeometryBin.randomModels.getNumModels(); i++) {
-          SGMatModelBin::MatModel obj
-            = tileGeometryBin.randomModels.getMatModel(i);          
-
-          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 (use_random_objects && matlib) {
+    tileGeometryBin.computeRandomObjects(matlib);
 
-          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));
-        }
-        RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
-        quadtree.buildQuadTree(models.begin(), models.end());
-        randomObjects = quadtree.getRoot();
-        randomObjects->setName("random objects");
-      }
-    }
+    if (tileGeometryBin.randomModels.getNumModels() > 0) {
+      // Generate a repeatable random seed
+      mt seed;
+      mt_init(&seed, unsigned(123));
 
-    if (use_random_vegetation && matlib) {
-      // Now add some random forest.
-      tileGeometryBin.computeRandomForest(matlib, vegetation_density);
+      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);
       
-      if (tileGeometryBin.randomForest.size() > 0) {
-        forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(),
-                                  options);
-        forestNode->setName("Random trees");
+        // 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));
       }
-    } 
+      RandomObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
+      quadtree.buildQuadTree(models.begin(), models.end());
+      randomObjects = quadtree.getRoot();
+      randomObjects->setName("random objects");
+    }
   }
 
+  if (use_random_vegetation && matlib) {
+    // Now add some random forest.
+    tileGeometryBin.computeRandomForest(matlib, vegetation_density);
+    
+    if (tileGeometryBin.randomForest.size() > 0) {
+      forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity(),
+                                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
   if (matlib)
     tileGeometryBin.computeRandomSurfaceLights(matlib);
@@ -736,6 +989,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
     groundLights0->addChild(geode);
     lightGroup->addChild(groundLights0);
   }
+  
   if (tileGeometryBin.randomTileLights.getNumLights() > 0) {
     osg::Group* groundLights1 = new osg::Group;
     groundLights1->setStateSet(lightManager->getGroundLightStateSet());
@@ -778,6 +1032,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
     vasiGeode->setStateSet(lightManager->getRunwayLightStateSet());
     lightGroup->addChild(vasiGeode);
   }
+  
   Effect* runwayEffect = 0;
   if (tileGeometryBin.runwayLights.getNumLights() > 0
       || !tileGeometryBin.rabitLights.empty()
@@ -841,7 +1096,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
     transform->addChild(lightLOD);
   }
   
-  if (randomObjects.valid() || forestNode.valid()) {
+  if (randomObjects.valid() || forestNode.valid() || buildingNode.valid()) {
   
     // Add a LoD node, so we don't try to display anything when the tile center
     // is more than 20km away.
@@ -849,6 +1104,7 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
     
     if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
     if (forestNode.valid())  objectLOD->addChild(forestNode.get(), 0, 20000);
+    if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 20000);
     
     unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
     objectLOD->setNodeMask(nodeMask);