add time_delta interfaces

This commit is contained in:
David Rose 2011-06-02 18:21:33 +00:00
parent 33886780c4
commit a552cfa859
3 changed files with 208 additions and 123 deletions

View File

@ -111,6 +111,58 @@ get_terrain() const {
return _terrain; return _terrain;
} }
////////////////////////////////////////////////////////////////////
// Function: SpeedTreeNode::set_time_delta
// Access: Published
// Description: Specifies an offset that is to be added each frame to
// the global clock's frame_time for the purpose of
// animating the trees in this particular node. Also
// see set_global_time_delta().
////////////////////////////////////////////////////////////////////
INLINE void SpeedTreeNode::
set_time_delta(double delta) {
_time_delta = delta;
}
////////////////////////////////////////////////////////////////////
// Function: SpeedTreeNode::get_time_delta
// Access: Published
// Description: Returns an offset that is to be added each frame to
// the global clock's frame_time for the purpose of
// animating the trees in this particular node. Also
// see get_global_time_delta().
////////////////////////////////////////////////////////////////////
INLINE double SpeedTreeNode::
get_time_delta() const {
return _time_delta;
}
////////////////////////////////////////////////////////////////////
// Function: SpeedTreeNode::set_global_time_delta
// Access: Published, Static
// Description: Specifies an offset that is to be added each frame to
// the global clock's frame_time for the purpose of
// animating the trees in all SpeedTreeNodes. Also
// see set_time_delta().
////////////////////////////////////////////////////////////////////
INLINE void SpeedTreeNode::
set_global_time_delta(double delta) {
_global_time_delta = delta;
}
////////////////////////////////////////////////////////////////////
// Function: SpeedTreeNode::get_global_time_delta
// Access: Published, Static
// Description: Returns an offset that is to be added each frame to
// the global clock's frame_time for the purpose of
// animating the trees in all SpeedTreeNodes. Also
// see get_time_delta().
////////////////////////////////////////////////////////////////////
INLINE double SpeedTreeNode::
get_global_time_delta() {
return _global_time_delta;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: SpeedTreeNode::InstanceList::Constructor // Function: SpeedTreeNode::InstanceList::Constructor
// Access: Public // Access: Public

View File

@ -43,6 +43,7 @@
#include "dxGraphicsStateGuardian9.h" #include "dxGraphicsStateGuardian9.h"
#endif #endif
double SpeedTreeNode::_global_time_delta = 0.0;
bool SpeedTreeNode::_authorized; bool SpeedTreeNode::_authorized;
bool SpeedTreeNode::_done_first_init; bool SpeedTreeNode::_done_first_init;
TypeHandle SpeedTreeNode::_type_handle; TypeHandle SpeedTreeNode::_type_handle;
@ -65,13 +66,14 @@ PStatCollector SpeedTreeNode::_draw_speedtree_terrain_update_pcollector("Draw:Sp
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
SpeedTreeNode:: SpeedTreeNode::
SpeedTreeNode(const string &name) : SpeedTreeNode(const string &name) :
PandaNode(name) PandaNode(name),
#ifdef ST_DELETE_FOREST_HACK #ifdef ST_DELETE_FOREST_HACK
// Early versions of SpeedTree don't destruct unused CForestRender // Early versions of SpeedTree don't destruct unused CForestRender
// objects correctly. To avoid crashes, we have to leak these // objects correctly. To avoid crashes, we have to leak these
// things. // things.
, _forest_render(*(new SpeedTree::CForestRender)) _forest_render(*(new SpeedTree::CForestRender)),
#endif #endif
_time_delta(0.0)
{ {
init_node(); init_node();
// For now, set an infinite bounding volume. Maybe in the future // For now, set an infinite bounding volume. Maybe in the future
@ -92,14 +94,14 @@ SpeedTreeNode(const string &name) :
shaders_dir = token_filename.get_dirname(); shaders_dir = token_filename.get_dirname();
} else { } else {
if (!shaders_dir.is_directory()) { if (!shaders_dir.is_directory()) {
speedtree_cat.warning() speedtree_cat.warning()
<< "speedtree-shaders-dir is set to " << shaders_dir << "speedtree-shaders-dir is set to " << shaders_dir
<< ", which doesn't exist.\n"; << ", which doesn't exist.\n";
} else { } else {
speedtree_cat.warning() speedtree_cat.warning()
<< "speedtree-shaders-dir is set to " << shaders_dir << "speedtree-shaders-dir is set to " << shaders_dir
<< ", which exists but doesn't contain " << token_filename << ", which exists but doesn't contain " << token_filename
<< ".\n"; << ".\n";
} }
} }
} }
@ -169,7 +171,7 @@ add_tree(const STTree *tree) {
if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to register tree " << tree->get_fullpath() << "\n"; << "Failed to register tree " << tree->get_fullpath() << "\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
} }
} }
@ -225,7 +227,7 @@ remove_all_trees() {
const STTree *tree = instance_list->get_tree(); const STTree *tree = instance_list->get_tree();
if (!_forest_render.UnregisterTree(tree->get_tree())) { if (!_forest_render.UnregisterTree(tree->get_tree())) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to unregister tree " << tree->get_fullpath() << "\n"; << "Failed to unregister tree " << tree->get_fullpath() << "\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
} }
delete instance_list; delete instance_list;
@ -313,7 +315,7 @@ void SpeedTreeNode::
add_instances(const NodePath &root, const TransformState *transform) { add_instances(const NodePath &root, const TransformState *transform) {
nassertv(!root.is_empty()); nassertv(!root.is_empty());
r_add_instances(root.node(), transform->compose(root.get_transform()), r_add_instances(root.node(), transform->compose(root.get_transform()),
Thread::get_current_thread()); Thread::get_current_thread());
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -361,12 +363,12 @@ add_instances_from(const SpeedTreeNode *other, const TransformState *transform)
CPT(TransformState) new_trans = transform->compose(other_trans); CPT(TransformState) new_trans = transform->compose(other_trans);
if (speedtree_follow_terrain && has_terrain()) { if (speedtree_follow_terrain && has_terrain()) {
STTransform new_transform = new_trans; STTransform new_transform = new_trans;
new_transform._pos[2] = _terrain->get_height(new_transform._pos[0], new_transform._pos[1]); new_transform._pos[2] = _terrain->get_height(new_transform._pos[0], new_transform._pos[1]);
this_instance_list.add_instance(new_transform); this_instance_list.add_instance(new_transform);
} else { } else {
this_instance_list.add_instance(new_trans.p()); this_instance_list.add_instance(new_trans.p());
} }
} }
} }
@ -387,12 +389,12 @@ add_instances_from(const SpeedTreeNode *other, const TransformState *transform)
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void SpeedTreeNode:: void SpeedTreeNode::
add_random_instances(const STTree *tree, int quantity, add_random_instances(const STTree *tree, int quantity,
float x_min, float x_max, float x_min, float x_max,
float y_min, float y_max, float y_min, float y_max,
float scale_min, float scale_max, float scale_min, float scale_max,
float height_min, float height_max, float height_min, float height_max,
float slope_min, float slope_max, float slope_min, float slope_max,
Randomizer &randomizer) { Randomizer &randomizer) {
InstanceList &instance_list = add_tree(tree); InstanceList &instance_list = add_tree(tree);
_needs_repopulate = true; _needs_repopulate = true;
@ -407,12 +409,12 @@ add_random_instances(const STTree *tree, int quantity,
// Spin till we find a valid match with terrain. // Spin till we find a valid match with terrain.
int repeat_count = speedtree_max_random_try_count; 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)) { 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[0] = randomizer.random_real(x_max - x_min) + x_min;
transform._pos[1] = randomizer.random_real(y_max - y_min) + y_min; transform._pos[1] = randomizer.random_real(y_max - y_min) + y_min;
if (--repeat_count == 0) { if (--repeat_count == 0) {
nassert_raise("Exceeded speedtree-max-random-try-count; bad placement parameters?"); nassert_raise("Exceeded speedtree-max-random-try-count; bad placement parameters?");
return; return;
} }
} }
transform._pos[2] = _terrain->get_height(transform._pos[0], transform._pos[1]); transform._pos[2] = _terrain->get_height(transform._pos[0], transform._pos[1]);
@ -483,7 +485,7 @@ add_from_stf(const Filename &stf_filename, const LoaderOptions &options) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool SpeedTreeNode:: bool SpeedTreeNode::
add_from_stf(istream &in, const Filename &pathname, add_from_stf(istream &in, const Filename &pathname,
const LoaderOptions &options, Loader *loader) { const LoaderOptions &options, Loader *loader) {
if (loader == NULL) { if (loader == NULL) {
loader = Loader::get_global_ptr(); loader = Loader::get_global_ptr();
} }
@ -514,14 +516,14 @@ add_from_stf(istream &in, const Filename &pathname,
PT(PandaNode) srt_root = loader->load_sync(srt_filename); PT(PandaNode) srt_root = loader->load_sync(srt_filename);
if (srt_root != NULL) { if (srt_root != NULL) {
NodePath srt(srt_root); NodePath srt(srt_root);
NodePath srt_np = srt.find("**/+SpeedTreeNode"); NodePath srt_np = srt.find("**/+SpeedTreeNode");
if (!srt_np.is_empty()) { if (!srt_np.is_empty()) {
SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node()); SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node());
if (srt_node->get_num_trees() >= 1) { if (srt_node->get_num_trees() >= 1) {
tree = srt_node->get_tree(0); tree = srt_node->get_tree(0);
} }
} }
} }
already_loaded[srt_filename] = tree; already_loaded[srt_filename] = tree;
} }
@ -536,14 +538,14 @@ add_from_stf(istream &in, const Filename &pathname,
in >> pos[0] >> pos[1] >> pos[2] >> rotate >> scale; in >> pos[0] >> pos[1] >> pos[2] >> rotate >> scale;
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 height_min, height_max, slope_min, slope_max; float height_min, height_max, slope_min, slope_max;
in >> height_min >> height_max >> slope_min >> slope_max; in >> height_min >> height_max >> slope_min >> slope_max;
} }
if (tree != NULL) { if (tree != NULL) {
add_instance(tree, STTransform(pos, rad_2_deg(rotate), scale)); add_instance(tree, STTransform(pos, rad_2_deg(rotate), scale));
} }
} }
in >> os_filename; in >> os_filename;
@ -664,19 +666,19 @@ snap_to_terrain() {
int num_instances = instance_list->get_num_instances(); int num_instances = instance_list->get_num_instances();
if (_terrain != (STTerrain *)NULL) { if (_terrain != (STTerrain *)NULL) {
for (int i = 0; i < num_instances; ++i) { for (int i = 0; i < num_instances; ++i) {
STTransform trans = instance_list->get_instance(i); STTransform trans = instance_list->get_instance(i);
LPoint3f pos = trans.get_pos(); LPoint3f pos = trans.get_pos();
pos[2] = _terrain->get_height(pos[0], pos[1]); pos[2] = _terrain->get_height(pos[0], pos[1]);
trans.set_pos(pos); trans.set_pos(pos);
instance_list->set_instance(i, trans); instance_list->set_instance(i, trans);
} }
} else { } else {
for (int i = 0; i < num_instances; ++i) { for (int i = 0; i < num_instances; ++i) {
STTransform trans = instance_list->get_instance(i); STTransform trans = instance_list->get_instance(i);
LPoint3f pos = trans.get_pos(); LPoint3f pos = trans.get_pos();
pos[2] = 0.0f; pos[2] = 0.0f;
trans.set_pos(pos); trans.set_pos(pos);
instance_list->set_instance(i, trans); instance_list->set_instance(i, trans);
} }
} }
} }
@ -746,12 +748,24 @@ reload_config() {
_terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy); _terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy);
_terrain_render.SetHint(SpeedTree::CTerrain::HINT_MAX_NUM_VISIBLE_CELLS, _terrain_render.SetHint(SpeedTree::CTerrain::HINT_MAX_NUM_VISIBLE_CELLS,
speedtree_max_num_visible_cells); speedtree_max_num_visible_cells);
_visible_terrain.Reserve(speedtree_max_num_visible_cells); _visible_terrain.Reserve(speedtree_max_num_visible_cells);
_needs_repopulate = true; _needs_repopulate = true;
} }
////////////////////////////////////////////////////////////////////
// Function: SpeedTreeNode::set_wind
// Access: Published
// Description: Specifies the overall wind strength and direction.
// Gusts are controlled internally.
////////////////////////////////////////////////////////////////////
void SpeedTreeNode::
set_wind(double strength, const LVector3f &direction) {
_forest_render.SetGlobalWindStrength(strength);
_forest_render.SetGlobalWindDirection(SpeedTree::Vec3(direction[0], direction[1], direction[2]));
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: SpeedTreeNode::authorize // Function: SpeedTreeNode::authorize
// Access: Published, Static // Access: Published, Static
@ -770,10 +784,10 @@ authorize(const string &license) {
SpeedTree::CCore::Authorize(license.c_str()); SpeedTree::CCore::Authorize(license.c_str());
} else { } else {
if (!speedtree_license.empty()) { if (!speedtree_license.empty()) {
SpeedTree::CCore::Authorize(speedtree_license.c_str()); SpeedTree::CCore::Authorize(speedtree_license.c_str());
} }
} }
_authorized = SpeedTree::CCore::IsAuthorized(); _authorized = SpeedTree::CCore::IsAuthorized();
SpeedTree::CCore::SetTextureFlip(true); SpeedTree::CCore::SetTextureFlip(true);
@ -791,13 +805,14 @@ SpeedTreeNode::
SpeedTreeNode(const SpeedTreeNode &copy) : SpeedTreeNode(const SpeedTreeNode &copy) :
PandaNode(copy), PandaNode(copy),
_os_shaders_dir(copy._os_shaders_dir), _os_shaders_dir(copy._os_shaders_dir),
_shadow_infos(copy._shadow_infos) _shadow_infos(copy._shadow_infos),
#ifdef ST_DELETE_FOREST_HACK #ifdef ST_DELETE_FOREST_HACK
// Early versions of SpeedTree don't destruct unused CForestRender // Early versions of SpeedTree don't destruct unused CForestRender
// objects correctly. To avoid crashes, we have to leak these // objects correctly. To avoid crashes, we have to leak these
// things. // things.
, _forest_render(*(new SpeedTree::CForestRender)) _forest_render(*(new SpeedTree::CForestRender)),
#endif #endif
_time_delta(copy._time_delta)
{ {
init_node(); init_node();
@ -807,7 +822,7 @@ SpeedTreeNode(const SpeedTreeNode &copy) :
// No way to copy these parameters, so we just re-assign them. // No way to copy these parameters, so we just re-assign them.
_terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy); _terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy);
_terrain_render.SetHint(SpeedTree::CTerrain::HINT_MAX_NUM_VISIBLE_CELLS, _terrain_render.SetHint(SpeedTree::CTerrain::HINT_MAX_NUM_VISIBLE_CELLS,
speedtree_max_num_visible_cells); speedtree_max_num_visible_cells);
_visible_terrain.Reserve(speedtree_max_num_visible_cells); _visible_terrain.Reserve(speedtree_max_num_visible_cells);
Trees::const_iterator ti; Trees::const_iterator ti;
@ -816,7 +831,7 @@ SpeedTreeNode(const SpeedTreeNode &copy) :
const STTree *tree = instance_list->get_tree(); const STTree *tree = instance_list->get_tree();
if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to register tree " << tree->get_fullpath() << "\n"; << "Failed to register tree " << tree->get_fullpath() << "\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
} }
@ -916,8 +931,8 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
STInstances &instances = instance_list->_instances; STInstances &instances = instance_list->_instances;
STInstances::iterator sti; STInstances::iterator sti;
for (sti = instances.begin(); sti != instances.end(); ++sti) { for (sti = instances.begin(); sti != instances.end(); ++sti) {
STTransform orig_transform = *sti; STTransform orig_transform = *sti;
(*sti) = orig_transform * xform; (*sti) = orig_transform * xform;
} }
} }
} }
@ -963,7 +978,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
} }
ClockObject *clock = ClockObject::get_global_clock(); ClockObject *clock = ClockObject::get_global_clock();
_forest_render.SetGlobalTime(clock->get_frame_time()); _forest_render.SetGlobalTime(clock->get_frame_time() + _time_delta + _global_time_delta);
_forest_render.AdvanceGlobalWind(); _forest_render.AdvanceGlobalWind();
// Compute the modelview and camera transforms, to pass to the // Compute the modelview and camera transforms, to pass to the
@ -980,9 +995,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
lens->get_projection_mat(); lens->get_projection_mat();
_view.Set(SpeedTree::Vec3(camera_pos[0], camera_pos[1], camera_pos[2]), _view.Set(SpeedTree::Vec3(camera_pos[0], camera_pos[1], camera_pos[2]),
SpeedTree::Mat4x4(projection_mat.get_data()), SpeedTree::Mat4x4(projection_mat.get_data()),
SpeedTree::Mat4x4(modelview_mat.get_data()), SpeedTree::Mat4x4(modelview_mat.get_data()),
lens->get_near(), lens->get_far()); lens->get_near(), lens->get_far());
// Convert the render state to SpeedTree's input. // Convert the render state to SpeedTree's input.
const RenderState *state = data._state; const RenderState *state = data._state;
@ -1012,17 +1027,17 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
for (int i = 0; i < la->get_num_on_lights(); ++i) { for (int i = 0; i < la->get_num_on_lights(); ++i) {
NodePath light = la->get_on_light(i); NodePath light = la->get_on_light(i);
if (!light.is_empty() && light.node()->is_of_type(DirectionalLight::get_class_type())) { if (!light.is_empty() && light.node()->is_of_type(DirectionalLight::get_class_type())) {
// A directional light. // A directional light.
DirectionalLight *light_obj = DCAST(DirectionalLight, light.node()); DirectionalLight *light_obj = DCAST(DirectionalLight, light.node());
if (dlight == NULL || light_obj->get_priority() > dlight->get_priority()) { if (dlight == NULL || light_obj->get_priority() > dlight->get_priority()) {
// Here's the most important directional light. // Here's the most important directional light.
dlight = light_obj; dlight = light_obj;
dlight_np = light; dlight_np = light;
} }
} else if (!light.is_empty() && light.node()->is_of_type(AmbientLight::get_class_type())) { } else if (!light.is_empty() && light.node()->is_of_type(AmbientLight::get_class_type())) {
// An ambient light. We keep the color only. // An ambient light. We keep the color only.
AmbientLight *light_obj = DCAST(AmbientLight, light.node()); AmbientLight *light_obj = DCAST(AmbientLight, light.node());
ambient_color += light_obj->get_color(); ambient_color += light_obj->get_color();
} }
} }
} }
@ -1105,9 +1120,9 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
// SpeedTree to render the forest during the actual draw. // SpeedTree to render the forest during the actual draw.
CullableObject *object = CullableObject *object =
new CullableObject(NULL, data._state, new CullableObject(NULL, data._state,
TransformState::make_identity(), TransformState::make_identity(),
TransformState::make_identity(), TransformState::make_identity(),
trav->get_gsg()); trav->get_gsg());
object->set_draw_callback(new DrawCallback(this)); object->set_draw_callback(new DrawCallback(this));
trav->get_cull_handler()->record_object(object, trav); trav->get_cull_handler()->record_object(object, trav);
} }
@ -1171,7 +1186,7 @@ compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
const SpeedTree::Vec3 &emin = extents.Min(); const SpeedTree::Vec3 &emin = extents.Min();
const SpeedTree::Vec3 &emax = extents.Max(); const SpeedTree::Vec3 &emax = extents.Max();
internal_bounds = new BoundingBox(LPoint3f(emin[0], emin[1], emin[2]), internal_bounds = new BoundingBox(LPoint3f(emin[0], emin[1], emin[2]),
LPoint3f(emax[0], emax[1], emax[2])); LPoint3f(emax[0], emax[1], emax[2]));
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -1277,10 +1292,13 @@ init_node() {
} }
_forest_render.SetHint(SpeedTree::CForest::HINT_MAX_NUM_VISIBLE_CELLS, _forest_render.SetHint(SpeedTree::CForest::HINT_MAX_NUM_VISIBLE_CELLS,
speedtree_max_num_visible_cells); speedtree_max_num_visible_cells);
_forest_render.SetCullCellSize(speedtree_cull_cell_size); _forest_render.SetCullCellSize(speedtree_cull_cell_size);
// Doesn't appear to be necessary to call this explicitly.
//_forest_render.EnableWind(true);
_is_valid = true; _is_valid = true;
} }
@ -1291,7 +1309,7 @@ init_node() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void SpeedTreeNode:: void SpeedTreeNode::
r_add_instances(PandaNode *node, const TransformState *transform, r_add_instances(PandaNode *node, const TransformState *transform,
Thread *current_thread) { Thread *current_thread) {
if (node->is_of_type(SpeedTreeNode::get_class_type()) && node != this) { if (node->is_of_type(SpeedTreeNode::get_class_type()) && node != this) {
SpeedTreeNode *other = DCAST(SpeedTreeNode, node); SpeedTreeNode *other = DCAST(SpeedTreeNode, node);
add_instances_from(other, transform); add_instances_from(other, transform);
@ -1331,8 +1349,8 @@ repopulate() {
if (!_forest_render.AddInstances(tree->get_tree(), &instances[0], instances.size())) { if (!_forest_render.AddInstances(tree->get_tree(), &instances[0], instances.size())) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to add " << instances.size() << "Failed to add " << instances.size()
<< " instances for " << *tree << "\n"; << " instances for " << *tree << "\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
} }
} }
@ -1361,10 +1379,10 @@ repopulate() {
} }
_visible_trees.Reserve(_forest_render.GetBaseTrees(), _visible_trees.Reserve(_forest_render.GetBaseTrees(),
_forest_render.GetBaseTrees().size(), _forest_render.GetBaseTrees().size(),
speedtree_max_num_visible_cells, speedtree_max_num_visible_cells,
max_instances_by_cell, max_instances_by_cell,
speedtree_horizontal_billboards); speedtree_horizontal_billboards);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -1385,7 +1403,7 @@ update_terrain_cells() {
// A temporary vertex data object for populating terrain. // A temporary vertex data object for populating terrain.
PT(GeomVertexData) vertex_data = PT(GeomVertexData) vertex_data =
new GeomVertexData("terrain", _terrain->get_vertex_format(), new GeomVertexData("terrain", _terrain->get_vertex_format(),
GeomEnums::UH_static); GeomEnums::UH_static);
int num_vertices = num_tile_res * num_tile_res; int num_vertices = num_tile_res * num_tile_res;
vertex_data->set_num_rows(num_vertices); vertex_data->set_num_rows(num_vertices);
size_t num_bytes = vertex_data->get_array(0)->get_data_size_bytes(); size_t num_bytes = vertex_data->get_array(0)->get_data_size_bytes();
@ -1399,8 +1417,8 @@ update_terrain_cells() {
//cerr << "populating cell " << cell_xi << " " << cell_yi << "\n"; //cerr << "populating cell " << cell_xi << " " << cell_yi << "\n";
_terrain->fill_vertices(vertex_data, _terrain->fill_vertices(vertex_data,
cell_xi * cell_size, cell_yi * cell_size, cell_xi * cell_size, cell_yi * cell_size,
cell_size, num_tile_res); cell_size, num_tile_res);
const GeomVertexArrayData *array_data = vertex_data->get_array(0); const GeomVertexArrayData *array_data = vertex_data->get_array(0);
CPT(GeomVertexArrayDataHandle) handle = array_data->get_handle(); CPT(GeomVertexArrayDataHandle) handle = array_data->get_handle();
@ -1502,7 +1520,7 @@ draw_callback(CallbackData *data) {
if (!terrain) { if (!terrain) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to render terrain\n"; << "Failed to render terrain\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
// Clear the terrain so we don't keep spamming error messages. // Clear the terrain so we don't keep spamming error messages.
@ -1525,11 +1543,16 @@ draw_callback(CallbackData *data) {
bool leaf_meshes = _forest_render.RenderLeafMeshes(_visible_trees, SpeedTree::RENDER_PASS_STANDARD); bool leaf_meshes = _forest_render.RenderLeafMeshes(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
bool leaf_cards = _forest_render.RenderLeafCards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view); bool leaf_cards = _forest_render.RenderLeafCards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view);
bool billboards = _forest_render.RenderBillboards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view); bool billboards = _forest_render.RenderBillboards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view);
// Sometimes billboards comes back false, particularly if wind is
// disabled; but the billboards appear to have been rendered
// successfully. Weird. Just removing this test from the
// condition.
if (!branches || !fronds || !leaf_meshes || !leaf_cards || !billboards) { if (!branches || !fronds || !leaf_meshes || !leaf_cards /* || !billboards */) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to render forest completely: " << "Failed to render forest completely: "
<< branches << " " << fronds << " " << leaf_meshes << " " << leaf_cards << " " << billboards << "\n"; << branches << " " << fronds << " " << leaf_meshes << " " << leaf_cards << " " << billboards << "\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
} }
} }
@ -1624,7 +1647,7 @@ setup_for_render(GraphicsStateGuardian *gsg) {
GLenum err = glewInit(); GLenum err = glewInit();
if (err != GLEW_OK) { if (err != GLEW_OK) {
speedtree_cat.error() speedtree_cat.error()
<< "GLEW initialization failed: %s\n", glewGetErrorString(err); << "GLEW initialization failed: %s\n", glewGetErrorString(err);
// Can't proceed without GLEW. // Can't proceed without GLEW.
_is_valid = false; _is_valid = false;
return; return;
@ -1634,7 +1657,7 @@ setup_for_render(GraphicsStateGuardian *gsg) {
// requires it. // requires it.
if (!GLEW_VERSION_2_0) { if (!GLEW_VERSION_2_0) {
speedtree_cat.error() speedtree_cat.error()
<< "The SpeedTree OpenGL implementation requires OpenGL 2.0 or better to run; this system has version " << glGetString(GL_VERSION) << "\n"; << "The SpeedTree OpenGL implementation requires OpenGL 2.0 or better to run; this system has version " << glGetString(GL_VERSION) << "\n";
_is_valid = false; _is_valid = false;
return; return;
} }
@ -1659,47 +1682,47 @@ setup_for_render(GraphicsStateGuardian *gsg) {
const STTree *tree = instance_list->get_tree(); const STTree *tree = instance_list->get_tree();
const STInstances &instances = instance_list->_instances; const STInstances &instances = instance_list->_instances;
if (instances.empty()) { if (instances.empty()) {
continue; continue;
} }
int max_instances = 2; int max_instances = 2;
SpeedTree::CMap<const SpeedTree::CTree*, SpeedTree::st_int32>::const_iterator si; SpeedTree::CMap<const SpeedTree::CTree*, SpeedTree::st_int32>::const_iterator si;
si = _population_stats.m_mMaxNumInstancesPerCellPerBase.find(tree->get_tree()); si = _population_stats.m_mMaxNumInstancesPerCellPerBase.find(tree->get_tree());
if (si != _population_stats.m_mMaxNumInstancesPerCellPerBase.end()) { if (si != _population_stats.m_mMaxNumInstancesPerCellPerBase.end()) {
max_instances = max(max_instances, (int)si->second); max_instances = max(max_instances, (int)si->second);
} }
// Get the speedtree-textures-dir to pass for initialization. // Get the speedtree-textures-dir to pass for initialization.
string os_textures_dir; string os_textures_dir;
if (!speedtree_textures_dir.empty()) { if (!speedtree_textures_dir.empty()) {
os_textures_dir = speedtree_textures_dir.get_value().to_os_specific(); os_textures_dir = speedtree_textures_dir.get_value().to_os_specific();
// Ensure the path ends with a terminal slash; SpeedTree requires this. // Ensure the path ends with a terminal slash; SpeedTree requires this.
#if defined(WIN32) || defined(WIN64) #if defined(WIN32) || defined(WIN64)
if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '\\') { if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '\\') {
os_textures_dir += "\\"; os_textures_dir += "\\";
} }
#else #else
if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '/') { if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '/') {
os_textures_dir += "/"; os_textures_dir += "/";
} }
#endif #endif
} }
if (!_forest_render.InitTreeGraphics((SpeedTree::CTreeRender *)tree->get_tree(), if (!_forest_render.InitTreeGraphics((SpeedTree::CTreeRender *)tree->get_tree(),
max_instances, speedtree_horizontal_billboards, max_instances, speedtree_horizontal_billboards,
os_textures_dir.c_str())) { os_textures_dir.c_str())) {
if (speedtree_cat.is_debug()) { if (speedtree_cat.is_debug()) {
speedtree_cat.debug() speedtree_cat.debug()
<< "Failed to init tree graphics for " << *tree << "\n"; << "Failed to init tree graphics for " << *tree << "\n";
write_error(speedtree_cat.debug()); write_error(speedtree_cat.debug());
} }
} }
} }
// Init overall graphics // Init overall graphics
if (!_forest_render.InitGraphics(false)) { if (!_forest_render.InitGraphics(false)) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to init graphics\n"; << "Failed to init graphics\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
_is_valid = false; _is_valid = false;
return; return;
@ -1713,12 +1736,12 @@ setup_for_render(GraphicsStateGuardian *gsg) {
if (has_terrain()) { if (has_terrain()) {
// Now initialize the terrain. // Now initialize the terrain.
if (!_terrain_render.Init(speedtree_terrain_num_lods, if (!_terrain_render.Init(speedtree_terrain_num_lods,
speedtree_terrain_resolution, speedtree_terrain_resolution,
speedtree_terrain_cell_size, speedtree_terrain_cell_size,
_terrain->get_st_vertex_format())) { _terrain->get_st_vertex_format())) {
speedtree_cat.warning() speedtree_cat.warning()
<< "Failed to init terrain\n"; << "Failed to init terrain\n";
write_error(speedtree_cat.warning()); write_error(speedtree_cat.warning());
} }
} }
@ -1757,8 +1780,8 @@ cull_forest() {
SpeedTree::SForestCullResultsRender &light_cull = _shadow_infos[smi]._light_cull; SpeedTree::SForestCullResultsRender &light_cull = _shadow_infos[smi]._light_cull;
_forest_render.ComputeLightView _forest_render.ComputeLightView
(_forest_render.GetLightDir(), _view.GetFrustumPoints(), smi, (_forest_render.GetLightDir(), _view.GetFrustumPoints(), smi,
light_view, 0.0f); light_view, 0.0f);
light_view.SetLodRefPoint(_view.GetCameraPos()); light_view.SetLodRefPoint(_view.GetCameraPos());
_forest_render.CullAndComputeLOD(light_view, light_cull, false); _forest_render.CullAndComputeLOD(light_view, light_cull, false);
@ -1949,7 +1972,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
if (!srt_np.is_empty()) { if (!srt_np.is_empty()) {
SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node()); SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node());
if (srt_node->get_num_trees() >= 1) { if (srt_node->get_num_trees() >= 1) {
_tree = (STTree *)srt_node->get_tree(0); _tree = (STTree *)srt_node->get_tree(0);
} }
} }
} }

View File

@ -140,6 +140,13 @@ PUBLISHED:
void reload_config(); void reload_config();
void set_wind(double strength, const LVector3f &direction);
INLINE void set_time_delta(double delta);
INLINE double get_time_delta() const;
INLINE static void set_global_time_delta(double delta);
INLINE static double get_global_time_delta();
static bool authorize(const string &license = ""); static bool authorize(const string &license = "");
public: public:
@ -248,6 +255,9 @@ private:
typedef pvector<ShadowInfo> ShadowInfos; typedef pvector<ShadowInfo> ShadowInfos;
ShadowInfos _shadow_infos; ShadowInfos _shadow_infos;
double _time_delta;
static double _global_time_delta;
static bool _authorized; static bool _authorized;
static bool _done_first_init; static bool _done_first_init;