]> git.mxchange.org Git - flightgear.git/commitdiff
Randomly-place object overhaul and enhancements
authordavid <david>
Sat, 20 Jul 2002 14:56:37 +0000 (14:56 +0000)
committerdavid <david>
Sat, 20 Jul 2002 14:56:37 +0000 (14:56 +0000)
-----------------------------------------------

Fixed a segfault on exit.

Changed the radius of the dummy bounding sphere from 10m to 1000m to
ensure that FOV culling doesn't leave anything out.

Allow an object to have more than one variant model, which will be
chosen randomly.  Simply repeat the <path>...</path> property.

Removed the <billboard> property and replaced it with <heading-type>,
which can be set to "fixed" (leave the model oriented as it is),
"random" (give the model a random heading between 0 and 359 deg), or
"billboard" (always turn the model to face the camera).  The default
is "fixed".  Models look much better when they are not all facing the
same direction.

Allow the user to group models with the same visual range, so that
there can be *many* fewer nodes in the scene graph when the models are
not visible.  This causes an XML-format change, so that instead of

  <object>
   <range-m>...</range-m>
   ...
  </object>
  <object>
   <range-m>...</range-m>
   ...
  </object>
  ...

we now have

  <object-group>
   <range-m>...</range-m>
   <object>
    ...
   </object>
   <object>
    ...
   </object>
   ...
  </object-group>

Every object in a group can still have its own model(s), coverage, and
heading-type, but they all share the same range selector.

This change should already help users with tight memory constraints,
but it will matter much more when we add more object types -- for
example, we can now add dozens of different urban building types
without bloating the scene graph or slowing down the LOD tests for
tris that are out of range (i.e. most of them).

src/Objects/newmat.cxx
src/Objects/newmat.hxx
src/Objects/obj.cxx

index 8a3da39e40c663147b5ef54b98802dd71896ab1a..d40c6ccd32a6ed9a341a1e3db8351f6fbe0d9a73 100644 (file)
@@ -37,6 +37,7 @@ SG_USING_STD(map);
 #endif
 
 #include <simgear/debug/logstream.hxx>
+#include <simgear/math/sg_random.h>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/misc/sgstream.hxx>
 
