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