]> git.mxchange.org Git - simgear.git/commitdiff
Random object support from Stuart Buchanan
authortimoore <timoore>
Sun, 6 Jan 2008 15:04:43 +0000 (15:04 +0000)
committertimoore <timoore>
Sun, 6 Jan 2008 15:04:43 +0000 (15:04 +0000)
In addition to Stuart's changes, there's an independent quad tree
builder class for constructing loose quad trees from scene graph nodes.

Stuart also implemented changes to the random number generator suggested by
Andy Ross.

12 files changed:
simgear/math/sg_random.c
simgear/math/sg_random.h
simgear/scene/material/matmodel.cxx
simgear/scene/material/matmodel.hxx
simgear/scene/tgdb/SGModelBin.hxx [new file with mode: 0755]
simgear/scene/tgdb/SGTexturedTriangleBin.hxx
simgear/scene/tgdb/obj.cxx
simgear/scene/tgdb/userdata.cxx
simgear/scene/tgdb/userdata.hxx
simgear/scene/util/Makefile.am
simgear/scene/util/QuadTreeBuilder.cxx [new file with mode: 0644]
simgear/scene/util/QuadTreeBuilder.hxx [new file with mode: 0644]

index b58cf9387f861650390d3087060295ecda862a28..e9004323b2fa93f7d5744005fa71abefb9b70088 100644 (file)
 // $Id$
 
 
-/* 
-   A C-program for MT19937, with initialization improved 2002/2/10.
-   Coded by Takuji Nishimura and Makoto Matsumoto.
-   This is a faster version by taking Shawn Cokus's optimization,
-   Matthe Bellew's simplification, Isaku Wada's real version.
-
-   Before using, initialize the state by using init_genrand(seed) 
-   or init_by_array(init_key, key_length).
-
-   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
-   All rights reserved.                          
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions
-   are met:
-
-     1. Redistributions of source code must retain the above copyright
-        notice, this list of conditions and the following disclaimer.
-
-     2. Redistributions in binary form must reproduce the above copyright
-        notice, this list of conditions and the following disclaimer in the
-        documentation and/or other materials provided with the distribution.
-
-     3. The names of its contributors may not be used to endorse or promote 
-        products derived from this software without specific prior written 
-        permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-   Any feedback is very welcome.
-   http://www.math.keio.ac.jp/matumoto/emt.html
-   email: matumoto@math.keio.ac.jp
-*/
+/*
+ * "Cleaned up" and simplified Mersenne Twister implementation.
+ * Vastly smaller and more easily understood and embedded.  Stores the
+ * state in a user-maintained structure instead of static memory, so
+ * you can have more than one, or save snapshots of the RNG state.
+ * Lacks the "init_by_array()" feature of the original code in favor
+ * of the simpler 32 bit seed initialization.  Lacks the floating
+ * point generator, which is an orthogonal problem not related to
+ * random number generation.  Verified to be identical to the original
+ * MT199367ar code through the first 10M generated numbers.
+ */
+
+
 
 #ifdef HAVE_CONFIG_H
 #  include <simgear_config.h>
 
 #include "sg_random.h"
 
-/* Period parameters */  
-#define N 624
-#define M 397
-#define MATRIX_A 0x9908b0dfUL   /* constant vector a */
-#define UMASK 0x80000000UL /* most significant w-r bits */
-#define LMASK 0x7fffffffUL /* least significant r bits */
-#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) )
-#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL))
-
-static unsigned long state[N]; /* the array for the state vector  */
-static int left = 1;
-static int initf = 0;
-static unsigned long *next;
-
-/* initializes state[N] with a seed */
-void init_genrand(unsigned long s)
+// Structure for the random number functions.
+mt random_seed;
+
+#define MT(i) mt->array[i]
+
+void mt_init(mt *mt, unsigned int seed)
 {
-    int j;
-    state[0]= s & 0xffffffffUL;
-    for (j=1; j<N; j++) {
-        state[j] = (1812433253UL * (state[j-1] ^ (state[j-1] >> 30)) + j); 
-        /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
-        /* In the previous versions, MSBs of the seed affect   */
-        /* only MSBs of the array state[].                        */
-        /* 2002/01/09 modified by Makoto Matsumoto             */
-        state[j] &= 0xffffffffUL;  /* for >32 bit machines */
-    }
-    left = 1; initf = 1;
+    int i;
+    MT(0)= seed;
+    for(i=1; i<MT_N; i++)
+        MT(i) = (1812433253 * (MT(i-1) ^ (MT(i-1) >> 30)) + i);
+    mt->index = MT_N+1;
 }
 
