]> git.mxchange.org Git - simgear.git/commitdiff
Random trees from Stuart Buchanan
authortimoore <timoore>
Sat, 2 Feb 2008 23:01:05 +0000 (23:01 +0000)
committertimoore <timoore>
Sat, 2 Feb 2008 23:01:05 +0000 (23:01 +0000)
Stuart's new file SGTreeBin.hxx has been split into 4 files:
TreeBin.[ch]xx and ShaderGeometry.[ch]xx.

13 files changed:
simgear/scene/material/mat.cxx
simgear/scene/material/mat.hxx
simgear/scene/material/matmodel.cxx
simgear/scene/material/matmodel.hxx
simgear/scene/tgdb/Makefile.am
simgear/scene/tgdb/SGModelBin.hxx
simgear/scene/tgdb/ShaderGeometry.cxx [new file with mode: 0644]
simgear/scene/tgdb/ShaderGeometry.hxx [new file with mode: 0644]
simgear/scene/tgdb/TreeBin.cxx [new file with mode: 0644]
simgear/scene/tgdb/TreeBin.hxx [new file with mode: 0644]
simgear/scene/tgdb/obj.cxx
simgear/scene/util/QuadTreeBuilder.cxx
simgear/scene/util/QuadTreeBuilder.hxx

index 7e590d63e002154d9672cdbea25900a27f4e1346..4257ae2a6a470afb7cd8c88f72653d293698b3f8 100644 (file)
@@ -141,6 +141,10 @@ SGMaterial::read_properties( const string &fg_root, const SGPropertyNode * props
   wrapv = props->getBoolValue("wrapv", true);
   mipmap = props->getBoolValue("mipmap", true);
   light_coverage = props->getDoubleValue("light-coverage", 0.0);
+  tree_coverage = props->getDoubleValue("tree-coverage", 0.0);
+  tree_height = props->getDoubleValue("tree-height-m", 0.0);
+  tree_width = props->getDoubleValue("tree-width-m", 0.0);
+  tree_range = props->getDoubleValue("tree-range-m", 0.0);
 
   // surface values for use with ground reactions
   solid = props->getBoolValue("solid", true);
@@ -177,6 +181,14 @@ SGMaterial::read_properties( const string &fg_root, const SGPropertyNode * props
   for (unsigned int i = 0; i < object_group_nodes.size(); i++)
     object_groups.push_back(new SGMatModelGroup(object_group_nodes[i]));
 
+  vector<SGPropertyNode_ptr> tree_texture_nodes =
+    ((SGPropertyNode *)props)->getChildren("tree-texture");
+  for (unsigned int i = 0; i < tree_texture_nodes.size(); i++) {
+    SGPath tpath( fg_root );
+    tpath.append(tree_texture_nodes[i]->getStringValue());
+    tree_textures.push_back(tpath.str());
+  }
+
   // read glyph table for taxi-/runway-signs
   vector<SGPropertyNode_ptr> glyph_nodes = props->getChildren("glyph");
   for (unsigned int i = 0; i < glyph_nodes.size(); i++) {
index 0a0fbd13289c7781140497307ddad3fdf20b7393..38cc7c95cc3992d99742bc884b9bb3b3b95ffeaa 100644 (file)
@@ -142,10 +142,47 @@ public:
    *
    * A smaller number means more generated night lighting.
    *
-   * @return The area (m^2?) covered by each light.
+   * @return The area (m^2) covered by each light.
    */
   inline double get_light_coverage () const { return light_coverage; }
 
+  /**
+   * Get the forest coverage.
+   *
+   * A smaller number means more generated forest canopy.
+   *
+   * @return The area (m^2) covered by each canopy.
+   */
+  inline double get_tree_coverage () const { return tree_coverage; }
+
+  /**
+   * Get the forest height.
+   *
+   * @return The average height of the trees.
+   */
+  inline double get_tree_height () const { return tree_height; }
+
+  /**
+   * Get the forest width.
+   *
+   * @return The average width of the trees.
+   */
+  inline double get_tree_width () const { return tree_width; }
+
+  /**
+   * Get the forest LoD range.
+   *
+   * @return The LoD range for the trees.
+   */
+  inline double get_tree_range () const { return tree_range; }
+  
+  /**
+   * Get the list of textures to use for trees in the forest
+   *
+   * @return the vector of forest textures to use.
+   */
+  inline vector<string> get_tree_textures () const { return  tree_textures; }
+
   /**
    * Return if the surface material is solid, if it is not solid, a fluid
    * can be assumed, that is usually water.
@@ -259,6 +296,18 @@ private:
 
   // coverage of night lighting.
   double light_coverage;
+  
+  // coverage of trees
+  double tree_coverage;
+  
+  // Range at which trees become visible
+  double tree_range;
+
+  // Height of the tree
+  double tree_height;
+
+  // Width of the tree
+  double tree_width;
 
   // True if the material is solid, false if it is a fluid
   bool solid;
@@ -286,7 +335,9 @@ private:
 
   // taxiway-/runway-sign texture elements
   map<string, SGSharedPtr<SGMaterialGlyph> > glyphs;
-
+  
+  // The list of forest textures, used when creating trees
+  vector<string> tree_textures;
 \f
   ////////////////////////////////////////////////////////////////////
   // Internal constructors and methods.
index 3d58d18ebba003be7a27e5158d110d0a24f34a6b..7af83c7595e72b8dd63cb4932fce2bd4e519f871 100644 (file)
@@ -142,33 +142,6 @@ SGMatModel::load_models ( SGModelLib *modellib,
         // the billboarding should be handled
         // there).
         
-        // Create multiple LoD nodes so instead of all objects
-        // of the same type appearing at once, some appear further
-        // away.
-        //
-        // Very basic hardcoded distribution:
-        // 4 at normal range
-        // 2 at 1.5 times normal range
-        // 1 at 2 time normal range.
-        //
-        // We achieve this by creating the three different LoD
-        // nodes and adding them to the _models list multiple times.
-        
-        osg::LOD * lod1 = new osg::LOD;
-        lod1->setName("Model LOD");
-        lod1->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
-        lod1->setRange(0, 0, _range_m);
-        
-        osg::LOD * lod15 = new osg::LOD;
-        lod15->setName("Model LOD - 1.5");
-        lod15->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
-        lod15->setRange(0, 0, 1.5 * _range_m);
-
-        osg::LOD * lod2 = new osg::LOD;
-        lod2->setName("Model LOD - 2.0");
-        lod2->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
-        lod2->setRange(0, 0, 2.0 * _range_m);
-        
         if (_heading_type == HEADING_BILLBOARD) {
           // if the model is a billboard, it is likely :
           // 1. a branch with only leaves,
@@ -180,27 +153,10 @@ SGMatModel::load_models ( SGModelLib *modellib,
           stateSet->setAttributeAndModes(alphaFunc,
                                          osg::StateAttribute::OVERRIDE);
           stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
-          
-             lod1->addChild(entity);
-             lod15->addChild(entity);
-             lod2->addChild(entity);
-        } else {
-          lod1->addChild(entity);
-          lod15->addChild(entity);
-          lod2->addChild(entity);
-        }
-      
-        // Vary the distribution of LoDs by adding multiple times.
-        _models.push_back(lod1);
-        _models.push_back(lod1);
-        _models.push_back(lod1);
-        _models.push_back(lod1);
-
-        _models.push_back(lod15);
-        _models.push_back(lod15);
-
-        _models.push_back(lod2);
-
+        } 
+        
+        _models.push_back(entity);
+        
       } else {
         SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
       }
@@ -240,6 +196,24 @@ SGMatModel::get_coverage_m2 () const
   return _coverage_m2;
 }
 
+double SGMatModel::get_range_m() const
+{
+  return _range_m;
+}
+
+double SGMatModel::get_randomized_range_m(mt* seed) const
+{
+  double lrand = mt_rand(seed);
+  
+  // Note that the LoD is not completely randomized.
+  // 10% at 2   * range_m
+  // 30% at 1.5 * range_m
+  // 60% at 1   * range_m
+  if (lrand < 0.1) return 2   * _range_m;
+  if (lrand < 0.4) return 1.5 * _range_m;
+  else return _range_m;
+}
+
 SGMatModel::HeadingType
 SGMatModel::get_heading_type () const
 {
index f173f5c0a309ae9636659a63e1820f2c89a90899..982e89a95293015d6183071865df50ce5309d7ba 100644 (file)
@@ -40,6 +40,7 @@
 #include <simgear/structure/SGReferenced.hxx>
 #include <simgear/structure/SGSharedPtr.hxx>
 #include <simgear/props/props.hxx>
+#include <simgear/math/sg_random.h>
 
 SG_USING_STD(string);
 
@@ -112,6 +113,19 @@ public:
      */
     double get_coverage_m2 () const;
 
+    /**
+     * Get the visual range of the object in meters.
+     *
+     * @return The visual range.
+     */
+    double get_range_m () const;
+    
+    /**
+     * Get a randomized visual range
+     *
+     * @return a randomized visual range
+     */    
+    double get_randomized_range_m(mt* seed) const;    
 
     /**
      * Get the heading type for the object.
index 6eba2549a029f0875e07141ce6e4399687f51806..7c0a7fa3243518102cbed1548701867c89fca99f 100644 (file)
@@ -18,7 +18,8 @@ include_HEADERS = \
        SGTexturedTriangleBin.hxx \
        SGTriangleBin.hxx \
        SGVertexArrayBin.hxx \
-       GroundLightManager.hxx
+       GroundLightManager.hxx \
+       ShaderGeometry.hxx
 
 libsgtgdb_a_SOURCES = \
        apt_signs.cxx \
@@ -28,6 +29,8 @@ libsgtgdb_a_SOURCES = \
        SGOceanTile.cxx \
        SGReaderWriterBTG.cxx \
        SGVasiDrawable.cxx \
-       GroundLightManager.cxx
+       GroundLightManager.cxx \
+       ShaderGeometry.cxx \
+       TreeBin.cxx
 
 INCLUDES = -I$(top_srcdir)
index 1c8514187351ab839959a172d1a726183f8403da..bd61a948a6680e307422ad187e3a1028ca946cde 100755 (executable)
 class SGMatModelBin {
 public:
   struct MatModel {
-    MatModel(const SGVec3f& p, SGMatModel *m) :
-      position(p), model(m)
+    MatModel(const SGVec3f& p, SGMatModel *m, int l) :
+      position(p), model(m), lod(l)
     { }
     SGVec3f position;
     SGMatModel *model;
+    int lod;
   };
   typedef std::vector<MatModel> MatModelList;
 
   void insert(const MatModel& model)
   { 
-    float x = model.position.x();
-    float y = model.position.y();
-    float z = model.position.z();
-  
-    if (_models.size() == 0) 
-    {
-      min_x = x;
-      max_x = x;
-      
-      min_y = y;
-      max_y = y;
-      
-      min_z = z;
-      max_z = z;
-    }
-    else
-    {      
-      min_x = SGMisc<float>::min(min_x, x);
-      max_x = SGMisc<float>::max(max_x, x);
-
-      min_y = SGMisc<float>::min(min_y, y);
-      max_y = SGMisc<float>::max(max_y, y);
-
-      min_z = SGMisc<float>::min(min_z, z);
-      max_z = SGMisc<float>::max(max_z, z);
-    }
-    
     _models.push_back(model);   
   }
   
-  void insert(const SGVec3f& p, SGMatModel *m)
-  { insert(MatModel(p, m)); }
+  void insert(const SGVec3f& p, SGMatModel *m, int l)
+  { insert(MatModel(p, m, l)); }
 
   unsigned getNumModels() const
   { return _models.size(); }
@@ -77,12 +51,6 @@ public:
   
 private:
   MatModelList _models;
-  float min_x;
-  float max_x;
-  float min_y;
-  float max_y;
-  float min_z;
-  float max_z;
 };
 
 #endif
diff --git a/simgear/scene/tgdb/ShaderGeometry.cxx b/simgear/scene/tgdb/ShaderGeometry.cxx
new file mode 100644 (file)
index 0000000..d98b5b6
--- /dev/null
@@ -0,0 +1,32 @@
+#include "ShaderGeometry.hxx"
+
+using namespace osg;
+
+namespace simgear
+{
+void ShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
+{
+    for(PositionSizeList::const_iterator itr = _trees.begin();
+        itr != _trees.end();
+        ++itr) {
+        glColor4fv(itr->ptr());
+        _geometry->draw(renderInfo);
+    }
+}
+
+BoundingBox ShaderGeometry::computeBound() const
+{
+    BoundingBox geom_box = _geometry->getBound();
+    BoundingBox bb;
+    for(PositionSizeList::const_iterator itr = _trees.begin();
+        itr != _trees.end();
+        ++itr) {
+        bb.expandBy(geom_box.corner(0)*(*itr)[3] +
+                    Vec3((*itr)[0], (*itr)[1], (*itr)[2]));
+        bb.expandBy(geom_box.corner(7)*(*itr)[3] +
+                    Vec3((*itr)[0], (*itr)[1], (*itr)[2]));
+    }
+    return bb;
+}
+
+}
diff --git a/simgear/scene/tgdb/ShaderGeometry.hxx b/simgear/scene/tgdb/ShaderGeometry.hxx
new file mode 100644 (file)
index 0000000..78eb1d4
--- /dev/null
@@ -0,0 +1,79 @@
+/* -*-c++-*-
+ *
+ * Copyright (C) 2008 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 SHADER_GEOMETRY_HXX
+#define SHADER_GEOMETRY_HXX 1
+
+#include <vector>
+
+#include <osg/BoundingBox>
+#include <osg/CopyOp>
+#include <osg/Drawable>
+#include <osg/Geometry>
+#include <osg/RenderInfo>
+#include <osg/Vec3>
+#include <osg/Vec4>
+
+namespace simgear
+{
+
+class ShaderGeometry : public osg::Drawable
+{
+    public:
+        ShaderGeometry()
+        { 
+          setUseDisplayList(false); 
+        }
+        
+        /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
+        ShaderGeometry(const ShaderGeometry& ShaderGeometry,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):
+            osg::Drawable(ShaderGeometry,copyop) {}
+
+        META_Object(osg,ShaderGeometry);
+        
+        typedef std::vector<osg::Vec4> PositionSizeList;
+        
+        virtual void drawImplementation(osg::RenderInfo& renderInfo) const;
+        virtual osg::BoundingBox computeBound() const;
+    
+        
+        void setGeometry(osg::Geometry* geometry)
+        {
+            _geometry = geometry;
+        }
+        
+    void addTree(const osg::Vec3& position, float scale)
+        {
+            _trees.push_back(osg::Vec4(position, scale));
+        }
+        
+        osg::ref_ptr<osg::Geometry> _geometry;
+
+        PositionSizeList _trees;
+
+    protected:
+    
+        virtual ~ShaderGeometry() {}
+        
+};
+
+}
+#endif
diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx
new file mode 100644 (file)
index 0000000..dfad0dc
--- /dev/null
@@ -0,0 +1,244 @@
+/* -*-c++-*-
+ *
+ * Copyright (C) 2008 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.
+ *
+ */
+
+#include <osg/AlphaFunc>
+#include <osg/Billboard>
+#include <osg/BlendFunc>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Material>
+#include <osg/Math>
+#include <osg/MatrixTransform>
+#include <osg/Matrix>
+#include <osg/StateSet>
+#include <osg/Texture2D>
+#include <osg/TexEnv>
+
+#include <osgDB/ReadFile>
+#include <osgDB/FileUtils>
+
+#include <simgear/misc/sg_path.hxx>
+
+#include "ShaderGeometry.hxx"
+#include "TreeBin.hxx"
+
+#define SG_TREE_QUAD_TREE_SIZE 32
+
+namespace simgear
+{
+
+osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate)
+{
+
+    //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f),
+    // set up the coords
+    osg::Vec3Array& v = *(new osg::Vec3Array(8));
+    osg::Vec2Array& t = *(new osg::Vec2Array(8));
+    
+    /*
+    float rotation = 0.0f;
+    float sw = sinf(rotation)*w*0.5f;
+    float cw = cosf(rotation)*w*0.5f;
+
+    v[0].set(pos.x()-sw,pos.y()-cw,pos.z()+0.0f);
+    v[1].set(pos.x()+sw,pos.y()+cw,pos.z()+0.0f);
+    v[2].set(pos.x()+sw,pos.y()+cw,pos.z()+h);
+    v[3].set(pos.x()-sw,pos.y()-cw,pos.z()+h);
+
+    v[4].set(pos.x()-cw,pos.y()+sw,pos.z()+0.0f);
+    v[5].set(pos.x()+cw,pos.y()-sw,pos.z()+0.0f);
+    v[6].set(pos.x()+cw,pos.y()-sw,pos.z()+h);
+    v[7].set(pos.x()-cw,pos.y()+sw,pos.z()+h);
+    */
+    float cw = w*0.5f;
+
+    v[0].set(0.0f,-cw,0.0f);
+    v[1].set(0.0f, cw,0.0f);
+    v[2].set(0.0f, cw,h);
+    v[3].set(0.0f,-cw,h);
+
+    v[4].set(-cw,0.0f,0.0f);
+    v[5].set( cw,0.0f,0.0f);
+    v[6].set( cw,0.0f,h);
+    v[7].set(-cw,0.0f,h);
+
+    t[0].set(0.0f,0.0f);
+    t[1].set(1.0f,0.0f);
+    t[2].set(1.0f,1.0f);
+    t[3].set(0.0f,1.0f);
+
+    t[4].set(0.0f,0.0f);
+    t[5].set(1.0f,0.0f);
+    t[6].set(1.0f,1.0f);
+    t[7].set(0.0f,1.0f);
+    
+    for (unsigned int i = 0; i < 8; i++)
+    {
+      v[i] = v[i] * rotate;      
+    }
+
+    osg::Geometry *geom = new osg::Geometry;
+
+    geom->setVertexArray( &v );
+
+    geom->setTexCoordArray( 0, &t );
+
+    geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) );
+
+    return geom;
+}
+
+osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform)
+{
+    // Set up some shared structures. 
+    // FIXME: Currently we only take the texture, height and width of the first tree in the forest. In the future
+    // we should be able to handle multiple textures etc.    
+    TreeBin::Tree firstTree = forest.getTree(0);
+    
+    osg::Geometry* shared_geometry = createOrthQuads(firstTree.width, 
+                                                     firstTree.height, 
+                                                     transform);
+    osg::Group* group = new osg::Group;
+
+    osg::Texture2D *tex = new osg::Texture2D;
+    tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
+    tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
+    tex->setImage(osgDB::readImageFile(firstTree.texture));
+
+    osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
+    alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.05f);
+
+    osg::StateSet *dstate = new osg::StateSet;
+    dstate->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
+    dstate->setTextureAttribute(0, new osg::TexEnv );
+    dstate->setAttributeAndModes( new osg::BlendFunc, osg::StateAttribute::ON );
+    dstate->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON );
+    dstate->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
+    dstate->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
+    
+    osg::StateSet* stateset = new osg::StateSet;
+    stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
+    stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
+
+    osg::Program* program = new osg::Program;
+    stateset->setAttribute(program);
+    osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
+    stateset->addUniform(baseTextureSampler);
+
+    /*
+     * FIXME: Currently, calculating the diffuse term results in a bad 
+     * "flickering" and a tendency of the diffuse term be either 
+     * 0.0 of 1.0. Hence, it has been commented out in the shader below.
+     * I (Stuart) suspect it may be because the light is so distant that
+     * we're seeing floating point representation issues.
+     */
+    char vertexShaderSource[] = 
+//        "varying vec3 N;\n"\r
+//        "varying vec3 v;\n"
+        "varying vec2 texcoord;\n"
+        "varying float fogFactor;\n"
+        "\n"
+        "void main(void)\n"
+        "{\n"
+//        "  v = vec3(gl_ModelViewMatrix * gl_Vertex);\n"\r
+//        "  N = normalize(gl_NormalMatrix * gl_Normal);\n"\r
+        "  texcoord = gl_MultiTexCoord0.st;\n"        
+        "  vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n"
+        "  gl_Position   = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
+           "  const float LOG2 = 1.442695;\n"\r
+           "  gl_FogFragCoord = gl_Position.z;\n"\r
+           "  fogFactor = exp2( -gl_Fog.density * gl_Fog.density * gl_FogFragCoord * gl_FogFragCoord * LOG2 );\n"\r
+           "  fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
+        "}\n";
+
+    char fragmentShaderSource[] = 
+        "uniform sampler2D baseTexture; \n"
+//        "varying vec3 N;\n"\r
+//        "varying vec3 v;\n"
+        "varying vec2 texcoord;\n"
+        "varying float fogFactor;\n"
+        "\n"
+        "void main(void) \n"
+        "{ \n"
+        "  vec4 base = texture2D( baseTexture, texcoord);\n"
+//        "  vec3 L = normalize(gl_LightSource[0].position.xyz);\n"
+//        "  vec4 vDiffuse = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);\n"
+//        "  vDiffuse = sqrt(clamp(vDiffuse, 0.0, 1.0));\n"
+//        "  vec4 vAmbient = gl_FrontLightProduct[0].ambient;\n"
+//        "  vec4 finalColor = base * (vAmbient + vDiffuse);\n"
+          "  vec4 finalColor = base * gl_FrontLightProduct[0].diffuse;\n"
+          "  gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n"
+        "}\n";
+
+    osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertexShaderSource);
+    program->addShader(vertex_shader);
+    
+    osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource);
+    program->addShader(fragment_shader);
+   
+    // Now, create a quadtree for the forest.
+    osg::ref_ptr<osg::Group> _root;
+    ShaderGeometry* leaves[SG_TREE_QUAD_TREE_SIZE][SG_TREE_QUAD_TREE_SIZE];
+
+    // Determine the extents of the tree, and a list of the required textures for later.    
+    osg::BoundingBox extents;
+    for (unsigned int i = 0; i < forest.getNumTrees(); i++)
+    {    
+        const osg::Vec3f center = forest.getTree(i).position.osg() * transform;
+        extents.expandBy(center);
+    }
+    
+    const osg::Vec2 quadMin(extents.xMin(), extents.yMin());
+    const osg::Vec2 quadMax(extents.xMax(), extents.yMax());
+
+    for (int i = 0; i < SG_TREE_QUAD_TREE_SIZE; ++i) {
+      osg::LOD* interior = new osg::LOD;      
+      //osg::Group* interior = new osg::Group;
+      group->addChild(interior);
+      for (int j = 0; j < SG_TREE_QUAD_TREE_SIZE; ++j) {
+        osg::Geode* geode = new osg::Geode;
+        leaves[i][j] = new ShaderGeometry();
+        leaves[i][j]->setGeometry(shared_geometry);
+        geode->setStateSet(stateset);
+        geode->addDrawable(leaves[i][j]);
+        interior->addChild(geode, 0, firstTree.range);        
+      }
+    }
+    
+    // Now we've got our quadtree, add the trees based on location.
+    
+    for (unsigned int i = 0; i < forest.getNumTrees(); i++)
+    {    
+      TreeBin::Tree t = forest.getTree(i);
+      osg::Vec3 center = t.position.osg() * transform;
+
+      int x = (int)(SG_TREE_QUAD_TREE_SIZE * (center.x() - quadMin.x()) / (quadMax.x() - quadMin.x()));
+      x = osg::clampTo(x, 0, (SG_TREE_QUAD_TREE_SIZE - 1));
+      int y = (int)(SG_TREE_QUAD_TREE_SIZE * (center.y() - quadMin.y()) / (quadMax.y() - quadMin.y()));
+      y = osg::clampTo(y, 0, (SG_TREE_QUAD_TREE_SIZE -1));
+    
+      leaves[y][x]->addTree(t.position.osg(), t.height);
+    }
+    
+    return group;
+}
+
+}
diff --git a/simgear/scene/tgdb/TreeBin.hxx b/simgear/scene/tgdb/TreeBin.hxx
new file mode 100644 (file)
index 0000000..48a327f
--- /dev/null
@@ -0,0 +1,66 @@
+/* -*-c++-*-
+ *
+ * Copyright (C) 2008 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 TREE_BIN_HXX
+#define TREE_BIN_HXX
+
+#include <vector>
+
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/Matrix>
+
+#include <simgear/math/SGVec3.hxx>
+
+namespace simgear
+{
+class TreeBin {
+public:
+    struct Tree {
+        Tree(const SGVec3f& p, string t, float h, float w, double r) :
+            position(p), texture(t), height(h), width(w), range(r)
+        { }
+        SGVec3f position;
+        string texture;
+        float height;
+        float width;
+        double range;
+    };
+    typedef std::vector<Tree> TreeList;
+
+    void insert(const Tree& t)
+    { _trees.push_back(t); }
+    void insert(const SGVec3f& p, string t, float h, float w, double r)
+    { insert(Tree(p, t, h, w, r)); }
+
+    unsigned getNumTrees() const
+    { return _trees.size(); }
+    const Tree& getTree(unsigned i) const
+    { return _trees[i]; }
+
+private:
+    TreeList _trees;
+};
+
+osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate);
+osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform);
+}
+#endif
index 882751373f32888b0e24e53adc93fa55160c1d95..5fd630d0ac0724df38cb879a83451a9953bb4a33 100644 (file)
@@ -56,6 +56,7 @@
 #include "SGTexturedTriangleBin.hxx"
 #include "SGLightBin.hxx"
 #include "SGModelBin.hxx"
+#include "TreeBin.hxx"
 #include "SGDirectionalLightBin.hxx"
 #include "GroundLightManager.hxx"
 
@@ -73,6 +74,7 @@ struct SGTileGeometryBin {
   SGMaterialTriangleMap materialTriangleMap;
   SGLightBin tileLights;
   SGLightBin randomTileLights;
+  TreeBin randomForest;
   SGDirectionalLightBin runwayLights;
   SGDirectionalLightBin taxiLights;
   SGDirectionalLightListBin vasiLights;
@@ -80,7 +82,7 @@ struct SGTileGeometryBin {
   SGLightListBin odalLights;
   SGDirectionalLightListBin reilLights;
   SGMatModelBin randomModels;
-  
+
   static SGVec4f
   getMaterialLightColor(const SGMaterial* material)
   {
@@ -448,10 +450,45 @@ struct SGTileGeometryBin {
       }
     }
   }
+
+  void computeRandomForest(SGMaterialLib* matlib)
+  {
+    SGMaterialTriangleMap::iterator i;
+        
+    // generate a repeatable random seed
+    mt seed;
+    mt_init(&seed, unsigned(586));
+    
+    for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+      SGMaterial *mat = matlib->find(i->first);
+      if (!mat)
+        continue;
+
+      float coverage = mat->get_tree_coverage();
+      if (coverage <= 0)
+        continue;
+        
+      vector<string> textures = mat->get_tree_textures();
+      
+      std::vector<SGVec3f> randomPoints;
+      i->second.addRandomSurfacePoints(coverage, 0, randomPoints);
+      std::vector<SGVec3f>::iterator j;
+      for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {      
+        int k = mt_rand(&seed) * textures.size();        
+        if (k == textures.size()) k--;         
+        randomForest.insert(*j, textures[k], mat->get_tree_height(), mat->get_tree_width(), mat->get_tree_range());
+      }
+    }
+  }
   
   void computeRandomObjects(SGMaterialLib* matlib)
   {
     SGMaterialTriangleMap::iterator i;
+    
+    // generate a repeatable random seed
+    mt seed;
+    mt_init(&seed, unsigned(123));
+
     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
       SGMaterial *mat = matlib->find(i->first);
       if (!mat)
@@ -478,7 +515,7 @@ struct SGTileGeometryBin {
               i->second.addRandomPoints(object->get_coverage_m2(), randomPoints);
               std::vector<SGVec3f>::iterator l;
               for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
-                randomModels.insert(*l, object);
+                randomModels.insert(*l, object, object->get_randomized_range_m(&seed));
               }
             }
           }
@@ -517,13 +554,22 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
 
   osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
   osg::ref_ptr<osg::Group> randomObjects;
+  osg::ref_ptr<osg::Group> randomForest;
   osg::Group* terrainGroup = new osg::Group;
-
+  
   osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
   if (node)
     terrainGroup->addChild(node);
     
   if (use_random_objects) {
+  
+    // Simple matrix for used for flipping models that have been oriented
+    // with the center of the tile but upside down.
+    static const osg::Matrix flip(1,  0,  0, 0,
+                                  0, -1,  0, 0,
+                                  0,  0, -1, 0,
+                                  0,  0,  0, 1);     
+  
     tileGeometryBin.computeRandomObjects(matlib);
     
     if (tileGeometryBin.randomModels.getNumModels() > 0) {
@@ -536,12 +582,9 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
       // based on the centre of the tile, as the small angular differences  
       // between different points on the tile aren't worth worrying about 
       // for random objects. We also need to flip the orientation 180 degrees
-      static const osg::Matrix flip(1,  0,  0, 0,
-                                    0, -1,  0, 0,
-                                    0,  0, -1, 0,
-                                    0,  0,  0, 1);     
       osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg());
-      std::vector<osg::ref_ptr<osg::Node> > models;
+      
+      LodMap models;
       
       for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) {
         SGMatModelBin::MatModel obj = tileGeometryBin.randomModels.getMatModel(i);
@@ -566,12 +609,21 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
         }
 
         position->addChild(node);        
-        models.push_back(position);
-        // Add to the leaf of the quadtree based on object location.
+        models.insert(std::pair<osg::ref_ptr<osg::Node>,int>(position, obj.lod));
       }
+      
       randomObjects = QuadTreeBuilder::makeQuadTree(models, world2Tile);
       randomObjects->setName("random objects");
     }
+      
+    // Now add some random forest.
+    tileGeometryBin.computeRandomForest(matlib);
+    
+    if (tileGeometryBin.randomForest.getNumTrees() > 0) {
+      osg::Matrix forAtt = flip * world2Tile;
+      randomForest = createForest(tileGeometryBin.randomForest, forAtt);
+      randomForest->setName("random trees");      
+    } 
   }
 
   if (calc_lights) {
@@ -606,7 +658,7 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
       lightGroup->addChild(groundLights2);
     }
   }
-
+    
   if (!tileGeometryBin.vasiLights.empty()) {
     SGVec4f red(1, 0, 0, 1);
     SGMaterial* mat = matlib->find("RWY_RED_LIGHTS");
@@ -689,12 +741,15 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
     transform->addChild(lightLOD);
   }
   
-  if (randomObjects.valid()) {
+  if (randomObjects.valid() || randomForest.valid()) {
   
     // Add a LoD node, so we don't try to display anything when the tile center
     // is more than 20km away.
     osg::LOD* objectLOD = new osg::LOD;
-    objectLOD->addChild(randomObjects.get(), 0, 20000);
+    
+    if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000);
+    if (randomForest.valid())  objectLOD->addChild(randomForest.get(), 0, 20000);
+    
     unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT;
     objectLOD->setNodeMask(nodeMask);
     transform->addChild(objectLOD);
index 4fe230c5b5cbe10c57067c943a30e9c5dc89e7d9..b4dcf0723c3f32e23dc4e3869284bf44bf499d39 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <osg/BoundingBox>
 #include <osg/Math>
+#include <simgear/scene/util/SGNodeMasks.hxx>
 
 #include "QuadTreeBuilder.hxx"
 
@@ -27,35 +28,36 @@ namespace simgear
 QuadTreeBuilder::QuadTreeBuilder(const Vec2& min, const Vec2& max) :
     _root(new osg::Group), _min(min), _max(max)
 {
-    for (int i = 0; i < 4; ++i) {
+    for (int i = 0; i < QUAD_TREE_LEAVES; ++i) {
         Group* interior = new osg::Group;
         _root->addChild(interior);
-        for (int j = 0; j < 4; ++j) {
-            Group* leaf = new osg::Group;
-            interior->addChild(leaf);
-            _leaves[i][j] = leaf;
+        for (int j = 0; j < QUAD_TREE_LEAVES; ++j) {
+            LOD* lod  = new osg::LOD;
+            interior->addChild(lod);
+            _leaves[i][j] = lod;
         }
     }
 }
 
-void QuadTreeBuilder::addNode(Node* node, const Matrix& transform)
+void QuadTreeBuilder::addNode(Node* node, int lod, const Matrix& transform)
 {
     Vec3 center = node->getBound().center() * transform;
 
-    int x = (int)(4.0 * (center.x() - _min.x()) / (_max.x() - _min.x()));
-    x = clampTo(x, 0, 3);
-    int y = (int)(4.0 * (center.y() - _min.y()) / (_max.y() - _min.y()));
-    y = clampTo(y, 0, 3);
-    _leaves[y][x]->addChild(node);
+    int x = (int)(QUAD_TREE_LEAVES * (center.x() - _min.x()) / (_max.x() - _min.x()));
+    x = clampTo(x, 0, (QUAD_TREE_LEAVES - 1));
+    int y = (int)(QUAD_TREE_LEAVES * (center.y() - _min.y()) / (_max.y() - _min.y()));
+    y = clampTo(y, 0, (QUAD_TREE_LEAVES -1));
+    
+    _leaves[y][x]->addChild(node, 0, lod);
 }
 
-osg::Group* QuadTreeBuilder::makeQuadTree(vector<ref_ptr<Node> >& nodes,
+osg::Group* QuadTreeBuilder::makeQuadTree(LodMap& models,
                                           const Matrix& transform)
 {
     typedef vector<ref_ptr<Node> > NodeList;
     BoundingBox extents;
-    for (NodeList::iterator iter = nodes.begin(); iter != nodes.end(); ++iter) {
-        const Vec3 center = (*iter)->getBound().center() * transform;
+    for (LodMap::iterator iter = models.begin(); iter != models.end(); ++iter) {
+        const Vec3 center = (*iter).first->getBound().center() * transform;
         extents.expandBy(center);
     }
     const Vec2 quadMin(extents.xMin(), extents.yMin());
@@ -63,10 +65,10 @@ osg::Group* QuadTreeBuilder::makeQuadTree(vector<ref_ptr<Node> >& nodes,
     ref_ptr<Group> result;
     {
         QuadTreeBuilder quadTree(quadMin, quadMax);
-        for (NodeList::iterator iter = nodes.begin();
-             iter != nodes.end();
+        for (LodMap::iterator iter = models.begin();
+             iter != models.end();
              ++iter) {
-            quadTree.addNode(iter->get(), transform);
+            quadTree.addNode(iter->first.get(), iter->second, transform);
         }
         result = quadTree.getRoot();
     }
index 6a0e2dd1992f38a4146cc8c16bb72245527078bb..ff16fb46f1da543f4bbab6a0346fb43d07961161 100644 (file)
 #include <osg/Group>
 #include <osg/Matrix>
 #include <osg/Vec2>
+#include <osg/LOD>
+
+#define QUAD_TREE_LEAVES 4
 
 namespace simgear
 {
+typedef std::map<osg::ref_ptr<osg::Node>,int> LodMap;
+
 // Create a quad tree based on x, y extents
 class QuadTreeBuilder {
 public:
     QuadTreeBuilder(const osg::Vec2& min, const osg::Vec2& max);
     ~QuadTreeBuilder() {}
     osg::Group* getRoot() { return _root.get(); }
-    // Add node to the quadtree using its x, y
-    void addNode(osg::Node* node, const osg::Matrix& transform);
-    // Make a quadtree of nodes from a vector of nodes
-    static osg::Group* makeQuadTree(std::vector<osg::ref_ptr<osg::Node> >& nodes,
+    // Add node to the quadtree using its x, y and LoD
+    void addNode(osg::Node* node, int lod, const osg::Matrix& transform);
+    // Make a quadtree of nodes from a map of nodes and LOD values
+    static osg::Group* makeQuadTree(LodMap& nodes,
                                     const osg::Matrix& transform);
 protected:
     osg::ref_ptr<osg::Group> _root;
-    osg::Group* _leaves[4][4];
+    osg::LOD* _leaves[QUAD_TREE_LEAVES][QUAD_TREE_LEAVES];
     osg::Vec2 _min;
     osg::Vec2 _max;
 };