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()),
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 }
ordered_vector::size
size_type_0 size() const
Returns the number of elements in the ordered vector.
Definition: ordered_vector.I:221
PandaNode::combine_with
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:319
DatagramIterator::get_string
std::string get_string()
Extracts a variable-length string.
Definition: datagramIterator.cxx:26
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
STTransform::get_pos
const LPoint3 & get_pos() const
Returns the translation component.
Definition: stTransform.I:124
GeomVertexArrayData
This is the data for one array of a GeomVertexData structure.
Definition: geomVertexArrayData.h:58
STTerrain::get_normal_map
const Filename & get_normal_map() const
Returns the normal map that should be applied to the terrain.
Definition: stTerrain.I:27
SpeedTreeNode::add_instances_from
void add_instances_from(const SpeedTreeNode *other)
Adds all of the instances defined within the indicated SpeedTreeNode as instances of this node.
Definition: speedTreeNode.cxx:303
cullTraverser.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CallbackData
This is a generic data block that is passed along to a CallbackObject when a callback is made.
Definition: callbackData.h:29
Loader
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
config_putil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::get_cwd
Filename get_cwd() const
Returns the current directory name.
Definition: virtualFileSystem.cxx:455
Lens::get_projection_mat
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
SceneSetup::get_scene_root
const NodePath & get_scene_root() const
Returns the root node of the scene.
Definition: sceneSetup.I:83
SpeedTreeNode::has_terrain
bool has_terrain() const
Returns true if a valid terrain has been associated with the node, false otherwise.
Definition: speedTreeNode.I:81
STTree::is_valid
bool is_valid() const
Returns true if the tree was successfully loaded and is ready to be used, false otherwise.
Definition: stTree.I:28
pvector< SpeedTree::CInstance >
STTerrain::get_splat_layer
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
GraphicsStateGuardian::get_pipe
get_pipe
Returns the graphics pipe on which this GSG was created.
Definition: graphicsStateGuardian.h:117
Filename::get_dirname
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
SpeedTreeNode::set_terrain
void set_terrain(STTerrain *terrain)
Associated a terrain with the node.
Definition: speedTreeNode.cxx:558
SpeedTreeNode::get_terrain
STTerrain * get_terrain() const
Returns the terrain associated with the node, or NULL if there is no terrain.
Definition: speedTreeNode.I:90
ov_set::sort
void sort()
Maps to sort_unique().
Definition: ordered_vector.I:731
CullableObject
The smallest atom of cull.
Definition: cullableObject.h:41
Filename::from_os_specific
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
ClockObject
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:58
ordered_vector::reserve
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...
Definition: ordered_vector.I:572
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
DirectionalLight::get_direction
get_direction
Returns the direction in which the light is aimed.
Definition: directionalLight.h:53
SpeedTreeNode::add_instances
void add_instances(const NodePath &root, const TransformState *transform=TransformState::make_identity())
Walks the scene graph beginning at root, looking for nested SpeedTreeNodes.
Definition: speedTreeNode.cxx:292
SpeedTreeNode::combine_with
virtual PandaNode * combine_with(PandaNode *other)
Collapses this node with the other node, if possible, and returns a pointer to the combined node,...
Definition: speedTreeNode.cxx:815
pandabase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomTransformer
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
Definition: geomTransformer.h:42
Filename::to_os_specific
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
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
VirtualFileSystem::get_file
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
Definition: virtualFileSystem.cxx:516
cullableObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
sceneGraphReducer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
Filename::resolve_filename
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
SpeedTreeNode::modify_instance_list
InstanceList & modify_instance_list(const STTree *tree)
Returns a modifiable list of transforms that corresponds to the instances of this tree.
Definition: speedTreeNode.cxx:267
SpeedTreeNode::add_instance
void add_instance(const STTree *tree, const STTransform &transform)
Adds a new instance of the indicated tree at the indicated transform.
Definition: speedTreeNode.cxx:275
CullTraverser::get_scene
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:35
SpeedTreeNode::set_wind
void set_wind(double strength, const LVector3 &direction)
Specifies the overall wind strength and direction.
Definition: speedTreeNode.cxx:707
SpeedTreeNode::setup_terrain
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...
Definition: speedTreeNode.cxx:540
clockObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Randomizer::random_real
double random_real(double range)
Returns a random double in the range [0, range).
Definition: randomizer.I:53
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
stBasicTerrain.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ClockObject::get_global_clock
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
CullTraverser
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
GeomDrawCallbackData::get_gsg
GraphicsStateGuardianBase * get_gsg() const
Returns a pointer to the current GSG.
Definition: geomDrawCallbackData.I:39
speedTreeNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TransformState::get_mat
get_mat
Returns the matrix that describes the transform.
Definition: transformState.h:156
NodePath::get_transform
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
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
NodePath::get_parent
get_parent
Returns the NodePath to the parent of the referenced node: that is, this NodePath,...
Definition: nodePath.h:242
SpeedTreeNode::prepare_scene
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...
Definition: speedTreeNode.cxx:1046
SpeedTreeNode::apply_attribs_to_vertices
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...
Definition: speedTreeNode.cxx:847
VirtualFileSystem::exists
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
Definition: virtualFileSystem.I:18
LoaderOptions
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
SpeedTreeNode::add_from_stf
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...
Definition: speedTreeNode.cxx:402
SpeedTreeNode::has_instance_list
bool has_instance_list(const STTree *tree) const
Returns true if the indicated tree has any instances within this node, false otherwise.
Definition: speedTreeNode.cxx:237
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PandaNode::get_children
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:782
STTerrain
This is the abstract base class that defines the interface needed to describe a terrain for rendering...
Definition: stTerrain.h:34
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
RenderState
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
SpeedTreeNode::authorize
static bool authorize(const std::string &license="")
Make this call to initialized the SpeedTree API and verify the license.
Definition: speedTreeNode.cxx:720
BoundingBox
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Definition: boundingBox.h:29
Lens::get_coordinate_system
get_coordinate_system
Returns the coordinate system that all 3-d computations are performed within for this Lens.
Definition: lens.h:74
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
SpeedTreeNode::write_error
static void write_error(std::ostream &out)
Writes the current SpeedTree error message to the indicated stream.
Definition: speedTreeNode.cxx:1124
STTransform
Represents a transform that may be applied to a particular instance of a tree when added to the Speed...
Definition: stTransform.h:26
AccumulatedAttribs
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
Definition: accumulatedAttribs.h:30
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
Datagram::add_string
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
AmbientLight
A light source that seems to illuminate all points in space at once.
Definition: ambientLight.h:26
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
SpeedTreeNode::InstanceList::get_tree
const STTree * get_tree() const
Returns the particular tree this list refers to.
Definition: speedTreeNode.I:153
PandaNode::write_datagram
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:3583
dxGraphicsStateGuardian9.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpeedTreeNode::is_valid
bool is_valid() const
Returns true if the node is valid and ready to render, false otherwise.
Definition: speedTreeNode.I:20
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
SpeedTreeNode::InstanceList::write_datagram
void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: speedTreeNode.cxx:1767
SpeedTreeNode::output
virtual void output(std::ostream &out) const
Writes a brief description of the node to the indicated output stream.
Definition: speedTreeNode.cxx:1094
STTree::get_tree
const SpeedTree::CTreeRender * get_tree() const
Returns a const pointer to the internal SpeedTree object.
Definition: stTree.I:36
GeomDrawCallbackData
This specialization on CallbackData is passed when the callback is initiated from deep within the dra...
Definition: geomDrawCallbackData.h:28
DSearchPath::append_directory
void append_directory(const Filename &directory)
Adds a new directory to the end of the search list.
Definition: dSearchPath.cxx:147
CullHandler::record_object
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
DSearchPath
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
SpeedTreeNode::snap_to_terrain
void snap_to_terrain()
Adjusts all the trees in this node so that their Z position matches the height of the terrain at thei...
Definition: speedTreeNode.cxx:609
SpeedTreeNode::InstanceList
Definition: speedTreeNode.h:58
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
ordered_vector::push_back
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
Definition: ordered_vector.I:614
STBasicTerrain
A specific implementation of STTerrain that supports basic heightmaps loaded from an image file,...
Definition: stBasicTerrain.h:27
TransformState
Indicates a coordinate-system transform on vertices.
Definition: transformState.h:54
TextureAttrib::has_all_off
bool has_all_off() const
Returns true if this attrib turns off all stages (although it may also turn some on).
Definition: textureAttrib.I:205
Light::get_color
get_color
Returns the basic color of the light.
Definition: light.h:49
boundingSphere.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpeedTreeNode::add_tree
InstanceList & add_tree(const STTree *tree)
Adds a new tree for rendering.
Definition: speedTreeNode.cxx:151
Filename::find_on_searchpath
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
Lens::get_near
get_near
Returns the position of the near plane (or cylinder, sphere, whatever).
Definition: lens.h:113
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
CullTraverserData
This collects together the pieces of data that are accumulated for each node while walking the scene ...
Definition: cullTraverserData.h:40
Filename::get_fullpath
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
PandaNode::get_child
get_child
Returns the nth child node of this node.
Definition: pandaNode.h:124
STTree::get_fullpath
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
GraphicsPipe
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Lens
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
DirectionalLight
A light shining from infinitely far away in a particular direction, like sunlight.
Definition: directionalLight.h:25
SpeedTreeNode::InstanceList::get_num_instances
get_num_instances
Returns the number of instances of this tree.
Definition: speedTreeNode.h:68
ambientLight.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
STTransform::fillin
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
NodePath::find
NodePath find(const std::string &path) const
Searches for a node below the referenced node that matches the indicated string.
Definition: nodePath.cxx:314
SpeedTreeNode::get_instance_list
get_instance_list
Returns a list of transforms that corresponds to the instances at which the indicated tree appears.
Definition: speedTreeNode.h:97
SpeedTreeNode::InstanceList::set_instance
void set_instance(int n, const STTransform &transform)
Replaces the transform of the nth instance of this tree.
Definition: speedTreeNode.I:178
GeomVertexArrayDataHandle
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
Definition: geomVertexArrayData.h:250
Factory::register_factory
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
Filename::is_directory
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
SpeedTreeNode::register_with_read_factory
static void register_with_read_factory()
Tells the BamReader how to create objects of type SpeedTreeNode.
Definition: speedTreeNode.cxx:1678
STTransform::set_pos
void set_pos(const LPoint3 &pos)
Replaces the translation component.
Definition: stTransform.I:116
Randomizer
A handy class to return random numbers.
Definition: randomizer.h:26
ordered_vector::end
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
Definition: ordered_vector.I:50
SpeedTreeNode::InstanceList::fillin
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: speedTreeNode.cxx:1797
NodePath
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
STTerrain::get_max_height
PN_stdfloat get_max_height() const
Returns the largest height value that might be returned by get_height().
Definition: stTerrain.I:105
STTerrain::get_min_height
PN_stdfloat get_min_height() const
Returns the smallest height value that might be returned by get_height().
Definition: stTerrain.I:96
GraphicsStateGuardian::get_internal_coordinate_system
virtual CoordinateSystem get_internal_coordinate_system() const
Returns the coordinate system used internally by the GSG.
Definition: graphicsStateGuardian.cxx:407
SpeedTreeNode::add_for_draw
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,...
Definition: speedTreeNode.cxx:1022
textureAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpeedTreeNode::InstanceList::add_instance
int add_instance(const STTransform &transform)
Adds a new instance of this tree at the indicated transform.
Definition: speedTreeNode.I:188
SpeedTreeNode::write_datagram
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: speedTreeNode.cxx:1687
omniBoundingVolume.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ClockObject::get_frame_time
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
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:742
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Datagram::add_uint32
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
SpeedTreeNode::InstanceList::get_instance
get_instance
Returns the transform of the nth instance of this tree.
Definition: speedTreeNode.h:68
VirtualFileSystem::close_read_file
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
Definition: virtualFileSystem.cxx:867
SpeedTreeNode::is_renderable
virtual bool is_renderable() const
Returns true if there is some value to visiting this particular node during the cull traversal for an...
Definition: speedTreeNode.cxx:1012
LightAttrib::get_on_light
get_on_light
Returns the nth light turned on by the attribute, sorted in render order.
Definition: lightAttrib.h:74
VirtualFile
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
GeomDrawCallbackData::set_lost_state
void set_lost_state(bool lost_state)
Sets the lost_state flag.
Definition: geomDrawCallbackData.I:63
STTransform::write_datagram
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
deg_2_rad.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename::make_absolute
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname),...
Definition: filename.cxx:968
GraphicsStateGuardianBase
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Definition: graphicsStateGuardianBase.h:110
SpeedTreeNode::get_tree
get_tree
Returns the STTree pointer for the nth tree.
Definition: speedTreeNode.h:95
Light::get_priority
get_priority
Returns the priority associated with this light.
Definition: light.h:64
loader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpeedTreeNode::add_random_instances
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.
Definition: speedTreeNode.cxx:358
ConfigVariableFilename::get_value
get_value
Returns the variable's value.
Definition: configVariableFilename.h:57
CullTraverser::get_gsg
GraphicsStateGuardianBase * get_gsg() const
Returns the GraphicsStateGuardian in effect.
Definition: cullTraverser.I:18
GraphicsStateGuardian
Encapsulates all the communication with a particular instance of a given rendering backend.
Definition: graphicsStateGuardian.h:65
Loader::get_global_ptr
static Loader * get_global_ptr()
Returns a pointer to the global Loader.
Definition: loader.I:212
STTerrain::is_valid
bool is_valid() const
Returns true if the terrain data is well-defined and ready to use.
Definition: stTerrain.I:18
lightAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
directionalLight.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomDrawCallbackData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:641
DatagramIterator::get_uint32
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: datagramIterator.I:164
LightAttrib::get_num_on_lights
get_num_on_lights
Returns the number of lights that are turned on by the attribute.
Definition: lightAttrib.h:74
SceneSetup::get_cs_transform
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
LightAttrib
Indicates which set of lights should be considered "on" to illuminate geometry at this level and belo...
Definition: lightAttrib.h:30
BoundingVolume
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
Definition: boundingVolume.h:41
SpeedTreeNode::remove_tree
int remove_tree(const STTree *tree)
Removes all instances of the indicated tree.
Definition: speedTreeNode.cxx:185
STTerrain::get_num_splat_layers
int get_num_splat_layers() const
Returns the number of splat layers that are to be applied to the terrain.
Definition: stTerrain.I:48
PandaNode
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
Filename::make_relative_to
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
SpeedTreeNode::get_num_trees
get_num_trees
Returns the number of unique tree objects that have been added to the node.
Definition: speedTreeNode.h:95
TextureAttrib
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:31
NodePath::node
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
Lens::get_far
get_far
Returns the position of the far plane (or cylinder, sphere, whatever).
Definition: lens.h:114
TypedObject::is_exact_type
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
BamWriter::get_filename
get_filename
If a BAM is a file, then the BamWriter should contain the name of the file.
Definition: bamWriter.h:92
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
SceneSetup::get_lens
const Lens * get_lens() const
Returns the particular Lens used for rendering.
Definition: sceneSetup.I:131
SpeedTreeNode
Interfaces with the SpeedTree library to render SpeedTree objects, especially trees,...
Definition: speedTreeNode.h:50
CullTraverser::get_cull_handler
CullHandler * get_cull_handler() const
Returns the object that will receive the culled Geoms.
Definition: cullTraverser.I:152
cullHandler.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SpeedTreeNode::compute_internal_bounds
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.
Definition: speedTreeNode.cxx:1059
STTerrain::get_splat_layer_tiling
PN_stdfloat get_splat_layer_tiling(int n) const
Returns the tiling value of the nth splat layer.
Definition: stTerrain.I:66
SpeedTreeNode::make_copy
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition: speedTreeNode.cxx:800
PandaNode::xform
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:304
ordered_vector::begin
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
Definition: ordered_vector.I:41
SpeedTreeNode::count_total_instances
int count_total_instances() const
Returns the total number of trees that will be rendered by this node, counting all instances of all t...
Definition: speedTreeNode.cxx:134
parse_params
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
graphicsStateGuardian.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
STTerrain::load_data
virtual void load_data()=0
This will be called at some point after initialization.
Definition: stTerrain.cxx:76
SpeedTreeNode::remove_all_trees
void remove_all_trees()
Removes all instances of all trees from the node.
Definition: speedTreeNode.cxx:214
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
SpeedTreeNode::cull_callback
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
Definition: speedTreeNode.cxx:884
STTerrain::get_splat_map
const Filename & get_splat_map() const
Returns the splat map that should be applied to the terrain.
Definition: stTerrain.I:38
SpeedTreeNode::reload_config
void reload_config()
Re-reads the current setting of all of the relevant config variables and applies them to this node.
Definition: speedTreeNode.cxx:643
ordered_vector::clear
void clear()
Removes all elements from the ordered vector.
Definition: ordered_vector.I:412
STTree
Encapsulates a single tree model in the SpeedTree library, as loaded from an SRT file.
Definition: stTree.h:28
ConfigVariable::get_num_words
size_t get_num_words() const
Returns the number of words in the variable's value.
Definition: configVariable.I:100
NodePath::is_empty
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
DXGraphicsStateGuardian9
A GraphicsStateGuardian for rendering into DirectX9 contexts.
Definition: dxGraphicsStateGuardian9.h:57
boundingBox.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.