Browse Source

add_random_instances(), get_slope()

David Rose 15 years ago
parent
commit
1c8d2859f1

+ 25 - 16
panda/src/speedtree/config_speedtree.cxx

@@ -95,11 +95,13 @@ ConfigVariableBool speedtree_transmission_lighting
 
 
 ConfigVariableBool speedtree_detail_layer
 ConfigVariableBool speedtree_detail_layer
 ("speedtree-detail-layer", false,
 ("speedtree-detail-layer", false,
- PRC_DESC("Undocumented SpeedTree parameter."));
+ PRC_DESC("True to render the detail texture layer defined on tree and "
+	  "terrain objects, false otherwise."));
 
 
 ConfigVariableBool speedtree_detail_normal_mapping
 ConfigVariableBool speedtree_detail_normal_mapping
 ("speedtree-detail-normal-mapping", false,
 ("speedtree-detail-normal-mapping", false,
- PRC_DESC("True to enable normal maps in SpeedTree."));
+ PRC_DESC("True to render the detail normal maps defined on tree and "
+	  "terrain objects, false otherwise."));
 
 
 ConfigVariableBool speedtree_ambient_contrast
 ConfigVariableBool speedtree_ambient_contrast
 ("speedtree-ambient-contrast", false,
 ("speedtree-ambient-contrast", false,
@@ -109,13 +111,10 @@ ConfigVariableDouble speedtree_transmission_scalar
 ("speedtree-transmission-scalar", 1.0f,
 ("speedtree-transmission-scalar", 1.0f,
  PRC_DESC("Undocumented SpeedTree parameter."));
  PRC_DESC("Undocumented SpeedTree parameter."));
 
 
-ConfigVariableDouble speedtree_fog_start_distance
-("speedtree-fog-start-distance", 2500.0,
- PRC_DESC("Specifies the nearest distance at which fog begins to be visible."));
-
-ConfigVariableDouble speedtree_fog_end_distance
-("speedtree-fog-end-distance", 5000.0,
- PRC_DESC("Specifies the distance at and beyond which fog is complete."));
+ConfigVariableDouble speedtree_fog_distance
+("speedtree-fog-distance", "2500 5000",
+ PRC_DESC("Specifies the nearest and farthest distance of the fog on trees "
+	  "and terrain."));
 
 
 ConfigVariableDouble speedtree_fog_color
 ConfigVariableDouble speedtree_fog_color
 ("speedtree-fog-color", "1.0 1.0 1.0",
 ("speedtree-fog-color", "1.0 1.0 1.0",
@@ -126,13 +125,9 @@ ConfigVariableDouble speedtree_sky_color
  PRC_DESC("Specifies the r g b color of the SpeedTree sky, when the sky "
  PRC_DESC("Specifies the r g b color of the SpeedTree sky, when the sky "
 	  "is enabled.  Currently unused."));
 	  "is enabled.  Currently unused."));
 
 
-ConfigVariableDouble speedtree_sky_fog_min
-("speedtree-sky-fog-min", -0.5,
- PRC_DESC("Undocumented SpeedTree parameter."));
-
-ConfigVariableDouble speedtree_sky_fog_max
-("speedtree-sky-fog-max", 1.0,
- PRC_DESC("Undocumented SpeedTree parameter."));
+ConfigVariableDouble speedtree_sky_fog
+("speedtree-sky-fog", "-0.5 1.0",
+ PRC_DESC("Specifies the range of fog in the sky.  -1 is down, 1 is up."));
 
 
 ConfigVariableDouble speedtree_sun_color
 ConfigVariableDouble speedtree_sun_color
 ("speedtree-sun-color", "1.0 1.0 0.85",
 ("speedtree-sun-color", "1.0 1.0 0.85",
@@ -239,6 +234,20 @@ ConfigVariableDouble speedtree_area_scale
 	  "scales from kilometers to feet.  You should set a different "
 	  "scales from kilometers to feet.  You should set a different "
 	  "scale if you use units other than feet."));
 	  "scale if you use units other than feet."));
 
 
