]> git.mxchange.org Git - simgear.git/commitdiff
Random object and vegetation masking based on bitmap file.
authorStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Sun, 5 Feb 2012 23:05:57 +0000 (23:05 +0000)
committerStuart Buchanan <stuart_d_buchanan@yahoo.co.uk>
Sun, 5 Feb 2012 23:05:57 +0000 (23:05 +0000)
Also adds a property controlling vegetation density.

simgear/scene/material/mat.cxx
simgear/scene/material/mat.hxx
simgear/scene/material/matmodel.cxx
simgear/scene/material/matmodel.hxx
simgear/scene/tgdb/SGModelBin.hxx
simgear/scene/tgdb/SGReaderWriterBTG.cxx
simgear/scene/tgdb/SGTexturedTriangleBin.hxx
simgear/scene/tgdb/obj.cxx
simgear/scene/tgdb/obj.hxx
simgear/scene/tgdb/userdata.cxx
simgear/scene/tgdb/userdata.hxx

index f70a9a3b43341538f4c364d4be34e8d987283dd3..0431fbfff12528c36a424f9b3cb290aebd31baa5 100644 (file)
@@ -30,7 +30,7 @@
 #include <string.h>
 #include <map>
 #include <vector>
-#include<string>
+#include <string>
 
 #include <boost/foreach.hpp>
 #include "mat.hxx"