-static void next_state(void)
+unsigned int mt_rand32(mt *mt)
 {
-    unsigned long *p=state;
-    int j;
-
-    /* if init_genrand() has not been called, */
-    /* a default initial seed is used         */
-    if (initf==0) init_genrand(5489UL);
-
-    left = N;
-    next = state;
-
-    for (j=N-M+1; --j; p++) 
-        *p = p[M] ^ TWIST(p[0], p[1]);
-
-    for (j=M; --j; p++) 
-        *p = p[M-N] ^ TWIST(p[0], p[1]);
+    unsigned int i, y;
+    if(mt->index >= MT_N) {
+        for(i=0; i<MT_N; i++) {
+            y = (MT(i) & 0x80000000) | (MT((i+1)%MT_N) & 0x7fffffff);
+            MT(i) = MT((i+MT_M)%MT_N) ^ (y>>1) ^ (y&1 ? 0x9908b0df : 0);
+        }
+        mt->index = 0;
+    }
+    y = MT(mt->index++);
+    y ^= (y >> 11);
+    y ^= (y << 7) & 0x9d2c5680;
+    y ^= (y << 15) & 0xefc60000;
+    y ^= (y >> 18);
+    return y;
+}
 
-    *p = p[M-N] ^ TWIST(p[0], state[0]);
+double mt_rand(mt *mt)
+{
+    /* divided by 2^32-1 */ 
+    return (double)mt_rand32(mt) * (1.0/4294967295.0); 
 }
 
 // Seed the random number generater with time() so we don't see the
 // same sequence every time
 void sg_srandom_time() {
-    init_genrand(time(NULL));
+    mt_init(&random_seed, (unsigned int) time(NULL));
 }
 
 // Seed the random number generater with time() in 10 minute intervals
 // so we get the same sequence within 10 minutes interval.
 // This is useful for synchronizing two display systems.
 void sg_srandom_time_10() {
-    init_genrand(time(NULL) / 600);
+    mt_init(&random_seed, (unsigned int) time(NULL) / 600);
 }
 
-
 // Seed the random number generater with your own seed so can set up
 // repeatable randomization.
 void sg_srandom( unsigned int seed ) {
-    init_genrand( seed );
+    mt_init(&random_seed, seed);
 }
 
-
 // return a random number between [0.0, 1.0)
 double sg_random() {
-    unsigned long y;
-
-    if (--left == 0)
-               next_state();
-    y = *next++;
-
-    /* Tempering */
-    y ^= (y >> 11);
-    y ^= (y << 7) & 0x9d2c5680UL;
-    y ^= (y << 15) & 0xefc60000UL;
-    y ^= (y >> 18);
-
-    return (double)y * (1.0/4294967295.0); 
-    /* divided by 2^32-1 */ 
+  return mt_rand(&random_seed);
 }
 
-
index e93a9420de484e6b6c375ab463e960df0b8ab134..1fed733d3aa5c683d8903177350ab70a0643c126 100644 (file)
@@ -33,6 +33,31 @@ extern "C" {
 #endif                                   
 
 
+#define MT_N 624
+#define MT_M 397
+
+/**
+ * Structure to hold MT algorithm state to easily allow independant
+ * sets of random numbers with different seeds.
+ */
+struct {unsigned int array[MT_N]; int index; } typedef mt;
+
+/**
+ * Initialize a new MT state with a given seed.
+ */
+void mt_init(mt *mt, unsigned int seed);
+
+/**
+ * Generate a new 32-bit random number based on the given MT state.
+ */
+unsigned int mt_rand32( mt *mt);
+
+/**
+ * Generate a new random number between [0.0, 1.0) based 
+ * on the given MT state.
+ */
+double mt_rand(mt *mt);
 /**
  * Seed the random number generater with time() so we don't see the
  * same sequence every time.
@@ -51,7 +76,7 @@ void sg_srandom_time_10();
  * repeatable randomization.
  * @param seed random number generator seed
  */
-void sg_srandom( unsigned int seed );
+void sg_srandom(unsigned int seed );
 
 /**
  * Return a random number between [0.0, 1.0)
index 789f29ee0867512f14590944b8ac5f86714292bb..3d58d18ebba003be7a27e5158d110d0a24f34a6b 100644 (file)
@@ -137,15 +137,39 @@ SGMatModel::load_models ( SGModelLib *modellib,
                                                 prop_root, sim_time_sec,
                                                 /*cache_object*/ true );
       if (entity != 0) {
-                                // FIXME: this stuff can be handled
-                                // in the XML wrapper as well (at least,
-                                // the billboarding should be handled
-                                // there).
-        osg::LOD * lod = new osg::LOD;
-        lod->setName("Model LOD");
-        lod->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT);
-        lod->setRange(0, 0, _range_m);
-       if (_heading_type == HEADING_BILLBOARD) {
+        // FIXME: this stuff can be handled
+        // in the XML wrapper as well (at least,
+        // 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,
           // 2. a tree or a non rectangular shape faked by transparency
@@ -156,18 +180,29 @@ 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);
 
