From d0db1d1f0706e372a2586f248a67f71d11a9dbb0 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 7 Oct 2010 23:04:46 +0000 Subject: [PATCH] SpeedTree: first pass --- panda/src/speedtree/Sources.pp | 47 + panda/src/speedtree/config_speedtree.cxx | 107 ++ panda/src/speedtree/config_speedtree.h | 39 + panda/src/speedtree/loaderFileTypeSrt.cxx | 85 ++ panda/src/speedtree/loaderFileTypeSrt.h | 59 ++ .../speedtree/pandaspeedtree_composite1.cxx | 6 + panda/src/speedtree/speedTreeNode.I | 164 +++ panda/src/speedtree/speedTreeNode.cxx | 981 ++++++++++++++++++ panda/src/speedtree/speedTreeNode.h | 198 ++++ panda/src/speedtree/speedtree_api.cxx | 15 + panda/src/speedtree/speedtree_api.h | 27 + panda/src/speedtree/speedtree_parameters.h.pp | 18 + panda/src/speedtree/stTransform.I | 130 +++ panda/src/speedtree/stTransform.cxx | 54 + panda/src/speedtree/stTransform.h | 67 ++ panda/src/speedtree/stTree.I | 70 ++ panda/src/speedtree/stTree.cxx | 100 ++ panda/src/speedtree/stTree.h | 79 ++ 18 files changed, 2246 insertions(+) create mode 100644 panda/src/speedtree/Sources.pp create mode 100644 panda/src/speedtree/config_speedtree.cxx create mode 100644 panda/src/speedtree/config_speedtree.h create mode 100644 panda/src/speedtree/loaderFileTypeSrt.cxx create mode 100644 panda/src/speedtree/loaderFileTypeSrt.h create mode 100644 panda/src/speedtree/pandaspeedtree_composite1.cxx create mode 100644 panda/src/speedtree/speedTreeNode.I create mode 100644 panda/src/speedtree/speedTreeNode.cxx create mode 100644 panda/src/speedtree/speedTreeNode.h create mode 100644 panda/src/speedtree/speedtree_api.cxx create mode 100644 panda/src/speedtree/speedtree_api.h create mode 100644 panda/src/speedtree/speedtree_parameters.h.pp create mode 100644 panda/src/speedtree/stTransform.I create mode 100644 panda/src/speedtree/stTransform.cxx create mode 100644 panda/src/speedtree/stTransform.h create mode 100644 panda/src/speedtree/stTree.I create mode 100644 panda/src/speedtree/stTree.cxx create mode 100644 panda/src/speedtree/stTree.h diff --git a/panda/src/speedtree/Sources.pp b/panda/src/speedtree/Sources.pp new file mode 100644 index 0000000000..cbf9c63293 --- /dev/null +++ b/panda/src/speedtree/Sources.pp @@ -0,0 +1,47 @@ +#define BUILD_DIRECTORY $[HAVE_SPEEDTREE] + +#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m prc:c + +#define USE_PACKAGES speedtree $[if $[eq $[SPEEDTREE_API],opengl],gl cg cggl] $[if $[eq $[SPEEDTREE_API],directx9],dx9 cg cgdx9] +#define BUILDING_DLL BUILDING_PANDASKEL + +#begin lib_target + #define TARGET pandaspeedtree + #define LOCAL_LIBS \ + display text pgraph gobj linmath putil + + #define COMBINED_SOURCES $[TARGET]_composite1.cxx + + #define SOURCES \ + config_speedtree.h \ + loaderFileTypeSrt.h \ + speedtree_api.h \ + speedTreeNode.h \ + stTransform.h \ + stTree.h + + // A generated file + #define SOURCES $[SOURCES] speedtree_parameters.h + + #define INCLUDED_SOURCES \ + config_speedtree.cxx \ + loaderFileTypeSrt.cxx \ + speedtree_api.cxx \ + speedTreeNode.cxx \ + stTransform.cxx \ + stTree.cxx + + #define INSTALL_HEADERS \ + speedtree_parameters.h \ + speedtree_api.h \ + speedTreeNode.h \ + stTransform.h \ + stTree.h + + #define IGATESCAN all + +#end lib_target + + +#include $[THISDIRPREFIX]speedtree_parameters.h.pp diff --git a/panda/src/speedtree/config_speedtree.cxx b/panda/src/speedtree/config_speedtree.cxx new file mode 100644 index 0000000000..64185ba6b0 --- /dev/null +++ b/panda/src/speedtree/config_speedtree.cxx @@ -0,0 +1,107 @@ +// Filename: config_speedtree.cxx +// Created by: drose (30Sep10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "config_speedtree.h" +#include "speedTreeNode.h" +#include "stTree.h" +#include "loaderFileTypeSrt.h" +#include "loaderFileTypeRegistry.h" +#include "dconfig.h" + +Configure(config_speedtree); +NotifyCategoryDef(speedtree, ""); + +ConfigureFn(config_speedtree) { + init_libspeedtree(); +} + +ConfigVariableString speedtree_license +("speedtree-license", "", + PRC_DESC("Specify the license string to pass to SpeedTreeNode::authorize() by default.")); + +ConfigVariableFilename speedtree_shaders_dir +("speedtree-shaders-dir", Filename(Filename::from_os_specific(SPEEDTREE_BIN_DIR), "Shaders"), + PRC_DESC("Specifies the directory in which to locate SpeedTree's system " + "shaders at runtime. If this is empty, the default is based on " + "SPEEDTREE_BIN_DIR, as provided at compile time.")); + +ConfigVariableBool speedtree_allow_horizontal_billboards +("speedtree-allow-horizontal-billboards", true, + PRC_DESC("Set this true to allow the use of horizontal billboards in " + "SpeedTree, or false to disallow them. Documentation on this " + "feature is sparse, but presumably enabling them increases " + "visual quality and also causes a greater performance impact.")); + +ConfigVariableInt speedtree_max_num_visible_cells +("speedtree-max-num-visible-cells", 75, + PRC_DESC("Specifies the maximum number of cells in a single SpeedTree forest " + "frustum. This is used internally by SpeedTree's billboard system.")); + +ConfigVariableInt speedtree_max_billboard_images_by_base +("speedtree-max-billboard-images-by-base", 20, + PRC_DESC("Specifies the maximum number of billboard images used by any single " + "tree.")); + +ConfigVariableDouble speedtree_cull_cell_size +("speedtree-cull-cell-size", 1200, + PRC_DESC("Specifies the size of a single SpeedTree cull cell, in Panda " + "units. Increasing this number decreases the number of " + "individual calls that must be made to render geometry, " + "while increasing the number of trees that are rendered " + "per call.")); + +//////////////////////////////////////////////////////////////////// +// Function: init_libspeedtree +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libspeedtree() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + SpeedTreeNode::init_type(); + STTree::init_type(); + LoaderFileTypeSrt::init_type(); + + LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr(); + reg->register_type(new LoaderFileTypeSrt); +} + +// We need a SpeedTree custom allocator to integrate with Panda's +// memory management. +class STCustomAllocator : public SpeedTree::CAllocator { +public: + void *Alloc(size_t block_size) { + return PANDA_MALLOC_ARRAY(block_size); + } + + void Free(void *block) { + if (block != NULL) { + PANDA_FREE_ARRAY(block); + } + } +}; + +// Hook our custom allocator into SpeedTree. +#ifndef CPPPARSER +static STCustomAllocator custom_allocator; +static SpeedTree::CAllocatorInterface allocator_interface(&custom_allocator); +#endif // CPPPARSER diff --git a/panda/src/speedtree/config_speedtree.h b/panda/src/speedtree/config_speedtree.h new file mode 100644 index 0000000000..ae672546f2 --- /dev/null +++ b/panda/src/speedtree/config_speedtree.h @@ -0,0 +1,39 @@ +// Filename: config_speedtree.h +// Created by: drose (30Sep10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_SPEEDTREE_H +#define CONFIG_SPEEDTREE_H + +#include "pandabase.h" +#include "notifyCategoryProxy.h" +#include "configVariableBool.h" +#include "configVariableDouble.h" +#include "configVariableString.h" +#include "configVariableInt.h" +#include "configVariableFilename.h" + +NotifyCategoryDecl(speedtree, EXPCL_PANDASKEL, EXPTP_PANDASKEL); + +extern ConfigVariableString speedtree_license; +extern ConfigVariableFilename speedtree_shaders_dir; +extern ConfigVariableBool speedtree_allow_horizontal_billboards; +extern ConfigVariableInt speedtree_max_num_visible_cells; +extern ConfigVariableInt speedtree_max_billboard_images_by_base; +extern ConfigVariableDouble speedtree_cull_cell_size; + +extern EXPCL_PANDASKEL void init_libspeedtree(); + +#endif + + diff --git a/panda/src/speedtree/loaderFileTypeSrt.cxx b/panda/src/speedtree/loaderFileTypeSrt.cxx new file mode 100644 index 0000000000..662e151254 --- /dev/null +++ b/panda/src/speedtree/loaderFileTypeSrt.cxx @@ -0,0 +1,85 @@ +// Filename: loaderFileTypeSrt.cxx +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "loaderFileTypeSrt.h" +#include "speedTreeNode.h" +#include "stTree.h" + +TypeHandle LoaderFileTypeSrt::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeSrt::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +LoaderFileTypeSrt:: +LoaderFileTypeSrt() { +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeSrt::get_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string LoaderFileTypeSrt:: +get_name() const { + return "SpeedTree compiled tree"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeSrt::get_extension +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string LoaderFileTypeSrt:: +get_extension() const { + return "srt"; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeSrt::supports_compressed +// Access: Published, Virtual +// Description: Returns true if this file type can transparently load +// compressed files (with a .pz extension), false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool LoaderFileTypeSrt:: +supports_compressed() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: LoaderFileTypeSrt::load_file +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PT(PandaNode) LoaderFileTypeSrt:: +load_file(const Filename &path, const LoaderOptions &, + BamCacheRecord *record) const { + if (!path.is_regular_file()) { + // Quietly fail if the file doesn't exist. The Loader expects + // this. + return NULL; + } + + PT(STTree) tree = new STTree(path); + if (!tree->is_valid()) { + return NULL; + } + + PT(SpeedTreeNode) st = new SpeedTreeNode(path.get_basename()); + st->add_instance(tree, STTransform()); + + return st.p(); +} diff --git a/panda/src/speedtree/loaderFileTypeSrt.h b/panda/src/speedtree/loaderFileTypeSrt.h new file mode 100644 index 0000000000..1b1b01b677 --- /dev/null +++ b/panda/src/speedtree/loaderFileTypeSrt.h @@ -0,0 +1,59 @@ +// Filename: loaderFileTypeSrt.h +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOADERFILETYPESRT_H +#define LOADERFILETYPESRT_H + +#include "pandabase.h" + +#include "loaderFileType.h" + +//////////////////////////////////////////////////////////////////// +// Class : LoaderFileTypeSrt +// Description : This defines the Loader interface to read SpeedTree +// SRT files, which describe a single tree. It actually +// returns a SpeedTreeNode with just a single tree +// within it. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDASKEL LoaderFileTypeSrt : public LoaderFileType { +public: + LoaderFileTypeSrt(); + + virtual string get_name() const; + virtual string get_extension() const; + virtual bool supports_compressed() const; + + virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options, + BamCacheRecord *record) const; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LoaderFileType::init_type(); + register_type(_type_handle, "LoaderFileTypeSrt", + LoaderFileType::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#endif + diff --git a/panda/src/speedtree/pandaspeedtree_composite1.cxx b/panda/src/speedtree/pandaspeedtree_composite1.cxx new file mode 100644 index 0000000000..710b1e201b --- /dev/null +++ b/panda/src/speedtree/pandaspeedtree_composite1.cxx @@ -0,0 +1,6 @@ +#include "config_speedtree.cxx" +#include "loaderFileTypeSrt.cxx" +#include "speedTreeNode.cxx" +#include "speedtree_api.cxx" +#include "stTransform.cxx" +#include "stTree.cxx" diff --git a/panda/src/speedtree/speedTreeNode.I b/panda/src/speedtree/speedTreeNode.I new file mode 100644 index 0000000000..feaca878a1 --- /dev/null +++ b/panda/src/speedtree/speedTreeNode.I @@ -0,0 +1,164 @@ +// Filename: speedTreeNode.I +// Created by: drose (30Sep10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::is_valid +// Access: Published +// Description: Returns true if the node is valid and ready to +// render, false otherwise. Note that this might not +// become false until after the first time the node is +// rendered. +//////////////////////////////////////////////////////////////////// +INLINE bool SpeedTreeNode:: +is_valid() const { + return _is_valid; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::get_num_trees +// Access: Published +// Description: Returns the number of unique tree objects that have +// been added to the node. This count does not include +// multiple instances of the same tree that appear in +// different transforms. +//////////////////////////////////////////////////////////////////// +INLINE int SpeedTreeNode:: +get_num_trees() const { + return (int)_trees.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::get_tree +// Access: Published +// Description: Returns the STTree pointer for the nth tree. +// See get_num_trees(). +//////////////////////////////////////////////////////////////////// +INLINE const STTree *SpeedTreeNode:: +get_tree(int n) const { + nassertr(n >= 0 && n < (int)_trees.size(), NULL); + InstanceList *instance_list = _trees[n]; + return instance_list->get_tree(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::get_instance_list +// Access: Published +// Description: Returns a list of transforms that corresponds to the +// instances at which the nth tree appears. +//////////////////////////////////////////////////////////////////// +INLINE const SpeedTreeNode::InstanceList &SpeedTreeNode:: +get_instance_list(int n) const { + nassertr(n >= 0 && n < (int)_trees.size(), *(InstanceList *)NULL); + InstanceList *instance_list = _trees[n]; + return *instance_list; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::modify_tree +// Access: Published +// Description: Returns a modifiable STTree pointer for the nth tree +// instance. +//////////////////////////////////////////////////////////////////// +INLINE STTree *SpeedTreeNode:: +modify_tree(int n) { + nassertr(n >= 0 && n < (int)_trees.size(), NULL); + InstanceList *instance_list = _trees[n]; + _needs_repopulate = true; + return (STTree *)instance_list->get_tree(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SpeedTreeNode::InstanceList:: +InstanceList(const STTree *tree) : _tree((STTree *)tree) { +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::operator < +// Access: Public +// Description: Used for comparison for ov_set. +//////////////////////////////////////////////////////////////////// +INLINE bool SpeedTreeNode::InstanceList:: +operator < (const InstanceList &other) const { + return _tree < other._tree; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::get_tree +// Access: Published +// Description: Returns the particular tree this list refers to. +//////////////////////////////////////////////////////////////////// +INLINE const STTree *SpeedTreeNode::InstanceList:: +get_tree() const { + return _tree; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::get_num_instances +// Access: Published +// Description: Returns the number of instances of this tree. +//////////////////////////////////////////////////////////////////// +INLINE int SpeedTreeNode::InstanceList:: +get_num_instances() const { + return (int)_instances.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::get_instance +// Access: Published +// Description: Returns the transform of the nth instance of this +// tree. +//////////////////////////////////////////////////////////////////// +INLINE STTransform SpeedTreeNode::InstanceList:: +get_instance(int n) const { + nassertr(n >= 0 && n < (int)_instances.size(), STTransform::ident_mat()); + return _instances[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::add_instance +// Access: Published +// Description: Adds a new instance of this tree at the indicated +// transform. Returns the index number of the new +// instance. +//////////////////////////////////////////////////////////////////// +INLINE int SpeedTreeNode::InstanceList:: +add_instance(const STTransform &transform) { + _instances.push_back(transform); + return ((int)_instances.size() - 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::remove_instance +// Access: Published +// Description: Removes the nth instance of this tree. +//////////////////////////////////////////////////////////////////// +INLINE void SpeedTreeNode::InstanceList:: +remove_instance(int n) { + nassertv(n >= 0 && n < (int)_instances.size()); + _instances.erase(_instances.begin() + n); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::DrawCallback::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SpeedTreeNode::DrawCallback:: +DrawCallback(SpeedTreeNode *node) : _node(node) { +} diff --git a/panda/src/speedtree/speedTreeNode.cxx b/panda/src/speedtree/speedTreeNode.cxx new file mode 100644 index 0000000000..416757edc4 --- /dev/null +++ b/panda/src/speedtree/speedTreeNode.cxx @@ -0,0 +1,981 @@ +// Filename: speedTreeNode.cxx +// Created by: drose (13Mar09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "pandabase.h" +#include "speedTreeNode.h" +#include "virtualFileSystem.h" +#include "config_util.h" +#include "cullTraverser.h" +#include "cullableObject.h" +#include "cullHandler.h" +#include "omniBoundingVolume.h" +#include "boundingSphere.h" +#include "boundingBox.h" +#include "clockObject.h" +#include "geomDrawCallbackData.h" +#include "graphicsStateGuardian.h" +#include "textureAttrib.h" + +#ifdef SPEEDTREE_OPENGL +#include "glew/glew.h" +#endif // SPEEDTREE_OPENGL + +bool SpeedTreeNode::_authorized; +bool SpeedTreeNode::_done_first_init; +TypeHandle SpeedTreeNode::_type_handle; +TypeHandle SpeedTreeNode::DrawCallback::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +SpeedTreeNode:: +SpeedTreeNode(const string &name) : + PandaNode(name), + _forest(*(new SpeedTree::CForestRender)) // HACK! SpeedTree doesn't destruct unused CForestRender objects correctly. Temporarily leaking these things until SpeedTree is fixed. +{ + init_node(); + // For now, set an infinite bounding volume. Maybe in the future + // we'll change this to match whatever set of trees we're holding, + // though it probably doesn't really matter too much. + //set_internal_bounds(new OmniBoundingVolume); + // set_internal_bounds(new BoundingSphere(LPoint3f::zero(), 10.0f)); + + // Intialize the render params. + SpeedTree::SForestRenderInfo render_info; + + // First, get the shader directory. + if (!speedtree_shaders_dir.get_value().is_directory()) { + speedtree_cat.warning() + << "speedtree-shaders-dir is set to " << speedtree_shaders_dir + << ", which doesn't exist.\n"; + } + + string shaders_dir = speedtree_shaders_dir.get_value().to_os_specific(); + // Ensure the path ends with a terminal slash; SpeedTree requires this. +#ifdef WIN32 + if (!shaders_dir.empty() && shaders_dir[shaders_dir.length() - 1] != '\\') { + shaders_dir += "\\"; + } +#else + if (!shaders_dir.empty() && shaders_dir[shaders_dir.length() - 1] != '/') { + shaders_dir += "/"; + } +#endif + + render_info.m_strShaderPath = shaders_dir.c_str(); + render_info.m_nMaxBillboardImagesByBase = speedtree_max_billboard_images_by_base; + render_info.m_nNumShadowMaps = 1; + render_info.m_nShadowMapResolution = 0; + _forest.SetRenderInfo(render_info); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::count_total_instances +// Access: Published +// Description: Returns the total number of trees that will be +// rendered by this node, counting all instances of all +// trees. +//////////////////////////////////////////////////////////////////// +int SpeedTreeNode:: +count_total_instances() const { + int total_instances = 0; + Trees::const_iterator ti; + for (ti = _trees.begin(); ti != _trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + total_instances += instance_list->get_num_instances(); + } + + return total_instances; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::add_tree +// Access: Published +// Description: Adds a new tree for rendering. Returns the +// InstanceList which can be used to add to the +// instances for this tree. If the tree has previously +// been added, returns the existing InstanceList. +//////////////////////////////////////////////////////////////////// +SpeedTreeNode::InstanceList &SpeedTreeNode:: +add_tree(const STTree *tree) { + nassertr(is_valid(), *(InstanceList *)NULL); + nassertr(tree->is_valid(), *(InstanceList *)NULL); + + InstanceList ilist(tree); + Trees::iterator ti = _trees.find(&ilist); + if (ti == _trees.end()) { + // This is the first time that this particular tree has been + // added. + InstanceList *instance_list = new InstanceList(tree); + pair result = _trees.insert(instance_list); + ti = result.first; + bool inserted = result.second; + nassertr(inserted, *(*ti)); + + if (!_forest.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { + speedtree_cat.warning() + << "Failed to register tree " << tree->get_filename() << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + } + + _needs_repopulate = true; + mark_internal_bounds_stale(); + InstanceList *instance_list = (*ti); + return *instance_list; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::remove_tree +// Access: Published +// Description: Removes all instances of the indicated tree. Returns +// the number of instances removed. +//////////////////////////////////////////////////////////////////// +int SpeedTreeNode:: +remove_tree(const STTree *tree) { + InstanceList ilist(tree); + Trees::iterator ti = _trees.find(&ilist); + if (ti == _trees.end()) { + // The tree was not already present. + return 0; + } + + if (!_forest.UnregisterTree(tree->get_tree())) { + speedtree_cat.warning() + << "Failed to unregister tree " << tree->get_filename() << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + + _needs_repopulate = true; + mark_internal_bounds_stale(); + + InstanceList *instance_list = (*ti); + int num_removed = instance_list->get_num_instances(); + _trees.erase(ti); + delete instance_list; + + return num_removed; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::remove_all_trees +// Access: Published +// Description: Removes all instances of all trees from the node. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +remove_all_trees() { + Trees::iterator ti; + for (ti = _trees.begin(); ti != _trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + const STTree *tree = instance_list->get_tree(); + if (!_forest.UnregisterTree(tree->get_tree())) { + speedtree_cat.warning() + << "Failed to unregister tree " << tree->get_filename() << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + delete instance_list; + } + + _trees.clear(); + _needs_repopulate = true; + mark_internal_bounds_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::has_instance_list +// Access: Published +// Description: Returns true if the indicated tree has any instances +// within this node, false otherwise. +//////////////////////////////////////////////////////////////////// +bool SpeedTreeNode:: +has_instance_list(const STTree *tree) const { + InstanceList ilist(tree); + Trees::const_iterator ti = _trees.find(&ilist); + return (ti != _trees.end()); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::get_instance_list +// Access: Published +// Description: Returns a list of transforms that corresponds to the +// instances at which the indicated tree appears. You +// should ensure that has_instance_list() returns true +// before calling this method. +//////////////////////////////////////////////////////////////////// +const SpeedTreeNode::InstanceList &SpeedTreeNode:: +get_instance_list(const STTree *tree) const { + InstanceList ilist(tree); + Trees::const_iterator ti = _trees.find(&ilist); + if (ti == _trees.end()) { + // The tree was not already present. + static InstanceList empty_list((STTree *)NULL); + return empty_list; + } + + InstanceList *instance_list = (*ti); + return *instance_list; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::modify_instance_list +// Access: Published +// Description: Returns a modifiable list of transforms that +// corresponds to the instances of this tree. This is +// equivalent to add_tree(). +//////////////////////////////////////////////////////////////////// +SpeedTreeNode::InstanceList &SpeedTreeNode:: +modify_instance_list(const STTree *tree) { + return add_tree(tree); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::add_instance +// Access: Published +// Description: Adds a new instance of the indicated tree at the +// indicated transform. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +add_instance(const STTree *tree, const STTransform &transform) { + add_tree(tree).add_instance(transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::add_instances +// Access: Published +// Description: Walks the scene graph beginning at root, looking for +// nested SpeedTreeNodes. For each SpeedTreeNode found, +// adds all of the instances defined within that +// SpeedTreeNode as instances of this node, after +// applying the indicated scene-graph transform. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +add_instances(const NodePath &root, const TransformState *transform) { + nassertv(!root.is_empty()); + r_add_instances(root.node(), transform->compose(root.get_transform()), + Thread::get_current_thread()); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::authorize +// Access: Published, Static +// Description: Make this call to initialized the SpeedTree API and +// verify the license. If an empty string is passed for +// the license, the config variable speedtree-license is +// consulted. Returns true on success, false on +// failure. If this call is not made explicitly, it +// will be made implicitly the first time a +// SpeedTreeNode is created. +//////////////////////////////////////////////////////////////////// +bool SpeedTreeNode:: +authorize(const string &license) { + if (!_authorized) { + if (!license.empty()) { + SpeedTree::CCore::Authorize(license.c_str()); + } else { + if (!speedtree_license.empty()) { + SpeedTree::CCore::Authorize(speedtree_license.c_str()); + } + } + + _authorized = SpeedTree::CCore::IsAuthorized(); + + SpeedTree::CCore::SetTextureFlip(true); + } + + return _authorized; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::Copy Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +SpeedTreeNode:: +SpeedTreeNode(const SpeedTreeNode ©) : + PandaNode(copy), + _forest(*(new SpeedTree::CForestRender)) // HACK! SpeedTree doesn't destruct unused CForestRender objects correctly. Temporarily leaking these things until SpeedTree is fixed. +{ + init_node(); + + _forest.SetRenderInfo(copy._forest.GetRenderInfo()); + + Trees::const_iterator ti; + for (ti = copy._trees.begin(); ti != copy._trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + const STTree *tree = instance_list->get_tree(); + if (!_forest.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { + speedtree_cat.warning() + << "Failed to register tree " << tree->get_filename() << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + + _trees.push_back(new InstanceList(*instance_list)); + } + + _needs_repopulate = true; + mark_internal_bounds_stale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::make_copy +// Access: Public, Virtual +// Description: Returns a newly-allocated Node that is a shallow copy +// of this one. It will be a different Node pointer, +// but its internal data may or may not be shared with +// that of the original Node. +//////////////////////////////////////////////////////////////////// +PandaNode *SpeedTreeNode:: +make_copy() const { + return new SpeedTreeNode(*this); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::safe_to_combine +// Access: Public, Virtual +// Description: Returns true if it is generally safe to combine this +// particular kind of PandaNode with other kinds of +// PandaNodes of compatible type, adding children or +// whatever. For instance, an LODNode should not be +// combined with any other PandaNode, because its set of +// children is meaningful. +//////////////////////////////////////////////////////////////////// +bool SpeedTreeNode:: +safe_to_combine() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::cull_callback +// Access: Public, Virtual +// Description: This function will be called during the cull +// traversal to perform any additional operations that +// should be performed at cull time. This may include +// additional manipulation of render state or additional +// visible/invisible decisions, or any other arbitrary +// operation. +// +// Note that this function will *not* be called unless +// set_cull_callback() is called in the constructor of +// the derived class. It is necessary to call +// set_cull_callback() to indicated that we require +// cull_callback() to be called. +// +// By the time this function is called, the node has +// already passed the bounding-volume test for the +// viewing frustum, and the node's transform and state +// have already been applied to the indicated +// CullTraverserData object. +// +// The return value is true if this node should be +// visible, or false if it should be culled. +//////////////////////////////////////////////////////////////////// +bool SpeedTreeNode:: +cull_callback(CullTraverser *trav, CullTraverserData &data) { + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, trav->get_gsg()); + nassertr(gsg != (GraphicsStateGuardian *)NULL, true); + if (!validate_api(gsg)) { + return true; + } + + ClockObject *clock = ClockObject::get_global_clock(); + _forest.SetGlobalTime(clock->get_frame_time()); + _forest.AdvanceGlobalWind(); + + // Compute the modelview and camera transforms, to pass to the + // SpeedTree CView structure. + CPT(TransformState) modelview = data.get_modelview_transform(trav); + modelview = gsg->get_cs_transform()->compose(modelview); + CPT(TransformState) camera_transform = modelview->invert_compose(TransformState::make_identity()); + const LMatrix4f &modelview_mat = modelview->get_mat(); + const LPoint3f &camera_pos = camera_transform->get_pos(); + const Lens *lens = trav->get_scene()->get_lens(); + + LMatrix4f projection_mat = + LMatrix4f::convert_mat(gsg->get_internal_coordinate_system(), lens->get_coordinate_system()) * + lens->get_projection_mat(); + + _view.Set(SpeedTree::Vec3(camera_pos[0], camera_pos[1], camera_pos[2]), + SpeedTree::Mat4x4(projection_mat.get_data()), + SpeedTree::Mat4x4(modelview_mat.get_data()), + lens->get_near(), lens->get_far()); + + if (!_forest.UploadViewShaderParameters(_view)) { + speedtree_cat.warning() + << "Couldn't set view parameters\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + + if (!_needs_repopulate) { + // Don't bother culling now unless we're correctly fully + // populated. (Culling won't be accurate unless the forest has + // been populated, but we have to be in the draw traversal to + // populate.) + _forest.CullAndComputeLOD(_view, _visible_trees); + } + // cerr << _visible_trees.m_aVisibleCells.size() << " visible cells\n"; + + // Recurse onto the node's children. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::is_renderable +// Access: Public, Virtual +// Description: Returns true if there is some value to visiting this +// particular node during the cull traversal for any +// camera, false otherwise. This will be used to +// optimize the result of get_net_draw_show_mask(), so +// that any subtrees that contain only nodes for which +// is_renderable() is false need not be visited. +//////////////////////////////////////////////////////////////////// +bool SpeedTreeNode:: +is_renderable() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::add_for_draw +// Access: Public, Virtual +// Description: Adds the node's contents to the CullResult we are +// building up during the cull traversal, so that it +// will be drawn at render time. For most nodes other +// than GeomNodes, this is a do-nothing operation. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +add_for_draw(CullTraverser *trav, CullTraverserData &data) { + if (_is_valid) { + // We create a CullableObject that has an explicit draw_callback + // into this node, so that we can make the appropriate calls into + // SpeedTree to render the forest during the actual draw. + CullableObject *object = + new CullableObject(NULL, data._state, + TransformState::make_identity(), + TransformState::make_identity(), + trav->get_gsg()); + object->set_draw_callback(new DrawCallback(this)); + trav->get_cull_handler()->record_object(object, trav); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::prepare_scene +// Access: Published +// Description: Walks through the scene graph beginning at this node, +// and does whatever initialization is required to +// render the scene properly with the indicated GSG. It +// is not strictly necessary to call this, since the GSG +// will initialize itself when the scene is rendered, +// but this may take some of the overhead away from that +// process. +// +// In particular, this will ensure that textures within +// the scene are loaded in texture memory, and display +// lists are built up from static geometry. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +prepare_scene(GraphicsStateGuardianBase *gsgbase, const RenderState *) { + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, gsgbase); + if (validate_api(gsg)) { + setup_for_render(gsg); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::compute_internal_bounds +// Access: Protected, Virtual +// Description: Returns a newly-allocated BoundingVolume that +// represents the internal contents of the node. Should +// be overridden by PandaNode classes that contain +// something internally. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const { + internal_vertices = 0; + + SpeedTree::CExtents extents; + Trees::const_iterator ti; + for (ti = _trees.begin(); ti != _trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + const STTree *tree = instance_list->get_tree(); + + const STInstances &st_instances = instance_list->_instances; + STInstances::const_iterator ii; + for (ii = st_instances.begin(); ii != st_instances.end(); ++ii) { + SpeedTree::CExtents tree_extents = tree->get_tree()->GetExtents(); + tree_extents.Rotate((*ii).GetRotationAngle()); + tree_extents.Scale((*ii).GetScale()); + tree_extents.Translate((*ii).GetPos()); + extents.ExpandAround(tree_extents); + } + } + + const SpeedTree::Vec3 &emin = extents.Min(); + const SpeedTree::Vec3 &emax = extents.Max(); + internal_bounds = new BoundingBox(LPoint3f(emin[0], emin[1], emin[2]), + LPoint3f(emax[0], emax[1], emax[2])); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::output +// Access: Public, Virtual +// Description: Writes a brief description of the node to the +// indicated output stream. This is invoked by the << +// operator. It may be overridden in derived classes to +// include some information relevant to the class. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +output(ostream &out) const { + PandaNode::output(out); + out + << " (" << get_num_trees() << " unique trees with " + << count_total_instances() << " total instances)"; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::write +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +write(ostream &out, int indent_level) const { + PandaNode::write(out, indent_level); + + Trees::const_iterator ti; + for (ti = _trees.begin(); ti != _trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + instance_list->write(out, indent_level + 2); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::init_node +// Access: Private +// Description: Called from the constructor to initialize some +// internal values. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +init_node() { + PandaNode::set_cull_callback(); + + _is_valid = false; + _needs_repopulate = false; + + // Ensure we have a license. + if (!authorize()) { + speedtree_cat.warning() + << "SpeedTree license not available.\n"; + return; + } + + _forest.SetHint(SpeedTree::CForest::HINT_MAX_NUM_VISIBLE_CELLS, speedtree_max_num_visible_cells); + + _forest.SetCullCellSize(speedtree_cull_cell_size); + + _is_valid = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::r_add_instances +// Access: Private +// Description: The recursive implementation of add_instances(). +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +r_add_instances(PandaNode *node, const TransformState *transform, + Thread *current_thread) { + if (node->is_of_type(SpeedTreeNode::get_class_type()) && node != this) { + SpeedTreeNode *other = DCAST(SpeedTreeNode, node); + + int num_trees = other->get_num_trees(); + for (int ti = 0; ti < num_trees; ++ti) { + const InstanceList &other_instance_list = other->get_instance_list(ti); + const STTree *tree = other_instance_list.get_tree(); + InstanceList &this_instance_list = add_tree(tree); + + int num_instances = other_instance_list.get_num_instances(); + for (int i = 0; i < num_instances; ++i) { + CPT(TransformState) other_trans = other_instance_list.get_instance(i); + CPT(TransformState) new_trans = transform->compose(other_trans); + this_instance_list.add_instance(new_trans.p()); + } + } + } + + Children children = node->get_children(current_thread); + for (int i = 0; i < children.get_num_children(); i++) { + PandaNode *child = children.get_child(i); + CPT(TransformState) child_transform = transform->compose(child->get_transform()); + r_add_instances(child, child_transform, current_thread); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::repopulate +// Access: Private +// Description: Rebuilds the internal structures as necessary for +// rendering. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +repopulate() { + _forest.ClearInstances(); + + Trees::iterator ti; + for (ti = _trees.begin(); ti != _trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + const STTree *tree = instance_list->get_tree(); + const STInstances &instances = instance_list->_instances; + if (instances.empty()) { + // There are no instances, so don't bother. (This shouldn't + // happen often, because we remove trees from the SpeedTreeNode + // when their instance list goes empty, though it's possible if + // the user has explicitly removed all of the instances.) + continue; + } + + if (!_forest.AddInstances(tree->get_tree(), &instances[0], instances.size())) { + speedtree_cat.warning() + << "Failed to add " << instances.size() + << " instances for " << *tree << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + } + + _forest.GetPopulationStats(_population_stats); + print_forest_stats(_population_stats); + + // setup billboard caps based on instances-per-cell stats + int max_instances_by_cell = 1; + for (ti = _trees.begin(); ti != _trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + const STTree *tree = instance_list->get_tree(); + const STInstances &instances = instance_list->_instances; + if (instances.empty()) { + continue; + } + + int max_instances = 1; + SpeedTree::CMap::const_iterator si; + si = _population_stats.m_mMaxNumInstancesPerCellPerBase.find(tree->get_tree()); + if (si != _population_stats.m_mMaxNumInstancesPerCellPerBase.end()) { + max_instances = max(max_instances, (int)si->second); + } + + max_instances_by_cell = max(max_instances_by_cell, max_instances); + } + + _visible_trees.Reserve(_forest.GetBaseTrees(), + _forest.GetBaseTrees().size(), + speedtree_max_num_visible_cells, + max_instances_by_cell, + speedtree_allow_horizontal_billboards); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::validate_api +// Access: Private +// Description: Returns true if the indicated GSG shares the +// appropriate API for this SpeedTreeNode, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool SpeedTreeNode:: +validate_api(GraphicsStateGuardian *gsg) { + GraphicsPipe *pipe = gsg->get_pipe(); + nassertr(pipe != (GraphicsPipe *)NULL, true); + +#if defined(SPEEDTREE_OPENGL) + static const string compiled_api = "OpenGL"; +#elif defined(SPEEDTREE_DIRECTX9) + static const string compiled_api = "DirectX9"; +#else + #error Unexpected graphics API. +#endif + + if (pipe->get_interface_name() != compiled_api) { + ostringstream stream; + stream + << "SpeedTree is compiled for " << compiled_api + << ", cannot render with " << pipe->get_interface_name(); + nassert_raise(stream.str()); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::draw_callback +// Access: Private +// Description: Called when the node is visited during the draw +// traversal, by virtue of our DrawCallback construct. +// This makes the calls into SpeedTree to perform the +// actual rendering. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +draw_callback(CallbackData *data) { + GeomDrawCallbackData *geom_cbdata; + DCAST_INTO_V(geom_cbdata, data); + + // Check the input state. + const RenderState *state = geom_cbdata->get_object()->_state; + + bool show_textures = true; + const TextureAttrib *texattrib = DCAST(TextureAttrib, state->get_attrib(TextureAttrib::get_class_slot())); + if (texattrib != (TextureAttrib *)NULL) { + show_textures = !texattrib->has_all_off(); + } + _forest.EnableTexturing(show_textures); + + GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, geom_cbdata->get_gsg()); + + setup_for_render(gsg); + + // start the forest render + _forest.StartRender(); + + bool branches = _forest.RenderBranches(_visible_trees, SpeedTree::RENDER_PASS_STANDARD); + bool fronds = _forest.RenderFronds(_visible_trees, SpeedTree::RENDER_PASS_STANDARD); + bool leaf_meshes = _forest.RenderLeafMeshes(_visible_trees, SpeedTree::RENDER_PASS_STANDARD); + bool leaf_cards = _forest.RenderLeafCards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view); + bool billboards = _forest.RenderBillboards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view); + + if (!branches || !fronds || !leaf_meshes || !leaf_cards || !billboards) { + speedtree_cat.warning() + << "Failed to render forest completely: " + << branches << " " << fronds << " " << leaf_meshes << " " << leaf_cards << " " << billboards << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + + _forest.EndRender(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::setup_for_render +// Access: Private +// Description: Does whatever calls are necessary to set up the +// forest for rendering--create vbuffers, load shaders, +// and whatnot. Primarily, this is the calls to +// InitTreeGraphics and the like. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +setup_for_render(GraphicsStateGuardian *gsg) { + if (!_done_first_init) { + // This is the first time we have entered the draw callback since + // creating any SpeedTreeNode. Now we have an opportunity to do + // any initial setup that requires a graphics context. + +#ifdef SPEEDTREE_OPENGL + // For OpenGL, we have to ensure GLEW has been initialized. + // (SpeedTree uses it, though Panda doesn't.) + GLenum err = glewInit(); + if (err != GLEW_OK) { + speedtree_cat.error() + << "GLEW initialization failed: %s\n", glewGetErrorString(err); + // Can't proceed without GLEW. + _is_valid = false; + return; + } + + // Insist that OpenGL 2.0 is available as the SpeedTree renderer + // requires it. + if (!GLEW_VERSION_2_0) { + speedtree_cat.error() + << "The SpeedTree OpenGL implementation requires OpenGL 2.0 or better to run; this system has version " << glGetString(GL_VERSION) << "\n"; + _is_valid = false; + return; + } +#endif // SPEEDTREE_OPENGL + + _done_first_init = true; + } + + if (_needs_repopulate) { + repopulate(); + + // Now init per-tree graphics + Trees::const_iterator ti; + for (ti = _trees.begin(); ti != _trees.end(); ++ti) { + InstanceList *instance_list = (*ti); + const STTree *tree = instance_list->get_tree(); + const STInstances &instances = instance_list->_instances; + if (instances.empty()) { + continue; + } + + int max_instances = 2; + SpeedTree::CMap::const_iterator si; + si = _population_stats.m_mMaxNumInstancesPerCellPerBase.find(tree->get_tree()); + if (si != _population_stats.m_mMaxNumInstancesPerCellPerBase.end()) { + max_instances = max(max_instances, (int)si->second); + } + + if (!_forest.InitTreeGraphics((SpeedTree::CTreeRender *)tree->get_tree(), + max_instances, speedtree_allow_horizontal_billboards)) { + speedtree_cat.warning() + << "Failed to init tree graphics for " << *tree << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + } + } + + // Init overall graphics + if (!_forest.InitGraphics(false)) { + speedtree_cat.warning() + << "Failed to init graphics\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + _is_valid = false; + return; + } + + // This call apparently must be made at draw time, not earlier, + // because it might attempt to create OpenGL index buffers and + // such. + _forest.UpdateTreeCellExtents(); + + // If we needed to repopulate, it means we didn't cull in the cull + // traversal. Do it now. + _forest.CullAndComputeLOD(_view, _visible_trees); + + _needs_repopulate = false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::print_forest_stats +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +print_forest_stats(const SpeedTree::CForest::SPopulationStats &forest_stats) const { + fprintf(stderr, "\n Forest Population Statistics\n"); + fprintf(stderr, " ---------------------------------------------------\n"); + fprintf(stderr, " # of tree cull cells: %d\n", forest_stats.m_nNumCells); + fprintf(stderr, " # of unique base trees: %d\n", forest_stats.m_nNumBaseTrees); + fprintf(stderr, " total # of instances: %d\n", forest_stats.m_nNumInstances); + fprintf(stderr, " average # of instances per base: %g\n", forest_stats.m_fAverageNumInstancesPerBase); + fprintf(stderr, " max # of billboards/instances per cell: %d\n", forest_stats.m_nMaxNumBillboardsPerCell); + fprintf(stderr, " max # of instances per cell per base:\n"); + SpeedTree::CMap::const_iterator i; + for (i = forest_stats.m_mMaxNumInstancesPerCellPerBase.begin( ); i != forest_stats.m_mMaxNumInstancesPerCellPerBase.end( ); ++i) { + fprintf(stderr, " %35s: %4d\n", SpeedTree::CFixedString(i->first->GetFilename( )).NoPath( ).c_str( ), i->second); + } + fprintf(stderr, " average # instances per cell: %g\n", forest_stats.m_fAverageInstancesPerCell); + fprintf(stderr, " max # of billboard images: %d\n", forest_stats.m_nMaxNumBillboardImages); + fprintf(stderr, "\n"); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::register_with_read_factory +// Access: Public, Static +// Description: Tells the BamReader how to create objects of type +// SpeedTreeNode. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::write_datagram +// Access: Public, Virtual +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +write_datagram(BamWriter *manager, Datagram &dg) { + PandaNode::write_datagram(manager, dg); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::make_from_bam +// Access: Protected, Static +// Description: This function is called by the BamReader's factory +// when a new object of type SpeedTreeNode is encountered +// in the Bam file. It should create the SpeedTreeNode +// and extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWritable *SpeedTreeNode:: +make_from_bam(const FactoryParams ¶ms) { + SpeedTreeNode *node = new SpeedTreeNode(""); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + node->fillin(scan, manager); + + return node; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::fillin +// Access: Protected +// Description: This internal function is called by make_from_bam to +// read in all of the relevant data from the BamFile for +// the new SpeedTreeNode. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +fillin(DatagramIterator &scan, BamReader *manager) { + PandaNode::fillin(scan, manager); +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode::InstanceList:: +output(ostream &out) const { + out << *_tree << ": " << _instances.size() << " instances."; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::InstanceList::write +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode::InstanceList:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) + << *_tree << ": " << _instances.size() << " instances.\n"; + STInstances::const_iterator ii; + for (ii = _instances.begin(); ii != _instances.end(); ++ii) { + indent(out, indent_level + 2) + << STTransform(*ii) << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::DrawCallback::do_callback +// Access: Public, Virtual +// Description: This method called when the callback is triggered; it +// *replaces* the original function. To continue +// performing the original function, you must call +// cbdata->upcall() during the callback. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode::DrawCallback:: +do_callback(CallbackData *data) { + _node->draw_callback(data); +} diff --git a/panda/src/speedtree/speedTreeNode.h b/panda/src/speedtree/speedTreeNode.h new file mode 100644 index 0000000000..46a0c0f9f4 --- /dev/null +++ b/panda/src/speedtree/speedTreeNode.h @@ -0,0 +1,198 @@ +// Filename: speedTreeNode.h +// Created by: drose (30Sep10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef SPEEDTREENODE_H +#define SPEEDTREENODE_H + +#include "pandabase.h" +#include "pandaNode.h" +#include "pointerTo.h" +#include "stTree.h" +#include "stTransform.h" +#include "callbackObject.h" + +#include "speedtree_api.h" + +//////////////////////////////////////////////////////////////////// +// Class : SpeedTreeNode +// Description : Interfaces with the SpeedTree library to render +// SpeedTree objects like a collection of trees, +// terrain, or grass within the Panda3D scene graph. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDASKEL SpeedTreeNode : public PandaNode { +private: + // This definition is required by InstanceList, below. + typedef pvector STInstances; + +PUBLISHED: + // This nested class keeps a linear list of transforms, for the + // purpose of recording instances of a particular STTree. It is + // used below. + class InstanceList { + public: + INLINE InstanceList(const STTree *tree); + INLINE bool operator < (const InstanceList &other) const; + + PUBLISHED: + INLINE const STTree *get_tree() const; + + INLINE int get_num_instances() const; + INLINE STTransform get_instance(int n) const; + MAKE_SEQ(get_instances, get_num_instances, get_instance); + + INLINE int add_instance(const STTransform &transform); + INLINE void remove_instance(int n); + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + + private: + PT(STTree) _tree; + STInstances _instances; + friend class SpeedTreeNode; + }; + +PUBLISHED: + SpeedTreeNode(const string &name); + + INLINE bool is_valid() const; + + INLINE int get_num_trees() const; + INLINE const STTree *get_tree(int n) const; + MAKE_SEQ(get_trees, get_num_trees, get_tree); + const InstanceList &get_instance_list(int n) const; + MAKE_SEQ(get_instance_lists, get_num_trees, get_instance_list); + INLINE STTree *modify_tree(int n); + + int count_total_instances() const; + + InstanceList &add_tree(const STTree *tree); + int remove_tree(const STTree *tree); + void remove_all_trees(); + + bool has_instance_list(const STTree *tree) const; + const InstanceList &get_instance_list(const STTree *tree) const; + InstanceList &modify_instance_list(const STTree *tree); + + void add_instance(const STTree *tree, const STTransform &transform); + void add_instances(const NodePath &root, const TransformState *transform = TransformState::make_identity()); + + static bool authorize(const string &license = ""); + +public: + SpeedTreeNode(const SpeedTreeNode ©); + + virtual PandaNode *make_copy() const; + virtual bool safe_to_combine() const; + + virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data); + virtual bool is_renderable() const; + virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); + + void prepare_scene(GraphicsStateGuardianBase *gsgbase, const RenderState *net_state); + + virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, + int &internal_vertices, + int pipeline_stage, + Thread *current_thread) const; + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level) const; + +private: + void init_node(); + void r_add_instances(PandaNode *node, const TransformState *transform, + Thread *current_thread); + + void repopulate(); + bool validate_api(GraphicsStateGuardian *gsg); + void draw_callback(CallbackData *cbdata); + void setup_for_render(GraphicsStateGuardian *gsg); + void print_forest_stats(const SpeedTree::CForest::SPopulationStats &forest_stats) const; + +private: + class DrawCallback : public CallbackObject { + public: + ALLOC_DELETED_CHAIN(DrawCallback); + INLINE DrawCallback(SpeedTreeNode *node); + virtual void do_callback(CallbackData *cbdata); + + private: + PT(SpeedTreeNode) _node; + + public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CallbackObject::init_type(); + register_type(_type_handle, "SpeedTreeNode::DrawCallback", + CallbackObject::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + static TypeHandle _type_handle; + }; + +private: + // A list of instances per each unique tree. + typedef ov_set > Trees; + Trees _trees; + + SpeedTree::CForestRender &_forest; // Hack! + SpeedTree::CView _view; + SpeedTree::SForestCullResultsRender _visible_trees; + SpeedTree::CForest::SPopulationStats _population_stats; + bool _needs_repopulate; + bool _is_valid; + + static bool _authorized; + static bool _done_first_init; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + PandaNode::init_type(); + register_type(_type_handle, "SpeedTreeNode", + PandaNode::get_class_type()); + DrawCallback::init_type(); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class SpeedTreeNode::DrawCallback; +}; + +#include "speedTreeNode.I" + +#endif diff --git a/panda/src/speedtree/speedtree_api.cxx b/panda/src/speedtree/speedtree_api.cxx new file mode 100644 index 0000000000..6cf49f8ff4 --- /dev/null +++ b/panda/src/speedtree/speedtree_api.cxx @@ -0,0 +1,15 @@ +// Filename: speedtree_api.cxx +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "speedtree_api.h" diff --git a/panda/src/speedtree/speedtree_api.h b/panda/src/speedtree/speedtree_api.h new file mode 100644 index 0000000000..af107988e5 --- /dev/null +++ b/panda/src/speedtree/speedtree_api.h @@ -0,0 +1,27 @@ +// Filename: speedtree_api.h +// Created by: drose (05Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef SPEEDTREE_API_H +#define SPEEDTREE_API_H + +// This header file should be included first, to pull in any of the +// required headers from the SpeedTree API, needed in this directory. + +#include "speedtree_parameters.h" +#include "Core/Core.h" +#include "Forest/Forest.h" +#include "Renderers/OpenGL/OpenGLRenderer.h" + +#endif // SPEEDTREE_API_H + diff --git a/panda/src/speedtree/speedtree_parameters.h.pp b/panda/src/speedtree/speedtree_parameters.h.pp new file mode 100644 index 0000000000..1810d68dd6 --- /dev/null +++ b/panda/src/speedtree/speedtree_parameters.h.pp @@ -0,0 +1,18 @@ +// This file is read and processed by ppremake to generate +// speedtree_parameters.h, which is #included by speedtree_api.h. + +#output speedtree_parameters.h notouch +/* speedtree_parameters.h. Generated automatically by $[PPREMAKE] $[PPREMAKE_VERSION] from $[notdir $[THISFILENAME]]. */ +/********************************** DO NOT EDIT ****************************/ + +/* We need to define the appropriate macro to tell the SpeedTree + headers which API we intend to use. This should be one of + SPEEDTREE_OPENGL or SPEEDTREE_DIRECTX9 (or, when Panda supports it, + SPEEDTREE_DIRECTX10). */ +# define SPEEDTREE_$[upcase $[SPEEDTREE_API]] + +/* The default directory in which to search for SpeedTree's provided + shaders and terrain files. */ +# define SPEEDTREE_BIN_DIR "$[SPEEDTREE_BIN_DIR]" + +#end speedtree_parameters.h diff --git a/panda/src/speedtree/stTransform.I b/panda/src/speedtree/stTransform.I new file mode 100644 index 0000000000..9ee81c86af --- /dev/null +++ b/panda/src/speedtree/stTransform.I @@ -0,0 +1,130 @@ +// Filename: stTransform.I +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::Default Constructor +// Access: Published +// Description: The default constructor creates an identity transform. +//////////////////////////////////////////////////////////////////// +INLINE STTransform:: +STTransform() : + _pos(0.0f, 0.0f, 0.0f), + _rotate(0.0f), + _scale(1.0f) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::Constructor +// Access: Published +// Description: Construct a transform with componentwise inputs. +//////////////////////////////////////////////////////////////////// +INLINE STTransform:: +STTransform(const LPoint3f &pos, float rotate, float scale) : + _pos(pos), + _rotate(rotate), + _scale(scale) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::Constructor +// Access: Published +// Description: Construct a transform with componentwise inputs. +//////////////////////////////////////////////////////////////////// +INLINE STTransform:: +STTransform(float x, float y, float z, float rotate, float scale) : + _pos(x, y, z), + _rotate(rotate), + _scale(scale) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE STTransform:: +STTransform(const STTransform ©) : + _pos(copy._pos), + _rotate(copy._rotate), + _scale(copy._scale) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void STTransform:: +operator = (const STTransform ©) { + _pos = copy._pos; + _rotate = copy._rotate; + _scale = copy._scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::CInstance constructor +// Access: Public +// Description: This is used internally to construct an STTransform +// from a SpeedTree::CInstance object. +//////////////////////////////////////////////////////////////////// +INLINE STTransform:: +STTransform(const SpeedTree::CInstance &instance) { + const SpeedTree::Vec3 &pos = instance.GetPos(); + _pos.set(pos[0], pos[1], pos[2]); + _rotate = rad_2_deg(instance.GetRotationAngle()); + _scale = instance.GetScale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::CInstance operator +// Access: Public +// Description: This is used internally to convert an STTransform +// into a SpeedTree::CInstance object. +//////////////////////////////////////////////////////////////////// +INLINE STTransform:: +operator SpeedTree::CInstance () const { + SpeedTree::CInstance instance; + instance.SetPos(SpeedTree::Vec3(_pos[0], _pos[1], _pos[2])); + instance.SetRotation(deg_2_rad(_rotate)); + instance.SetScale(_scale); + return instance; +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::TransformState operator +// Access: Public +// Description: This is used internally to convert an STTransform +// into a TransformState pointer. +//////////////////////////////////////////////////////////////////// +INLINE STTransform:: +operator CPT(TransformState) () const { + return TransformState::make_pos_hpr_scale(_pos, + LVecBase3f(_rotate, 0.0f, 0.0f), + LVecBase3f(_scale, _scale, _scale)); +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::ident_mat +// Access: Published, Static +// Description: Returns a global identity transform object. +//////////////////////////////////////////////////////////////////// +INLINE const STTransform &STTransform:: +ident_mat() { + return _ident_mat; +} diff --git a/panda/src/speedtree/stTransform.cxx b/panda/src/speedtree/stTransform.cxx new file mode 100644 index 0000000000..d5564a2a8a --- /dev/null +++ b/panda/src/speedtree/stTransform.cxx @@ -0,0 +1,54 @@ +// Filename: stTransform.cxx +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "stTransform.h" + +STTransform STTransform::_ident_mat; + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::Constructor +// Access: Published +// Description: This constructor accepts a Panda TransformState, for +// instance as extracted from the scene graph. +//////////////////////////////////////////////////////////////////// +STTransform:: +STTransform(const TransformState *trans) { +#ifndef NDEBUG + // Ensure these are initialized to reasonable values in case we fail + // an assertion below. + _pos.set(0.0f, 0.0f, 0.0f); + _rotate = 0.0f; + _scale = 1.0f; +#endif + + nassertv(trans->has_components()); + _pos = trans->get_pos(); + + const LVecBase3f &hpr = trans->get_hpr(); + nassertv(IS_NEARLY_ZERO(hpr[1]) && IS_NEARLY_ZERO(hpr[2])); + _rotate = hpr[0]; + + nassertv(trans->has_uniform_scale()); + _scale = trans->get_uniform_scale(); +} + +//////////////////////////////////////////////////////////////////// +// Function: STTransform::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void STTransform:: +output(ostream &out) const { + out << "STTransform(" << _pos << ", " << _rotate << ", " << _scale << ")"; +} diff --git a/panda/src/speedtree/stTransform.h b/panda/src/speedtree/stTransform.h new file mode 100644 index 0000000000..39be45fcf2 --- /dev/null +++ b/panda/src/speedtree/stTransform.h @@ -0,0 +1,67 @@ +// Filename: stTransform.h +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef STTRANSFORM_H +#define STTRANSFORM_H + +#include "pandabase.h" +#include "transformState.h" +#include "speedtree_api.h" +#include "deg_2_rad.h" + +//////////////////////////////////////////////////////////////////// +// Class : STTransform +// Description : Represents a transform that may be applied to a +// particular instance of a tree when added to the +// SpeedTreeNode. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDASKEL STTransform { +PUBLISHED: + INLINE STTransform(); + STTransform(const TransformState *trans); + INLINE STTransform(const LPoint3f &pos, float rotate = 0.0f, float scale = 1.0f); + INLINE STTransform(float x, float y, float z, float rotate, float scale); + INLINE STTransform(const STTransform ©); + INLINE void operator = (const STTransform ©); + +public: + INLINE STTransform(const SpeedTree::CInstance &instance); + INLINE operator SpeedTree::CInstance () const; + INLINE operator CPT(TransformState) () const; + +PUBLISHED: + INLINE static const STTransform &ident_mat(); + + INLINE const LPoint3f &get_pos() const; + INLINE float get_rotate() const; + INLINE float get_scale() const; + + void output(ostream &out) const; + +private: + LPoint3f _pos; + float _rotate; + float _scale; + + static STTransform _ident_mat; +}; + +INLINE ostream &operator << (ostream &out, const STTransform &transform) { + transform.output(out); + return out; +} + +#include "stTransform.I" + +#endif diff --git a/panda/src/speedtree/stTree.I b/panda/src/speedtree/stTree.I new file mode 100644 index 0000000000..26b21adb0e --- /dev/null +++ b/panda/src/speedtree/stTree.I @@ -0,0 +1,70 @@ +// Filename: stTree.I +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: STTree::get_fullpath +// Access: Published +// Description: Returns the full pathname to the SRT file that was +// loaded for this tree, as passed to the constructor. +//////////////////////////////////////////////////////////////////// +INLINE const Filename &STTree:: +get_fullpath() const { + return _fullpath; +} + +//////////////////////////////////////////////////////////////////// +// Function: STTree::get_filename +// Access: Published +// Description: Returns the original filename given for the SRT file +// for this tree, before resolving it along the +// model-path, as passed to the constructor. +//////////////////////////////////////////////////////////////////// +INLINE const Filename &STTree:: +get_filename() const { + return _filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: STTree::is_valid +// Access: Published +// Description: Returns true if the tree was successfully loaded and +// is ready to be used, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool STTree:: +is_valid() const { + return _is_valid; +} + +//////////////////////////////////////////////////////////////////// +// Function: STTree::get_tree +// Access: Public +// Description: Returns a const pointer to the internal SpeedTree +// object. +//////////////////////////////////////////////////////////////////// +INLINE const SpeedTree::CTreeRender *STTree:: +get_tree() const { + return &_tree; +} + +//////////////////////////////////////////////////////////////////// +// Function: STTree::modify_tree +// Access: Public +// Description: Returns a modifiable pointer to the internal SpeedTree +// object. +//////////////////////////////////////////////////////////////////// +INLINE SpeedTree::CTreeRender *STTree:: +modify_tree() { + return &_tree; +} diff --git a/panda/src/speedtree/stTree.cxx b/panda/src/speedtree/stTree.cxx new file mode 100644 index 0000000000..cd3e4e6918 --- /dev/null +++ b/panda/src/speedtree/stTree.cxx @@ -0,0 +1,100 @@ +// Filename: stTree.cxx +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "stTree.h" +#include "speedTreeNode.h" + +TypeHandle STTree::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: STTree::Constructor +// Access: Published +// Description: The constructor reads the indicated SRT file +// immediately. Check is_valid() to determine whether +// the read was successful or not. Note that the +// filename must be a fully-qualified pathname; the +// STTree constructor does not search the model-path. +// (However, the user-specified relative filename may be +// specified as an optional second parameter, which is +// used for documentary purposes only.) +//////////////////////////////////////////////////////////////////// +STTree:: +STTree(const Filename &fullpath, const Filename &filename) : + Namable(fullpath.get_basename_wo_extension()), + _fullpath(fullpath), + _filename(filename) +{ + if (_filename.empty()) { + _filename = fullpath; + } + + _is_valid = false; + + // Ensure we have a license. + if (!SpeedTreeNode::authorize()) { + speedtree_cat.warning() + << "SpeedTree license not available.\n"; + return; + } + + // Can't use VFS, due to SpeedTree's insistence on using fopen() to + // load dds textures and such. So we go ahead and use the low-level + // Filename interface directly. + /* + Filename tree_filename = filename; + if (!tree_filename.resolve_filename(get_model_path(), "srt")) { + speedtree_cat.warning() + << "Couldn't find: " << filename << "\n"; + return false; + } + */ + + string os_fullpath = _fullpath.to_os_specific(); + if (!_tree.LoadTree(os_fullpath.c_str())) { + speedtree_cat.warning() + << "Couldn't read: " << _fullpath << "\n"; + speedtree_cat.warning() + << SpeedTree::CCore::GetError() << "\n"; + return; + } + + speedtree_cat.info() + << "Read " << _filename << "\n"; + _is_valid = true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: STTree::Copy Constructor +// Access: Private +// Description: An STTree copy constructor is not supported. +//////////////////////////////////////////////////////////////////// +STTree:: +STTree(const STTree ©) { + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: STTree::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void STTree:: +output(ostream &out) const { + if (!is_valid()) { + out << "(invalid STTree)"; + } else { + out << "STTree(" << get_name() << ")"; + } +} diff --git a/panda/src/speedtree/stTree.h b/panda/src/speedtree/stTree.h new file mode 100644 index 0000000000..45cd503c42 --- /dev/null +++ b/panda/src/speedtree/stTree.h @@ -0,0 +1,79 @@ +// Filename: stTree.h +// Created by: drose (06Oct10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef STTREE_H +#define STTREE_H + +#include "pandabase.h" +#include "typedReferenceCount.h" +#include "namable.h" +#include "speedtree_api.h" + +class SpeedTreeNode; + +//////////////////////////////////////////////////////////////////// +// Class : STTree +// Description : Encapsulates a single tree model in the SpeedTree +// library, as loaded from an SRT file. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDASKEL STTree : public TypedReferenceCount, public Namable { +PUBLISHED: + STTree(const Filename &fullpath, const Filename &filename = Filename()); +private: + STTree(const STTree ©); + +PUBLISHED: + INLINE const Filename &get_fullpath() const; + INLINE const Filename &get_filename() const; + + INLINE bool is_valid() const; + + virtual void output(ostream &out) const; + +public: + INLINE const SpeedTree::CTreeRender *get_tree() const; + INLINE SpeedTree::CTreeRender *modify_tree(); + +private: + Filename _fullpath; + Filename _filename; + bool _is_valid; + SpeedTree::CTreeRender _tree; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "STTree", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const STTree &tree) { + tree.output(out); + return out; +} + +#include "stTree.I" + +#endif