@@ -95,12 +96,17 @@ local_file_exists( const string& path ) {
 // Implementation of FGNewMat::Object.
 ////////////////////////////////////////////////////////////////////////
 
-FGNewMat::Object::Object (const SGPropertyNode * node)
-  : _path(node->getStringValue("path")),
-    _model(0),
+FGNewMat::Object::Object (const SGPropertyNode * node, double range_m)
+  : _models_loaded(false),
     _coverage_m2(node->getDoubleValue("coverage-m2", 100000)),
-    _range_m(node->getDoubleValue("range-m", 2000))
+    _range_m(range_m)
 {
+                               // Note all the model paths
+  vector <SGPropertyNode_ptr> path_nodes = node->getChildren("path");
+  for (int i = 0; i < path_nodes.size(); i++)
+    _paths.push_back(path_nodes[i]->getStringValue());
+
+                               // Note the heading type
   string hdg = node->getStringValue("heading-type", "fixed");
   if (hdg == "fixed") {
     _heading_type = HEADING_FIXED;
@@ -117,41 +123,67 @@ FGNewMat::Object::Object (const SGPropertyNode * node)
 
 FGNewMat::Object::~Object ()
 {
-  _model->deRef();
+  for (int i = 0; i < _models.size(); i++) {
+    if (_models[i] != 0) {
+      _models[i]->deRef();
+      _models[i] = 0;
+    }
+  }
 }
 
-const string &
-FGNewMat::Object::get_path () const
+int
+FGNewMat::Object::get_model_count () const
 {
-  return _path;
+  return _models.size();
 }
 
-ssgEntity *
-FGNewMat::Object::get_model () const
+inline void
+FGNewMat::Object::load_models () const
 {
                                // Load model only on demand
-  if (_model == 0) {
-    SGPath path = globals->get_fg_root();
-    path.append(_path);
-    ssgTexturePath((char *)path.dir().c_str());
-    ssgEntity * entity = load_object((char *)path.c_str());
-    if (entity != 0) {
-      float ranges[] = {0, _range_m};
-      _model = new ssgRangeSelector;
-      ((ssgRangeSelector *)_model)->setRanges(ranges, 2);
-      if (_heading_type == HEADING_BILLBOARD) {
-       ssgCutout * cutout = new ssgCutout(false);
-       cutout->addKid(entity);
-       ((ssgBranch *)_model)->addKid(cutout);
+  if (!_models_loaded) {
+    for (int i = 0; i < _paths.size(); i++) {
+      SGPath path = globals->get_fg_root();
+      path.append(_paths[i]);
+      ssgTexturePath((char *)path.dir().c_str());
+      ssgEntity * entity = load_object((char *)path.c_str());
+      if (entity != 0) {
+       float ranges[] = {0, _range_m};
+       ssgRangeSelector * lod = new ssgRangeSelector;
+       lod->setRanges(ranges, 2);
+       if (_heading_type == HEADING_BILLBOARD) {
+         ssgCutout * cutout = new ssgCutout(false);
+         cutout->addKid(entity);
+         lod->addKid(cutout);
+       } else {
+         lod->addKid(entity);
+       }
+       lod->ref();
+       _models.push_back(lod);
       } else {
-       ((ssgBranch *)_model)->addKid(entity);
+       SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << path.str());
       }
-      _model->ref();
-    } else {
-      SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << path.str());
     }
   }
-  return _model;
+  _models_loaded = true;
+}
+
+ssgEntity *
+FGNewMat::Object::get_model (int index) const
+{
+  load_models();
+  return _models[index];
+}
+
+ssgEntity *
+FGNewMat::Object::get_random_model () const
+{
+  load_models();
+  int nModels = _models.size();
+  int index = int(sg_random() * nModels);
+  if (index >= nModels)
+    index = 0;
+  return _models[index];
 }
 
 double
@@ -160,12 +192,59 @@ FGNewMat::Object::get_coverage_m2 () const
   return _coverage_m2;
 }
 
+FGNewMat::Object::HeadingType
+FGNewMat::Object::get_heading_type () const
+{
+  return _heading_type;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGNewMat::ObjectGroup.
+////////////////////////////////////////////////////////////////////////
+
+FGNewMat::ObjectGroup::ObjectGroup (SGPropertyNode * node)
+  : _range_m(node->getDoubleValue("range-m", 2000))
+{
+                               // Load the object subnodes
+  vector<SGPropertyNode_ptr> object_nodes =
+    ((SGPropertyNode *)node)->getChildren("object");
+  for (unsigned int i = 0; i < object_nodes.size(); i++) {
+    const SGPropertyNode * object_node = object_nodes[i];
+    if (object_node->hasChild("path"))
+      _objects.push_back(new Object(object_node, _range_m));
+    else
+      SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object");
+  }
+}
+
+FGNewMat::ObjectGroup::~ObjectGroup ()
+{
+  for (int i = 0; i < _objects.size(); i++) {
+    delete _objects[i];
+    _objects[i] = 0;
+  }
+}
+
 double
-FGNewMat::Object::get_range_m () const
+FGNewMat::ObjectGroup::get_range_m () const
 {
   return _range_m;
 }
 
+int
+FGNewMat::ObjectGroup::get_object_count () const
+{
+  return _objects.size();
+}
+
+FGNewMat::Object *
+FGNewMat::ObjectGroup::get_object (int index) const
+{
+  return _objects[index];
+}
+
 
 \f
 ////////////////////////////////////////////////////////////////////////
@@ -195,9 +274,9 @@ FGNewMat::FGNewMat (ssgSimpleState * s)
 
 FGNewMat::~FGNewMat (void)
 {
-  for (unsigned int i = 0; i < objects.size(); i++) {
-    delete objects[i];
-    objects[i] = 0;
+  for (unsigned int i = 0; i < object_groups.size(); i++) {
+    delete object_groups[i];
+    object_groups[i] = 0;
   }
 }
 
@@ -249,15 +328,10 @@ FGNewMat::read_properties (const SGPropertyNode * props)
   emission[2] = props->getDoubleValue("emissive/b", 0.0);
   emission[3] = props->getDoubleValue("emissive/a", 0.0);
 
-  vector<SGPropertyNode_ptr> object_nodes =
-    ((SGPropertyNode *)props)->getChildren("object");
-  for (unsigned int i = 0; i < object_nodes.size(); i++) {
-    const SGPropertyNode * object_node = object_nodes[i];
-    if (object_node->hasChild("path"))
-      objects.push_back(new Object(object_node));
-    else
-      SG_LOG(SG_INPUT, SG_ALERT, "No path supplied for object");
-  }
+  vector<SGPropertyNode_ptr> object_group_nodes =
+    ((SGPropertyNode *)props)->getChildren("object-group");
+  for (unsigned int i = 0; i < object_group_nodes.size(); i++)
+    object_groups.push_back(new ObjectGroup(object_group_nodes[i]));
 }
 
 
index 5060517889a74b1103e30c333c2746b06523931b..c803824404c4265b63f7d33a61c20edc5868eb29 100644 (file)
@@ -66,9 +66,10 @@ public:
 
 \f
   //////////////////////////////////////////////////////////////////////
-  // Inner class.
+  // Inner classes.
   //////////////////////////////////////////////////////////////////////
 
+  class ObjectGroup;
 
   /**
    * A randomly-placeable object.
@@ -83,24 +84,46 @@ public:
       HEADING_RANDOM
     };
 
-    const string &get_path () const;
-    ssgEntity * get_model () const;
+    int get_model_count () const;
+    ssgEntity * get_model (int index) const;
+    ssgEntity * get_random_model () const;
     double get_coverage_m2 () const;
-    double get_range_m () const;
     HeadingType get_heading_type () const;
   protected:
-    friend class FGNewMat;
-    Object (const SGPropertyNode * node);
+    friend class ObjectGroup;
+    Object (const SGPropertyNode * node, double range_m);
     virtual ~Object ();
   private:
-    string _path;
-    mutable ssgEntity * _model;
+    void load_models () const;
+    vector<string> _paths;
+    mutable vector<ssgEntity *> _models;
+    mutable bool _models_loaded;
     double _coverage_m2;
     double _range_m;
     HeadingType _heading_type;
   };
 
 
+  /**
+   * A collection of related objects with the same visual range.
+   */
+  class ObjectGroup
+  {
+  public:
+    virtual ~ObjectGroup ();
+    double get_range_m () const;
+    int get_object_count () const;
+    Object * get_object (int index) const;
+  protected:
+    friend class FGNewMat;
+    ObjectGroup (SGPropertyNode * node);
+  private:
+    double _range_m;
+    vector<Object *> _objects;
+  };
+
+
+
 \f
   ////////////////////////////////////////////////////////////////////
   // Public Constructors.
@@ -187,13 +210,15 @@ public:
   /**
    * Get the number of randomly-placed objects defined for this material.
    */
-  virtual int get_object_count () const { return objects.size(); }
+  virtual int get_object_group_count () const { return object_groups.size(); }
 
 
   /**
    * Get a randomly-placed object for this material.
    */
-  virtual Object * get_object (int index) const { return objects[index]; }
+  virtual ObjectGroup * get_object_group (int index) const {
+    return object_groups[index];
+  }
 
 
   /**
@@ -270,7 +295,7 @@ private:
   // true if texture loading deferred, and not yet loaded
   bool texture_loaded;
 
-  vector<Object *> objects;
+  vector<ObjectGroup *> object_groups;
 
   // ref count so we can properly delete if we have multiple
   // pointers to this record
index f0b2e6e24a97a7b1b82f8d6294398fd9547779c3..88ad034badf73d4c3245f8c8f186f22a9b41aaae 100644 (file)
@@ -313,6 +313,54 @@ static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
 }
 
 
+/**
+ * Create a rotation matrix to align an object for the current lat/lon.
+ *
+ * By default, objects are aligned for the north pole.  This code
+ * calculates a matrix to rotate them for the surface of the earth in
+ * the current location.
+ *
+ * TODO: there should be a single version of this method somewhere
+ * for all of SimGear.
+ *
+ * @param ROT The resulting rotation matrix.
+ * @param hdg_deg The object heading in degrees.
+ * @param lon_deg The longitude in degrees.
+ * @param lat_deg The latitude in degrees.
+ */
+static void
+makeWorldUpRotationMatrix (sgMat4 ROT, double hdg_deg,
+                          double lon_deg, double lat_deg)
+{
+       SGfloat sin_lat = sin( lat_deg * SGD_DEGREES_TO_RADIANS );
+       SGfloat cos_lat = cos( lat_deg * SGD_DEGREES_TO_RADIANS );
+       SGfloat sin_lon = sin( lon_deg * SGD_DEGREES_TO_RADIANS );
+       SGfloat cos_lon = cos( lon_deg * SGD_DEGREES_TO_RADIANS );
+       SGfloat sin_hdg = sin( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
+       SGfloat cos_hdg = cos( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
+
+       ROT[0][0] =  cos_hdg * sin_lat * cos_lon - sin_hdg * sin_lon;
+       ROT[0][1] =  cos_hdg * sin_lat * sin_lon + sin_hdg * cos_lon;
+       ROT[0][2] = -cos_hdg * cos_lat;
+       ROT[0][3] =  SG_ZERO;
+
+       ROT[1][0] = -sin_hdg * sin_lat * cos_lon - cos_hdg * sin_lon;
+       ROT[1][1] = -sin_hdg * sin_lat * sin_lon + cos_hdg * cos_lon;
+       ROT[1][2] =  sin_hdg * cos_lat;
+       ROT[1][3] =  SG_ZERO;
+
+       ROT[2][0] = cos_lat * cos_lon;
+       ROT[2][1] = cos_lat * sin_lon;
+       ROT[2][2] = sin_lat;
+       ROT[2][3] = SG_ZERO;
+
+       ROT[3][0] = SG_ZERO;
+       ROT[3][1] = SG_ZERO;
+       ROT[3][2] = SG_ZERO;
+       ROT[3][3] = SG_ONE ;
+}
+
+
 /**
  * Add an object to a random location inside a triangle.
  *
@@ -320,20 +368,27 @@ static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
  * @param p2 The second vertex of the triangle.
  * @param p3 The third vertex of the triangle.
  * @param center The center of the triangle.
- * @param ROT The world-up rotation matrix.
- * @param mat The material object.
- * @param object_index The index of the dynamically-placed object in
- *        the material.
+ * @param lon_deg The longitude of the surface center, in degrees.
+ * @param lat_deg The latitude of the surface center, in degrees.
+ * @param object The randomly-placed object.
  * @param branch The branch where the object should be added to the
  *        scene graph.
  */
 static void
 add_object_to_triangle (sgVec3 p1, sgVec3 p2, sgVec3 p3, sgVec3 center,
-                       sgMat4 ROT, FGNewMat::Object * object,
-                       ssgBranch * branch)
+                       double lon_deg, double lat_deg,
+                       FGNewMat::Object * object, ssgBranch * branch)
 {
+                               // Set up the random heading if required.
+    double hdg_deg = 0;
+    if (object->get_heading_type() == FGNewMat::Object::HEADING_RANDOM)
+      hdg_deg = sg_random() * 360;
+
     sgVec3 result;
 
+    sgMat4 ROT;
+    makeWorldUpRotationMatrix(ROT, hdg_deg, lon_deg, lat_deg);
+
     random_pt_inside_tri(result, p1, p2, p3);
     sgSubVec3(result, center);
     sgMat4 OBJ_pos, OBJ;
@@ -342,7 +397,7 @@ add_object_to_triangle (sgVec3 p1, sgVec3 p2, sgVec3 p3, sgVec3 center,
     sgPostMultMat4(OBJ, OBJ_pos);
     ssgTransform * pos = new ssgTransform;
     pos->setTransform(OBJ);
-    pos->addKid(object->get_model());
+    pos->addKid(object->get_random_model());
     branch->addKid(pos);
 }
 
@@ -353,9 +408,10 @@ public:
   float * p1;
   float * p2;
   float * p3;
-  FGNewMat::Object * object;
+  FGNewMat::ObjectGroup * object_group;
   ssgBranch * branch;
-  sgMat4 ROT;
+  double lon_deg;
+  double lat_deg;
 };
 
 
@@ -371,33 +427,40 @@ public:
  * @param mat The triangle's material.
  * @param object_index The index of the random object in the triangle.
  * @param branch The branch where the objects should be added.
- * @param ROT The rotation matrix to align objects with the earth's
- *        surface.
+ * @param lon_deg The longitude of the surface center, in degrees.
+ * @param lat_deg The latitude of the surface center, in degrees.
  */
 static void
 fill_in_triangle (float * p1, float * p2, float * p3,
-                 FGNewMat::Object *object, ssgBranch * branch, sgMat4 ROT)
+                 FGNewMat::ObjectGroup * object_group, ssgBranch * branch,
+                 double lon_deg, double lat_deg)
 {
-    sgVec3 center;
-    sgSetVec3(center,
-             (p1[0] + p2[0] + p3[0]) / 3.0,
-             (p1[1] + p2[1] + p3[1]) / 3.0,
-             (p1[2] + p2[2] + p3[2]) / 3.0);
-    double area = sgTriArea(p1, p2, p3);
-    double num = area / object->get_coverage_m2();
-
-    // place an object each unit of area
-    while ( num > 1.0 ) {
-      add_object_to_triangle(p1, p2, p3, center, ROT, object, branch);
-      num -= 1.0;
-    }
-    // for partial units of area, use a zombie door method to
-    // create the proper random chance of an object being created
-    // for this triangle
-    if ( num > 0.0 ) {
-      if ( sg_random() <= num ) {
-       // a zombie made it through our door
-       add_object_to_triangle(p1, p2, p3, center, ROT, object, branch);
+    int nObjects = object_group->get_object_count();
+    for (int i = 0; i < nObjects; i++) {
+      FGNewMat::Object * object = object_group->get_object(i);
+      sgVec3 center;
+      sgSetVec3(center,
+               (p1[0] + p2[0] + p3[0]) / 3.0,
+               (p1[1] + p2[1] + p3[1]) / 3.0,
+               (p1[2] + p2[2] + p3[2]) / 3.0);
+      double area = sgTriArea(p1, p2, p3);
+      double num = area / object->get_coverage_m2();
+
+      // place an object each unit of area
+      while ( num > 1.0 ) {
+       add_object_to_triangle(p1, p2, p3, center, lon_deg, lat_deg,
+                              object, branch);
+       num -= 1.0;
+      }
+      // for partial units of area, use a zombie door method to
+      // create the proper random chance of an object being created
+      // for this triangle
+      if ( num > 0.0 ) {
+       if ( sg_random() <= num ) {
+         // a zombie made it through our door
+         add_object_to_triangle(p1, p2, p3, center, lon_deg, lat_deg,
+                                object, branch);
+       }
       }
     }
 }
@@ -420,8 +483,8 @@ in_range_callback (ssgEntity * entity, int mask)
 {
   RandomObjectUserData * data = (RandomObjectUserData *)entity->getUserData();
   if (!data->is_filled_in) {
-    fill_in_triangle(data->p1, data->p2, data->p3, data->object,
-                    data->branch, data->ROT);
+    fill_in_triangle(data->p1, data->p2, data->p3, data->object_group,
+                    data->branch, data->lon_deg, data->lat_deg);
     data->is_filled_in = true;
   }
   return 1;
@@ -474,7 +537,7 @@ private:
   DummyBSphereEntity ()
   {
     bsphere.setCenter(0, 0, 0);
-    bsphere.setRadius(10);
+    bsphere.setRadius(1000);
   }
   static DummyBSphereEntity * entity;
 };
@@ -532,12 +595,13 @@ get_bounding_radius (sgVec3 center, float *p1, float *p2, float *p3)
  * @param mat The material data for the triangle.
  * @param branch The branch to which the randomly-placed objects
  *        should be added.
- * @param ROT A rotation matrix to align the objects with the earth's
- *        surface at the current lat/lon.
+ * @param lon_deg The longitude of the surface center, in degrees.
+ * @param lat_deg The latitude of the surface center, in degrees.
  */
 static void
 setup_triangle (float * p1, float * p2, float * p3,
-               FGNewMat * mat, ssgBranch * branch, sgMat4 ROT)
+               FGNewMat * mat, ssgBranch * branch,
+               double lon_deg, double lat_deg)
 {
                                // Set up a single center point for LOD
     sgVec3 center;
@@ -559,17 +623,17 @@ setup_triangle (float * p1, float * p2, float * p3,
     branch->addKid(location);
 
                                // Iterate through all the object types.
-    int num_objects = mat->get_object_count();
-    for (int i = 0; i < num_objects; i++) {
+    int num_groups = mat->get_object_group_count();
+    for (int i = 0; i < num_groups; i++) {
                                // Look up the random object.
-        FGNewMat::Object * object = mat->get_object(i);
+        FGNewMat::ObjectGroup * group = mat->get_object_group(i);
 
                                // Set up the range selector for the entire
                                // triangle; note that we use the object
                                // range plus the bounding radius here, to
                                // allow for objects far from the center.
        float ranges[] = {0,
-                         object->get_range_m() + bounding_radius,
+                         group->get_range_m() + bounding_radius,
                           500000};
        ssgRangeSelector * lod = new ssgRangeSelector;
        lod->setRanges(ranges, 3);
@@ -588,9 +652,10 @@ setup_triangle (float * p1, float * p2, float * p3,
        data->p1 = p1;
        data->p2 = p2;
        data->p3 = p3;
-       data->object = object;
+       data->object_group = group;
        data->branch = in_range;
-       sgCopyMat4(data->ROT, ROT);
+       data->lon_deg = lon_deg;
+       data->lat_deg = lat_deg;
 
                                // Set up the in-range node.
        in_range->setUserData(data);
@@ -608,54 +673,6 @@ setup_triangle (float * p1, float * p2, float * p3,
 }
 
 
-/**
- * Create a rotation matrix to align an object for the current lat/lon.
- *
- * By default, objects are aligned for the north pole.  This code
- * calculates a matrix to rotate them for the surface of the earth in
- * the current location.
- *
- * TODO: there should be a single version of this method somewhere
- * for all of SimGear.
- *
- * @param ROT The resulting rotation matrix.
- * @param hdg_deg The object heading in degrees.
- * @param lon_deg The longitude in degrees.
- * @param lat_deg The latitude in degrees.
- */
-void
-makeWorldUpRotationMatrix (sgMat4 ROT, double hdg_deg,
-                          double lon_deg, double lat_deg)
-{
-       SGfloat sin_lat = sin( lat_deg * SGD_DEGREES_TO_RADIANS );
-       SGfloat cos_lat = cos( lat_deg * SGD_DEGREES_TO_RADIANS );
-       SGfloat sin_lon = sin( lon_deg * SGD_DEGREES_TO_RADIANS );
-       SGfloat cos_lon = cos( lon_deg * SGD_DEGREES_TO_RADIANS );
-       SGfloat sin_hdg = sin( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
-       SGfloat cos_hdg = cos( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
-
-       ROT[0][0] =  cos_hdg * sin_lat * cos_lon - sin_hdg * sin_lon;
-       ROT[0][1] =  cos_hdg * sin_lat * sin_lon + sin_hdg * cos_lon;
-       ROT[0][2] = -cos_hdg * cos_lat;
-       ROT[0][3] =  SG_ZERO;
-
-       ROT[1][0] = -sin_hdg * sin_lat * cos_lon - cos_hdg * sin_lon;
-       ROT[1][1] = -sin_hdg * sin_lat * sin_lon + cos_hdg * cos_lon;
-       ROT[1][2] =  sin_hdg * cos_lat;
-       ROT[1][3] =  SG_ZERO;
-
-       ROT[2][0] = cos_lat * cos_lon;
-       ROT[2][1] = cos_lat * sin_lon;
-       ROT[2][2] = sin_lat;
-       ROT[2][3] = SG_ZERO;
-
-       ROT[3][0] = SG_ZERO;
-       ROT[3][1] = SG_ZERO;
-       ROT[3][2] = SG_ZERO;
-       ROT[3][3] = SG_ONE ;
-}
-
-
 /**
  * Randomly place objects on a surface.
  *
@@ -677,8 +694,6 @@ gen_random_surface_objects (ssgLeaf *leaf,
                            float lat_deg,
                            const string &material_name)
 {
-    float hdg_deg = 0.0;       // do something here later
-
                                // First, look up the material
                                // for this surface.
     FGNewMat * mat = material_lib.find(material_name);
@@ -689,8 +704,7 @@ gen_random_surface_objects (ssgLeaf *leaf,
 
                                // If the material has no randomly-placed
                                // objects, return now.
-    int num_objects = mat->get_object_count();
-    if (num_objects < 1)
+    if (mat->get_object_group_count() < 1)
       return;
 
                                // If the surface has no triangles, return
@@ -699,12 +713,6 @@ gen_random_surface_objects (ssgLeaf *leaf,
     if (num_tris < 1)
       return;
 
-                               // Make a rotation matrix to align the
-                               // object for this point on the earth's
-                               // surface.
-    sgMat4 ROT;
-    makeWorldUpRotationMatrix(ROT, hdg_deg, lon_deg, lat_deg);
-
                                // generate a repeatable random seed
     sg_srandom((unsigned int)(leaf->getVertex(0)[0]));
 
@@ -716,7 +724,7 @@ gen_random_surface_objects (ssgLeaf *leaf,
       setup_triangle(leaf->getVertex(n1),
                     leaf->getVertex(n2),
                     leaf->getVertex(n3),
-                    mat, branch, ROT);
+                    mat, branch, lon_deg, lat_deg);
     }
 }