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