+ConfigVariableBool speedtree_follow_terrain
+("speedtree-follow-terrain", true,
+ PRC_DESC("Set this true to have trees automatically snap to the terrain "
+	  "height when loaded into a SpeedTree node with a configured "
+	  "terrain.  If this is false, you may still call "
+	  "SpeedTreeNode::snap_to_terrain() afterwards."));
+
+ConfigVariableInt speedtree_max_random_try_count
+("speedtree-max-random-try-count", 1000,
+ PRC_DESC("This is a cheesy limit to detect bad parameters passed to "
+	  "SpeedTreeNode::add_random_instances().  If this number of attempts "
+	  "to find a legal place for a tree fail in a row, the parameters "
+	  "are deemed to be in error, and the function fails."));
+
 ConfigVariableBool speedtree_5_2_stf
 ConfigVariableBool speedtree_5_2_stf
 ("speedtree-5-2-stf", 
 ("speedtree-5-2-stf", 
 #if SPEEDTREE_VERSION_MAJOR > 5 || (SPEEDTREE_VERSION_MAJOR == 5 && SPEEDTREE_VERSION_MINOR >= 2)
 #if SPEEDTREE_VERSION_MAJOR > 5 || (SPEEDTREE_VERSION_MAJOR == 5 && SPEEDTREE_VERSION_MINOR >= 2)

+ 3 - 2
panda/src/speedtree/config_speedtree.h

@@ -42,8 +42,7 @@ extern ConfigVariableBool speedtree_detail_layer;
 extern ConfigVariableBool speedtree_detail_normal_mapping;
 extern ConfigVariableBool speedtree_detail_normal_mapping;
 extern ConfigVariableBool speedtree_ambient_contrast;
 extern ConfigVariableBool speedtree_ambient_contrast;
 extern ConfigVariableDouble speedtree_transmission_scalar;
 extern ConfigVariableDouble speedtree_transmission_scalar;
-extern ConfigVariableDouble speedtree_fog_start_distance;
-extern ConfigVariableDouble speedtree_fog_end_distance;
+extern ConfigVariableDouble speedtree_fog_distance;
 extern ConfigVariableDouble speedtree_fog_color;
 extern ConfigVariableDouble speedtree_fog_color;
 extern ConfigVariableDouble speedtree_sky_color;
 extern ConfigVariableDouble speedtree_sky_color;
 extern ConfigVariableDouble speedtree_sky_fog_min;
 extern ConfigVariableDouble speedtree_sky_fog_min;
@@ -70,6 +69,8 @@ extern ConfigVariableBool speedtree_show_overlays;
 extern ConfigVariableInt speedtree_max_num_visible_cells;
 extern ConfigVariableInt speedtree_max_num_visible_cells;
 extern ConfigVariableDouble speedtree_cull_cell_size;
 extern ConfigVariableDouble speedtree_cull_cell_size;
 extern ConfigVariableDouble speedtree_area_scale;
 extern ConfigVariableDouble speedtree_area_scale;
+extern ConfigVariableBool speedtree_follow_terrain;
+extern ConfigVariableInt speedtree_max_random_try_count;
 extern ConfigVariableBool speedtree_5_2_stf;
 extern ConfigVariableBool speedtree_5_2_stf;
 
 
 extern EXPCL_PANDASPEEDTREE void init_libspeedtree();
 extern EXPCL_PANDASPEEDTREE void init_libspeedtree();

+ 81 - 11
panda/src/speedtree/speedTreeNode.cxx

@@ -291,7 +291,13 @@ modify_instance_list(const STTree *tree) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void SpeedTreeNode::
 void SpeedTreeNode::
 add_instance(const STTree *tree, const STTransform &transform) {
 add_instance(const STTree *tree, const STTransform &transform) {
-  add_tree(tree).add_instance(transform);
+  if (speedtree_follow_terrain && has_terrain()) {
+    STTransform new_transform = transform;
+    new_transform._pos[2] = _terrain->get_height(new_transform._pos[0], new_transform._pos[1]);
+    add_tree(tree).add_instance(new_transform);
+  } else {
+    add_tree(tree).add_instance(transform);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -353,11 +359,71 @@ add_instances_from(const SpeedTreeNode *other, const TransformState *transform)
     for (int i = 0; i < num_instances; ++i) {
     for (int i = 0; i < num_instances; ++i) {
       CPT(TransformState) other_trans = other_instance_list.get_instance(i);
       CPT(TransformState) other_trans = other_instance_list.get_instance(i);
       CPT(TransformState) new_trans = transform->compose(other_trans);
       CPT(TransformState) new_trans = transform->compose(other_trans);
-      this_instance_list.add_instance(new_trans.p());
+
+      if (speedtree_follow_terrain && has_terrain()) {
+	STTransform new_transform = new_trans;
+	new_transform._pos[2] = _terrain->get_height(new_transform._pos[0], new_transform._pos[1]);
+	this_instance_list.add_instance(new_transform);
+
+      } else {
+	this_instance_list.add_instance(new_trans.p());
+      }
     }
     }
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: SpeedTreeNode::add_random_instances
+//       Access: Published
+//  Description: Creates a number of random instances of the indicated
+//               true, within the indicated range.  If a terrain is
+//               present, height_min and height_max restrict trees to
+//               the (x, y) positions that fall within the indicated
+//               terrain, and slope_min and slope_max restrict trees
+//               to the (x, y) positions that have a matching slope.
+//               If a terrain is not present, height_min and
+//               height_max specify a random range of Z heights, and
+//               slope_min and slope_max are ignored.
+////////////////////////////////////////////////////////////////////
+void SpeedTreeNode::
+add_random_instances(const STTree *tree, int quantity, 
+		     float x_min, float x_max, 
+		     float y_min, float y_max,
+		     float scale_min, float scale_max,
+		     float height_min, float height_max,
+		     float slope_min, float slope_max,
+		     Randomizer &randomizer) {
+  InstanceList &instance_list = add_tree(tree);
+  _needs_repopulate = true;
+
+  for (int i = 0; i < quantity; ++i) {
+    STTransform transform;
+    transform._pos[0] = randomizer.random_real(x_max - x_min) + x_min;
+    transform._pos[1] = randomizer.random_real(y_max - y_min) + y_min;
+    transform._rotate = randomizer.random_real(360.0);
+    transform._scale = randomizer.random_real(scale_max - scale_min) + scale_min;
+
+    if (has_terrain()) {
+      // Spin till we find a valid match with terrain.
+      int repeat_count = speedtree_max_random_try_count;
+      while (!_terrain->placement_is_acceptable(transform._pos[0], transform._pos[1], height_min, height_max, slope_min, slope_max)) {
+	transform._pos[0] = randomizer.random_real(x_max - x_min) + x_min;
+	transform._pos[1] = randomizer.random_real(y_max - y_min) + y_min;
+	if (--repeat_count == 0) {
+	  nassert_raise("Exceeded speedtree-max-random-try-count; bad placement parameters?");
+	  return;
+	}
+      }
+      transform._pos[2] = _terrain->get_height(transform._pos[0], transform._pos[1]);
+      
+    } else {
+      // No terrain; just pick a random height.
+      transform._pos[2] = randomizer.random_real(height_max - height_min) + height_min;
+    }
+    instance_list.add_instance(transform);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SpeedTreeNode::add_from_stf
 //     Function: SpeedTreeNode::add_from_stf
 //       Access: Published
 //       Access: Published
@@ -472,8 +538,8 @@ add_from_stf(istream &in, const Filename &pathname,
       if (!speedtree_5_2_stf) {
       if (!speedtree_5_2_stf) {
 	// 5.1 or earlier stf files also included these additional
 	// 5.1 or earlier stf files also included these additional
 	// values, which we will ignore:
 	// values, which we will ignore:
-	float elev_min, elev_max, slope_min, slope_max;
-	in >> elev_min >> elev_max >> slope_min >> slope_max;
+	float height_min, height_max, slope_min, slope_max;
+	in >> height_min >> height_max >> slope_min >> slope_max;
       }
       }
 
 
       if (tree != NULL) {
       if (tree != NULL) {
@@ -576,6 +642,10 @@ set_terrain(STTerrain *terrain) {
   _terrain_render.SetRenderInfo(trender_info);
   _terrain_render.SetRenderInfo(trender_info);
 
 
   _terrain_render.SetHeightHints(terrain->get_min_height(), terrain->get_max_height());
   _terrain_render.SetHeightHints(terrain->get_min_height(), terrain->get_max_height());
+
+  if (speedtree_follow_terrain) {
+    snap_to_terrain();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -655,12 +725,12 @@ reload_config() {
   render_info.m_bDetailNormalMapping = speedtree_detail_normal_mapping;
   render_info.m_bDetailNormalMapping = speedtree_detail_normal_mapping;
   render_info.m_bAmbientContrast = speedtree_ambient_contrast;
   render_info.m_bAmbientContrast = speedtree_ambient_contrast;
   render_info.m_fTransmissionScalar = speedtree_transmission_scalar;
   render_info.m_fTransmissionScalar = speedtree_transmission_scalar;
-  render_info.m_fFogStartDistance = speedtree_fog_start_distance;
-  render_info.m_fFogEndDistance = speedtree_fog_end_distance;
+  render_info.m_fFogStartDistance = speedtree_fog_distance[0];
+  render_info.m_fFogEndDistance = speedtree_fog_distance[1];
   render_info.m_vFogColor = SpeedTree::Vec3(speedtree_fog_color[0], speedtree_fog_color[1], speedtree_fog_color[2]);
   render_info.m_vFogColor = SpeedTree::Vec3(speedtree_fog_color[0], speedtree_fog_color[1], speedtree_fog_color[2]);
   render_info.m_vSkyColor = SpeedTree::Vec3(speedtree_sky_color[0], speedtree_sky_color[1], speedtree_sky_color[2]);
   render_info.m_vSkyColor = SpeedTree::Vec3(speedtree_sky_color[0], speedtree_sky_color[1], speedtree_sky_color[2]);
-  render_info.m_fSkyFogMin = speedtree_sky_fog_min;
-  render_info.m_fSkyFogMax = speedtree_sky_fog_max;
+  render_info.m_fSkyFogMin = speedtree_sky_fog[0];
+  render_info.m_fSkyFogMax = speedtree_sky_fog[1];
   render_info.m_vSunColor = SpeedTree::Vec3(speedtree_sun_color[0], speedtree_sun_color[1], speedtree_sun_color[2]);
   render_info.m_vSunColor = SpeedTree::Vec3(speedtree_sun_color[0], speedtree_sun_color[1], speedtree_sun_color[2]);
   render_info.m_fSunSize = speedtree_sun_size;
   render_info.m_fSunSize = speedtree_sun_size;
   render_info.m_fSunSpreadExponent = speedtree_sun_spread_exponent;
   render_info.m_fSunSpreadExponent = speedtree_sun_spread_exponent;
@@ -1445,9 +1515,9 @@ draw_callback(CallbackData *data) {
     PStatTimer timer1(_draw_speedtree_trees_pcollector);
     PStatTimer timer1(_draw_speedtree_trees_pcollector);
 
 
     //  SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_ALPHA_TESTING;
     //  SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_ALPHA_TESTING;
-    //  SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_ALPHA_TO_COVERAGE;
-    //  SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_BLENDING;
-    SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_NOTHING;
+    SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_ALPHA_TO_COVERAGE;
+    //SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_BLENDING;
+    //SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_NOTHING;
     set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode(mode));
     set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode(mode));
     
     
     bool branches = _forest_render.RenderBranches(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
     bool branches = _forest_render.RenderBranches(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);

+ 9 - 0
panda/src/speedtree/speedTreeNode.h

@@ -26,6 +26,7 @@
 #include "transformState.h"
 #include "transformState.h"
 #include "nodePath.h"
 #include "nodePath.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
+#include "randomizer.h"
 #include "speedtree_api.h"
 #include "speedtree_api.h"
 
 
 class Loader;
 class Loader;
@@ -115,6 +116,14 @@ PUBLISHED:
   void add_instances(const NodePath &root, const TransformState *transform = TransformState::make_identity());
   void add_instances(const NodePath &root, const TransformState *transform = TransformState::make_identity());
   void add_instances_from(const SpeedTreeNode *other);
   void add_instances_from(const SpeedTreeNode *other);
   void add_instances_from(const SpeedTreeNode *other, const TransformState *transform);
   void add_instances_from(const SpeedTreeNode *other, const TransformState *transform);
+  void add_random_instances(const STTree *tree, int quantity, 
+			    float x_min, float x_max, 
+			    float y_min, float y_max,
+			    float scale_min, float scale_max,
+			    float height_min, float height_max,
+			    float slope_min, float slope_max,
+			    Randomizer &randomizer = Randomizer());
+
   bool add_from_stf(const Filename &stf_filename, 
   bool add_from_stf(const Filename &stf_filename, 
 		    const LoaderOptions &options = LoaderOptions());
 		    const LoaderOptions &options = LoaderOptions());
   bool add_from_stf(istream &in, const Filename &pathname, 
   bool add_from_stf(istream &in, const Filename &pathname, 

+ 11 - 2
panda/src/speedtree/stBasicTerrain.I

@@ -51,6 +51,17 @@ get_size() const {
   return _size;
   return _size;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: STBasicTerrain::interpolate
+//       Access: Protected, Static
+//  Description: Convenience function to calculate the linear
+//               interpolation from A to B.
+////////////////////////////////////////////////////////////////////
+INLINE float STBasicTerrain::
+interpolate(float a, float b, float t) {
+  return (a + (b - a) * t);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: STBasicTerrain::InterpolationData::Constructor
 //     Function: STBasicTerrain::InterpolationData::Constructor
 //       Access: Public
 //       Access: Public
@@ -103,8 +114,6 @@ ValueType STBasicTerrain::InterpolationData<ValueType>::
 calc_bilinear_interpolation(float u, float v) const {
 calc_bilinear_interpolation(float u, float v) const {
   u -= cfloor(u);
   u -= cfloor(u);
   v -= cfloor(v);
   v -= cfloor(v);
-  nassertr((u >= 0.0f) && (u < 1.0f) && 
-	   (v >= 0.0f) && (v < 1.0f), 0);
   
   
   u *= (float)_width;
   u *= (float)_width;
   v *= (float)_height;
   v *= (float)_height;

+ 93 - 1
panda/src/speedtree/stBasicTerrain.cxx

@@ -297,7 +297,7 @@ get_smooth_height(float x, float y, float radius) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float STBasicTerrain::
 float STBasicTerrain::
 get_slope(float x, float y) const {
 get_slope(float x, float y) const {
-  return 0.0f;
+  return _slope_data.calc_bilinear_interpolation(x / _size, y / _size);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -394,10 +394,102 @@ read_height_map() {
       _max_height = max(_max_height, v);
       _max_height = max(_max_height, v);
     }
     }
   }
   }
+
+  compute_slope(0.5f);
   
   
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: STBasicTerrain::compute_slope
+//       Access: Protected
+//  Description: Once _height_data has been filled in, compute the
+//               corresponding values for _slope_data.
+////////////////////////////////////////////////////////////////////
+void STBasicTerrain::
+compute_slope(float smoothing) {
+  nassertv(!_height_data._data.empty());
+
+  int width = _height_data._width;
+  int height = _height_data._height;
+  _slope_data.reset(width, height);
+
+  float u_spacing = _size / (float)width;
+  float v_spacing = _size / (float)height;
+
+  for (int i = 0; i < width; ++i) {
+    int left = (i + width - 1) % width;
+    int right = (i + 1) % width;
+
+    for (int j = 0; j < height; ++j) {
+      int top = (j + height - 1) % height;
+      int bottom = (j + 1) % height;
+
+      float slope = 0.0f;
+      float this_height = _height_data._data[i + j * width];
+      slope += catan2(cabs(this_height - _height_data._data[right + j * width]), u_spacing);
+      slope += catan2(cabs(this_height - _height_data._data[left + j * width]), u_spacing);
+      slope += catan2(cabs(this_height - _height_data._data[i + top * width]), v_spacing);
+      slope += catan2(cabs(this_height - _height_data._data[i + bottom * width]), v_spacing);
+
+      slope *= (0.5f / MathNumbers::pi_f);
+
+      if (slope > 1.0f) {
+	slope = 1.0f;
+      }
+      _slope_data._data[i + j * width] = slope;
+    }
+  }
+
+  if (smoothing > 0.0f) {
+    // Create a temporary array for smoothing data.
+    InterpolationData<float> smoothing_data;
+    smoothing_data.reset(width, height);
+    float *smoothed = &smoothing_data._data[0];
+
+    int steps = int(smoothing);
+    float last_interpolation = smoothing - steps;
+    ++steps;
+    for (int si = 0; si < steps; ++si) {
+
+      // compute smoothed normals
+      for (int i = 0; i < width; ++i) {
+	int left = (i + width - 1) % width;
+	int right = (i + 1) % width;
+
+	for (int j = 0; j < height; ++j) {
+	  int top = (j + height - 1) % height;
+	  int bottom = (j + 1) % height;
+
+	  smoothed[i + j * width] = (_slope_data._data[right + j * width] + 
+				     _slope_data._data[left + j * width] + 
+				     _slope_data._data[i + top * width] + 
+				     _slope_data._data[i + bottom * width] +
+				     _slope_data._data[right + top * width] +
+				     _slope_data._data[right + bottom * width] +
+				     _slope_data._data[left + top * width] +
+				     _slope_data._data[left + bottom * width]);
+	  smoothed[i + j * width] *= 0.125f;
+	}
+      }
+
+      // interpolate or set
+      if (si == steps - 1) {
+	// last step, interpolate
+	for (int i = 0; i < width; ++i) {
+	  for (int j = 0; j < height; ++j) {
+	    _slope_data._data[i + j * width] = interpolate(_slope_data._data[i + j * width], smoothed[i + j * width], last_interpolation);
+	  }
+	}
+
+      } else {
+	// full smoothing step, copy everything
+	_slope_data = smoothing_data;
+      }
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: STBasicTerrain::read_quoted_filename
 //     Function: STBasicTerrain::read_quoted_filename
 //       Access: Private, Static
 //       Access: Private, Static

+ 3 - 0
panda/src/speedtree/stBasicTerrain.h

@@ -57,6 +57,9 @@ PUBLISHED:
 
 
 protected:
 protected:
   bool read_height_map();
   bool read_height_map();
+  void compute_slope(float smoothing);
+
+  INLINE float interpolate(float a, float b, float t);
 
 
 private:
 private:
   static void read_quoted_filename(Filename &result, istream &in, 
   static void read_quoted_filename(Filename &result, istream &in, 

+ 23 - 0
panda/src/speedtree/stTerrain.cxx

@@ -129,6 +129,29 @@ get_slope(float x, float y) const {
   return 0.0f;
   return 0.0f;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: STTerrain::placement_is_acceptable
+//       Access: Published
+//  Description: Returns true if the elevation and slope of point (x,
+//               y) fall within the requested limits, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool STTerrain::
+placement_is_acceptable(float x, float y,
+			float height_min, float height_max, 
+			float slope_min, float slope_max) {
+  float height = get_height(x, y);
+  if (height < height_min || height > height_max) {
+    return false;
+  }
+
+  float slope = get_slope(x, y);
+  if (slope < slope_min || slope > slope_max) {
+    return false;
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: STTerrain::fill_vertices
 //     Function: STTerrain::fill_vertices
 //       Access: Published, Virtual
 //       Access: Published, Virtual

+ 4 - 0
panda/src/speedtree/stTerrain.h

@@ -65,6 +65,10 @@ PUBLISHED:
   virtual float get_smooth_height(float x, float y, float radius) const;
   virtual float get_smooth_height(float x, float y, float radius) const;
   virtual float get_slope(float x, float y) const;
   virtual float get_slope(float x, float y) const;
 
 
+  bool placement_is_acceptable(float x, float y,
+			       float height_min, float height_max, 
+			       float slope_min, float slope_max);
+
   virtual void fill_vertices(GeomVertexData *data,
   virtual void fill_vertices(GeomVertexData *data,
 			     float start_x, float start_y,
 			     float start_x, float start_y,
 			     float size_xy, int num_xy) const;
 			     float size_xy, int num_xy) const;

+ 1 - 1
panda/src/speedtree/stTransform.h

@@ -59,7 +59,7 @@ public:
   void write_datagram(BamWriter *manager, Datagram &dg);
   void write_datagram(BamWriter *manager, Datagram &dg);
   void fillin(DatagramIterator &scan, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
 
 
-private:
+public:
   LPoint3f _pos;
   LPoint3f _pos;
   float _rotate;
   float _rotate;
   float _scale;
   float _scale;