-          osg::Transform* transform = new osg::Transform;
-          transform->setName("Model Billboard Transform");
-          transform->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
-         transform->addChild(entity);
-         lod->addChild(transform);
-       } else {
-         lod->addChild(entity);
-       }
-       _models.push_back(lod);
       } else {
-       SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
+        SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]);
       }
     }
   }
@@ -255,4 +290,6 @@ SGMatModelGroup::get_object (int index) const
 }
 
 
+
+
 // end of matmodel.cxx
index 85363e80fe4140b0642ec63b3ff57d1c59dd820f..f173f5c0a309ae9636659a63e1820f2c89a90899 100644 (file)
@@ -34,6 +34,8 @@
 
 #include <osg/ref_ptr>
 #include <osg/Node>
+#include <osg/NodeVisitor>
+#include <osg/Billboard>
 
 #include <simgear/structure/SGReferenced.hxx>
 #include <simgear/structure/SGSharedPtr.hxx>
@@ -119,6 +121,7 @@ public:
     HeadingType get_heading_type () const;
 
     virtual ~SGMatModel ();
+    
 
 protected:
 
@@ -199,5 +202,4 @@ private:
     vector<SGSharedPtr<SGMatModel> > _objects;
 };
 
-
 #endif // _SG_MAT_MODEL_HXX 
