From a552cfa859ab55bfe0e2ae844734ab337beb5428 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 2 Jun 2011 18:21:33 +0000 Subject: [PATCH] add time_delta interfaces --- panda/src/speedtree/speedTreeNode.I | 52 +++++ panda/src/speedtree/speedTreeNode.cxx | 269 ++++++++++++++------------ panda/src/speedtree/speedTreeNode.h | 10 + 3 files changed, 208 insertions(+), 123 deletions(-) diff --git a/panda/src/speedtree/speedTreeNode.I b/panda/src/speedtree/speedTreeNode.I index e5e3fde7b5..d8e1639f04 100644 --- a/panda/src/speedtree/speedTreeNode.I +++ b/panda/src/speedtree/speedTreeNode.I @@ -111,6 +111,58 @@ get_terrain() const { 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 // Access: Public diff --git a/panda/src/speedtree/speedTreeNode.cxx b/panda/src/speedtree/speedTreeNode.cxx index a9056fe8dc..bb640d708a 100644 --- a/panda/src/speedtree/speedTreeNode.cxx +++ b/panda/src/speedtree/speedTreeNode.cxx @@ -43,6 +43,7 @@ #include "dxGraphicsStateGuardian9.h" #endif +double SpeedTreeNode::_global_time_delta = 0.0; bool SpeedTreeNode::_authorized; bool SpeedTreeNode::_done_first_init; TypeHandle SpeedTreeNode::_type_handle; @@ -65,13 +66,14 @@ PStatCollector SpeedTreeNode::_draw_speedtree_terrain_update_pcollector("Draw:Sp //////////////////////////////////////////////////////////////////// SpeedTreeNode:: SpeedTreeNode(const string &name) : - PandaNode(name) + PandaNode(name), #ifdef ST_DELETE_FOREST_HACK // Early versions of SpeedTree don't destruct unused CForestRender // objects correctly. To avoid crashes, we have to leak these // things. - , _forest_render(*(new SpeedTree::CForestRender)) + _forest_render(*(new SpeedTree::CForestRender)), #endif + _time_delta(0.0) { init_node(); // 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(); } else { if (!shaders_dir.is_directory()) { - speedtree_cat.warning() - << "speedtree-shaders-dir is set to " << shaders_dir - << ", which doesn't exist.\n"; + speedtree_cat.warning() + << "speedtree-shaders-dir is set to " << shaders_dir + << ", which doesn't exist.\n"; } else { - speedtree_cat.warning() - << "speedtree-shaders-dir is set to " << shaders_dir - << ", which exists but doesn't contain " << token_filename - << ".\n"; + speedtree_cat.warning() + << "speedtree-shaders-dir is set to " << shaders_dir + << ", which exists but doesn't contain " << token_filename + << ".\n"; } } } @@ -169,7 +171,7 @@ add_tree(const STTree *tree) { if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { speedtree_cat.warning() - << "Failed to register tree " << tree->get_fullpath() << "\n"; + << "Failed to register tree " << tree->get_fullpath() << "\n"; write_error(speedtree_cat.warning()); } } @@ -225,7 +227,7 @@ remove_all_trees() { const STTree *tree = instance_list->get_tree(); if (!_forest_render.UnregisterTree(tree->get_tree())) { speedtree_cat.warning() - << "Failed to unregister tree " << tree->get_fullpath() << "\n"; + << "Failed to unregister tree " << tree->get_fullpath() << "\n"; write_error(speedtree_cat.warning()); } delete instance_list; @@ -313,7 +315,7 @@ void SpeedTreeNode:: add_instances(const NodePath &root, const TransformState *transform) { nassertv(!root.is_empty()); 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); 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); + 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()); + this_instance_list.add_instance(new_trans.p()); } } } @@ -387,12 +389,12 @@ add_instances_from(const SpeedTreeNode *other, const TransformState *transform) //////////////////////////////////////////////////////////////////// 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) { + 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; @@ -407,12 +409,12 @@ add_random_instances(const STTree *tree, int quantity, // 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[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]); @@ -483,7 +485,7 @@ add_from_stf(const Filename &stf_filename, const LoaderOptions &options) { //////////////////////////////////////////////////////////////////// bool SpeedTreeNode:: add_from_stf(istream &in, const Filename &pathname, - const LoaderOptions &options, Loader *loader) { + const LoaderOptions &options, Loader *loader) { if (loader == NULL) { 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); if (srt_root != NULL) { - NodePath srt(srt_root); - NodePath srt_np = srt.find("**/+SpeedTreeNode"); - if (!srt_np.is_empty()) { - SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node()); - if (srt_node->get_num_trees() >= 1) { - tree = srt_node->get_tree(0); - } - } + NodePath srt(srt_root); + NodePath srt_np = srt.find("**/+SpeedTreeNode"); + if (!srt_np.is_empty()) { + SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node()); + if (srt_node->get_num_trees() >= 1) { + tree = srt_node->get_tree(0); + } + } } 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; if (!speedtree_5_2_stf) { - // 5.1 or earlier stf files also included these additional - // values, which we will ignore: - float height_min, height_max, slope_min, slope_max; - in >> height_min >> height_max >> slope_min >> slope_max; + // 5.1 or earlier stf files also included these additional + // values, which we will ignore: + float height_min, height_max, slope_min, slope_max; + in >> height_min >> height_max >> slope_min >> slope_max; } 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; @@ -664,19 +666,19 @@ snap_to_terrain() { int num_instances = instance_list->get_num_instances(); if (_terrain != (STTerrain *)NULL) { for (int i = 0; i < num_instances; ++i) { - STTransform trans = instance_list->get_instance(i); - LPoint3f pos = trans.get_pos(); - pos[2] = _terrain->get_height(pos[0], pos[1]); - trans.set_pos(pos); - instance_list->set_instance(i, trans); + STTransform trans = instance_list->get_instance(i); + LPoint3f pos = trans.get_pos(); + pos[2] = _terrain->get_height(pos[0], pos[1]); + trans.set_pos(pos); + instance_list->set_instance(i, trans); } } else { for (int i = 0; i < num_instances; ++i) { - STTransform trans = instance_list->get_instance(i); - LPoint3f pos = trans.get_pos(); - pos[2] = 0.0f; - trans.set_pos(pos); - instance_list->set_instance(i, trans); + STTransform trans = instance_list->get_instance(i); + LPoint3f pos = trans.get_pos(); + pos[2] = 0.0f; + trans.set_pos(pos); + instance_list->set_instance(i, trans); } } } @@ -746,12 +748,24 @@ reload_config() { _terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy); _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); _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 // Access: Published, Static @@ -770,10 +784,10 @@ authorize(const string &license) { SpeedTree::CCore::Authorize(license.c_str()); } else { if (!speedtree_license.empty()) { - SpeedTree::CCore::Authorize(speedtree_license.c_str()); + SpeedTree::CCore::Authorize(speedtree_license.c_str()); } } - + _authorized = SpeedTree::CCore::IsAuthorized(); SpeedTree::CCore::SetTextureFlip(true); @@ -791,13 +805,14 @@ SpeedTreeNode:: SpeedTreeNode(const SpeedTreeNode ©) : PandaNode(copy), _os_shaders_dir(copy._os_shaders_dir), - _shadow_infos(copy._shadow_infos) + _shadow_infos(copy._shadow_infos), #ifdef ST_DELETE_FOREST_HACK // Early versions of SpeedTree don't destruct unused CForestRender // objects correctly. To avoid crashes, we have to leak these // things. - , _forest_render(*(new SpeedTree::CForestRender)) + _forest_render(*(new SpeedTree::CForestRender)), #endif + _time_delta(copy._time_delta) { init_node(); @@ -807,7 +822,7 @@ SpeedTreeNode(const SpeedTreeNode ©) : // No way to copy these parameters, so we just re-assign them. _terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy); _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); Trees::const_iterator ti; @@ -816,7 +831,7 @@ SpeedTreeNode(const SpeedTreeNode ©) : const STTree *tree = instance_list->get_tree(); if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { speedtree_cat.warning() - << "Failed to register tree " << tree->get_fullpath() << "\n"; + << "Failed to register tree " << tree->get_fullpath() << "\n"; 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::iterator sti; for (sti = instances.begin(); sti != instances.end(); ++sti) { - STTransform orig_transform = *sti; - (*sti) = orig_transform * xform; + STTransform orig_transform = *sti; + (*sti) = orig_transform * xform; } } } @@ -963,7 +978,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { } 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(); // Compute the modelview and camera transforms, to pass to the @@ -980,9 +995,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { lens->get_projection_mat(); _view.Set(SpeedTree::Vec3(camera_pos[0], camera_pos[1], camera_pos[2]), - SpeedTree::Mat4x4(projection_mat.get_data()), - SpeedTree::Mat4x4(modelview_mat.get_data()), - lens->get_near(), lens->get_far()); + SpeedTree::Mat4x4(projection_mat.get_data()), + SpeedTree::Mat4x4(modelview_mat.get_data()), + lens->get_near(), lens->get_far()); // Convert the render state to SpeedTree's input. 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) { NodePath light = la->get_on_light(i); if (!light.is_empty() && light.node()->is_of_type(DirectionalLight::get_class_type())) { - // A directional light. - DirectionalLight *light_obj = DCAST(DirectionalLight, light.node()); - if (dlight == NULL || light_obj->get_priority() > dlight->get_priority()) { - // Here's the most important directional light. - dlight = light_obj; - dlight_np = light; - } + // A directional light. + DirectionalLight *light_obj = DCAST(DirectionalLight, light.node()); + if (dlight == NULL || light_obj->get_priority() > dlight->get_priority()) { + // Here's the most important directional light. + dlight = light_obj; + dlight_np = light; + } } else if (!light.is_empty() && light.node()->is_of_type(AmbientLight::get_class_type())) { - // An ambient light. We keep the color only. - AmbientLight *light_obj = DCAST(AmbientLight, light.node()); - ambient_color += light_obj->get_color(); + // An ambient light. We keep the color only. + AmbientLight *light_obj = DCAST(AmbientLight, light.node()); + 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. CullableObject *object = new CullableObject(NULL, data._state, - TransformState::make_identity(), - TransformState::make_identity(), - trav->get_gsg()); + TransformState::make_identity(), + TransformState::make_identity(), + trav->get_gsg()); object->set_draw_callback(new DrawCallback(this)); 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 &emax = extents.Max(); 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, - speedtree_max_num_visible_cells); + speedtree_max_num_visible_cells); _forest_render.SetCullCellSize(speedtree_cull_cell_size); + // Doesn't appear to be necessary to call this explicitly. + //_forest_render.EnableWind(true); + _is_valid = true; } @@ -1291,7 +1309,7 @@ init_node() { //////////////////////////////////////////////////////////////////// void SpeedTreeNode:: 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) { SpeedTreeNode *other = DCAST(SpeedTreeNode, node); add_instances_from(other, transform); @@ -1331,8 +1349,8 @@ repopulate() { if (!_forest_render.AddInstances(tree->get_tree(), &instances[0], instances.size())) { speedtree_cat.warning() - << "Failed to add " << instances.size() - << " instances for " << *tree << "\n"; + << "Failed to add " << instances.size() + << " instances for " << *tree << "\n"; write_error(speedtree_cat.warning()); } } @@ -1361,10 +1379,10 @@ repopulate() { } _visible_trees.Reserve(_forest_render.GetBaseTrees(), - _forest_render.GetBaseTrees().size(), - speedtree_max_num_visible_cells, - max_instances_by_cell, - speedtree_horizontal_billboards); + _forest_render.GetBaseTrees().size(), + speedtree_max_num_visible_cells, + max_instances_by_cell, + speedtree_horizontal_billboards); } //////////////////////////////////////////////////////////////////// @@ -1385,7 +1403,7 @@ update_terrain_cells() { // A temporary vertex data object for populating terrain. PT(GeomVertexData) vertex_data = new GeomVertexData("terrain", _terrain->get_vertex_format(), - GeomEnums::UH_static); + GeomEnums::UH_static); int num_vertices = num_tile_res * num_tile_res; vertex_data->set_num_rows(num_vertices); 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"; _terrain->fill_vertices(vertex_data, - cell_xi * cell_size, cell_yi * cell_size, - cell_size, num_tile_res); + cell_xi * cell_size, cell_yi * cell_size, + cell_size, num_tile_res); const GeomVertexArrayData *array_data = vertex_data->get_array(0); CPT(GeomVertexArrayDataHandle) handle = array_data->get_handle(); @@ -1502,7 +1520,7 @@ draw_callback(CallbackData *data) { if (!terrain) { speedtree_cat.warning() - << "Failed to render terrain\n"; + << "Failed to render terrain\n"; write_error(speedtree_cat.warning()); // 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_cards = _forest_render.RenderLeafCards(_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() - << "Failed to render forest completely: " - << branches << " " << fronds << " " << leaf_meshes << " " << leaf_cards << " " << billboards << "\n"; + << "Failed to render forest completely: " + << branches << " " << fronds << " " << leaf_meshes << " " << leaf_cards << " " << billboards << "\n"; write_error(speedtree_cat.warning()); } } @@ -1624,7 +1647,7 @@ setup_for_render(GraphicsStateGuardian *gsg) { GLenum err = glewInit(); if (err != GLEW_OK) { speedtree_cat.error() - << "GLEW initialization failed: %s\n", glewGetErrorString(err); + << "GLEW initialization failed: %s\n", glewGetErrorString(err); // Can't proceed without GLEW. _is_valid = false; return; @@ -1634,7 +1657,7 @@ setup_for_render(GraphicsStateGuardian *gsg) { // requires it. if (!GLEW_VERSION_2_0) { 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; return; } @@ -1659,47 +1682,47 @@ setup_for_render(GraphicsStateGuardian *gsg) { const STTree *tree = instance_list->get_tree(); const STInstances &instances = instance_list->_instances; if (instances.empty()) { - continue; + continue; } int max_instances = 2; SpeedTree::CMap::const_iterator si; si = _population_stats.m_mMaxNumInstancesPerCellPerBase.find(tree->get_tree()); 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. string os_textures_dir; if (!speedtree_textures_dir.empty()) { - os_textures_dir = speedtree_textures_dir.get_value().to_os_specific(); - // Ensure the path ends with a terminal slash; SpeedTree requires this. + os_textures_dir = speedtree_textures_dir.get_value().to_os_specific(); + // Ensure the path ends with a terminal slash; SpeedTree requires this. #if defined(WIN32) || defined(WIN64) - if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '\\') { - os_textures_dir += "\\"; - } + if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '\\') { + os_textures_dir += "\\"; + } #else - if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '/') { - os_textures_dir += "/"; - } + if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '/') { + os_textures_dir += "/"; + } #endif } if (!_forest_render.InitTreeGraphics((SpeedTree::CTreeRender *)tree->get_tree(), - max_instances, speedtree_horizontal_billboards, - os_textures_dir.c_str())) { - if (speedtree_cat.is_debug()) { - speedtree_cat.debug() - << "Failed to init tree graphics for " << *tree << "\n"; - write_error(speedtree_cat.debug()); - } + max_instances, speedtree_horizontal_billboards, + os_textures_dir.c_str())) { + if (speedtree_cat.is_debug()) { + speedtree_cat.debug() + << "Failed to init tree graphics for " << *tree << "\n"; + write_error(speedtree_cat.debug()); + } } } // Init overall graphics if (!_forest_render.InitGraphics(false)) { speedtree_cat.warning() - << "Failed to init graphics\n"; + << "Failed to init graphics\n"; write_error(speedtree_cat.warning()); _is_valid = false; return; @@ -1713,12 +1736,12 @@ setup_for_render(GraphicsStateGuardian *gsg) { if (has_terrain()) { // Now initialize the terrain. if (!_terrain_render.Init(speedtree_terrain_num_lods, - speedtree_terrain_resolution, - speedtree_terrain_cell_size, - _terrain->get_st_vertex_format())) { - speedtree_cat.warning() - << "Failed to init terrain\n"; - write_error(speedtree_cat.warning()); + speedtree_terrain_resolution, + speedtree_terrain_cell_size, + _terrain->get_st_vertex_format())) { + speedtree_cat.warning() + << "Failed to init terrain\n"; + write_error(speedtree_cat.warning()); } } @@ -1757,8 +1780,8 @@ cull_forest() { SpeedTree::SForestCullResultsRender &light_cull = _shadow_infos[smi]._light_cull; _forest_render.ComputeLightView - (_forest_render.GetLightDir(), _view.GetFrustumPoints(), smi, - light_view, 0.0f); + (_forest_render.GetLightDir(), _view.GetFrustumPoints(), smi, + light_view, 0.0f); light_view.SetLodRefPoint(_view.GetCameraPos()); _forest_render.CullAndComputeLOD(light_view, light_cull, false); @@ -1949,7 +1972,7 @@ fillin(DatagramIterator &scan, BamReader *manager) { if (!srt_np.is_empty()) { SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node()); if (srt_node->get_num_trees() >= 1) { - _tree = (STTree *)srt_node->get_tree(0); + _tree = (STTree *)srt_node->get_tree(0); } } } diff --git a/panda/src/speedtree/speedTreeNode.h b/panda/src/speedtree/speedTreeNode.h index 27a647921f..cd5000f587 100644 --- a/panda/src/speedtree/speedTreeNode.h +++ b/panda/src/speedtree/speedTreeNode.h @@ -140,6 +140,13 @@ PUBLISHED: 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 = ""); public: @@ -248,6 +255,9 @@ private: typedef pvector ShadowInfos; ShadowInfos _shadow_infos; + double _time_delta; + static double _global_time_delta; + static bool _authorized; static bool _done_first_init;