Panda3D
speedTreeNode.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file speedTreeNode.cxx
10  * @author drose
11  * @date 2009-03-13
12  */
13 
14 #include "pandabase.h"
15 #include "speedTreeNode.h"
16 #include "stBasicTerrain.h"
17 #include "virtualFileSystem.h"
18 #include "config_putil.h"
19 #include "cullTraverser.h"
20 #include "cullableObject.h"
21 #include "cullHandler.h"
22 #include "omniBoundingVolume.h"
23 #include "boundingSphere.h"
24 #include "boundingBox.h"
25 #include "clockObject.h"
26 #include "geomDrawCallbackData.h"
27 #include "graphicsStateGuardian.h"
28 #include "textureAttrib.h"
29 #include "lightAttrib.h"
30 #include "directionalLight.h"
31 #include "ambientLight.h"
32 #include "loader.h"
33 #include "deg_2_rad.h"
34 #include "sceneGraphReducer.h"
35 #include "pStatTimer.h"
36 
37 #ifdef SPEEDTREE_OPENGL
38 #include <glew/glew.h>
39 #endif // SPEEDTREE_OPENGL
40 
41 #ifdef SPEEDTREE_DIRECTX9
43 #endif
44 
45 using std::istream;
46 using std::ostream;
47 using std::string;
48 
49 double SpeedTreeNode::_global_time_delta = 0.0;
50 bool SpeedTreeNode::_authorized;
51 bool SpeedTreeNode::_done_first_init;
52 TypeHandle SpeedTreeNode::_type_handle;
53 TypeHandle SpeedTreeNode::DrawCallback::_type_handle;
54 
55 PStatCollector SpeedTreeNode::_cull_speedtree_pcollector("Cull:SpeedTree");
56 PStatCollector SpeedTreeNode::_cull_speedtree_shadows_pcollector("Cull:SpeedTree:Shadows");
57 PStatCollector SpeedTreeNode::_cull_speedtree_trees_pcollector("Cull:SpeedTree:Trees");
58 PStatCollector SpeedTreeNode::_cull_speedtree_terrain_pcollector("Cull:SpeedTree:Terrain");
59 PStatCollector SpeedTreeNode::_draw_speedtree_pcollector("Draw:SpeedTree");
60 PStatCollector SpeedTreeNode::_draw_speedtree_shadows_pcollector("Draw:SpeedTree:Shadows");
61 PStatCollector SpeedTreeNode::_draw_speedtree_trees_pcollector("Draw:SpeedTree:Trees");
62 PStatCollector SpeedTreeNode::_draw_speedtree_terrain_pcollector("Draw:SpeedTree:Terrain");
63 PStatCollector SpeedTreeNode::_draw_speedtree_terrain_update_pcollector("Draw:SpeedTree:Terrain:Update");
64 
65 /**
66  *
67  */
68 SpeedTreeNode::
69 SpeedTreeNode(const string &name) :
70  PandaNode(name),
71 #ifdef ST_DELETE_FOREST_HACK
72  // Early versions of SpeedTree don't destruct unused CForestRender objects
73  // correctly. To avoid crashes, we have to leak these things.
74  _forest_render(*(new SpeedTree::CForestRender)),
75 #endif
76  _time_delta(0.0)
77 {
78  init_node();
79  // For now, set an infinite bounding volume. Maybe in the future we'll
80  // change this to match whatever set of trees we're holding, though it
81  // probably doesn't really matter too much. set_internal_bounds(new
82  // OmniBoundingVolume); set_internal_bounds(new
83  // BoundingSphere(LPoint3::zero(), 10.0f));
84 
85  // Intialize the render params. First, get the shader directory.
86  Filename shaders_dir = speedtree_shaders_dir;
87 
88  // We expect the shader directory to contain at least this one token
89  // filename (to prove it's the right directory).
90  Filename token_filename = "Branch.hlsl";
91  if (!Filename(shaders_dir, token_filename).exists()) {
92  // If that shader directory doesn't work, look along the model-path.
93  if (token_filename.resolve_filename(get_model_path())) {
94  shaders_dir = token_filename.get_dirname();
95  } else {
96  if (!shaders_dir.is_directory()) {
97  speedtree_cat.warning()
98  << "speedtree-shaders-dir is set to " << shaders_dir
99  << ", which doesn't exist.\n";
100  } else {
101  speedtree_cat.warning()
102  << "speedtree-shaders-dir is set to " << shaders_dir
103  << ", which exists but doesn't contain " << token_filename
104  << ".\n";
105  }
106  }
107  }
108 
109  _os_shaders_dir = shaders_dir.to_os_specific();
110  // Ensure the path ends with a terminal slash; SpeedTree requires this.
111 #if defined(WIN32) || defined(WIN64)
112  if (!_os_shaders_dir.empty() && _os_shaders_dir[_os_shaders_dir.length() - 1] != '\\') {
113  _os_shaders_dir += "\\";
114  }
115 #else
116  if (!_os_shaders_dir.empty() && _os_shaders_dir[_os_shaders_dir.length() - 1] != '/') {
117  _os_shaders_dir += "/";
118  }
119 #endif
120 
121  SpeedTree::SForestRenderInfo render_info;
122  render_info.m_strShaderPath = _os_shaders_dir.c_str();
123  _forest_render.SetRenderInfo(render_info);
124 
125  // Now apply the rest of the config settings.
126  reload_config();
127 }
128 
129 /**
130  * Returns the total number of trees that will be rendered by this node,
131  * counting all instances of all trees.
132  */
133 int SpeedTreeNode::
135  int total_instances = 0;
136  Trees::const_iterator ti;
137  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
138  InstanceList *instance_list = (*ti);
139  total_instances += instance_list->get_num_instances();
140  }
141 
142  return total_instances;
143 }
144 
145 /**
146  * Adds a new tree for rendering. Returns the InstanceList which can be used
147  * to add to the instances for this tree. If the tree has previously been
148  * added, returns the existing InstanceList.
149  */
151 add_tree(const STTree *tree) {
152  // TODO: These should be nassertr instead of assert, but there's nothing we
153  // can really return when the assert fails.
154  assert(is_valid());
155  assert(tree->is_valid());
156 
157  InstanceList ilist(tree);
158  Trees::iterator ti = _trees.find(&ilist);
159  if (ti == _trees.end()) {
160  // This is the first time that this particular tree has been added.
161  InstanceList *instance_list = new InstanceList(tree);
162  std::pair<Trees::iterator, bool> result = _trees.insert(instance_list);
163  ti = result.first;
164  bool inserted = result.second;
165  nassertr(inserted, *(*ti));
166 
167  if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) {
168  speedtree_cat.warning()
169  << "Failed to register tree " << tree->get_fullpath() << "\n";
170  write_error(speedtree_cat.warning());
171  }
172  }
173 
174  _needs_repopulate = true;
175  mark_internal_bounds_stale();
176  InstanceList *instance_list = (*ti);
177  return *instance_list;
178 }
179 
180 /**
181  * Removes all instances of the indicated tree. Returns the number of
182  * instances removed.
183  */
184 int SpeedTreeNode::
185 remove_tree(const STTree *tree) {
186  InstanceList ilist(tree);
187  Trees::iterator ti = _trees.find(&ilist);
188  if (ti == _trees.end()) {
189  // The tree was not already present.
190  return 0;
191  }
192 
193  if (!_forest_render.UnregisterTree(tree->get_tree())) {
194  speedtree_cat.warning()
195  << "Failed to unregister tree " << tree->get_fullpath() << "\n";
196  write_error(speedtree_cat.warning());
197  }
198 
199  _needs_repopulate = true;
200  mark_internal_bounds_stale();
201 
202  InstanceList *instance_list = (*ti);
203  int num_removed = instance_list->get_num_instances();
204  _trees.erase(ti);
205  delete instance_list;
206 
207  return num_removed;
208 }
209 
210 /**
211  * Removes all instances of all trees from the node.
212  */
213 void SpeedTreeNode::
215  Trees::iterator ti;
216  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
217  InstanceList *instance_list = (*ti);
218  const STTree *tree = instance_list->get_tree();
219  if (!_forest_render.UnregisterTree(tree->get_tree())) {
220  speedtree_cat.warning()
221  << "Failed to unregister tree " << tree->get_fullpath() << "\n";
222  write_error(speedtree_cat.warning());
223  }
224  delete instance_list;
225  }
226 
227  _trees.clear();
228  _needs_repopulate = true;
229  mark_internal_bounds_stale();
230 }
231 
232 /**
233  * Returns true if the indicated tree has any instances within this node,
234  * false otherwise.
235  */
236 bool SpeedTreeNode::
237 has_instance_list(const STTree *tree) const {
238  InstanceList ilist(tree);
239  Trees::const_iterator ti = _trees.find(&ilist);
240  return (ti != _trees.end());
241 }
242 
243 /**
244  * Returns a list of transforms that corresponds to the instances at which the
245  * indicated tree appears. You should ensure that has_instance_list() returns
246  * true before calling this method.
247  */
249 get_instance_list(const STTree *tree) const {
250  InstanceList ilist(tree);
251  Trees::const_iterator ti = _trees.find(&ilist);
252  if (ti == _trees.end()) {
253  // The tree was not already present.
254  static InstanceList empty_list(nullptr);
255  return empty_list;
256  }
257 
258  InstanceList *instance_list = (*ti);
259  return *instance_list;
260 }
261 
262 /**
263  * Returns a modifiable list of transforms that corresponds to the instances
264  * of this tree. This is equivalent to add_tree().
265  */
268  return add_tree(tree);
269 }
270 
271 /**
272  * Adds a new instance of the indicated tree at the indicated transform.
273  */
274 void SpeedTreeNode::
275 add_instance(const STTree *tree, const STTransform &transform) {
276  if (speedtree_follow_terrain && has_terrain()) {
277  STTransform new_transform = transform;
278  new_transform._pos[2] = _terrain->get_height(new_transform._pos[0], new_transform._pos[1]);
279  add_tree(tree).add_instance(new_transform);
280  } else {
281  add_tree(tree).add_instance(transform);
282  }
283 }
284 
285 /**
286  * Walks the scene graph beginning at root, looking for nested SpeedTreeNodes.
287  * For each SpeedTreeNode found, adds all of the instances defined within that
288  * SpeedTreeNode as instances of this node, after applying the indicated
289  * scene-graph transform.
290  */
291 void SpeedTreeNode::
292 add_instances(const NodePath &root, const TransformState *transform) {
293  nassertv(!root.is_empty());
294  r_add_instances(root.node(), transform->compose(root.get_transform()),
295  Thread::get_current_thread());
296 }
297 
298 /**
299  * Adds all of the instances defined within the indicated SpeedTreeNode as
300  * instances of this node. Does not recurse to children.
301  */
302 void SpeedTreeNode::
304  int num_trees = other->get_num_trees();
305  for (int ti = 0; ti < num_trees; ++ti) {
306  const InstanceList &other_instance_list = other->get_instance_list(ti);
307  const STTree *tree = other_instance_list.get_tree();
308  InstanceList &this_instance_list = add_tree(tree);
309 
310  int num_instances = other_instance_list.get_num_instances();
311  for (int i = 0; i < num_instances; ++i) {
312  STTransform other_trans = other_instance_list.get_instance(i);
313  this_instance_list.add_instance(other_trans);
314  }
315  }
316 }
317 
318 /**
319  * Adds all of the instances defined within the indicated SpeedTreeNode as
320  * instances of this node, after applying the indicated scene-graph transform.
321  * Does not recurse to children.
322  */
323 void SpeedTreeNode::
324 add_instances_from(const SpeedTreeNode *other, const TransformState *transform) {
325  int num_trees = other->get_num_trees();
326  for (int ti = 0; ti < num_trees; ++ti) {
327  const InstanceList &other_instance_list = other->get_instance_list(ti);
328  const STTree *tree = other_instance_list.get_tree();
329  InstanceList &this_instance_list = add_tree(tree);
330 
331  int num_instances = other_instance_list.get_num_instances();
332  for (int i = 0; i < num_instances; ++i) {
333  CPT(TransformState) other_trans = other_instance_list.get_instance(i);
334  CPT(TransformState) new_trans = transform->compose(other_trans);
335 
336  if (speedtree_follow_terrain && has_terrain()) {
337  STTransform new_transform = new_trans;
338  new_transform._pos[2] = _terrain->get_height(new_transform._pos[0], new_transform._pos[1]);
339  this_instance_list.add_instance(new_transform);
340 
341  } else {
342  this_instance_list.add_instance(new_trans.p());
343  }
344  }
345  }
346 }
347 
348 /**
349  * Creates a number of random instances of the indicated true, within the
350  * indicated range. If a terrain is present, height_min and height_max
351  * restrict trees to the (x, y) positions that fall within the indicated
352  * terrain, and slope_min and slope_max restrict trees to the (x, y) positions
353  * that have a matching slope. If a terrain is not present, height_min and
354  * height_max specify a random range of Z heights, and slope_min and slope_max
355  * are ignored.
356  */
357 void SpeedTreeNode::
358 add_random_instances(const STTree *tree, int quantity,
359  PN_stdfloat x_min, PN_stdfloat x_max,
360  PN_stdfloat y_min, PN_stdfloat y_max,
361  PN_stdfloat scale_min, PN_stdfloat scale_max,
362  PN_stdfloat height_min, PN_stdfloat height_max,
363  PN_stdfloat slope_min, PN_stdfloat slope_max,
364  Randomizer &randomizer) {
365  InstanceList &instance_list = add_tree(tree);
366  _needs_repopulate = true;
367 
368  for (int i = 0; i < quantity; ++i) {
369  STTransform transform;
370  transform._pos[0] = randomizer.random_real(x_max - x_min) + x_min;
371  transform._pos[1] = randomizer.random_real(y_max - y_min) + y_min;
372  transform._rotate = randomizer.random_real(360.0);
373  transform._scale = randomizer.random_real(scale_max - scale_min) + scale_min;
374 
375  if (has_terrain()) {
376  // Spin till we find a valid match with terrain.
377  int repeat_count = speedtree_max_random_try_count;
378  while (!_terrain->placement_is_acceptable(transform._pos[0], transform._pos[1], height_min, height_max, slope_min, slope_max)) {
379  transform._pos[0] = randomizer.random_real(x_max - x_min) + x_min;
380  transform._pos[1] = randomizer.random_real(y_max - y_min) + y_min;
381  if (--repeat_count == 0) {
382  nassert_raise("Exceeded speedtree-max-random-try-count; bad placement parameters?");
383  return;
384  }
385  }
386  transform._pos[2] = _terrain->get_height(transform._pos[0], transform._pos[1]);
387 
388  } else {
389  // No terrain; just pick a random height.
390  transform._pos[2] = randomizer.random_real(height_max - height_min) + height_min;
391  }
392  instance_list.add_instance(transform);
393  }
394 }
395 
396 /**
397  * Opens and reads the named STF (SpeedTree Forest) file, and adds the SRT
398  * files named within as instances of this node. Returns true on success,
399  * false on failure.
400  */
401 bool SpeedTreeNode::
402 add_from_stf(const Filename &stf_filename, const LoaderOptions &options) {
404 
405  Filename fullpath = Filename::text_filename(stf_filename);
406  vfs->resolve_filename(fullpath, get_model_path());
407 
408  if (!vfs->exists(fullpath)) {
409  speedtree_cat.warning()
410  << "Couldn't find " << stf_filename << "\n";
411  return false;
412  }
413 
414  PT(VirtualFile) file = vfs->get_file(fullpath);
415  if (file == nullptr) {
416  // No such file.
417  speedtree_cat.error()
418  << "Could not find " << stf_filename << "\n";
419  return false;
420  }
421 
422  if (speedtree_cat.is_debug()) {
423  speedtree_cat.debug()
424  << "Reading STF file " << fullpath << "\n";
425  }
426 
427  istream *in = file->open_read_file(true);
428  bool success = add_from_stf(*in, fullpath, options);
429  vfs->close_read_file(in);
430 
431  return success;
432 }
433 
434 /**
435  * Reads text data from the indicated stream, which is understood to represent
436  * the named STF (SpeedTree Forest) file, and adds the SRT files named within
437  * as instances of this node. Returns true on success, false on failure.
438  *
439  * The pathname is used for reference only; if nonempty, it provides a search
440  * directory for named SRT files.
441  *
442  * The Loader and LoaderOptions, if provided, are used to load the SRT files.
443  * If the Loader pointer is NULL, the default global Loader is used instead.
444  */
445 bool SpeedTreeNode::
446 add_from_stf(istream &in, const Filename &pathname,
447  const LoaderOptions &options, Loader *loader) {
448  if (loader == nullptr) {
449  loader = Loader::get_global_ptr();
450  }
451  string os_filename;
452 
453  Filename dirname = pathname.get_dirname();
454  dirname.make_absolute();
455  DSearchPath search;
456  search.append_directory(dirname);
457 
458  typedef pmap<Filename, CPT(STTree) > AlreadyLoaded;
459  AlreadyLoaded already_loaded;
460 
461  // The STF file format doesn't allow for spaces in the SRT filename.
462  in >> os_filename;
463  while (in && !in.eof()) {
464  CPT(STTree) tree;
465  Filename srt_filename = Filename::from_os_specific(os_filename);
466  AlreadyLoaded::iterator ai = already_loaded.find(srt_filename);
467  if (ai != already_loaded.end()) {
468  tree = (*ai).second;
469  } else {
470  // Resolve the SRT filename relative to the STF file first.
471  srt_filename.resolve_filename(search);
472 
473  // Now load up the SRT file using the Panda loader (which will also
474  // search the model-path if necessary).
475  PT(PandaNode) srt_root = loader->load_sync(srt_filename);
476 
477  if (srt_root != nullptr) {
478  NodePath srt(srt_root);
479  NodePath srt_np = srt.find("**/+SpeedTreeNode");
480  if (!srt_np.is_empty()) {
481  SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node());
482  if (srt_node->get_num_trees() >= 1) {
483  tree = srt_node->get_tree(0);
484  }
485  }
486  }
487  already_loaded[srt_filename] = tree;
488  }
489 
490  // Now we've loaded the SRT data, so apply it the appropriate number of
491  // times to the locations specified.
492  int num_instances;
493  in >> num_instances;
494  for (int ni = 0; ni < num_instances && in && !in.eof(); ++ni) {
495  LPoint3 pos;
496  PN_stdfloat rotate, scale;
497  in >> pos[0] >> pos[1] >> pos[2] >> rotate >> scale;
498 
499  if (!speedtree_5_2_stf) {
500  // 5.1 or earlier stf files also included these additional values,
501  // which we will ignore:
502  PN_stdfloat height_min, height_max, slope_min, slope_max;
503  in >> height_min >> height_max >> slope_min >> slope_max;
504  }
505 
506  if (tree != nullptr) {
507  add_instance(tree, STTransform(pos, rad_2_deg(rotate), scale));
508  }
509  }
510  in >> os_filename;
511  }
512 
513  // Consume any whitespace at the end of the file.
514  in >> std::ws;
515 
516  if (!in.eof()) {
517  // If we didn't read all the way to end-of-file, there was an error.
518  in.clear();
519  string text;
520  in >> text;
521  speedtree_cat.error()
522  << "Unexpected text in " << pathname << " at \"" << text << "\"\n";
523  return false;
524  }
525 
526  // Return true if we successfully read all the way to end-of-file.
527  return true;
528 }
529 
530 /**
531  * A convenience function to set up terrain geometry by reading a terrain.txt
532  * file as defined by SpeedTree. This file names the various map files that
533  * define the terrain, as well as defining parameters size as its size and
534  * color.
535  *
536  * This method implicitly creates a STBasicTerrain object and passes it to
537  * set_terrain().
538  */
539 bool SpeedTreeNode::
540 setup_terrain(const Filename &terrain_file) {
541  PT(STBasicTerrain) terrain = new STBasicTerrain;
542  if (terrain->setup_terrain(terrain_file)) {
543  set_terrain(terrain);
544  return true;
545  }
546 
547  return false;
548 }
549 
550 /**
551  * Associated a terrain with the node. If the terrain has not already been
552  * loaded prior to this call, load_data() will be called immediately.
553  *
554  * The terrain will be rendered using SpeedTree callbacks, and trees may be
555  * repositioned with a call to snap_to_terrain().
556  */
557 void SpeedTreeNode::
559  _terrain = nullptr;
560  _needs_repopulate = true;
561 
562  if (terrain == nullptr) {
563  return;
564  }
565 
566  if (!terrain->is_valid()) {
567  // If the terrain was not already loaded, load it immediately.
568  terrain->load_data();
569  }
570 
571  nassertv(terrain->is_valid());
572  nassertv(terrain->get_num_splat_layers() == SpeedTree::c_nNumTerrainSplatLayers);
573  _terrain = terrain;
574 
575  _terrain_render.SetShaderLoader(_forest_render.GetShaderLoader());
576 
577  SpeedTree::STerrainRenderInfo trender_info;
578  trender_info.m_strShaderPath = _os_shaders_dir.c_str();
579 
580  string os_specific = terrain->get_normal_map().to_os_specific();
581  trender_info.m_strNormalMap = os_specific.c_str();
582  os_specific = terrain->get_splat_map().to_os_specific();
583  trender_info.m_strSplatMap = os_specific.c_str();
584 
585  for (int i = 0; i < SpeedTree::c_nNumTerrainSplatLayers; ++i) {
586  os_specific = terrain->get_splat_layer(i).to_os_specific();
587  trender_info.m_astrSplatLayers[i] = os_specific.c_str();
588  trender_info.m_afSplatTileValues[i] = terrain->get_splat_layer_tiling(i);
589  }
590 
591  trender_info.m_fNormalMapBlueScale = 1.0f;
592  trender_info.m_bShadowsEnabled = false; // what does this do?
593  trender_info.m_bZPrePass = false;
594 
595  _terrain_render.SetRenderInfo(trender_info);
596 
597  _terrain_render.SetHeightHints(terrain->get_min_height(), terrain->get_max_height());
598 
599  if (speedtree_follow_terrain) {
600  snap_to_terrain();
601  }
602 }
603 
604 /**
605  * Adjusts all the trees in this node so that their Z position matches the
606  * height of the terrain at their X, Y position.
607  */
608 void SpeedTreeNode::
610  Trees::iterator ti;
611  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
612  InstanceList *instance_list = (*ti);
613 
614  int num_instances = instance_list->get_num_instances();
615  if (_terrain != nullptr) {
616  for (int i = 0; i < num_instances; ++i) {
617  STTransform trans = instance_list->get_instance(i);
618  LPoint3 pos = trans.get_pos();
619  pos[2] = _terrain->get_height(pos[0], pos[1]);
620  trans.set_pos(pos);
621  instance_list->set_instance(i, trans);
622  }
623  } else {
624  for (int i = 0; i < num_instances; ++i) {
625  STTransform trans = instance_list->get_instance(i);
626  LPoint3 pos = trans.get_pos();
627  pos[2] = 0.0f;
628  trans.set_pos(pos);
629  instance_list->set_instance(i, trans);
630  }
631  }
632  }
633 
634  _needs_repopulate = true;
635 }
636 
637 /**
638  * Re-reads the current setting of all of the relevant config variables and
639  * applies them to this node. This can be called after changing config
640  * settings, to make them apply to this particular node.
641  */
642 void SpeedTreeNode::
644 
645  _shadow_infos.clear();
646  int num_shadow_maps = speedtree_cascading_shadow_splits.get_num_words();
647  if (num_shadow_maps > SpeedTree::c_nMaxNumShadowMaps) {
648  speedtree_cat.warning()
649  << "SpeedTree is current compiled to support a maximum of "
650  << SpeedTree::c_nMaxNumShadowMaps << " shadow maps.\n";
651  num_shadow_maps = SpeedTree::c_nMaxNumShadowMaps;
652  }
653  _shadow_infos.insert(_shadow_infos.begin(), num_shadow_maps, ShadowInfo());
654  for (int smi = 0; smi < num_shadow_maps; ++smi) {
655  _shadow_infos[smi]._shadow_split = speedtree_cascading_shadow_splits[smi];
656  }
657 
658  SpeedTree::SForestRenderInfo render_info = _forest_render.GetRenderInfo();
659 
660  render_info.m_nMaxAnisotropy = speedtree_max_anisotropy;
661  render_info.m_bHorizontalBillboards = speedtree_horizontal_billboards;
662  render_info.m_fAlphaTestScalar = speedtree_alpha_test_scalar;
663  render_info.m_bZPrePass = speedtree_z_pre_pass;
664  render_info.m_nMaxBillboardImagesByBase = speedtree_max_billboard_images_by_base;
665  render_info.m_fVisibility = speedtree_visibility;
666  render_info.m_fGlobalLightScalar = speedtree_global_light_scalar;
667  render_info.m_sLightMaterial.m_vSpecular = SpeedTree::Vec4(speedtree_specular_color[0], speedtree_specular_color[1], speedtree_specular_color[2], speedtree_specular_color[3]);
668  render_info.m_sLightMaterial.m_vEmissive = SpeedTree::Vec4(speedtree_emissive_color[0], speedtree_emissive_color[1], speedtree_emissive_color[2], speedtree_emissive_color[3]);
669  render_info.m_bSpecularLighting = speedtree_specular_lighting;
670  render_info.m_bTransmissionLighting = speedtree_transmission_lighting;
671  render_info.m_bDetailLayer = speedtree_detail_layer;
672  render_info.m_bDetailNormalMapping = speedtree_detail_normal_mapping;
673  render_info.m_bAmbientContrast = speedtree_ambient_contrast;
674  render_info.m_fTransmissionScalar = speedtree_transmission_scalar;
675  render_info.m_fFogStartDistance = speedtree_fog_distance[0];
676  render_info.m_fFogEndDistance = speedtree_fog_distance[1];
677  render_info.m_vFogColor = SpeedTree::Vec3(speedtree_fog_color[0], speedtree_fog_color[1], speedtree_fog_color[2]);
678  render_info.m_vSkyColor = SpeedTree::Vec3(speedtree_sky_color[0], speedtree_sky_color[1], speedtree_sky_color[2]);
679  render_info.m_fSkyFogMin = speedtree_sky_fog[0];
680  render_info.m_fSkyFogMax = speedtree_sky_fog[1];
681  render_info.m_vSunColor = SpeedTree::Vec3(speedtree_sun_color[0], speedtree_sun_color[1], speedtree_sun_color[2]);
682  render_info.m_fSunSize = speedtree_sun_size;
683  render_info.m_fSunSpreadExponent = speedtree_sun_spread_exponent;
684  render_info.m_fSunFogBloom = speedtree_sun_fog_bloom;
685  render_info.m_nNumShadowMaps = num_shadow_maps;
686  render_info.m_nShadowMapResolution = speedtree_shadow_map_resolution;
687  render_info.m_bSmoothShadows = speedtree_smooth_shadows;
688  render_info.m_bShowShadowSplitsOnTerrain = speedtree_show_shadow_splits_on_terrain;
689  render_info.m_bWindEnabled = speedtree_wind_enabled;
690  render_info.m_bFrondRippling = speedtree_frond_rippling;
691 
692  _forest_render.SetRenderInfo(render_info);
693 
694  _terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy);
695  _terrain_render.SetHint(SpeedTree::CTerrain::HINT_MAX_NUM_VISIBLE_CELLS,
696  speedtree_max_num_visible_cells);
697  _visible_terrain.Reserve(speedtree_max_num_visible_cells);
698 
699  _needs_repopulate = true;
700 }
701 
702 /**
703  * Specifies the overall wind strength and direction. Gusts are controlled
704  * internally.
705  */
706 void SpeedTreeNode::
707 set_wind(double strength, const LVector3 &direction) {
708  _forest_render.SetGlobalWindStrength(strength);
709  _forest_render.SetGlobalWindDirection(SpeedTree::Vec3(direction[0], direction[1], direction[2]));
710 }
711 
712 /**
713  * Make this call to initialized the SpeedTree API and verify the license. If
714  * an empty string is passed for the license, the config variable speedtree-
715  * license is consulted. Returns true on success, false on failure. If this
716  * call is not made explicitly, it will be made implicitly the first time a
717  * SpeedTreeNode is created.
718  */
719 bool SpeedTreeNode::
720 authorize(const string &license) {
721  if (!_authorized) {
722  if (!license.empty()) {
723  SpeedTree::CCore::Authorize(license.c_str());
724  } else {
725  if (!speedtree_license.empty()) {
726  SpeedTree::CCore::Authorize(speedtree_license.c_str());
727  }
728  }
729 
730  _authorized = SpeedTree::CCore::IsAuthorized();
731 
732  SpeedTree::CCore::SetTextureFlip(true);
733  }
734 
735  return _authorized;
736 }
737 
738 /**
739  *
740  */
741 SpeedTreeNode::
742 SpeedTreeNode(const SpeedTreeNode &copy) :
743  PandaNode(copy),
744  _os_shaders_dir(copy._os_shaders_dir),
745  _shadow_infos(copy._shadow_infos),
746 #ifdef ST_DELETE_FOREST_HACK
747  // Early versions of SpeedTree don't destruct unused CForestRender objects
748  // correctly. To avoid crashes, we have to leak these things.
749  _forest_render(*(new SpeedTree::CForestRender)),
750 #endif
751  _time_delta(copy._time_delta)
752 {
753  init_node();
754 
755  _forest_render.SetRenderInfo(copy._forest_render.GetRenderInfo());
756  _terrain_render.SetRenderInfo(copy._terrain_render.GetRenderInfo());
757 
758  // No way to copy these parameters, so we just re-assign them.
759  _terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy);
760  _terrain_render.SetHint(SpeedTree::CTerrain::HINT_MAX_NUM_VISIBLE_CELLS,
761  speedtree_max_num_visible_cells);
762  _visible_terrain.Reserve(speedtree_max_num_visible_cells);
763 
764  Trees::const_iterator ti;
765  for (ti = copy._trees.begin(); ti != copy._trees.end(); ++ti) {
766  InstanceList *instance_list = (*ti);
767  const STTree *tree = instance_list->get_tree();
768  if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) {
769  speedtree_cat.warning()
770  << "Failed to register tree " << tree->get_fullpath() << "\n";
771  write_error(speedtree_cat.warning());
772  }
773 
774  _trees.push_back(new InstanceList(*instance_list));
775  }
776  _trees.sort();
777 
778  set_terrain(copy._terrain);
779 
780  _needs_repopulate = true;
781  mark_internal_bounds_stale();
782 }
783 
784 /**
785  *
786  */
787 SpeedTreeNode::
788 ~SpeedTreeNode() {
790  // Help reduce memory waste from ST_DELETE_FOREST_HACK.
791  _forest_render.ClearInstances();
792 }
793 
794 /**
795  * Returns a newly-allocated Node that is a shallow copy of this one. It will
796  * be a different Node pointer, but its internal data may or may not be shared
797  * with that of the original Node.
798  */
800 make_copy() const {
801  return new SpeedTreeNode(*this);
802 }
803 
804 /**
805  * Collapses this node with the other node, if possible, and returns a pointer
806  * to the combined node, or NULL if the two nodes cannot safely be combined.
807  *
808  * The return value may be this, other, or a new node altogether.
809  *
810  * This function is called from GraphReducer::flatten(), and need not deal
811  * with children; its job is just to decide whether to collapse the two nodes
812  * and what the collapsed node should look like.
813  */
816  if (is_exact_type(get_class_type()) &&
817  other->is_exact_type(get_class_type())) {
818  // Two SpeedTreeNodes can combine by moving trees from one to the other,
819  // similar to the way GeomNodes combine.
820  SpeedTreeNode *gother = DCAST(SpeedTreeNode, other);
821 
822  // But, not if they both have a terrain set.
823  if (has_terrain() && gother->has_terrain()) {
824  return nullptr;
825 
826  } else if (gother->has_terrain()) {
827  set_terrain(gother->get_terrain());
828  }
829 
830  add_instances_from(gother);
831  return this;
832  }
833 
834  return PandaNode::combine_with(other);
835 }
836 
837 /**
838  * Applies whatever attributes are specified in the AccumulatedAttribs object
839  * (and by the attrib_types bitmask) to the vertices on this node, if
840  * appropriate. If this node uses geom arrays like a GeomNode, the supplied
841  * GeomTransformer may be used to unify shared arrays across multiple
842  * different nodes.
843  *
844  * This is a generalization of xform().
845  */
846 void SpeedTreeNode::
847 apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
848  GeomTransformer &transformer) {
849  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
850  STTransform xform = attribs._transform;
851  Trees::iterator ti;
852  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
853  InstanceList *instance_list = (*ti);
854  STInstances &instances = instance_list->_instances;
855  STInstances::iterator sti;
856  for (sti = instances.begin(); sti != instances.end(); ++sti) {
857  STTransform orig_transform = *sti;
858  (*sti) = orig_transform * xform;
859  }
860  }
861  }
862  mark_internal_bounds_stale();
863 }
864 
865 /**
866  * This function will be called during the cull traversal to perform any
867  * additional operations that should be performed at cull time. This may
868  * include additional manipulation of render state or additional
869  * visible/invisible decisions, or any other arbitrary operation.
870  *
871  * Note that this function will *not* be called unless set_cull_callback() is
872  * called in the constructor of the derived class. It is necessary to call
873  * set_cull_callback() to indicated that we require cull_callback() to be
874  * called.
875  *
876  * By the time this function is called, the node has already passed the
877  * bounding-volume test for the viewing frustum, and the node's transform and
878  * state have already been applied to the indicated CullTraverserData object.
879  *
880  * The return value is true if this node should be visible, or false if it
881  * should be culled.
882  */
883 bool SpeedTreeNode::
885  if (!_is_valid) {
886  return false;
887  }
888  PStatTimer timer(_cull_speedtree_pcollector);
889 
890  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, trav->get_gsg());
891  nassertr(gsg != nullptr, true);
892  if (!validate_api(gsg)) {
893  return false;
894  }
895 
897  _forest_render.SetGlobalTime(clock->get_frame_time() + _time_delta + _global_time_delta);
898  _forest_render.AdvanceGlobalWind();
899 
900  // Compute the modelview and camera transforms, to pass to the SpeedTree
901  // CView structure.
902  CPT(TransformState) orig_modelview = data.get_modelview_transform(trav);
903  CPT(TransformState) modelview = trav->get_scene()->get_cs_transform()->compose(orig_modelview);
904  CPT(TransformState) camera_transform = modelview->invert_compose(TransformState::make_identity());
905  LMatrix4f modelview_mat = LCAST(float, modelview->get_mat());
906  const LPoint3 &camera_pos = camera_transform->get_pos();
907  const Lens *lens = trav->get_scene()->get_lens();
908 
909  LMatrix4f projection_mat =
910  LCAST(float, LMatrix4::convert_mat(gsg->get_internal_coordinate_system(), lens->get_coordinate_system()) *
911  lens->get_projection_mat());
912 
913  _view.Set(SpeedTree::Vec3(camera_pos[0], camera_pos[1], camera_pos[2]),
914  SpeedTree::Mat4x4(projection_mat.get_data()),
915  SpeedTree::Mat4x4(modelview_mat.get_data()),
916  lens->get_near(), lens->get_far());
917 
918  // Convert the render state to SpeedTree's input.
919  const RenderState *state = data._state;
920 
921  // Check texture state. If all textures are disabled, then we ask SpeedTree
922  // to disable textures.
923  bool show_textures = true;
924  const TextureAttrib *ta = DCAST(TextureAttrib, state->get_attrib(TextureAttrib::get_class_slot()));
925  if (ta != nullptr) {
926  show_textures = !ta->has_all_off();
927  }
928  _forest_render.EnableTexturing(show_textures);
929  _terrain_render.EnableTexturing(show_textures);
930 
931  // Check lighting state. SpeedTree only supports a single directional
932  // light; we look for a directional light in the lighting state and pass its
933  // direction and color to SpeedTree. We also accumulate the ambient light
934  // colors.
935  LColor ambient_color(0.0f, 0.0f, 0.0f, 0.0f);
936  DirectionalLight *dlight = nullptr;
937  NodePath dlight_np;
938  LColor diffuse_color;
939 
940  int diffuse_priority = 0;
941  const LightAttrib *la = DCAST(LightAttrib, state->get_attrib(LightAttrib::get_class_slot()));
942  if (la != nullptr) {
943  for (int i = 0; i < la->get_num_on_lights(); ++i) {
944  NodePath light = la->get_on_light(i);
945  if (!light.is_empty() && light.node()->is_of_type(DirectionalLight::get_class_type())) {
946  // A directional light.
947  DirectionalLight *light_obj = DCAST(DirectionalLight, light.node());
948  if (dlight == nullptr || light_obj->get_priority() > dlight->get_priority()) {
949  // Here's the most important directional light.
950  dlight = light_obj;
951  dlight_np = light;
952  }
953  } else if (!light.is_empty() && light.node()->is_of_type(AmbientLight::get_class_type())) {
954  // An ambient light. We keep the color only.
955  AmbientLight *light_obj = DCAST(AmbientLight, light.node());
956  ambient_color += light_obj->get_color();
957  }
958  }
959  }
960 
961  if (dlight != nullptr) {
962  CPT(TransformState) transform = dlight_np.get_transform(trav->get_scene()->get_scene_root().get_parent());
963  LVector3 dir = dlight->get_direction() * transform->get_mat();
964  dir.normalize();
965  _light_dir = SpeedTree::Vec3(dir[0], dir[1], dir[2]);
966  diffuse_color = dlight->get_color();
967 
968  } else {
969  // No light. But there's no way to turn off lighting in SpeedTree. In
970  // lieu of this, we just shine a light from above.
971  _light_dir = SpeedTree::Vec3(0.0, 0.0, -1.0);
972 
973  // Also, we set ambient and diffuse colors to the same full-white value.
974  ambient_color.set(1.0f, 1.0f, 1.0f, 1.0f);
975  diffuse_color.set(1.0f, 1.0f, 1.0f, 1.0f);
976  }
977 
978  SpeedTree::SForestRenderInfo render_info = _forest_render.GetRenderInfo();
979  render_info.m_sLightMaterial.m_vAmbient = SpeedTree::Vec4(ambient_color[0], ambient_color[1], ambient_color[2], 1.0f);
980  render_info.m_sLightMaterial.m_vDiffuse = SpeedTree::Vec4(diffuse_color[0], diffuse_color[1], diffuse_color[2], 1.0f);
981  _forest_render.SetRenderInfo(render_info);
982 
983  _forest_render.SetLightDir(_light_dir);
984 
985  SpeedTree::st_float32 updated_splits[SpeedTree::c_nMaxNumShadowMaps];
986  memset(updated_splits, 0, sizeof(updated_splits));
987  for (int smi = 0; smi < (int)_shadow_infos.size(); ++smi) {
988  updated_splits[smi] = _shadow_infos[smi]._shadow_split;
989  };
990 
991  _forest_render.SetCascadedShadowMapDistances(updated_splits, lens->get_far());
992  _forest_render.SetShadowFadePercentage(speedtree_shadow_fade);
993 
994  if (!_needs_repopulate) {
995  // Don't bother culling now unless we're correctly fully populated.
996  // (Culling won't be accurate unless the forest has been populated, but we
997  // have to be in the draw traversal to populate.)
998  cull_forest();
999  }
1000 
1001  // Recurse onto the node's children.
1002  return true;
1003 }
1004 
1005 /**
1006  * Returns true if there is some value to visiting this particular node during
1007  * the cull traversal for any camera, false otherwise. This will be used to
1008  * optimize the result of get_net_draw_show_mask(), so that any subtrees that
1009  * contain only nodes for which is_renderable() is false need not be visited.
1010  */
1011 bool SpeedTreeNode::
1012 is_renderable() const {
1013  return true;
1014 }
1015 
1016 /**
1017  * Adds the node's contents to the CullResult we are building up during the
1018  * cull traversal, so that it will be drawn at render time. For most nodes
1019  * other than GeomNodes, this is a do-nothing operation.
1020  */
1021 void SpeedTreeNode::
1023  if (_is_valid) {
1024  // We create a CullableObject that has an explicit draw_callback into this
1025  // node, so that we can make the appropriate calls into SpeedTree to
1026  // render the forest during the actual draw.
1027  CullableObject *object =
1028  new CullableObject(nullptr, data._state,
1029  TransformState::make_identity());
1030  object->set_draw_callback(new DrawCallback(this));
1031  trav->get_cull_handler()->record_object(object, trav);
1032  }
1033 }
1034 
1035 /**
1036  * Walks through the scene graph beginning at this node, and does whatever
1037  * initialization is required to render the scene properly with the indicated
1038  * GSG. It is not strictly necessary to call this, since the GSG will
1039  * initialize itself when the scene is rendered, but this may take some of the
1040  * overhead away from that process.
1041  *
1042  * In particular, this will ensure that textures within the scene are loaded
1043  * in texture memory, and display lists are built up from static geometry.
1044  */
1045 void SpeedTreeNode::
1047  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase);
1048  if (validate_api(gsg)) {
1049  setup_for_render(gsg);
1050  }
1051 }
1052 
1053 /**
1054  * Returns a newly-allocated BoundingVolume that represents the internal
1055  * contents of the node. Should be overridden by PandaNode classes that
1056  * contain something internally.
1057  */
1058 void SpeedTreeNode::
1060  int &internal_vertices,
1061  int pipeline_stage,
1062  Thread *current_thread) const {
1063  internal_vertices = 0;
1064 
1065  SpeedTree::CExtents extents;
1066  Trees::const_iterator ti;
1067  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
1068  InstanceList *instance_list = (*ti);
1069  const STTree *tree = instance_list->get_tree();
1070 
1071  const STInstances &st_instances = instance_list->_instances;
1072  STInstances::const_iterator ii;
1073  for (ii = st_instances.begin(); ii != st_instances.end(); ++ii) {
1074  SpeedTree::CExtents tree_extents = tree->get_tree()->GetExtents();
1075  tree_extents.Rotate((*ii).GetRotationAngle());
1076  tree_extents.Scale((*ii).GetScale());
1077  tree_extents.Translate((*ii).GetPos());
1078  extents.ExpandAround(tree_extents);
1079  }
1080  }
1081 
1082  const SpeedTree::Vec3 &emin = extents.Min();
1083  const SpeedTree::Vec3 &emax = extents.Max();
1084  internal_bounds = new BoundingBox(LPoint3(emin[0], emin[1], emin[2]),
1085  LPoint3(emax[0], emax[1], emax[2]));
1086 }
1087 
1088 /**
1089  * Writes a brief description of the node to the indicated output stream.
1090  * This is invoked by the << operator. It may be overridden in derived
1091  * classes to include some information relevant to the class.
1092  */
1093 void SpeedTreeNode::
1094 output(ostream &out) const {
1095  PandaNode::output(out);
1096  out
1097  << " (" << get_num_trees() << " unique trees with "
1098  << count_total_instances() << " total instances)";
1099 }
1100 
1101 /**
1102  *
1103  */
1104 void SpeedTreeNode::
1105 write(ostream &out, int indent_level) const {
1106  PandaNode::write(out, indent_level);
1107 
1108  // This makes NodePath.ls() too confusing.
1109  /*
1110  Trees::const_iterator ti;
1111  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
1112  InstanceList *instance_list = (*ti);
1113  indent(out, indent_level + 2)
1114  << *instance_list << "\n";
1115  }
1116  */
1117 }
1118 
1119 
1120 /**
1121  * Writes the current SpeedTree error message to the indicated stream.
1122  */
1123 void SpeedTreeNode::
1124 write_error(ostream &out) {
1125  const char *error = SpeedTree::CCore::GetError();
1126  if (error != nullptr) {
1127  out << error;
1128  }
1129  out << "\n";
1130 }
1131 
1132 /**
1133  * Uses SpeedTree::CRenderState to set the indicated transparency mode.
1134  */
1135 void SpeedTreeNode::
1136 set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode eMode) const {
1137  // turn all modes off (no telling what render state the client application
1138  // might be in before this call)
1139  SpeedTree::CRenderState::SetBlending(false);
1140  SpeedTree::CRenderState::SetAlphaTesting(false);
1141  SpeedTree::CRenderState::SetAlphaToCoverage(false);
1142 
1143  switch (eMode) {
1144  case SpeedTree::TRANS_TEXTURE_ALPHA_TESTING:
1145  SpeedTree::CRenderState::SetAlphaTesting(true);
1146  break;
1147  case SpeedTree::TRANS_TEXTURE_ALPHA_TO_COVERAGE:
1148  SpeedTree::CRenderState::SetAlphaToCoverage(true);
1149  break;
1150  case SpeedTree::TRANS_TEXTURE_BLENDING:
1151  SpeedTree::CRenderState::SetBlending(true);
1152  break;
1153  default:
1154  // intentionally do nothing (TRANS_TEXTURE_NOTHING)
1155  break;
1156  }
1157 }
1158 
1159 /**
1160  * Called from the constructor to initialize some internal values.
1161  */
1162 void SpeedTreeNode::
1163 init_node() {
1164  PandaNode::set_cull_callback();
1165 
1166  _is_valid = false;
1167  _needs_repopulate = false;
1168 
1169  // Ensure we have a license.
1170  if (!authorize()) {
1171  speedtree_cat.warning()
1172  << "SpeedTree license not available.\n";
1173  return;
1174  }
1175 
1176  _forest_render.SetHint(SpeedTree::CForest::HINT_MAX_NUM_VISIBLE_CELLS,
1177  speedtree_max_num_visible_cells);
1178 
1179  _forest_render.SetCullCellSize(speedtree_cull_cell_size);
1180 
1181  // Doesn't appear to be necessary to call this explicitly.
1182  // _forest_render.EnableWind(true);
1183 
1184  _is_valid = true;
1185 }
1186 
1187 /**
1188  * The recursive implementation of add_instances().
1189  */
1190 void SpeedTreeNode::
1191 r_add_instances(PandaNode *node, const TransformState *transform,
1192  Thread *current_thread) {
1193  if (node->is_of_type(SpeedTreeNode::get_class_type()) && node != this) {
1194  SpeedTreeNode *other = DCAST(SpeedTreeNode, node);
1195  add_instances_from(other, transform);
1196  }
1197 
1198  Children children = node->get_children(current_thread);
1199  for (int i = 0; i < children.get_num_children(); i++) {
1200  PandaNode *child = children.get_child(i);
1201  CPT(TransformState) child_transform = transform->compose(child->get_transform());
1202  r_add_instances(child, child_transform, current_thread);
1203  }
1204 }
1205 
1206 
1207 /**
1208  * Rebuilds the internal structures as necessary for rendering.
1209  */
1210 void SpeedTreeNode::
1211 repopulate() {
1212  _forest_render.ClearInstances();
1213 
1214  Trees::iterator ti;
1215  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
1216  InstanceList *instance_list = (*ti);
1217  const STTree *tree = instance_list->get_tree();
1218  const STInstances &instances = instance_list->_instances;
1219  if (instances.empty()) {
1220  // There are no instances, so don't bother. (This shouldn't happen
1221  // often, because we remove trees from the SpeedTreeNode when their
1222  // instance list goes empty, though it's possible if the user has
1223  // explicitly removed all of the instances.)
1224  continue;
1225  }
1226 
1227  if (!_forest_render.AddInstances(tree->get_tree(), &instances[0], instances.size())) {
1228  speedtree_cat.warning()
1229  << "Failed to add " << instances.size()
1230  << " instances for " << *tree << "\n";
1231  write_error(speedtree_cat.warning());
1232  }
1233  }
1234 
1235  _forest_render.GetPopulationStats(_population_stats);
1236  print_forest_stats(_population_stats);
1237 
1238  // setup billboard caps based on instances-per-cell stats
1239  int max_instances_by_cell = 1;
1240  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
1241  InstanceList *instance_list = (*ti);
1242  const STTree *tree = instance_list->get_tree();
1243  const STInstances &instances = instance_list->_instances;
1244  if (instances.empty()) {
1245  continue;
1246  }
1247 
1248  int max_instances = 1;
1249  SpeedTree::CMap<const SpeedTree::CTree*, SpeedTree::st_int32>::const_iterator si;
1250  si = _population_stats.m_mMaxNumInstancesPerCellPerBase.find(tree->get_tree());
1251  if (si != _population_stats.m_mMaxNumInstancesPerCellPerBase.end()) {
1252  max_instances = std::max(max_instances, (int)si->second);
1253  }
1254 
1255  max_instances_by_cell = std::max(max_instances_by_cell, max_instances);
1256  }
1257 
1258  _visible_trees.Reserve(_forest_render.GetBaseTrees(),
1259  _forest_render.GetBaseTrees().size(),
1260  speedtree_max_num_visible_cells,
1261  max_instances_by_cell,
1262  speedtree_horizontal_billboards);
1263 }
1264 
1265 /**
1266  * Called once a frame to load vertex data for newly-visible terrain cells.
1267  */
1268 void SpeedTreeNode::
1269 update_terrain_cells() {
1270  nassertv(has_terrain());
1271 
1272  SpeedTree::TTerrainCellArray &cells = _visible_terrain.m_aCellsToUpdate;
1273 
1274  int num_tile_res = _terrain_render.GetMaxTileRes();
1275  PN_stdfloat cell_size = _terrain_render.GetCellSize();
1276 
1277  // A temporary vertex data object for populating terrain.
1278  PT(GeomVertexData) vertex_data =
1279  new GeomVertexData("terrain", _terrain->get_vertex_format(),
1280  GeomEnums::UH_static);
1281  int num_vertices = num_tile_res * num_tile_res;
1282  vertex_data->set_num_rows(num_vertices);
1283  size_t num_bytes = vertex_data->get_array(0)->get_data_size_bytes();
1284 
1285  int num_cells = (int)cells.size();
1286  for (int ci = 0; ci < num_cells; ++ci) {
1287  SpeedTree::CTerrainCell *cell = cells[ci];
1288  nassertv(cell != nullptr && cell->GetVbo() != nullptr);
1289  int cell_yi = cell->Row();
1290  int cell_xi = cell->Col();
1291  // cerr << "populating cell " << cell_xi << " " << cell_yi << "\n";
1292 
1293  _terrain->fill_vertices(vertex_data,
1294  cell_xi * cell_size, cell_yi * cell_size,
1295  cell_size, num_tile_res);
1296 
1297  const GeomVertexArrayData *array_data = vertex_data->get_array(0);
1298  CPT(GeomVertexArrayDataHandle) handle = array_data->get_handle();
1299  const unsigned char *data_pointer = handle->get_read_pointer(true);
1300  SpeedTree::CGeometryBuffer *vbo = (SpeedTree::CGeometryBuffer *)cell->GetVbo();
1301 
1302  nassertv(vbo->NumVertices() == num_tile_res * num_tile_res);
1303  nassertv(vbo->NumVertices() * vbo->VertexSize() == handle->get_data_size_bytes());
1304  vbo->OverwriteVertices(data_pointer, num_vertices, 0);
1305  }
1306 }
1307 
1308 /**
1309  * Returns true if the indicated GSG shares the appropriate API for this
1310  * SpeedTreeNode, false otherwise.
1311  */
1312 bool SpeedTreeNode::
1313 validate_api(GraphicsStateGuardian *gsg) {
1314  GraphicsPipe *pipe = gsg->get_pipe();
1315  nassertr(pipe != nullptr, true);
1316 
1317 #if defined(SPEEDTREE_OPENGL)
1318  static const string compiled_api = "OpenGL";
1319 #elif defined(SPEEDTREE_DIRECTX9)
1320  static const string compiled_api = "DirectX9";
1321 #else
1322  #error Unexpected graphics API.
1323 #endif
1324 
1325  if (pipe->get_interface_name() != compiled_api) {
1326  speedtree_cat.error()
1327  << "SpeedTree is compiled for " << compiled_api
1328  << ", cannot render with " << pipe->get_interface_name()
1329  << "\n";
1330  _is_valid = false;
1331  return false;
1332  }
1333 
1334  return true;
1335 }
1336 
1337 /**
1338  * Called when the node is visited during the draw traversal, by virtue of our
1339  * DrawCallback construct. This makes the calls into SpeedTree to perform the
1340  * actual rendering.
1341  */
1342 void SpeedTreeNode::
1343 draw_callback(CallbackData *data) {
1344  PStatTimer timer(_draw_speedtree_pcollector);
1345  GeomDrawCallbackData *geom_cbdata;
1346  DCAST_INTO_V(geom_cbdata, data);
1347 
1348  GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, geom_cbdata->get_gsg());
1349 
1350  setup_for_render(gsg);
1351 
1352  // Set some initial state requirements.
1353  SpeedTree::CRenderState::SetAlphaFunction(SpeedTree::ALPHAFUNC_GREATER, 0.0f);
1354 
1355  // start the forest render
1356  _forest_render.StartRender();
1357 
1358  if (_forest_render.ShadowsAreEnabled()) {
1359  // Update the shadow maps. TODO: consider updating these only every once
1360  // in a while, instead of every frame, as a simple optimization.
1361  PStatTimer timer(_draw_speedtree_shadows_pcollector);
1362  render_forest_into_shadow_maps();
1363  _forest_render.ClearBoundTextures( );
1364  }
1365 
1366  if (!_forest_render.UploadViewShaderParameters(_view)) {
1367  speedtree_cat.warning()
1368  << "Couldn't set view parameters\n";
1369  write_error(speedtree_cat.warning());
1370  }
1371 
1372  if (has_terrain()) {
1373  PStatTimer timer1(_draw_speedtree_terrain_pcollector);
1374  // Is this needed for terrain?
1375  _terrain_render.UploadShaderConstants
1376  (&_forest_render, _light_dir,
1377  _forest_render.GetRenderInfo().m_sLightMaterial);
1378 
1379  // set terrain render states
1380  set_transparent_texture_mode(SpeedTree::TRANS_TEXTURE_NOTHING);
1381 
1382  // render actual terrain
1383  bool terrain = _terrain_render.Render
1384  (&_forest_render, _visible_terrain, SpeedTree::RENDER_PASS_STANDARD,
1385  _light_dir, _forest_render.GetRenderInfo().m_sLightMaterial,
1386  &_forest_render.GetRenderStats());
1387 
1388  if (!terrain) {
1389  speedtree_cat.warning()
1390  << "Failed to render terrain\n";
1391  write_error(speedtree_cat.warning());
1392 
1393  // Clear the terrain so we don't keep spamming error messages.
1394  _terrain = nullptr;
1395  }
1396  }
1397 
1398  {
1399  // Now draw the actual trees.
1400  PStatTimer timer1(_draw_speedtree_trees_pcollector);
1401 
1402  // SpeedTree::ETextureAlphaRenderMode mode =
1403  // SpeedTree::TRANS_TEXTURE_ALPHA_TESTING;
1404  SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_ALPHA_TO_COVERAGE;
1405  // SpeedTree::ETextureAlphaRenderMode mode =
1406  // SpeedTree::TRANS_TEXTURE_BLENDING; SpeedTree::ETextureAlphaRenderMode
1407  // mode = SpeedTree::TRANS_TEXTURE_NOTHING;
1408  set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode(mode));
1409 
1410  bool branches = _forest_render.RenderBranches(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
1411  bool fronds = _forest_render.RenderFronds(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
1412  bool leaf_meshes = _forest_render.RenderLeafMeshes(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
1413  bool leaf_cards = _forest_render.RenderLeafCards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view);
1414  bool billboards = _forest_render.RenderBillboards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view);
1415 
1416  // Sometimes billboards comes back false, particularly if wind is
1417  // disabled; but the billboards appear to have been rendered successfully.
1418  // Weird. Just removing this test from the condition.
1419 
1420  if (!branches || !fronds || !leaf_meshes || !leaf_cards /* || !billboards */) {
1421  speedtree_cat.warning()
1422  << "Failed to render forest completely: "
1423  << branches << " " << fronds << " " << leaf_meshes << " " << leaf_cards << " " << billboards << "\n";
1424  write_error(speedtree_cat.warning());
1425  }
1426  }
1427 
1428  _forest_render.EndRender();
1429 
1430  if (_forest_render.ShadowsAreEnabled() && speedtree_show_overlays) {
1431  _forest_render.RenderOverlays();
1432  }
1433 
1434  // SpeedTree leaves the graphics state indeterminate. Make sure Panda
1435  // doesn't rely on anything in the state.
1436  geom_cbdata->set_lost_state(true);
1437 }
1438 
1439 
1440 /**
1441  * Renders the forest from the point of view of the light, to fill up the
1442  * shadow map(s).
1443  */
1444 void SpeedTreeNode::
1445 render_forest_into_shadow_maps() {
1446  bool success = true;
1447 
1448  // d3d10 allows A2C on render targets, so make sure to turn it off
1449  SpeedTree::CRenderState::SetMultisampling(false);
1450  SpeedTree::CRenderState::SetAlphaToCoverage(false);
1451 
1452 #if defined(SPEEDTREE_OPENGL)
1453  // Ensure the viewport is not constrained. SpeedTree doesn't expect that.
1454  glDisable(GL_SCISSOR_TEST);
1455 #endif
1456 
1457  for (int smi = 0; smi < (int)_shadow_infos.size(); ++smi) {
1458  const SpeedTree::CView &light_view = _shadow_infos[smi]._light_view;
1459  const SpeedTree::SForestCullResults &light_cull = _shadow_infos[smi]._light_cull;
1460 
1461  if (_forest_render.BeginShadowMap(smi, light_view)) {
1462  success &= _forest_render.UploadViewShaderParameters(light_view);
1463 
1464  // branch geometry can be rendered with backfacing triangle removed, so
1465  // a closer tolerance can be used
1466  SpeedTree::CRenderState::SetPolygonOffset(1.0f, 0.125f);
1467 
1468  success &= _forest_render.RenderBranches(light_cull, SpeedTree::RENDER_PASS_SHADOW);
1469 
1470  // the remaining geometry types cannot be backface culled, so we need a
1471  // much more aggressive offset
1472  SpeedTree::CRenderState::SetPolygonOffset(10.0f, 1.0f);
1473 
1474  success &= _forest_render.RenderFronds(light_cull, SpeedTree::RENDER_PASS_SHADOW);
1475  success &= _forest_render.RenderLeafMeshes(light_cull, SpeedTree::RENDER_PASS_SHADOW);
1476  success &= _forest_render.RenderLeafCards(light_cull, SpeedTree::RENDER_PASS_SHADOW, light_view);
1477 
1478  // We don't bother to render billboard geometry into the shadow map(s).
1479 
1480  success &= _forest_render.EndShadowMap(smi);
1481  }
1482  }
1483 
1484  // SpeedTree::CRenderState::SetMultisampling(m_sUserSettings.m_nSampleCount
1485  // > 0);
1486 
1487  if (!success) {
1488  speedtree_cat.warning()
1489  << "Failed to render shadow maps\n";
1490  write_error(speedtree_cat.warning());
1491  }
1492 }
1493 
1494 /**
1495  * Does whatever calls are necessary to set up the forest for rendering--
1496  * create vbuffers, load shaders, and whatnot. Primarily, this is the calls
1497  * to InitTreeGraphics and the like.
1498  */
1499 void SpeedTreeNode::
1500 setup_for_render(GraphicsStateGuardian *gsg) {
1501  if (!_done_first_init) {
1502  // This is the first time we have entered the draw callback since creating
1503  // any SpeedTreeNode. Now we have an opportunity to do any initial setup
1504  // that requires a graphics context.
1505 
1506 #ifdef SPEEDTREE_OPENGL
1507  // For OpenGL, we have to ensure GLEW has been initialized. (SpeedTree
1508  // uses it, though Panda doesn't.)
1509  GLenum err = glewInit();
1510  if (err != GLEW_OK) {
1511  speedtree_cat.error()
1512  << "GLEW initialization failed: %s\n", glewGetErrorString(err);
1513  // Can't proceed without GLEW.
1514  _is_valid = false;
1515  return;
1516  }
1517 
1518  // Insist that OpenGL 2.0 is available as the SpeedTree renderer requires
1519  // it.
1520  if (!GLEW_VERSION_2_0) {
1521  speedtree_cat.error()
1522  << "The SpeedTree OpenGL implementation requires OpenGL 2.0 or better to run; this system has version " << glGetString(GL_VERSION) << "\n";
1523  _is_valid = false;
1524  return;
1525  }
1526 #endif // SPEEDTREE_OPENGL
1527 
1528  _done_first_init = true;
1529  }
1530 
1531 #ifdef SPEEDTREE_DIRECTX9
1532  // In DirectX, we have to tell SpeedTree our device pointer.
1534  SpeedTree::DX9::SetDevice(dxgsg->_screen->_d3d_device);
1535 #endif // SPEEDTREE_DIRECTX9
1536 
1537  if (_needs_repopulate) {
1538  repopulate();
1539 
1540  // Now init per-tree graphics
1541  Trees::const_iterator ti;
1542  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
1543  InstanceList *instance_list = (*ti);
1544  const STTree *tree = instance_list->get_tree();
1545  const STInstances &instances = instance_list->_instances;
1546  if (instances.empty()) {
1547  continue;
1548  }
1549 
1550  int max_instances = 2;
1551  SpeedTree::CMap<const SpeedTree::CTree*, SpeedTree::st_int32>::const_iterator si;
1552  si = _population_stats.m_mMaxNumInstancesPerCellPerBase.find(tree->get_tree());
1553  if (si != _population_stats.m_mMaxNumInstancesPerCellPerBase.end()) {
1554  max_instances = std::max(max_instances, (int)si->second);
1555  }
1556 
1557  // Get the speedtree-textures-dir to pass for initialization.
1558  string os_textures_dir;
1559  if (!speedtree_textures_dir.empty()) {
1560  os_textures_dir = speedtree_textures_dir.get_value().to_os_specific();
1561  // Ensure the path ends with a terminal slash; SpeedTree requires
1562  // this.
1563 #if defined(WIN32) || defined(WIN64)
1564  if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '\\') {
1565  os_textures_dir += "\\";
1566  }
1567 #else
1568  if (!os_textures_dir.empty() && os_textures_dir[os_textures_dir.length() - 1] != '/') {
1569  os_textures_dir += "/";
1570  }
1571 #endif
1572  }
1573 
1574  if (!_forest_render.InitTreeGraphics((SpeedTree::CTreeRender *)tree->get_tree(),
1575  max_instances, speedtree_horizontal_billboards,
1576  os_textures_dir.c_str())) {
1577  if (speedtree_cat.is_debug()) {
1578  speedtree_cat.debug()
1579  << "Failed to init tree graphics for " << *tree << "\n";
1580  write_error(speedtree_cat.debug());
1581  }
1582  }
1583  }
1584 
1585  // Init overall graphics
1586  if (!_forest_render.InitGraphics(false)) {
1587  speedtree_cat.warning()
1588  << "Failed to init graphics\n";
1589  write_error(speedtree_cat.warning());
1590  _is_valid = false;
1591  return;
1592  }
1593 
1594  // This call apparently must be made at draw time, not earlier, because it
1595  // might attempt to create OpenGL index buffers and such.
1596  _forest_render.UpdateTreeCellExtents();
1597 
1598  if (has_terrain()) {
1599  // Now initialize the terrain.
1600  if (!_terrain_render.Init(speedtree_terrain_num_lods,
1601  speedtree_terrain_resolution,
1602  speedtree_terrain_cell_size,
1603  _terrain->get_st_vertex_format())) {
1604  speedtree_cat.warning()
1605  << "Failed to init terrain\n";
1606  write_error(speedtree_cat.warning());
1607  }
1608  }
1609 
1610  // If we needed to repopulate, it means we didn't cull in the cull
1611  // traversal. Do it now.
1612  cull_forest();
1613  _needs_repopulate = false;
1614  }
1615  if (has_terrain()) {
1616  PStatTimer timer1(_draw_speedtree_terrain_update_pcollector);
1617  update_terrain_cells();
1618  }
1619 }
1620 
1621 /**
1622  * Calls the SpeedTree methods to perform the needed cull calculations.
1623  */
1624 void SpeedTreeNode::
1625 cull_forest() {
1626  {
1627  PStatTimer timer1(_cull_speedtree_trees_pcollector);
1628  _forest_render.CullAndComputeLOD(_view, _visible_trees);
1629  }
1630  if (has_terrain()) {
1631  PStatTimer timer1(_cull_speedtree_terrain_pcollector);
1632  _terrain_render.CullAndComputeLOD(_view, _visible_terrain);
1633  }
1634 
1635  if (_forest_render.ShadowsAreEnabled()) {
1636  PStatTimer timer1(_cull_speedtree_shadows_pcollector);
1637  for (int smi = 0; smi < (int)_shadow_infos.size(); ++smi) {
1638  SpeedTree::CView &light_view = _shadow_infos[smi]._light_view;
1639  SpeedTree::SForestCullResultsRender &light_cull = _shadow_infos[smi]._light_cull;
1640 
1641  _forest_render.ComputeLightView
1642  (_forest_render.GetLightDir(), _view.GetFrustumPoints(), smi,
1643  light_view, 0.0f);
1644 
1645  light_view.SetLodRefPoint(_view.GetCameraPos());
1646  _forest_render.CullAndComputeLOD(light_view, light_cull, false);
1647  }
1648  }
1649 }
1650 
1651 
1652 /**
1653  *
1654  */
1655 void SpeedTreeNode::
1656 print_forest_stats(const SpeedTree::CForest::SPopulationStats &forest_stats) const {
1657  fprintf(stderr, "\n Forest Population Statistics\n");
1658  fprintf(stderr, " ---------------------------------------------------\n");
1659  fprintf(stderr, " # of tree cull cells: %d\n", forest_stats.m_nNumCells);
1660  fprintf(stderr, " # of unique base trees: %d\n", forest_stats.m_nNumBaseTrees);
1661  fprintf(stderr, " total # of instances: %d\n", forest_stats.m_nNumInstances);
1662  fprintf(stderr, " average # of instances per base: %g\n", forest_stats.m_fAverageNumInstancesPerBase);
1663  fprintf(stderr, " max # of billboards/instances per cell: %d\n", forest_stats.m_nMaxNumBillboardsPerCell);
1664  fprintf(stderr, " max # of instances per cell per base:\n");
1665  SpeedTree::CMap<const SpeedTree::CTree*, SpeedTree::st_int32>::const_iterator i;
1666  for (i = forest_stats.m_mMaxNumInstancesPerCellPerBase.begin( ); i != forest_stats.m_mMaxNumInstancesPerCellPerBase.end( ); ++i) {
1667  fprintf(stderr, " %35s: %4d\n", SpeedTree::CFixedString(i->first->GetFilename( )).NoPath( ).c_str( ), i->second);
1668  }
1669  fprintf(stderr, " average # instances per cell: %g\n", forest_stats.m_fAverageInstancesPerCell);
1670  fprintf(stderr, " max # of billboard images: %d\n", forest_stats.m_nMaxNumBillboardImages);
1671  fprintf(stderr, "\n");
1672 }
1673 
1674 /**
1675  * Tells the BamReader how to create objects of type SpeedTreeNode.
1676  */
1677 void SpeedTreeNode::
1679  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
1680 }
1681 
1682 /**
1683  * Writes the contents of this object to the datagram for shipping out to a
1684  * Bam file.
1685  */
1686 void SpeedTreeNode::
1688  PandaNode::write_datagram(manager, dg);
1689 
1690  int num_trees = _trees.size();
1691  dg.add_uint32(num_trees);
1692  Trees::const_iterator ti;
1693  for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
1694  InstanceList *instance_list = (*ti);
1695  instance_list->write_datagram(manager, dg);
1696  }
1697 }
1698 
1699 /**
1700  * This function is called by the BamReader's factory when a new object of
1701  * type SpeedTreeNode is encountered in the Bam file. It should create the
1702  * SpeedTreeNode and extract its information from the file.
1703  */
1704 TypedWritable *SpeedTreeNode::
1705 make_from_bam(const FactoryParams &params) {
1706  SpeedTreeNode *node = new SpeedTreeNode("");
1707  DatagramIterator scan;
1708  BamReader *manager;
1709 
1710  parse_params(params, scan, manager);
1711  node->fillin(scan, manager);
1712 
1713  return node;
1714 }
1715 
1716 /**
1717  * This internal function is called by make_from_bam to read in all of the
1718  * relevant data from the BamFile for the new SpeedTreeNode.
1719  */
1720 void SpeedTreeNode::
1721 fillin(DatagramIterator &scan, BamReader *manager) {
1722  PandaNode::fillin(scan, manager);
1723 
1724  int num_trees = scan.get_uint32();
1725  _trees.reserve(num_trees);
1726  for (int i = 0; i < num_trees; i++) {
1727  InstanceList *instance_list = new InstanceList(nullptr);
1728  instance_list->fillin(scan, manager);
1729  if (instance_list->get_tree() == nullptr) {
1730  // The tree wasn't successfully loaded. Don't keep it.
1731  delete instance_list;
1732  } else {
1733  _trees.push_back(instance_list);
1734  }
1735  }
1736 
1737  _trees.sort();
1738 }
1739 
1740 /**
1741  *
1742  */
1743 void SpeedTreeNode::InstanceList::
1744 output(ostream &out) const {
1745  out << *_tree << ": " << _instances.size() << " instances";
1746 }
1747 
1748 /**
1749  *
1750  */
1751 void SpeedTreeNode::InstanceList::
1752 write(ostream &out, int indent_level) const {
1753  indent(out, indent_level)
1754  << *_tree << ": " << _instances.size() << " instances.\n";
1755  STInstances::const_iterator ii;
1756  for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
1757  indent(out, indent_level + 2)
1758  << STTransform(*ii) << "\n";
1759  }
1760 }
1761 
1762 /**
1763  * Writes the contents of this object to the datagram for shipping out to a
1764  * Bam file.
1765  */
1768  // Compute the relative pathname to the SRT file.
1770 
1771  bool has_bam_dir = !manager->get_filename().empty();
1772  Filename bam_dir = manager->get_filename().get_dirname();
1773  Filename srt_filename = _tree->get_fullpath();
1774 
1775  bam_dir.make_absolute(vfs->get_cwd());
1776  if (!has_bam_dir || !srt_filename.make_relative_to(bam_dir, true)) {
1777  srt_filename.find_on_searchpath(get_model_path());
1778  }
1779 
1780  dg.add_string(srt_filename);
1781 
1782  // Now record the instances.
1783  int num_instances = _instances.size();
1784  dg.add_uint32(num_instances);
1785  STInstances::const_iterator ii;
1786  for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
1787  STTransform transform = (*ii);
1788  transform.write_datagram(manager, dg);
1789  }
1790 }
1791 
1792 /**
1793  * This internal function is called by make_from_bam to read in all of the
1794  * relevant data from the BamFile for the new SpeedTreeNode.
1795  */
1798  // Get the relative pathname to the SRT file.
1799  string srt_filename = scan.get_string();
1800 
1801  // Now load up the SRT file using the Panda loader (which will also search
1802  // the model-path if necessary).
1803  Loader *loader = Loader::get_global_ptr();
1804  PT(PandaNode) srt_root = loader->load_sync(srt_filename);
1805 
1806  if (srt_root != nullptr) {
1807  NodePath srt(srt_root);
1808  NodePath srt_np = srt.find("**/+SpeedTreeNode");
1809  if (!srt_np.is_empty()) {
1810  SpeedTreeNode *srt_node = DCAST(SpeedTreeNode, srt_np.node());
1811  if (srt_node->get_num_trees() >= 1) {
1812  _tree = (STTree *)srt_node->get_tree(0);
1813  }
1814  }
1815  }
1816 
1817  // Now read the instances.
1818  int num_instances = scan.get_uint32();
1819  _instances.reserve(num_instances);
1820  for (int i = 0; i < num_instances; i++) {
1821  STTransform transform;
1822  transform.fillin(scan, manager);
1823  _instances.push_back(transform);
1824  }
1825 }
1826 
1827 /**
1828  * This method called when the callback is triggered; it *replaces* the
1829  * original function. To continue performing the original function, you must
1830  * call cbdata->upcall() during the callback.
1831  */
1832 void SpeedTreeNode::DrawCallback::
1833 do_callback(CallbackData *data) {
1834  _node->draw_callback(data);
1835 }
PN_stdfloat get_max_height() const
Returns the largest height value that might be returned by get_height().
Definition: stTerrain.I:105
GraphicsStateGuardianBase * get_gsg() const
Returns a pointer to the current GSG.
A GraphicsStateGuardian for rendering into DirectX9 contexts.
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
A light shining from infinitely far away in a particular direction, like sunlight.
This specialization on CallbackData is passed when the callback is initiated from deep within the dra...
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition: nodePath.cxx:314
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Definition: boundingBox.h:29
CullHandler * get_cull_handler() const
Returns the object that will receive the culled Geoms.
A specific implementation of STTerrain that supports basic heightmaps loaded from an image file,...
STTerrain * get_terrain() const
Returns the terrain associated with the node, or NULL if there is no terrain.
Definition: speedTreeNode.I:90
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
Indicates a coordinate-system transform on vertices.
get_pipe
Returns the graphics pipe on which this GSG was created.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
bool has_terrain() const
Returns true if a valid terrain has been associated with the node, false otherwise.
Definition: speedTreeNode.I:81
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: pandaNode.cxx:3589
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void write_error(std::ostream &out)
Writes the current SpeedTree error message to the indicated stream.
int count_total_instances() const
Returns the total number of trees that will be rendered by this node, counting all instances of all t...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
virtual PandaNode * combine_with(PandaNode *other)
Collapses this PandaNode with the other PandaNode, if possible, and returns a pointer to the combined...
Definition: pandaNode.cxx:325
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
size_type_0 size() const
Returns the number of elements in the ordered vector.
void clear()
Removes all elements from the ordered vector.
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
A hierarchy of directories and files that appears to be one continuous file system,...
const LPoint3 & get_pos() const
Returns the translation component.
Definition: stTransform.I:124
virtual CoordinateSystem get_internal_coordinate_system() const
Returns the coordinate system used internally by the GSG.
virtual void output(std::ostream &out) const
Writes a brief description of the node to the indicated output stream.
void add_instances(const NodePath &root, const TransformState *transform=TransformState::make_identity())
Walks the scene graph beginning at root, looking for nested SpeedTreeNodes.
get_near
Returns the position of the near plane (or cylinder, sphere, whatever).
Definition: lens.h:113
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer)
Applies whatever attributes are specified in the AccumulatedAttribs object (and by the attrib_types b...
bool has_instance_list(const STTree *tree) const
Returns true if the indicated tree has any instances within this node, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A convenient class for loading models from disk, in bam or egg format (or any of a number of other fo...
Definition: loader.h:42
This collects together the pieces of data that are accumulated for each node while walking the scene ...
bool resolve_filename(const DSearchPath &searchpath, const std::string &default_extension=std::string())
Searches the given search path for the filename.
Definition: filename.cxx:1581
const Filename & get_normal_map() const
Returns the normal map that should be applied to the terrain.
Definition: stTerrain.I:27
bool setup_terrain(const Filename &terrain_file)
A convenience function to set up terrain geometry by reading a terrain.txt file as defined by SpeedTr...
const Lens * get_lens() const
Returns the particular Lens used for rendering.
Definition: sceneSetup.I:131
get_num_trees
Returns the number of unique tree objects that have been added to the node.
Definition: speedTreeNode.h:95
bool is_valid() const
Returns true if the node is valid and ready to render, false otherwise.
Definition: speedTreeNode.I:20
get_parent
Returns the NodePath to the parent of the referenced node: that is, this NodePath,...
Definition: nodePath.h:244
int remove_tree(const STTree *tree)
Removes all instances of the indicated tree.
A light source that seems to illuminate all points in space at once.
Definition: ambientLight.h:26
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the abstract base class that defines the interface needed to describe a terrain for rendering...
Definition: stTerrain.h:34
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual PandaNode * combine_with(PandaNode *other)
Collapses this node with the other node, if possible, and returns a pointer to the combined node,...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
This is a generic data block that is passed along to a CallbackObject when a callback is made.
Definition: callbackData.h:29
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
void append_directory(const Filename &directory)
Adds a new directory to the end of the search list.
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
void reserve(size_type_0 n)
Informs the vector of a planned change in size; ensures that the capacity of the vector is greater th...
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_mat
Returns the matrix that describes the transform.
std::string get_string()
Extracts a variable-length string.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
void snap_to_terrain()
Adjusts all the trees in this node so that their Z position matches the height of the terrain at thei...
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:31
bool is_valid() const
Returns true if the terrain data is well-defined and ready to use.
Definition: stTerrain.I:18
const TransformState * get_cs_transform() const
Returns the transform from the camera's coordinate system to the GSG's internal coordinate system.
Definition: sceneSetup.I:250
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
virtual bool is_renderable() const
Returns true if there is some value to visiting this particular node during the cull traversal for an...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this PandaNode by the indicated matrix, if it means anything to do so.
Definition: pandaNode.cxx:310
get_num_instances
Returns the number of instances of this tree.
Definition: speedTreeNode.h:68
A lightweight class that represents a single element that may be timed and/or counted via stats.
size_t get_num_words() const
Returns the number of words in the variable's value.
static Loader * get_global_ptr()
Returns a pointer to the global Loader.
Definition: loader.I:212
void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is called by make_from_bam to read in all of the relevant data from the BamFil...
Definition: stTransform.cxx:67
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_terrain(STTerrain *terrain)
Associated a terrain with the node.
get_value
Returns the variable's value.
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
const STTree * get_tree() const
Returns the particular tree this list refers to.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double random_real(double range)
Returns a random double in the range [0, range).
Definition: randomizer.I:53
void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
get_filename
If a BAM is a file, then the BamWriter should contain the name of the file.
Definition: bamWriter.h:92
get_instance_list
Returns a list of transforms that corresponds to the instances at which the indicated tree appears.
Definition: speedTreeNode.h:97
The smallest atom of cull.
get_on_light
Returns the nth light turned on by the attribute, sorted in render order.
Definition: lightAttrib.h:74
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.h:91
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
void set_instance(int n, const STTransform &transform)
Replaces the transform of the nth instance of this tree.
virtual void record_object(CullableObject *object, const CullTraverser *traverser)
This callback function is intended to be overridden by a derived class.
Definition: cullHandler.cxx:43
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
const NodePath & get_scene_root() const
Returns the root node of the scene.
Definition: sceneSetup.I:83
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
void add_instance(const STTree *tree, const STTransform &transform)
Adds a new instance of the indicated tree at the indicated transform.
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:58
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void remove_all_trees()
Removes all instances of all trees from the node.
bool is_valid() const
Returns true if the tree was successfully loaded and is ready to be used, false otherwise.
Definition: stTree.I:28
void sort()
Maps to sort_unique().
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:784
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
get_num_on_lights
Returns the number of lights that are turned on by the attribute.
Definition: lightAttrib.h:74
bool set_num_rows(int n)
Sets the length of the array to n rows in all of the various arrays (presumably by adding rows).
get_coordinate_system
Returns the coordinate system that all 3-d computations are performed within for this Lens.
Definition: lens.h:74
Interfaces with the SpeedTree library to render SpeedTree objects, especially trees,...
Definition: speedTreeNode.h:50
void prepare_scene(GraphicsStateGuardianBase *gsgbase, const RenderState *net_state)
Walks through the scene graph beginning at this node, and does whatever initialization is required to...
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
PN_stdfloat get_min_height() const
Returns the smallest height value that might be returned by get_height().
Definition: stTerrain.I:96
get_instance
Returns the transform of the nth instance of this tree.
Definition: speedTreeNode.h:68
bool has_all_off() const
Returns true if this attrib turns off all stages (although it may also turn some on).
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is called by make_from_bam to read in all of the relevant data from the BamFil...
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
GraphicsStateGuardianBase * get_gsg() const
Returns the GraphicsStateGuardian in effect.
Definition: cullTraverser.I:18
const SpeedTree::CTreeRender * get_tree() const
Returns a const pointer to the internal SpeedTree object.
Definition: stTree.I:36
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash,...
Definition: filename.cxx:1640
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
bool add_from_stf(const Filename &stf_filename, const LoaderOptions &options=LoaderOptions())
Opens and reads the named STF (SpeedTree Forest) file, and adds the SRT files named within as instanc...
Represents a transform that may be applied to a particular instance of a tree when added to the Speed...
Definition: stTransform.h:26
const Filename & get_splat_layer(int n) const
Returns the nth splat layer that is to be applied to the terrain.
Definition: stTerrain.I:56
void reload_config()
Re-reads the current setting of all of the relevant config variables and applies them to this node.
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname),...
Definition: filename.cxx:968
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, int &internal_vertices, int pipeline_stage, Thread *current_thread) const
Returns a newly-allocated BoundingVolume that represents the internal contents of the node.
const LMatrix4 & get_projection_mat(StereoChannel channel=SC_mono) const
Returns the complete transformation matrix from a 3-d point in space to a point on the film,...
Definition: lens.I:563
void set_pos(const LPoint3 &pos)
Replaces the translation component.
Definition: stTransform.I:116
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
const Filename & get_fullpath() const
Returns the full pathname to the SRT file that was loaded for this tree, as passed to the constructor...
Definition: stTree.I:19
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A thread; that is, a lightweight process.
Definition: thread.h:46
int find_on_searchpath(const DSearchPath &searchpath)
Performs the reverse of the resolve_filename() operation: assuming that the current filename is fully...
Definition: filename.cxx:1689
get_direction
Returns the direction in which the light is aimed.
int get_num_splat_layers() const
Returns the number of splat layers that are to be applied to the terrain.
Definition: stTerrain.I:48
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A handy class to return random numbers.
Definition: randomizer.h:26
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:35
Encapsulates all the communication with a particular instance of a given rendering backend.
virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data)
Adds the node's contents to the CullResult we are building up during the cull traversal,...
get_color
Returns the basic color of the light.
Definition: light.h:49
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
get_tree
Returns the STTree pointer for the nth tree.
Definition: speedTreeNode.h:95
void add_instances_from(const SpeedTreeNode *other)
Adds all of the instances defined within the indicated SpeedTreeNode as instances of this node.
static void register_with_read_factory()
Tells the BamReader how to create objects of type SpeedTreeNode.
int add_instance(const STTransform &transform)
Adds a new instance of this tree at the indicated transform.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
Filename get_cwd() const
Returns the current directory name.
virtual void load_data()=0
This will be called at some point after initialization.
Definition: stTerrain.cxx:76
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A class to retrieve the individual data elements previously stored in a Datagram.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static bool authorize(const std::string &license="")
Make this call to initialized the SpeedTree API and verify the license.
get_child
Returns the nth child node of this node.
Definition: pandaNode.h:124
InstanceList & modify_instance_list(const STTree *tree)
Returns a modifiable list of transforms that corresponds to the instances of this tree.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:758
void add_random_instances(const STTree *tree, int quantity, PN_stdfloat x_min, PN_stdfloat x_max, PN_stdfloat y_min, PN_stdfloat y_max, PN_stdfloat scale_min, PN_stdfloat scale_max, PN_stdfloat height_min, PN_stdfloat height_max, PN_stdfloat slope_min, PN_stdfloat slope_max, Randomizer &randomizer=Randomizer())
Creates a number of random instances of the indicated true, within the indicated range.
InstanceList & add_tree(const STTree *tree)
Adds a new tree for rendering.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
const Filename & get_splat_map() const
Returns the splat map that should be applied to the terrain.
Definition: stTerrain.I:38
PN_stdfloat get_splat_layer_tiling(int n) const
Returns the tiling value of the nth splat layer.
Definition: stTerrain.I:66
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
void set_lost_state(bool lost_state)
Sets the lost_state flag.
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
get_far
Returns the position of the far plane (or cylinder, sphere, whatever).
Definition: lens.h:114
get_priority
Returns the priority associated with this light.
Definition: light.h:64
void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: stTransform.cxx:56
Encapsulates a single tree model in the SpeedTree library, as loaded from an SRT file.
Definition: stTree.h:28
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
This is the data for one array of a GeomVertexData structure.
Indicates which set of lights should be considered "on" to illuminate geometry at this level and belo...
Definition: lightAttrib.h:30
void set_wind(double strength, const LVector3 &direction)
Specifies the overall wind strength and direction.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...