diff --git a/panda/src/speedtree/config_speedtree.cxx b/panda/src/speedtree/config_speedtree.cxx index 16991d5a10..3591ecdaf0 100644 --- a/panda/src/speedtree/config_speedtree.cxx +++ b/panda/src/speedtree/config_speedtree.cxx @@ -95,11 +95,13 @@ ConfigVariableBool speedtree_transmission_lighting ConfigVariableBool speedtree_detail_layer ("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 ("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 ("speedtree-ambient-contrast", false, @@ -109,13 +111,10 @@ ConfigVariableDouble speedtree_transmission_scalar ("speedtree-transmission-scalar", 1.0f, 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 ("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 " "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 ("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 " "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 ("speedtree-5-2-stf", #if SPEEDTREE_VERSION_MAJOR > 5 || (SPEEDTREE_VERSION_MAJOR == 5 && SPEEDTREE_VERSION_MINOR >= 2) diff --git a/panda/src/speedtree/config_speedtree.h b/panda/src/speedtree/config_speedtree.h index 97b4dbeec2..852b3f2775 100644 --- a/panda/src/speedtree/config_speedtree.h +++ b/panda/src/speedtree/config_speedtree.h @@ -42,8 +42,7 @@ extern ConfigVariableBool speedtree_detail_layer; extern ConfigVariableBool speedtree_detail_normal_mapping; extern ConfigVariableBool speedtree_ambient_contrast; 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_sky_color; extern ConfigVariableDouble speedtree_sky_fog_min; @@ -70,6 +69,8 @@ extern ConfigVariableBool speedtree_show_overlays; extern ConfigVariableInt speedtree_max_num_visible_cells; extern ConfigVariableDouble speedtree_cull_cell_size; extern ConfigVariableDouble speedtree_area_scale; +extern ConfigVariableBool speedtree_follow_terrain; +extern ConfigVariableInt speedtree_max_random_try_count; extern ConfigVariableBool speedtree_5_2_stf; extern EXPCL_PANDASPEEDTREE void init_libspeedtree(); diff --git a/panda/src/speedtree/speedTreeNode.cxx b/panda/src/speedtree/speedTreeNode.cxx index 0f9ee6fa81..a9056fe8dc 100644 --- a/panda/src/speedtree/speedTreeNode.cxx +++ b/panda/src/speedtree/speedTreeNode.cxx @@ -291,7 +291,13 @@ modify_instance_list(const STTree *tree) { //////////////////////////////////////////////////////////////////// void SpeedTreeNode:: 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) { CPT(TransformState) other_trans = other_instance_list.get_instance(i); 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 // Access: Published @@ -472,8 +538,8 @@ add_from_stf(istream &in, const Filename &pathname, if (!speedtree_5_2_stf) { // 5.1 or earlier stf files also included these additional // 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) { @@ -576,6 +642,10 @@ set_terrain(STTerrain *terrain) { _terrain_render.SetRenderInfo(trender_info); _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_bAmbientContrast = speedtree_ambient_contrast; 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_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_fSunSize = speedtree_sun_size; render_info.m_fSunSpreadExponent = speedtree_sun_spread_exponent; @@ -1445,9 +1515,9 @@ draw_callback(CallbackData *data) { PStatTimer timer1(_draw_speedtree_trees_pcollector); // 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)); bool branches = _forest_render.RenderBranches(_visible_trees, SpeedTree::RENDER_PASS_STANDARD); diff --git a/panda/src/speedtree/speedTreeNode.h b/panda/src/speedtree/speedTreeNode.h index 764b96e8f1..27a647921f 100644 --- a/panda/src/speedtree/speedTreeNode.h +++ b/panda/src/speedtree/speedTreeNode.h @@ -26,6 +26,7 @@ #include "transformState.h" #include "nodePath.h" #include "pStatCollector.h" +#include "randomizer.h" #include "speedtree_api.h" class Loader; @@ -115,6 +116,14 @@ PUBLISHED: 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, 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, const LoaderOptions &options = LoaderOptions()); bool add_from_stf(istream &in, const Filename &pathname, diff --git a/panda/src/speedtree/stBasicTerrain.I b/panda/src/speedtree/stBasicTerrain.I index 5d7def1e1f..10576a04b8 100644 --- a/panda/src/speedtree/stBasicTerrain.I +++ b/panda/src/speedtree/stBasicTerrain.I @@ -51,6 +51,17 @@ get_size() const { 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 // Access: Public @@ -103,8 +114,6 @@ ValueType STBasicTerrain::InterpolationData:: calc_bilinear_interpolation(float u, float v) const { u -= cfloor(u); v -= cfloor(v); - nassertr((u >= 0.0f) && (u < 1.0f) && - (v >= 0.0f) && (v < 1.0f), 0); u *= (float)_width; v *= (float)_height; diff --git a/panda/src/speedtree/stBasicTerrain.cxx b/panda/src/speedtree/stBasicTerrain.cxx index 6b01668347..d5f45b8fb6 100644 --- a/panda/src/speedtree/stBasicTerrain.cxx +++ b/panda/src/speedtree/stBasicTerrain.cxx @@ -297,7 +297,7 @@ get_smooth_height(float x, float y, float radius) const { //////////////////////////////////////////////////////////////////// float STBasicTerrain:: 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); } } + + compute_slope(0.5f); 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 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 // Access: Private, Static diff --git a/panda/src/speedtree/stBasicTerrain.h b/panda/src/speedtree/stBasicTerrain.h index 7f787e93d4..9fd55d945c 100644 --- a/panda/src/speedtree/stBasicTerrain.h +++ b/panda/src/speedtree/stBasicTerrain.h @@ -57,6 +57,9 @@ PUBLISHED: protected: bool read_height_map(); + void compute_slope(float smoothing); + + INLINE float interpolate(float a, float b, float t); private: static void read_quoted_filename(Filename &result, istream &in, diff --git a/panda/src/speedtree/stTerrain.cxx b/panda/src/speedtree/stTerrain.cxx index 5cac592e3b..e1c2c34ab1 100644 --- a/panda/src/speedtree/stTerrain.cxx +++ b/panda/src/speedtree/stTerrain.cxx @@ -129,6 +129,29 @@ get_slope(float x, float y) const { 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 // Access: Published, Virtual diff --git a/panda/src/speedtree/stTerrain.h b/panda/src/speedtree/stTerrain.h index 93a1bf78d8..1048342758 100644 --- a/panda/src/speedtree/stTerrain.h +++ b/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_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, float start_x, float start_y, float size_xy, int num_xy) const; diff --git a/panda/src/speedtree/stTransform.h b/panda/src/speedtree/stTransform.h index a5c06901fc..91404c95c5 100644 --- a/panda/src/speedtree/stTransform.h +++ b/panda/src/speedtree/stTransform.h @@ -59,7 +59,7 @@ public: void write_datagram(BamWriter *manager, Datagram &dg); void fillin(DatagramIterator &scan, BamReader *manager); -private: +public: LPoint3f _pos; float _rotate; float _scale;