diff --git a/simgear/scene/tgdb/SGModelBin.hxx b/simgear/scene/tgdb/SGModelBin.hxx
new file mode 100755 (executable)
index 0000000..1c85141
--- /dev/null
@@ -0,0 +1,88 @@
+/* -*-c++-*-
+ *
+ * Copyright (C) 2007 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_MAT_MODEL_BIN_HXX
+#define SG_MAT_MODEL_BIN_HXX
+
+#include <math.h>
+
+class SGMatModelBin {
+public:
+  struct MatModel {
+    MatModel(const SGVec3f& p, SGMatModel *m) :
+      position(p), model(m)
+    { }
+    SGVec3f position;
+    SGMatModel *model;
+  };
+  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)); }
+
+  unsigned getNumModels() const
+  { return _models.size(); }
+  const MatModel& getMatModel(unsigned i) const
+  { return _models[i]; }
+  
+private:
+  MatModelList _models;
+  float min_x;
+  float max_x;
+  float min_y;
+  float max_y;
+  float min_z;
+  float max_z;
+};
+
+#endif
index dcf78e1cad79fa322cd005fe90ad8ada536e3b8e..bec819780809a78a6a034b5fc5d4c1541ed77332 100644 (file)
@@ -30,6 +30,8 @@
 #include <simgear/math/SGMath.hxx>
 #include "SGTriangleBin.hxx"
 
+
+
 struct SGVertNormTex {
   SGVertNormTex()
   { }
@@ -94,6 +96,11 @@ protected:
 
 class SGTexturedTriangleBin : public SGTriangleBin<SGVertNormTex> {
 public:
+  SGTexturedTriangleBin()
+  {
+    seed = new mt;
+    mt_init(seed, 123);
+  }
 
   // Computes and adds random surface points to the points list.
   // The random points are computed with a density of (coverage points)/1
@@ -118,13 +125,13 @@ public:
       // For partial units of area, use a zombie door method to
       // create the proper random chance of a light being created
       // for this triangle
-      float unit = area + sg_random()*coverage;
+      float unit = area + mt_rand(seed)*coverage;
       
       SGVec3f offsetVector = offset*normalize(normal);
       // generate a light point for each unit of area
       while ( coverage < unit ) {
-        float a = sg_random();
-        float b = sg_random();
+        float a = mt_rand(seed);
+        float b = mt_rand(seed);
         if ( a + b > 1 ) {
           a = 1 - a;
           b = 1 - b;
@@ -136,6 +143,43 @@ public:
       }
     }
   }
+  
+   void addRandomPoints(float coverage, 
+                        std::vector<SGVec3f>& points) const
+  {
+    unsigned num = getNumTriangles();
+    for (unsigned i = 0; i < num; ++i) {
+      triangle_ref triangleRef = getTriangleRef(i);
+      SGVec3f v0 = getVertex(triangleRef[0]).vertex;
+      SGVec3f v1 = getVertex(triangleRef[1]).vertex;
+      SGVec3f v2 = getVertex(triangleRef[2]).vertex;
+      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);
+
+      // 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;
+        points.push_back(randomPoint);
+        num -= 1.0;
+      }
+    }
+  }
 
   osg::Geometry* buildGeometry(const TriangleVector& triangles) const
   {
@@ -196,6 +240,10 @@ public:
 
   osg::Geometry* buildGeometry() const
   { return buildGeometry(getTriangles()); }
+
+private:
+  // Random seed for the triangle.
+  mt* seed;
 };
 
 #endif
index 1f6cfd275fa269003415991cb749c46309d222ac..4e90441523018ba61da834211c546b2dfca4cdb4 100644 (file)
 #include <simgear/scene/model/SGOffsetTransform.hxx>
 #include <simgear/scene/util/SGUpdateVisitor.hxx>
 #include <simgear/scene/util/SGNodeMasks.hxx>
+#include <simgear/scene/util/QuadTreeBuilder.hxx>
 #include <simgear/threads/SGThread.hxx>
 #include <simgear/threads/SGGuard.hxx>
 
 #include "SGTexturedTriangleBin.hxx"
 #include "SGLightBin.hxx"
+#include "SGModelBin.hxx"
 #include "SGDirectionalLightBin.hxx"
 #include "GroundLightManager.hxx"
 
 
+#include "userdata.hxx"
 #include "pt_lights.hxx"
 
 using namespace simgear;
@@ -76,7 +79,8 @@ struct SGTileGeometryBin {
   SGDirectionalLightListBin rabitLights;
   SGLightListBin odalLights;
   SGDirectionalLightListBin reilLights;
-
+  SGMatModelBin randomModels;
+  
   static SGVec4f
   getMaterialLightColor(const SGMaterial* material)
   {
@@ -397,6 +401,11 @@ struct SGTileGeometryBin {
   void computeRandomSurfaceLights(SGMaterialLib* matlib)
   {
     SGMaterialTriangleMap::const_iterator i;
+        
+    // generate a repeatable random seed
+    mt* seed = new mt;
+    mt_init(seed, unsigned(123));
+    
     for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
       SGMaterial *mat = matlib->find(i->first);
       if (!mat)
@@ -411,16 +420,13 @@ struct SGTileGeometryBin {
         coverage = 10000;
       }
       
-      // generate a repeatable random seed
-      sg_srandom(unsigned(coverage));
-
       std::vector<SGVec3f> randomPoints;
       i->second.addRandomSurfacePoints(coverage, 3, randomPoints);
       std::vector<SGVec3f>::iterator j;
       for (j = randomPoints.begin(); j != randomPoints.end(); ++j) {
-        float zombie = sg_random();
+        float zombie = mt_rand(seed);
         // factor = sg_random() ^ 2, range = 0 .. 1 concentrated towards 0
-        float factor = sg_random();
+        float factor = mt_rand(seed);
         factor *= factor;
 
         float bright = 1;
@@ -442,6 +448,44 @@ struct SGTileGeometryBin {
       }
     }
   }
+  
+  void computeRandomObjects(SGMaterialLib* matlib)
+  {
+    SGMaterialTriangleMap::const_iterator i;
+    for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
+      SGMaterial *mat = matlib->find(i->first);
+      if (!mat)
+        continue;
+      
+      int group_count = mat->get_object_group_count();
+      
+      if (group_count > 0)
+      {      
+        for (int j = 0; j < group_count; j++)
+        {      
+          SGMatModelGroup *object_group =  mat->get_object_group(j);
+          int nObjects = object_group->get_object_count();
+          
+          if (nObjects > 0)
+          {
+            // For each of the random models in the group, determine an appropriate
+            // number of random placements and insert them.
+            for (int k = 0; k < nObjects; k++) {
+              SGMatModel * object = object_group->get_object(k);
+              
+              std::vector<SGVec3f> randomPoints;
+
+              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);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 
   bool insertBinObj(const SGBinObject& obj, SGMaterialLib* matlib)
   {
@@ -456,25 +500,79 @@ struct SGTileGeometryBin {
 osg::Node*
 SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects)
 {
-  SGBinObject obj;
-  if (!obj.read_bin(path))
+  SGBinObject tile;
+  if (!tile.read_bin(path))
     return false;
 
   SGTileGeometryBin tileGeometryBin;
-  if (!tileGeometryBin.insertBinObj(obj, matlib))
+  if (!tileGeometryBin.insertBinObj(tile, matlib))
     return false;
 
-  SGVec3d center = obj.get_gbs_center2();
+  SGVec3d center = tile.get_gbs_center2();
   SGGeod geodPos = SGGeod::fromCart(center);
   SGQuatd hlOr = SGQuatd::fromLonLat(geodPos);
   SGVec3f up = toVec3f(hlOr.backTransform(SGVec3d(0, 0, -1)));
+  osg::Matrix world2Tile(-hlOr.osg());
   GroundLightManager* lightManager = GroundLightManager::instance();
 
   osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
+  osg::ref_ptr<osg::Group> randomObjects;
   osg::Group* terrainGroup = new osg::Group;
+
   osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib);
   if (node)
     terrainGroup->addChild(node);
+    
+  if (use_random_objects) {
+    tileGeometryBin.computeRandomObjects(matlib);
+    
+    if (tileGeometryBin.randomModels.getNumModels() > 0) {
+      // Generate a repeatable random seed
+      mt* seed = new mt;
+      mt_init(seed, unsigned(123));
+    
+      // Determine an rotation matrix for the models to place them 
+      // perpendicular to the earth's surface. We use the same matrix, 
+      // 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;
+      
+      for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) {
+        SGMatModelBin::MatModel obj = tileGeometryBin.randomModels.getMatModel(i);
+        osg::Node* node = sgGetRandomModel(obj.model);
+        
+        // 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.
+        osg::Matrix mPos = osg::Matrix::translate(obj.position.osg());        
+        osg::MatrixTransform* 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;
+          osg::Matrix rot(cos(hdg), -sin(hdg), 0, 0,
+                          sin(hdg),  cos(hdg), 0, 0,
+                          0,         0, 1, 0,
+                          0,         0, 0, 1);     
+          position = new osg::MatrixTransform(rot * mAtt * mPos);
+        } else {
+          position = new osg::MatrixTransform(mAtt * mPos);
+        }
+
+        position->addChild(node);        
+        models.push_back(position);
+        // Add to the leaf of the quadtree based on object location.
+      }
+      randomObjects = QuadTreeBuilder::makeQuadTree(models, world2Tile);
+      randomObjects->setName("random objects");
+    }
+  }
 
   if (calc_lights) {
     // FIXME: ugly, has a side effect
@@ -590,5 +688,17 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
     lightLOD->setNodeMask(nodeMask);
     transform->addChild(lightLOD);
   }
+  
+  if (randomObjects.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);
+    unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT;
+    objectLOD->setNodeMask(nodeMask);
+    transform->addChild(objectLOD);
+  }
+  
   return transform;
 }
index 9e2b3f1fa1ad273e9ab649f64bf48d13a106570e..c1956edd3fdfb9251cc228555a642aa90683cc8c 100644 (file)
@@ -34,6 +34,7 @@
 #include <simgear/scene/material/mat.hxx>
 #include <simgear/scene/material/matmodel.hxx>
 
+#include "SGModelBin.hxx"
 #include "userdata.hxx"
 #include "SGReaderWriterBTG.hxx"
 
@@ -66,6 +67,13 @@ void sgUserDataInit( SGModelLib *m, const string &r,
     sim_time_sec = t;
 }
 
+ osg::Node* sgGetRandomModel(SGMatModel *obj) {
+   return obj->get_random_model(modellib, model_root, root_props, sim_time_sec);
+ }
+
+osg::Node* sgGetModel(int i, SGMatModel *obj) {
+   return obj->get_model(i, modellib, model_root, root_props, sim_time_sec);
+ }
 
 static void random_pt_inside_tri( float *res,
                                   float *n1, float *n2, float *n3 )
index 7001e9954eb396e8d7dca83227632eafd481b909..d60d54a4d821251bbb1c08cb63ffa746bb8630d6 100644 (file)
@@ -52,6 +52,15 @@ class SGPropertyNode;
 void sgUserDataInit( SGModelLib *m, const string &r,
                      SGPropertyNode *p, double t );
 
+/**
+ * Get a random model.
+ */
+osg::Node* sgGetRandomModel(SGMatModel *obj);
+
+/**
+ * Get a specific model.
+ */
+osg::Node* sgGetModel(int i, SGMatModel *obj);
 
 /**
  * User data for populating leaves when they come in range.
index 48218f079349de3abbb1feeb4bfccc925487553f..121900a5dc9ad0019f89084030444d248a8c8e30 100644 (file)
@@ -14,6 +14,7 @@ include_HEADERS = \
        SGStateAttributeVisitor.hxx \
        SGTextureStateAttributeVisitor.hxx \
        SGUpdateVisitor.hxx \
+       QuadTreeBuilder.hxx \
        RenderConstants.hxx \
        StateAttributeFactory.hxx \
        VectorArrayAdapter.hxx
@@ -25,6 +26,7 @@ libsgutil_a_SOURCES = \
        SGSceneUserData.cxx \
        SGStateAttributeVisitor.cxx \
        SGTextureStateAttributeVisitor.cxx \
-       StateAttributeFactory.cxx
+       StateAttributeFactory.cxx \
+       QuadTreeBuilder.cxx
 
 INCLUDES = -I$(top_srcdir)
diff --git a/simgear/scene/util/QuadTreeBuilder.cxx b/simgear/scene/util/QuadTreeBuilder.cxx
new file mode 100644 (file)
index 0000000..4fe230c
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright (C) 2008  Tim Moore
+//
+// 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/BoundingBox>
+#include <osg/Math>
+
+#include "QuadTreeBuilder.hxx"
+
+using namespace std;
+using namespace osg;
+
+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) {
+        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;
+        }
+    }
+}
+
+void QuadTreeBuilder::addNode(Node* node, 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);
+}
+
+osg::Group* QuadTreeBuilder::makeQuadTree(vector<ref_ptr<Node> >& nodes,
+                                          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;
+        extents.expandBy(center);
+    }
+    const Vec2 quadMin(extents.xMin(), extents.yMin());
+    const Vec2 quadMax(extents.xMax(), extents.yMax());
+    ref_ptr<Group> result;
+    {
+        QuadTreeBuilder quadTree(quadMin, quadMax);
+        for (NodeList::iterator iter = nodes.begin();
+             iter != nodes.end();
+             ++iter) {
+            quadTree.addNode(iter->get(), transform);
+        }
+        result = quadTree.getRoot();
+    }
+    return result.release();
+}
+
+}
diff --git a/simgear/scene/util/QuadTreeBuilder.hxx b/simgear/scene/util/QuadTreeBuilder.hxx
new file mode 100644 (file)
index 0000000..6a0e2dd
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright (C) 2008  Tim Moore
+//
+// 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 SIMGEAR_QUADTREEBUILDER_HXX
+#define SIMGEAR_QUADTREEBUILDER_HXX 1
+
+#include <vector>
+#include <osg/ref_ptr>
+#include <osg/Node>
+#include <osg/Group>
+#include <osg/Matrix>
+#include <osg/Vec2>
+
+namespace simgear
+{
+// 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,
+                                    const osg::Matrix& transform);
+protected:
+    osg::ref_ptr<osg::Group> _root;
+    osg::Group* _leaves[4][4];
+    osg::Vec2 _min;
+    osg::Vec2 _max;
+};
+}
+#endif