@@ -74,7 +74,7 @@ SGMaterial::_internal_state::_internal_state(Effect *e, bool l,
 {
 }
 
-SGMaterial::_internal_state::_internal_state(Effect *e, const string &t, bool l,
+SGMaterial::_internal_state::_internal_state(Effect *e, const string &t, bool l, 
                                              const SGReaderWriterOptions* o)
     : effect(e), effect_realized(l), options(o)
 {
@@ -118,14 +118,22 @@ void
 SGMaterial::read_properties(const SGReaderWriterOptions* options,
                             const SGPropertyNode *props)
 {
-                               // Gather the path(s) to the texture(s)
+  std::vector<bool> dds;
   std::vector<SGPropertyNode_ptr> textures = props->getChildren("texture");
   for (unsigned int i = 0; i < textures.size(); i++)
   {
     string tname = textures[i]->getStringValue();
+    
     if (tname.empty()) {
         tname = "unknown.rgb";
     }
+    
+    if (tname.rfind(".dds") == (tname.length() - 4)) {
+      dds.push_back(true);
+    } else {
+      dds.push_back(false);      
+    }  
+    
     SGPath tpath("Textures.high");
     tpath.append(tname);
     string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
@@ -134,7 +142,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
       tpath.append(tname);
       fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
     }
-
+    
     if (!fullTexPath.empty() ) {
       _internal_state st( NULL, fullTexPath, false, options );
       _status.push_back( st );
@@ -152,6 +160,15 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
       if (tname.empty()) {
           tname = "unknown.rgb";
       }
+      
+      if (j == 0) {
+        if (tname.rfind(".dds") == (tname.length() - 4)) {
+          dds.push_back(true);
+        } else {
+          dds.push_back(false);      
+        }  
+      }
+  
       SGPath tpath("Textures.high");
       tpath.append(tname);
       string fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
@@ -160,6 +177,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
         tpath.append(tname);
         fullTexPath = SGModelLib::findDataFile(tpath.str(), options);
       }
+      
       st.add_texture(fullTexPath, textures[j]->getIndex());
     }
 
@@ -175,6 +193,43 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
     _internal_state st( NULL, tpath.str(), true, options );
     _status.push_back( st );
   }
+  
+  std::vector<SGPropertyNode_ptr> masks = props->getChildren("object-mask");
+  for (unsigned int i = 0; i < masks.size(); i++)
+  {
+    string omname = masks[i]->getStringValue();
+    
+    if (! omname.empty()) {
+      SGPath ompath("Textures.high");
+      ompath.append(omname);
+      string fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
+      
+      if (fullMaskPath.empty()) {
+        ompath = SGPath("Textures");
+        ompath.append(omname);
+        fullMaskPath = SGModelLib::findDataFile(ompath.str(), options);
+      }    
+      
+      osg::Image* image = osgDB::readImageFile(fullMaskPath, options);
+      if (image->valid())
+      {
+        osg::Texture2D* object_mask = new osg::Texture2D;
+        
+        if (dds[i]) {
+          // Texture is a DDS. This is relevant for the object mask, as DDS
+          // textures have an origin at the bottom left rather than top
+          // left, therefore we flip the object mask vertically.
+          image->flipVertical();          
+        }
+        
+        object_mask->setImage(image);
+        object_mask->setDataVariance(osg::Object::STATIC);
+        object_mask->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+        object_mask->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
+        _masks.push_back(object_mask);
+      } 
+    }
+  } 
 
   xsize = props->getDoubleValue("xsize", 0.0);
   ysize = props->getDoubleValue("ysize", 0.0);
@@ -195,7 +250,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
     string treeTexPath = props->getStringValue("tree-texture");
     tree_texture = SGModelLib::findDataFile(treeTexPath, options);
   }
-
+  
   // surface values for use with ground reactions
   solid = props->getBoolValue("solid", true);
   friction_factor = props->getDoubleValue("friction-factor", 1.0);
@@ -253,7 +308,6 @@ void
 SGMaterial::init ()
 {
     _status.clear();
-    _current_ptr = 0;
     xsize = 0;
     ysize = 0;
     wrapu = true;
@@ -278,22 +332,47 @@ SGMaterial::init ()
     effect = "Effects/terrain-default";
 }
 
-Effect* SGMaterial::get_effect(int n)
+Effect* SGMaterial::get_effect(int i)
+{    
+    if(!_status[i].effect_realized) {
+        _status[i].effect->realizeTechniques(_status[i].options.get());
+        _status[i].effect_realized = true;
+    }
+    return _status[i].effect.get();
+}
+
+Effect* SGMaterial::get_effect(SGTexturedTriangleBin triangleBin)
 {
     if (_status.size() == 0) {
         SG_LOG( SG_GENERAL, SG_WARN, "No effect available.");
         return 0;
     }
-    int i = n >= 0 ? n : _current_ptr;
-    if(!_status[i].effect_realized) {
-        _status[i].effect->realizeTechniques(_status[i].options.get());
-        _status[i].effect_realized = true;
+    
+    int i = triangleBin.getTextureIndex() % _status.size();
+    return get_effect(i);
+}
+
+Effect* SGMaterial::get_effect()
+{
+    return get_effect(0);
+}
+
+
+osg::Texture2D* SGMaterial::get_object_mask(SGTexturedTriangleBin triangleBin)
+{
+    if (_status.size() == 0) {
+        SG_LOG( SG_GENERAL, SG_WARN, "No mask available.");
+        return 0;
+    }
+    
+    // Note that the object mask is closely linked to the texture/effect
+    // so we index based on the texture index, 
+    unsigned int i = triangleBin.getTextureIndex() % _status.size();
+    if (i < _masks.size()) {
+        return _masks[i];      
+    } else {
+        return 0;      
     }
-    // XXX This business of returning a "random" alternate texture is
-    // really bogus. It means that the appearance of the terrain
-    // depends on the order in which it is paged in!
-    _current_ptr = (_current_ptr + 1) % _status.size();
-    return _status[i].effect.get();
 }
 
 void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options)
index 06e72b725658dd2b813dfe8e1520e23c2aa52c9b..26d78333f83f540368dc293911840696544f7402 100644 (file)
 #include <map>
 
 #include <simgear/math/SGMath.hxx>
+#include "Effect.hxx"
+#include <simgear/scene/tgdb/SGTexturedTriangleBin.hxx>
 
 #include <osg/ref_ptr>
+#include <osg/Texture2D>
 
 namespace osg
 {
@@ -103,7 +106,14 @@ public:
   /**
    * Get the textured state.
    */
-  simgear::Effect *get_effect(int n = -1);
+  simgear::Effect* get_effect(SGTexturedTriangleBin triangleBin);
+  simgear::Effect* get_effect();
+
+  /**
+   * Get the textured state.
+   */
+  osg::Texture2D* get_object_mask(SGTexturedTriangleBin triangleBin);
+
 
   /**
    * Get the number of textures assigned to this material.
@@ -189,7 +199,7 @@ public:
    * @return the texture to use for trees.
    */
   inline std::string get_tree_texture () const { return  tree_texture; }
-
+  
   /**
    * Return if the surface material is solid, if it is not solid, a fluid
    * can be assumed, that is usually water.
@@ -293,9 +303,6 @@ private:
   // texture status
   std::vector<_internal_state> _status;
 
-  // Round-robin counter
-  mutable unsigned int _current_ptr;
-
   // texture size
   double xsize, ysize;
 
@@ -361,6 +368,10 @@ private:
   
   // Tree texture, typically a strip of applicable tree textures
   std::string tree_texture;
+  
+  // Object mask, a simple RGB texture used as a mask when placing
+  // random vegetation, objects and buildings
+  std::vector<osg::Texture2D*> _masks;
 \f
   ////////////////////////////////////////////////////////////////////
   // Internal constructors and methods.
@@ -369,6 +380,7 @@ private:
   void read_properties(const simgear::SGReaderWriterOptions* options,
                         const SGPropertyNode *props);
   void buildEffectProperties(const simgear::SGReaderWriterOptions* options);
+  simgear::Effect* get_effect(int i);
 };
 
 
index 6bd1e18961e948244f9820adbfa8375cffa276e8..2ff665d9c638f6f1c55d2d938e1f7e5da3246504 100644 (file)
@@ -55,6 +55,7 @@ using std::map;\f
 SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m)
   : _models_loaded(false),
     _coverage_m2(node->getDoubleValue("coverage-m2", 1000000)),
+    _spacing_m(node->getDoubleValue("spacing-m", 20)),
     _range_m(range_m)
 {
                                // Sanity check
@@ -77,6 +78,8 @@ SGMatModel::SGMatModel (const SGPropertyNode * node, double range_m)
     _heading_type = HEADING_BILLBOARD;
   } else if (hdg == "random") {
     _heading_type = HEADING_RANDOM;
+  } else if (hdg == "mask") {
+    _heading_type = HEADING_MASK;
   } else {
     _heading_type = HEADING_FIXED;
     SG_LOG(SG_INPUT, SG_ALERT, "Unknown heading type: " << hdg
@@ -135,11 +138,11 @@ SGMatModel::load_models( SGPropertyNode *prop_root )
 }
 
 osg::Node*
-SGMatModel::get_random_model( SGPropertyNode *prop_root, mt seed )
+SGMatModel::get_random_model( SGPropertyNode *prop_root, mt* seed )
 {
   load_models( prop_root ); // comment this out if preloading models
   int nModels = _models.size();
-  return _models[mt_rand(&seed) * nModels].get();
+  return _models[mt_rand(seed) * nModels].get();
 }
 
 double
@@ -153,6 +156,11 @@ double SGMatModel::get_range_m() const
   return _range_m;
 }
 
+double SGMatModel::get_spacing_m() const
+{
+  return _spacing_m;
+}
+
 double SGMatModel::get_randomized_range_m(mt* seed) const
 {
   double lrand = mt_rand(seed);
index 3e7d84a973ce06ec265104e5d3be947ee62ac637..1687302e26bbe3c150c863ebf86855537f6abe94 100644 (file)
@@ -66,7 +66,8 @@ public:
     enum HeadingType {
         HEADING_FIXED,
         HEADING_BILLBOARD,
-        HEADING_RANDOM
+        HEADING_RANDOM,
+        HEADING_MASK
     };
 
     /**
@@ -82,7 +83,7 @@ public:
      *
      * @return A randomly select model from the variants.
      */
-    osg::Node *get_random_model( SGPropertyNode *prop_root, mt seed );
+    osg::Node *get_random_model( SGPropertyNode *prop_root, mt *seed );
 
 
     /**
@@ -98,6 +99,15 @@ public:
      * @return The visual range.
      */
     double get_range_m () const;
+
+    /**
+     * Get the minimum spacing between this and any
+     * other objects in m
+     *
+     * @return The spacing in m.
+     */
+    double get_spacing_m () const;
+    
     
     /**
      * Get a randomized visual range
@@ -136,6 +146,7 @@ private:
     mutable std::vector<osg::ref_ptr<osg::Node> > _models;
     mutable bool _models_loaded;
     double _coverage_m2;
+    double _spacing_m;
     double _range_m;
     HeadingType _heading_type;
 };
index bd61a948a6680e307422ad187e3a1028ca946cde..914b52a6928b5a235ba74b6356a18a07bf1e2058 100644 (file)
 class SGMatModelBin {
 public:
   struct MatModel {
-    MatModel(const SGVec3f& p, SGMatModel *m, int l) :
-      position(p), model(m), lod(l)
+    MatModel(const SGVec3f& p, SGMatModel *m, int l, float rot) :
+      position(p), model(m), lod(l), rotation(rot)
     { }
     SGVec3f position;
     SGMatModel *model;
     int lod;
+    float rotation;
   };
   typedef std::vector<MatModel> MatModelList;
 
@@ -41,8 +42,8 @@ public:
     _models.push_back(model);   
   }
   
-  void insert(const SGVec3f& p, SGMatModel *m, int l)
-  { insert(MatModel(p, m, l)); }
+  void insert(const SGVec3f& p, SGMatModel *m, int l, float rot)
+  { insert(MatModel(p, m, l, rot)); }
 
   unsigned getNumModels() const
   { return _models.size(); }
index a3a0fe5c45480b6d38bda3edebcd9953d2bd1d28..71d2a00dd405ef0e1ba2c8cb45c36a3c340f9f9d 100644 (file)
@@ -62,6 +62,7 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
     SGMaterialLib* matlib = 0;
     bool useRandomObjects = false;
     bool useRandomVegetation = false;
+    float vegetation_density = 1.0f;
     const SGReaderWriterOptions* sgOptions;
     sgOptions = dynamic_cast<const SGReaderWriterOptions*>(options);
     if (sgOptions) {
@@ -74,12 +75,16 @@ SGReaderWriterBTG::readNode(const std::string& fileName,
             useRandomVegetation
                 = propertyNode->getBoolValue("/sim/rendering/random-vegetation",
                                              useRandomVegetation);
+            vegetation_density
+                = propertyNode->getFloatValue("/sim/rendering/vegetation-density",
+                                              vegetation_density);
         }
     }
 
     osg::Node* result = SGLoadBTG(fileName, matlib,
                                   useRandomObjects,
-                                  useRandomVegetation);
+                                  useRandomVegetation,
+                                  vegetation_density);
     if (result)
         return result;
     else
index 3df4280a123afb824bbc93fa5053736daa912cd3..999a64fc9750b7d6a895d583656a28cbae9cf635 100644 (file)
@@ -25,6 +25,8 @@
 #include <osg/Array>
 #include <osg/Geometry>
 #include <osg/PrimitiveSet>
+#include <osg/Texture2D>
+#include <stdio.h>
 
 #include <simgear/math/sg_random.h>
 #include <simgear/math/SGMath.hxx>
@@ -106,6 +108,7 @@ public:
   // The points are offsetted away from the triangles in
   // offset * positive normal direction.
   void addRandomSurfacePoints(float coverage, float offset,
+                              osg::Texture2D* object_mask,
                               std::vector<SGVec3f>& points)
   {
     unsigned num = getNumTriangles();
@@ -114,6 +117,9 @@ public:
       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
+      SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
+      SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
+      SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
       SGVec3f normal = cross(v1 - v0, v2 - v0);
       
       // Compute the area
@@ -140,8 +146,24 @@ public:
         }
         float c = 1 - a - b;
         SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
-        points.push_back(randomPoint);
-        unit -= coverage;
+        
+        if (object_mask != NULL) {
+          SGVec2f texCoord = a*t0 + b*t1 + c*t2;
+          
+          // Check this random point against the object mask
+          // red 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).r()) {                
+            points.push_back(randomPoint);        
+          }                    
+        } else {      
+          // No object mask, so simply place the object  
+          points.push_back(randomPoint);        
+        }
+        unit -= coverage;        
       }
     }
   }
@@ -151,6 +173,8 @@ public:
   void addRandomTreePoints(float wood_coverage, 
                            float tree_density,
                            float wood_size,
+                           osg::Texture2D* object_mask,
+                           float vegetation_density,
                            std::vector<SGVec3f>& points)
   {
     unsigned num = getNumTriangles();
@@ -159,83 +183,135 @@ public:
       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
+      SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
+      SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
+      SGVec2f t2 = 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 a point being created
-      // for this triangle
-      float unit = area + mt_rand(&seed)*wood_coverage;
 
-      int woodcount = (int) (unit / wood_coverage);
-
-      for (int j = 0; j < woodcount; j++) {
-
-        if (wood_size < area) {
-          // We need to place a wood within the triangle and populate it
-
-          // Determine the center of the wood
-          float x = mt_rand(&seed);
-          float y = mt_rand(&seed);
+      if (object_mask != NULL) {
+        // For partial units of area, use a zombie door method to
+        // create the proper random chance of a point being created
+        // for this triangle
+        float unit = area + mt_rand(&seed)*wood_coverage;
+        
+        // Vegetation density is linear, while we're creating woodland
+        // by area.
+        int woodcount = (int) (vegetation_density * 
+                               vegetation_density * 
+                               unit / wood_coverage);
+        
+        for (int j = 0; j < woodcount; j++) {
+          float a = mt_rand(&seed);
+          float b = mt_rand(&seed);
+
+          if ( a + b > 1.0f ) {
+            a = 1.0f - a;
+            b = 1.0f - b;
+          }
 
-          // Determine the size of this wood in m^2, and the number
-          // of trees in the wood
-          float ws = wood_size + wood_size * (mt_rand(&seed) - 0.5f);
-          unsigned total_trees = ws / tree_density;
-          float wood_length = sqrt(ws);
+          float c = 1.0f - a - b;
+
+          SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
+          SGVec2f texCoord = a*t0 + b*t1 + c*t2;
+          
+          // Check this random point against the object mask
+          // green 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).g()) {                
+            points.push_back(randomPoint);
+          }
+        }
+      } else {
+        // For partial units of area, use a zombie door method to
+        // create the proper random chance of a point being created
+        // for this triangle
+        float unit = area + mt_rand(&seed)*wood_coverage;
+        int woodcount = (int) (unit / wood_coverage);
+        
+        if (wood_size < 1.0) {
+          // A wood size of 0 is used for an even spread of woodland,
+          // where each wood contains a single tree. In this case we
+          // need to apply the vegetation_density to the wood count rather
+          // than the tree density.
+          woodcount = woodcount * vegetation_density;
+        }        
+        
+        for (int j = 0; j < woodcount; j++) {
+
+          if (wood_size < area) {
+            // We need to place a wood within the triangle and populate it
+
+            // Determine the center of the wood
+            float x = mt_rand(&seed);
+            float y = mt_rand(&seed);
+
+            // Determine the size of this wood in m^2, and the number
+            // of trees in the wood
+            float ws = wood_size + wood_size * (mt_rand(&seed) - 0.5f);
+            unsigned total_trees = ws / tree_density;            
+            
+            if (wood_size >= 1.0) {
+              total_trees = total_trees * vegetation_density;
+            }
+            
+            float wood_length = sqrt(ws);
 
-          // From our wood size, work out the fraction on the two axis.
-          // This will be used as a factor when placing trees in the wood.
-          float x_tree_factor = wood_length / length(v1 -v0);
-          float y_tree_factor = wood_length / length(v2 -v0);
+            // From our wood size, work out the fraction on the two axis.
+            // This will be used as a factor when placing trees in the wood.
+            float x_tree_factor = wood_length / length(v1 -v0);
+            float y_tree_factor = wood_length / length(v2 -v0);
 
-          for (unsigned k = 0; k <= total_trees; k++) {
+            for (unsigned k = 0; k <= total_trees; k++) {
 
-            float a = x + x_tree_factor * (mt_rand(&seed) - 0.5f);
-            float b = y + y_tree_factor * (mt_rand(&seed) - 0.5f);
+              float a = x + x_tree_factor * (mt_rand(&seed) - 0.5f);
+              float b = y + y_tree_factor * (mt_rand(&seed) - 0.5f);
 
 
-            // In some cases, the triangle side lengths are so small that the
-            // tree_factors become so large as to make placing the tree within
-            // the triangle almost impossible. In this case, we place them
-            // randomly across the triangle.
-            if (a < 0.0f || a > 1.0f) a = mt_rand(&seed);
-            if (b < 0.0f || b > 1.0f) b = mt_rand(&seed);
-            
-            if ( a + b > 1.0f ) {
-              a = 1.0f - a;
-              b = 1.0f - b;
-            }
+              // In some cases, the triangle side lengths are so small that the
+              // tree_factors become so large as to make placing the tree within
+              // the triangle almost impossible. In this case, we place them
+              // randomly across the triangle.
+              if (a < 0.0f || a > 1.0f) a = mt_rand(&seed);
+              if (b < 0.0f || b > 1.0f) b = mt_rand(&seed);
               
-            float c = 1.0f - a - b;
+              if ( a + b > 1.0f ) {
+                a = 1.0f - a;
+                b = 1.0f - b;
+              }
+                
+              float c = 1.0f - a - b;
+              SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
+              points.push_back(randomPoint);              
+            }
+          } else {
+            // This triangle is too small to contain a complete wood, so just
+            // distribute trees across it.
+            unsigned total_trees = area / tree_density;
 
-            SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
+            for (unsigned k = 0; k <= total_trees; k++) {
 
-            points.push_back(randomPoint);
-          }
-        } else {
-          // This triangle is too small to contain a complete wood, so just
-          // distribute trees across it.
-          unsigned total_trees = area / tree_density;
+              float a = mt_rand(&seed);
+              float b = mt_rand(&seed);
 
-          for (unsigned k = 0; k <= total_trees; k++) {
+              if ( a + b > 1.0f ) {
+                a = 1.0f - a;
+                b = 1.0f - b;
+              }
 
-            float a = mt_rand(&seed);
-            float b = mt_rand(&seed);
+              float c = 1.0f - a - b;
 
-            if ( a + b > 1.0f ) {
-              a = 1.0f - a;
-              b = 1.0f - b;
+              SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
+              SGVec2f texCoord = a*t0 + b*t1 + c*t2;
+              points.push_back(randomPoint);
             }
-
-            float c = 1.0f - a - b;
-
-            SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
-            points.push_back(randomPoint);
           }
         }
       }
@@ -243,7 +319,8 @@ public:
   }
   
    void addRandomPoints(float coverage, 
-                        std::vector<SGVec3f>& points)
+                        osg::Texture2D* object_mask,
+                        std::vector<std::pair<SGVec3f, float> >& points)
   {
     unsigned num = getNumTriangles();
     for (unsigned i = 0; i < num; ++i) {
@@ -251,6 +328,9 @@ public:
       SGVec3f v0 = getVertex(triangleRef[0]).vertex;
       SGVec3f v1 = getVertex(triangleRef[1]).vertex;
       SGVec3f v2 = getVertex(triangleRef[2]).vertex;
+      SGVec2f t0 = getVertex(triangleRef[0]).texCoord;
+      SGVec2f t1 = getVertex(triangleRef[1]).texCoord;
+      SGVec2f t2 = getVertex(triangleRef[2]).texCoord;
       SGVec3f normal = cross(v1 - v0, v2 - v0);
       
       // Compute the area
@@ -273,7 +353,25 @@ public:
         }
         float c = 1 - a - b;
         SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
-        points.push_back(randomPoint);
+        
+        if (object_mask != NULL) {
+          SGVec2f texCoord = a*t0 + b*t1 + c*t2;
+          
+          // Check this random point against the object mask
+          // blue (for buildings) channel. Also check
+          // that they are more than spacing metres away from
+          // any other point.
+          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()) {  
+            // The red channel contains the rotation for this object                                  
+            points.push_back(std::make_pair(randomPoint, img->getColor(x,y).r()));
+          }
+        } else {
+          points.push_back(std::make_pair(randomPoint, mt_rand(&seed)));
+        }        
         num -= 1.0;
       }
     }
@@ -338,6 +436,16 @@ public:
 
   osg::Geometry* buildGeometry() const
   { return buildGeometry(getTriangles()); }
+  
+  int getTextureIndex() {
+    if (empty() || getNumTriangles() == 0)
+      return 0;
+
+    triangle_ref triangleRef = getTriangleRef(0);
+    SGVec3f v0 = getVertex(triangleRef[0]).vertex;
+      
+    return floor(v0.x());
+  }
 
 private:
   // Random seed for the triangle.
index d080ff33ef84f1b1a6fd5d33bd0c862ebaf10e79..b7721b6c6a5624966f3e446dfa5e6eea19dd00c1 100644 (file)
@@ -42,6 +42,8 @@
 
 #include <boost/foreach.hpp>
 
+#include <algorithm>
+
 #include <simgear/debug/logstream.hxx>
 #include <simgear/io/sg_binobj.hxx>
 #include <simgear/math/sg_geodesy.hxx>
@@ -382,7 +384,7 @@ struct SGTileGeometryBin {
         mat = matlib->find(i->first);
       eg = new EffectGeode;
       if (mat)
-        eg->setEffect(mat->get_effect());
+        eg->setEffect(mat->get_effect(i->second));
       eg->addDrawable(geometry);
       eg->runGenerators(geometry);  // Generate extra data needed by effect
       if (group)
@@ -417,7 +419,7 @@ struct SGTileGeometryBin {
       }
       
       std::vector<SGVec3f> randomPoints;
-      i->second.addRandomSurfacePoints(coverage, 3, randomPoints);
+      i->second.addRandomSurfacePoints(coverage, 3, mat->get_object_mask(i->second), randomPoints);
       std::vector<SGVec3f>::iterator j;
       for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
         float zombie = mt_rand(&seed);
@@ -445,7 +447,7 @@ struct SGTileGeometryBin {
     }
   }
 
-  void computeRandomForest(SGMaterialLib* matlib)
+  void computeRandomForest(SGMaterialLib* matlib, float vegetation_density)
   {
     SGMaterialTriangleMap::iterator i;
 
@@ -493,6 +495,8 @@ struct SGTileGeometryBin {
       i->second.addRandomTreePoints(wood_coverage,
                                     mat->get_tree_density(),
                                     mat->get_wood_size(),
+                                    mat->get_object_mask(i->second),
+                                    vegetation_density,
                                     randomPoints);
       
       std::vector<SGVec3f>::iterator k;
@@ -531,12 +535,29 @@ struct SGTileGeometryBin {
             for (int k = 0; k < nObjects; k++) {
               SGMatModel * object = object_group->get_object(k);
 
-              std::vector<SGVec3f> randomPoints;
+              std::vector<std::pair<SGVec3f, float> > randomPoints;
 
-              i->second.addRandomPoints(object->get_coverage_m2(), randomPoints);
-              std::vector<SGVec3f>::iterator l;
+              i->second.addRandomPoints(object->get_coverage_m2(), 
+                                        mat->get_object_mask(i->second), 
+                                        randomPoints);
+              std::vector<std::pair<SGVec3f, float> >::iterator l;
               for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
-                randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed));
+                
+                // 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 = std::max(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;                
+                  }              
+                }            
+                if (!close) { 
+                  randomModels.insert(l->first, object, (int)object->get_randomized_range_m(&seed), l->second);
+                }
               }
             }
           }
@@ -544,6 +565,7 @@ struct SGTileGeometryBin {
       }
     }
   }
+  
 
   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
   {
@@ -579,7 +601,7 @@ typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
                         GetModelLODCoord>  RandomObjectsQuadtree;
 
 osg::Node*
-SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_objects, bool use_random_vegetation)
+SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_objects, bool use_random_vegetation, float vegetation_density)
 {
   SGBinObject tile;
   if (!tile.read_bin(path))
@@ -632,12 +654,14 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object
         for (unsigned int i = 0;
              i < tileGeometryBin.randomModels.getNumModels(); i++) {
           SGMatModelBin::MatModel obj
-            = tileGeometryBin.randomModels.getMatModel(i);
-          osg::Node* node = sgGetRandomModel(obj.model, seed);
+            = tileGeometryBin.randomModels.getMatModel(i);          
+            
+          osg::Node* node = sgGetRandomModel(obj.model, &seed);
         
           // Create a matrix to place the object in the correct
           // location, and then apply the rotation matrix created
-          // above, with an additional random heading rotation if appropriate.
+          // 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) {
@@ -646,6 +670,14 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object
             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);
@@ -660,7 +692,7 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_object
 
     if (use_random_vegetation && matlib) {
       // Now add some random forest.
-      tileGeometryBin.computeRandomForest(matlib);
+      tileGeometryBin.computeRandomForest(matlib, vegetation_density);
       
       if (tileGeometryBin.randomForest.size() > 0) {
         forestNode = createForest(tileGeometryBin.randomForest, osg::Matrix::identity());
index cdced8b697c26f703ff3f8fd6ce3075b220fa35a..66054a195ed47caaa706c90bbe666b82e2ca2a6c 100644 (file)
@@ -56,6 +56,10 @@ inline bool SGGenTile( const std::string&, const SGBucket& b,
 }
 
 osg::Node*
-SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool use_random_objects, bool use_random_vegetation);
+SGLoadBTG(const std::string& path, 
+          SGMaterialLib *matlib, 
+          bool use_random_objects, 
+          bool use_random_vegetation, 
+          float vegetation_density);
 
 #endif // _SG_OBJ_HXX
index bf5823c99dba7b0bb28b1066f8e5387e694d05a3..10fa8f7e7763d5cee644bf670b641325fc8e65db 100644 (file)
@@ -59,7 +59,7 @@ void sgUserDataInit( SGPropertyNode *p ) {
     root_props = p;
 }
 
-osg::Node* sgGetRandomModel(SGMatModel *obj, mt seed) {
+osg::Node* sgGetRandomModel(SGMatModel *obj, mt *seed) {
    return obj->get_random_model( root_props, seed );
  }
 
index fa5209e679024d29b3d9933531b6c3b989c97e76..553a8c593fe591c7e0d07c77c94b40fcc48f5b98 100644 (file)
@@ -43,7 +43,7 @@ void sgUserDataInit(SGPropertyNode *p);
 /**
  * Get a random model.
  */
-osg::Node* sgGetRandomModel(SGMatModel *obj, mt seed);
+osg::Node* sgGetRandomModel(SGMatModel *obj, mt *seed);
 
 namespace simgear
 {