From e7e576055fec7218f77310652530fdf0aa0d98cb Mon Sep 17 00:00:00 2001
From: david <david>
Date: Thu, 25 Jul 2002 17:32:31 +0000
Subject: [PATCH] Added a new range-selector layer above each tile.  That way,
 by default, each tile has only 3 extra SSG nodes unless it falls into range.

---
 src/Objects/obj.cxx | 191 ++++++++++++++++++++++++++++++++------------
 1 file changed, 142 insertions(+), 49 deletions(-)

diff --git a/src/Objects/obj.cxx b/src/Objects/obj.cxx
index 017c755df..bef13c199 100644
--- a/src/Objects/obj.cxx
+++ b/src/Objects/obj.cxx
@@ -402,7 +402,11 @@ add_object_to_triangle (sgVec3 p1, sgVec3 p2, sgVec3 p3, sgVec3 center,
     branch->addKid(pos);
 }
 
-class RandomObjectUserData : public ssgBase
+
+/**
+ * User data for populating triangles when they come in range.
+ */
+class TriUserData : public ssgBase
 {
 public:
   bool is_filled_in;
@@ -436,6 +440,9 @@ fill_in_triangle (float * p1, float * p2, float * p3,
 		  FGNewMat::ObjectGroup * object_group, ssgBranch * branch,
 		  double lon_deg, double lat_deg)
 {
+				// generate a repeatable random seed
+    sg_srandom((unsigned int)(p1[0]));
+
     int nObjects = object_group->get_object_count();
     for (int i = 0; i < nObjects; i++) {
       FGNewMat::Object * object = object_group->get_object(i);
@@ -466,7 +473,6 @@ fill_in_triangle (float * p1, float * p2, float * p3,
     }
 }
 
-
 /**
  * SSG callback for an in-range triangle of randomly-placed objects.
  *
@@ -480,9 +486,9 @@ fill_in_triangle (float * p1, float * p2, float * p3,
  * @return Always 1, to allow traversal and culling to continue.
  */
 static int
-in_range_callback (ssgEntity * entity, int mask)
+tri_in_range_callback (ssgEntity * entity, int mask)
 {
-  RandomObjectUserData * data = (RandomObjectUserData *)entity->getUserData();
+  TriUserData * data = (TriUserData *)entity->getUserData();
   if (!data->is_filled_in) {
     fill_in_triangle(data->p1, data->p2, data->p3, data->object_group,
 		     data->branch, data->lon_deg, data->lat_deg);
@@ -505,9 +511,9 @@ in_range_callback (ssgEntity * entity, int mask)
  * @return Always 0, to prevent any further traversal or culling.
  */
 static int
-out_of_range_callback (ssgEntity * entity, int mask)
+tri_out_of_range_callback (ssgEntity * entity, int mask)
 {
-  RandomObjectUserData * data = (RandomObjectUserData *)entity->getUserData();
+  TriUserData * data = (TriUserData *)entity->getUserData();
   if (data->is_filled_in) {
     data->branch->removeAllKids();
     data->is_filled_in = false;
@@ -630,7 +636,7 @@ setup_triangle (float * p1, float * p2, float * p3,
 				// allow for objects far from the center.
 	float ranges[] = {0,
 			  group->get_range_m() + bounding_radius,
-                          500000};
+                          SG_MAX};
 	ssgRangeSelector * lod = new ssgRangeSelector;
 	lod->setRanges(ranges, 3);
 	location->addKid(lod);
@@ -643,7 +649,7 @@ setup_triangle (float * p1, float * p2, float * p3,
 				// Set up the user data for if/when
 				// the random objects in this triangle
 				// are filled in.
-	RandomObjectUserData * data = new RandomObjectUserData;
+	TriUserData * data = new TriUserData;
 	data->is_filled_in = false;
 	data->p1 = p1;
 	data->p2 = p2;
@@ -656,29 +662,107 @@ setup_triangle (float * p1, float * p2, float * p3,
 				// Set up the in-range node.
 	in_range->setUserData(data);
 	in_range->setTravCallback(SSG_CALLBACK_PRETRAV,
-				 in_range_callback);
+				 tri_in_range_callback);
 	lod->addKid(in_range);
 
 				// Set up the out-of-range node.
 	out_of_range->setUserData(data);
 	out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
-				      out_of_range_callback);
+				      tri_out_of_range_callback);
 	out_of_range->addKid(DummyBSphereEntity::get_entity());
 	lod->addKid(out_of_range);
     }
 }
 
 
+/**
+ * User data for populating tiles when they come in range.
+ */
+class TileUserData : public ssgBase
+{
+public:
+  bool is_filled_in;
+  ssgLeaf * leaf;
+  FGNewMat * mat;
+  ssgBranch * branch;
+  double lon_deg;
+  double lat_deg;
+};
+
+
+/**
+ * SSG callback for an in-range tile of randomly-placed objects.
+ *
+ * This pretraversal callback is attached to a branch that is
+ * traversed only when a tile is in range.  If the tile is not
+ * currently prepared to be populated with randomly-placed objects,
+ * this callback will prepare it (actual population is handled by
+ * the tri_in_range_callback for individual triangles).
+ *
+ * @param entity The entity to which the callback is attached (not used).
+ * @param mask The entity's traversal mask (not used).
+ * @return Always 1, to allow traversal and culling to continue.
+ */
+static int
+tile_in_range_callback (ssgEntity * entity, int mask)
+{
+  TileUserData * data = (TileUserData *)entity->getUserData();
+
+  if (!data->is_filled_in) {
+				// Iterate through all the triangles
+				// and populate them.
+    int num_tris = data->leaf->getNumTriangles();
+    for ( int i = 0; i < num_tris; ++i ) {
+      short n1, n2, n3;
+      data->leaf->getTriangle(i, &n1, &n2, &n3);
+      setup_triangle(data->leaf->getVertex(n1),
+		     data->leaf->getVertex(n2),
+		     data->leaf->getVertex(n3),
+		     data->mat, data->branch, data->lon_deg, data->lat_deg);
+    }
+    data->is_filled_in = true;
+  }
+  return 1;
+}
+
+
+/**
+ * SSG callback for an out-of-range tile of randomly-placed objects.
+ *
+ * This pretraversal callback is attached to a branch that is
+ * traversed only when a tile is out of range.  If the tile is
+ * currently prepared to be populated with randomly-placed objects (or
+ * is actually populated), the objects will be removed.
+ *
+ * @param entity The entity to which the callback is attached (not used).
+ * @param mask The entity's traversal mask (not used).
+ * @return Always 0, to prevent any further traversal or culling.
+ */
+static int
+tile_out_of_range_callback (ssgEntity * entity, int mask)
+{
+  TileUserData * data = (TileUserData *)entity->getUserData();
+  if (data->is_filled_in) {
+    data->branch->removeAllKids();
+    data->is_filled_in = false;
+  }
+  return 0;
+}
+
+
 /**
  * Randomly place objects on a surface.
  *
  * The leaf node provides the geometry of the surface, while the
  * material provides the objects and placement density.  Latitude
  * and longitude are required so that the objects can be rotated
- * to the world-up vector.
+ * to the world-up vector.  This function does not actually add
+ * any objects; instead, it attaches an ssgRangeSelector to the
+ * branch with callbacks to generate the objects when needed.
  *
  * @param leaf The surface where the objects should be placed.
  * @param branch The branch that will hold the randomly-placed objects.
+ * @param center The center of the tile in FlightGear coordinates.
  * @param lon_deg The longitude of the surface center, in degrees.
  * @param lat_deg The latitude of the surface center, in degrees.
  * @param material_name The name of the surface's material.
@@ -686,12 +770,16 @@ setup_triangle (float * p1, float * p2, float * p3,
 static void
 gen_random_surface_objects (ssgLeaf *leaf,
 			    ssgBranch *branch,
-			    float lon_deg,
-			    float lat_deg,
+			    Point3D * center,
 			    const string &material_name)
 {
-				// First, look up the material
-				// for this surface.
+				// If the surface has no triangles, return
+				// now.
+    int num_tris = leaf->getNumTriangles();
+    if (num_tris < 1)
+      return;
+
+				// Get the material for this surface.
     FGNewMat * mat = material_lib.find(material_name);
     if (mat == 0) {
       SG_LOG(SG_INPUT, SG_ALERT, "Unknown material " << material_name);
@@ -703,25 +791,44 @@ gen_random_surface_objects (ssgLeaf *leaf,
     if (mat->get_object_group_count() < 1)
       return;
 
-				// If the surface has no triangles, return
-				// now.
-    int num_tris = leaf->getNumTriangles();
-    if (num_tris < 1)
-      return;
+				// Calculate the geodetic centre of
+				// the tile, for aligning automatic
+				// objects.
+    double lon_deg, lat_rad, lat_deg, alt_m, sl_radius_m;
+    Point3D geoc = sgCartToPolar3d(*center);
+    lon_deg = geoc.lon() * SGD_RADIANS_TO_DEGREES;
+    sgGeocToGeod(geoc.lat(), geoc.radius(),
+		 &lat_rad, &alt_m, &sl_radius_m);
+    lat_deg = lat_rad * SGD_RADIANS_TO_DEGREES;
 
-				// generate a repeatable random seed
-    sg_srandom((unsigned int)(leaf->getVertex(0)[0]));
+				// LOD for the tile
+				// max random object range: 20000m
+    float ranges[] = {0, 20000, 1000000};
+    ssgRangeSelector * lod = new ssgRangeSelector;
+    lod->setRanges(ranges, 3);
+    branch->addKid(lod);
 
-				// Iterate through all the triangles
-				// and populate them.
-    for ( int i = 0; i < num_tris; ++i ) {
-      short n1, n2, n3;
-      leaf->getTriangle(i, &n1, &n2, &n3);
-      setup_triangle(leaf->getVertex(n1),
-		     leaf->getVertex(n2),
-		     leaf->getVertex(n3),
-		     mat, branch, lon_deg, lat_deg);
-    }
+				// Create the in-range and out-of-range
+				// branches.
+    ssgBranch * in_range = new ssgBranch;
+    ssgBranch * out_of_range = new ssgBranch;
+    lod->addKid(in_range);
+    lod->addKid(out_of_range);
+
+    TileUserData * data = new TileUserData;
+    data->is_filled_in = false;
+    data->leaf = leaf;
+    data->mat = mat;
+    data->branch = in_range;
+    data->lon_deg = lon_deg;
+    data->lat_deg = lat_deg;
+
+    in_range->setUserData(data);
+    in_range->setTravCallback(SSG_CALLBACK_PRETRAV, tile_in_range_callback);
+    out_of_range->setUserData(data);
+    out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
+				   tile_out_of_range_callback);
+    out_of_range->addKid(DummyBSphereEntity::get_entity());
 }
 
 
@@ -1339,22 +1446,11 @@ bool fgBinObjLoad( const string& path, const bool is_base,
 
     geometry->setName( (char *)path.c_str() );
    
-    double geod_lon = 0.0, geod_lat = 0.0, geod_alt = 0.0,
-      geod_sl_radius = 0.0;
     if ( is_base ) {
 	// reference point (center offset/bounding sphere)
 	*center = obj.get_gbs_center();
 	*bounding_radius = obj.get_gbs_radius();
 
-				// Calculate the geodetic centre of
-				// the tile, for aligning automatic
-				// objects.
-	Point3D geoc = sgCartToPolar3d(*center);
-	geod_lon = geoc.lon();
-	sgGeocToGeod(geoc.lat(), geoc.radius(),
-		     &geod_lat, &geod_alt, &geod_sl_radius);
-	geod_lon *= SGD_RADIANS_TO_DEGREES;
-	geod_lat *= SGD_RADIANS_TO_DEGREES;
     }
 
     point_list nodes = obj.get_wgs84_nodes();
@@ -1419,8 +1515,7 @@ bool fgBinObjLoad( const string& path, const bool is_base,
 				  is_base, ground_lights );
 
 	if (use_random_objects)
-	  gen_random_surface_objects(leaf, geometry, geod_lon, geod_lat,
-				     material);
+	  gen_random_surface_objects(leaf, geometry, center, material);
 	geometry->addKid( leaf );
     }
 
@@ -1440,8 +1535,7 @@ bool fgBinObjLoad( const string& path, const bool is_base,
 				  is_base, ground_lights );
 
 	if (use_random_objects)
-	  gen_random_surface_objects(leaf, geometry, geod_lon, geod_lat,
-				     material);
+	  gen_random_surface_objects(leaf, geometry, center, material);
 	geometry->addKid( leaf );
     }
 
@@ -1460,8 +1554,7 @@ bool fgBinObjLoad( const string& path, const bool is_base,
 				  vertex_index, normal_index, tex_index,
 				  is_base, ground_lights );
 	if (use_random_objects)
-	  gen_random_surface_objects(leaf, geometry, geod_lon, geod_lat,
-				     material);
+	  gen_random_surface_objects(leaf, geometry, center, material);
 	geometry->addKid( leaf );
     }