terrain
This commit is contained in:
parent
66f1acc32f
commit
d93aac8ad8
|
|
@ -18,9 +18,11 @@
|
|||
loaderFileTypeSrt.h \
|
||||
loaderFileTypeStf.h \
|
||||
speedtree_api.h \
|
||||
speedTreeNode.h \
|
||||
stTransform.h \
|
||||
stTree.h
|
||||
speedTreeNode.h speedTreeNode.I \
|
||||
stBasicTerrain.h stBasicTerrain.I \
|
||||
stTerrain.h stTerrain.I \
|
||||
stTransform.h stTransform.I \
|
||||
stTree.h stTree.I
|
||||
|
||||
// A generated file
|
||||
#define SOURCES $[SOURCES] speedtree_parameters.h
|
||||
|
|
@ -31,15 +33,19 @@
|
|||
loaderFileTypeStf.cxx \
|
||||
speedtree_api.cxx \
|
||||
speedTreeNode.cxx \
|
||||
stBasicTerrain.cxx \
|
||||
stTerrain.cxx \
|
||||
stTransform.cxx \
|
||||
stTree.cxx
|
||||
|
||||
#define INSTALL_HEADERS \
|
||||
speedtree_parameters.h \
|
||||
speedtree_api.h \
|
||||
speedTreeNode.h \
|
||||
stTransform.h \
|
||||
stTree.h
|
||||
speedTreeNode.h speedTreeNode.I \
|
||||
stBasicTerrain.h stBasicTerrain.I \
|
||||
stTerrain.h stTerrain.I \
|
||||
stTransform.h stTransform.I \
|
||||
stTree.h stTree.I
|
||||
|
||||
#define IGATESCAN all
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "config_speedtree.h"
|
||||
#include "speedTreeNode.h"
|
||||
#include "stBasicTerrain.h"
|
||||
#include "stTerrain.h"
|
||||
#include "stTree.h"
|
||||
#include "loaderFileTypeSrt.h"
|
||||
#include "loaderFileTypeStf.h"
|
||||
|
|
@ -147,6 +149,26 @@ ConfigVariableDouble speedtree_sun_fog_bloom
|
|||
("speedtree-sun-fog-bloom", 0.0,
|
||||
PRC_DESC("Undocumented SpeedTree parameter."));
|
||||
|
||||
ConfigVariableDouble speedtree_ambient_color
|
||||
("speedtree-ambient-color", "1 1 1",
|
||||
PRC_DESC("Specifies the r g b color of the ambient light on SpeedTree "
|
||||
"surfaces."));
|
||||
|
||||
ConfigVariableDouble speedtree_diffuse_color
|
||||
("speedtree-diffuse-color", "1 1 1",
|
||||
PRC_DESC("Specifies the r g b color of the diffuse light on SpeedTree "
|
||||
"surfaces."));
|
||||
|
||||
ConfigVariableDouble speedtree_specular_color
|
||||
("speedtree-specular-color", "1 1 1",
|
||||
PRC_DESC("Specifies the r g b color of the specular reflections on SpeedTree "
|
||||
"surfaces."));
|
||||
|
||||
ConfigVariableDouble speedtree_emissive_color
|
||||
("speedtree-emissive-color", "0 0 0",
|
||||
PRC_DESC("Specifies the r g b color of the emissive light effect on SpeedTree "
|
||||
"surfaces."));
|
||||
|
||||
ConfigVariableInt speedtree_num_shadow_maps
|
||||
("speedtree-num-shadow-maps", 3,
|
||||
PRC_DESC("Specifies the number of shadow maps to use to render SpeedTree "
|
||||
|
|
@ -216,6 +238,8 @@ init_libspeedtree() {
|
|||
initialized = true;
|
||||
|
||||
SpeedTreeNode::init_type();
|
||||
STBasicTerrain::init_type();
|
||||
STTerrain::init_type();
|
||||
STTree::init_type();
|
||||
LoaderFileTypeSrt::init_type();
|
||||
LoaderFileTypeStf::init_type();
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@ extern ConfigVariableDouble speedtree_sun_color;
|
|||
extern ConfigVariableDouble speedtree_sun_size;
|
||||
extern ConfigVariableDouble speedtree_sun_spread_exponent;
|
||||
extern ConfigVariableDouble speedtree_sun_fog_bloom;
|
||||
extern ConfigVariableDouble speedtree_ambient_color;
|
||||
extern ConfigVariableDouble speedtree_diffuse_color;
|
||||
extern ConfigVariableDouble speedtree_specular_color;
|
||||
extern ConfigVariableDouble speedtree_emissive_color;
|
||||
extern ConfigVariableInt speedtree_num_shadow_maps;
|
||||
extern ConfigVariableInt speedtree_shadow_map_resolution;
|
||||
extern ConfigVariableBool speedtree_smooth_shadows;
|
||||
|
|
|
|||
|
|
@ -3,5 +3,7 @@
|
|||
#include "loaderFileTypeStf.cxx"
|
||||
#include "speedTreeNode.cxx"
|
||||
#include "speedtree_api.cxx"
|
||||
#include "stBasicTerrain.cxx"
|
||||
#include "stTerrain.cxx"
|
||||
#include "stTransform.cxx"
|
||||
#include "stTree.cxx"
|
||||
|
|
|
|||
|
|
@ -79,6 +79,38 @@ modify_tree(int n) {
|
|||
return (STTree *)instance_list->get_tree();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::clear_terrain
|
||||
// Access: Published
|
||||
// Description: Removes the terrain associated with the node.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void SpeedTreeNode::
|
||||
clear_terrain() {
|
||||
set_terrain(NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::has_terrain
|
||||
// Access: Published
|
||||
// Description: Returns true if a valid terrain has been associated
|
||||
// with the node, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool SpeedTreeNode::
|
||||
has_terrain() const {
|
||||
return _terrain != (STTerrain *)NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::get_terrain
|
||||
// Access: Published
|
||||
// Description: Returns the terrain associated with the node, or NULL
|
||||
// if there is no terrain.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE STTerrain *SpeedTreeNode::
|
||||
get_terrain() const {
|
||||
return _terrain;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::InstanceList::Constructor
|
||||
// Access: Public
|
||||
|
|
@ -130,6 +162,18 @@ get_instance(int n) const {
|
|||
return _instances[n];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::InstanceList::set_instance
|
||||
// Access: Published
|
||||
// Description: Replaces the transform of the nth instance of this
|
||||
// tree.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void SpeedTreeNode::InstanceList::
|
||||
set_instance(int n, const STTransform &transform) {
|
||||
nassertv(n >= 0 && n < (int)_instances.size());
|
||||
_instances[n] = transform;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::InstanceList::add_instance
|
||||
// Access: Published
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "pandabase.h"
|
||||
#include "speedTreeNode.h"
|
||||
#include "stBasicTerrain.h"
|
||||
#include "virtualFileSystem.h"
|
||||
#include "config_util.h"
|
||||
#include "cullTraverser.h"
|
||||
|
|
@ -57,7 +58,7 @@ SpeedTreeNode(const string &name) :
|
|||
// Early versions of SpeedTree don't destruct unused CForestRender
|
||||
// objects correctly. To avoid crashes, we have to leak these
|
||||
// things.
|
||||
, _forest(*(new SpeedTree::CForestRender))
|
||||
, _forest_render(*(new SpeedTree::CForestRender))
|
||||
#endif
|
||||
{
|
||||
init_node();
|
||||
|
|
@ -94,19 +95,19 @@ SpeedTreeNode(const string &name) :
|
|||
}
|
||||
}
|
||||
|
||||
string os_shaders_dir = shaders_dir.to_os_specific();
|
||||
_os_shaders_dir = shaders_dir.to_os_specific();
|
||||
// Ensure the path ends with a terminal slash; SpeedTree requires this.
|
||||
#ifdef WIN32
|
||||
if (!os_shaders_dir.empty() && os_shaders_dir[os_shaders_dir.length() - 1] != '\\') {
|
||||
os_shaders_dir += "\\";
|
||||
if (!_os_shaders_dir.empty() && _os_shaders_dir[_os_shaders_dir.length() - 1] != '\\') {
|
||||
_os_shaders_dir += "\\";
|
||||
}
|
||||
#else
|
||||
if (!os_shaders_dir.empty() && os_shaders_dir[os_shaders_dir.length() - 1] != '/') {
|
||||
os_shaders_dir += "/";
|
||||
if (!_os_shaders_dir.empty() && _os_shaders_dir[_os_shaders_dir.length() - 1] != '/') {
|
||||
_os_shaders_dir += "/";
|
||||
}
|
||||
#endif
|
||||
|
||||
render_info.m_strShaderPath = os_shaders_dir.c_str();
|
||||
render_info.m_strShaderPath = _os_shaders_dir.c_str();
|
||||
render_info.m_nMaxAnisotropy = speedtree_max_anisotropy;
|
||||
render_info.m_bHorizontalBillboards = speedtree_horizontal_billboards;
|
||||
render_info.m_fAlphaTestScalar = speedtree_alpha_test_scalar;
|
||||
|
|
@ -114,7 +115,10 @@ SpeedTreeNode(const string &name) :
|
|||
render_info.m_nMaxBillboardImagesByBase = speedtree_max_billboard_images_by_base;
|
||||
render_info.m_fVisibility = speedtree_visibility;
|
||||
render_info.m_fGlobalLightScalar = speedtree_global_light_scalar;
|
||||
//render_info.m_sLightMaterial = ...
|
||||
render_info.m_sLightMaterial.m_vAmbient = SpeedTree::Vec4(speedtree_ambient_color[0], speedtree_ambient_color[1], speedtree_ambient_color[2], 1.0f);
|
||||
render_info.m_sLightMaterial.m_vDiffuse = SpeedTree::Vec4(speedtree_diffuse_color[0], speedtree_diffuse_color[1], speedtree_diffuse_color[2], 1.0f);
|
||||
render_info.m_sLightMaterial.m_vSpecular = SpeedTree::Vec4(speedtree_specular_color[0], speedtree_specular_color[1], speedtree_specular_color[2], 1.0f);
|
||||
render_info.m_sLightMaterial.m_vEmissive = SpeedTree::Vec4(speedtree_emissive_color[0], speedtree_emissive_color[1], speedtree_emissive_color[2], 1.0f);
|
||||
render_info.m_bSpecularLighting = speedtree_specular_lighting;
|
||||
render_info.m_bTransmissionLighting = speedtree_transmission_lighting;
|
||||
render_info.m_bDetailLayer = speedtree_detail_layer;
|
||||
|
|
@ -138,7 +142,7 @@ SpeedTreeNode(const string &name) :
|
|||
render_info.m_bWindEnabled = speedtree_wind_enabled;
|
||||
render_info.m_bFrondRippling = speedtree_frond_rippling;
|
||||
|
||||
_forest.SetRenderInfo(render_info);
|
||||
_forest_render.SetRenderInfo(render_info);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -184,9 +188,9 @@ add_tree(const STTree *tree) {
|
|||
bool inserted = result.second;
|
||||
nassertr(inserted, *(*ti));
|
||||
|
||||
if (!_forest.RegisterTree((SpeedTree::CTree *)tree->get_tree())) {
|
||||
if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to register tree " << tree->get_filename() << "\n";
|
||||
<< "Failed to register tree " << tree->get_fullpath() << "\n";
|
||||
speedtree_cat.warning()
|
||||
<< SpeedTree::CCore::GetError() << "\n";
|
||||
}
|
||||
|
|
@ -213,9 +217,9 @@ remove_tree(const STTree *tree) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!_forest.UnregisterTree(tree->get_tree())) {
|
||||
if (!_forest_render.UnregisterTree(tree->get_tree())) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to unregister tree " << tree->get_filename() << "\n";
|
||||
<< "Failed to unregister tree " << tree->get_fullpath() << "\n";
|
||||
speedtree_cat.warning()
|
||||
<< SpeedTree::CCore::GetError() << "\n";
|
||||
}
|
||||
|
|
@ -242,9 +246,9 @@ remove_all_trees() {
|
|||
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())) {
|
||||
if (!_forest_render.UnregisterTree(tree->get_tree())) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to unregister tree " << tree->get_filename() << "\n";
|
||||
<< "Failed to unregister tree " << tree->get_fullpath() << "\n";
|
||||
speedtree_cat.warning()
|
||||
<< SpeedTree::CCore::GetError() << "\n";
|
||||
}
|
||||
|
|
@ -387,25 +391,33 @@ add_instances_from(const SpeedTreeNode *other, const TransformState *transform)
|
|||
// false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool SpeedTreeNode::
|
||||
add_from_stf(const Filename &pathname, const LoaderOptions &options) {
|
||||
add_from_stf(const Filename &stf_filename, const LoaderOptions &options) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
|
||||
Filename filename = Filename::text_filename(pathname);
|
||||
PT(VirtualFile) file = vfs->get_file(filename);
|
||||
Filename fullpath = Filename::text_filename(stf_filename);
|
||||
vfs->resolve_filename(fullpath, get_model_path());
|
||||
|
||||
if (!vfs->exists(fullpath)) {
|
||||
speedtree_cat.warning()
|
||||
<< "Couldn't find " << stf_filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
PT(VirtualFile) file = vfs->get_file(fullpath);
|
||||
if (file == (VirtualFile *)NULL) {
|
||||
// No such file.
|
||||
speedtree_cat.error()
|
||||
<< "Could not find " << pathname << "\n";
|
||||
<< "Could not find " << stf_filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (speedtree_cat.is_debug()) {
|
||||
speedtree_cat.debug()
|
||||
<< "Reading STF file " << filename << "\n";
|
||||
<< "Reading STF file " << fullpath << "\n";
|
||||
}
|
||||
|
||||
istream *in = file->open_read_file(true);
|
||||
bool success = add_from_stf(*in, pathname, options);
|
||||
bool success = add_from_stf(*in, fullpath, options);
|
||||
vfs->close_read_file(in);
|
||||
|
||||
return success;
|
||||
|
|
@ -513,6 +525,123 @@ add_from_stf(istream &in, const Filename &pathname,
|
|||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::setup_terrain
|
||||
// Access: Published
|
||||
// Description: A convenience function to set up terrain geometry by
|
||||
// reading a terrain.txt file as defined by SpeedTree.
|
||||
// This file names the various map files that define the
|
||||
// terrain, as well as defining parameters size as its
|
||||
// size and color.
|
||||
//
|
||||
// This method implicitly creates a STBasicTerrain
|
||||
// object and passes it to set_terrain().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool SpeedTreeNode::
|
||||
setup_terrain(const Filename &terrain_file) {
|
||||
PT(STBasicTerrain) terrain = new STBasicTerrain;
|
||||
if (terrain->setup_terrain(terrain_file)) {
|
||||
set_terrain(terrain);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::set_terrain
|
||||
// Access: Published
|
||||
// Description: Associated a terrain with the node. If the terrain
|
||||
// has not already been loaded prior to this call,
|
||||
// load_data() will be called immediately.
|
||||
//
|
||||
// The terrain will be rendered using SpeedTree
|
||||
// callbacks, and trees may be repositioned with a call
|
||||
// to snap_to_terrain().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void SpeedTreeNode::
|
||||
set_terrain(STTerrain *terrain) {
|
||||
_terrain = NULL;
|
||||
if (terrain == (STTerrain *)NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!terrain->is_valid()) {
|
||||
// If the terrain was not already loaded, load it immediately.
|
||||
terrain->load_data();
|
||||
}
|
||||
|
||||
nassertv(terrain->is_valid());
|
||||
nassertv(terrain->get_num_splat_layers() == SpeedTree::c_nNumTerrainSplatLayers);
|
||||
_terrain = terrain;
|
||||
|
||||
_terrain_render.SetShaderLoader(_forest_render.GetShaderLoader());
|
||||
|
||||
SpeedTree::STerrainRenderInfo render_info;
|
||||
render_info.m_strShaderPath = _os_shaders_dir.c_str();
|
||||
|
||||
string os_specific = terrain->get_normal_map().to_os_specific();
|
||||
render_info.m_strNormalMap = os_specific.c_str();
|
||||
os_specific = terrain->get_splat_map().to_os_specific();
|
||||
render_info.m_strSplatMap = os_specific.c_str();
|
||||
|
||||
for (int i = 0; i < SpeedTree::c_nNumTerrainSplatLayers; ++i) {
|
||||
os_specific = terrain->get_splat_layer(i).to_os_specific();
|
||||
render_info.m_astrSplatLayers[i] = os_specific.c_str();
|
||||
render_info.m_afSplatTileValues[i] = terrain->get_splat_layer_tiling(i);
|
||||
}
|
||||
|
||||
render_info.m_fNormalMapBlueScale = 1.0f;
|
||||
render_info.m_bShadowsEnabled = false;
|
||||
render_info.m_bZPrePass = false;
|
||||
|
||||
_terrain_render.SetRenderInfo(render_info);
|
||||
_terrain_render.SetMaxAnisotropy(speedtree_max_anisotropy);
|
||||
_terrain_render.SetHint(SpeedTree::CTerrain::HINT_MAX_NUM_VISIBLE_CELLS,
|
||||
speedtree_max_num_visible_cells);
|
||||
_visible_terrain.Reserve(speedtree_max_num_visible_cells);
|
||||
|
||||
_terrain_render.SetHeightHints(terrain->get_min_height(), terrain->get_max_height());
|
||||
|
||||
_needs_repopulate = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::snap_to_terrain
|
||||
// Access: Published
|
||||
// Description: Adjusts all the trees in this node so that their Z
|
||||
// position matches the height of the terrain at their
|
||||
// X, Y position.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void SpeedTreeNode::
|
||||
snap_to_terrain() {
|
||||
Trees::iterator ti;
|
||||
for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
|
||||
InstanceList *instance_list = (*ti);
|
||||
|
||||
int num_instances = instance_list->get_num_instances();
|
||||
if (_terrain != (STTerrain *)NULL) {
|
||||
for (int i = 0; i < num_instances; ++i) {
|
||||
STTransform trans = instance_list->get_instance(i);
|
||||
LPoint3f pos = trans.get_pos();
|
||||
pos[2] = _terrain->get_height(pos[0], pos[1]);
|
||||
trans.set_pos(pos);
|
||||
instance_list->set_instance(i, trans);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < num_instances; ++i) {
|
||||
STTransform trans = instance_list->get_instance(i);
|
||||
LPoint3f pos = trans.get_pos();
|
||||
pos[2] = 0.0f;
|
||||
trans.set_pos(pos);
|
||||
instance_list->set_instance(i, trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_needs_repopulate = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::authorize
|
||||
// Access: Published, Static
|
||||
|
|
@ -555,20 +684,20 @@ SpeedTreeNode(const SpeedTreeNode ©) :
|
|||
// Early versions of SpeedTree don't destruct unused CForestRender
|
||||
// objects correctly. To avoid crashes, we have to leak these
|
||||
// things.
|
||||
, _forest(*(new SpeedTree::CForestRender))
|
||||
, _forest_render(*(new SpeedTree::CForestRender))
|
||||
#endif
|
||||
{
|
||||
init_node();
|
||||
|
||||
_forest.SetRenderInfo(copy._forest.GetRenderInfo());
|
||||
_forest_render.SetRenderInfo(copy._forest_render.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())) {
|
||||
if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to register tree " << tree->get_filename() << "\n";
|
||||
<< "Failed to register tree " << tree->get_fullpath() << "\n";
|
||||
speedtree_cat.warning()
|
||||
<< SpeedTree::CCore::GetError() << "\n";
|
||||
}
|
||||
|
|
@ -588,7 +717,7 @@ SpeedTreeNode(const SpeedTreeNode ©) :
|
|||
SpeedTreeNode::
|
||||
~SpeedTreeNode() {
|
||||
// Help reduce memory waste from ST_DELETE_FOREST_HACK.
|
||||
_forest.ClearInstances();
|
||||
_forest_render.ClearInstances();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -702,8 +831,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
}
|
||||
|
||||
ClockObject *clock = ClockObject::get_global_clock();
|
||||
_forest.SetGlobalTime(clock->get_frame_time());
|
||||
_forest.AdvanceGlobalWind();
|
||||
_forest_render.SetGlobalTime(clock->get_frame_time());
|
||||
_forest_render.AdvanceGlobalWind();
|
||||
|
||||
// Compute the modelview and camera transforms, to pass to the
|
||||
// SpeedTree CView structure.
|
||||
|
|
@ -733,7 +862,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
if (ta != (TextureAttrib *)NULL) {
|
||||
show_textures = !ta->has_all_off();
|
||||
}
|
||||
_forest.EnableTexturing(show_textures);
|
||||
_forest_render.EnableTexturing(show_textures);
|
||||
_terrain_render.EnableTexturing(show_textures);
|
||||
|
||||
// Check lighting state. SpeedTree only supports a single
|
||||
// directional light; we look for a directional light in the
|
||||
|
|
@ -748,21 +878,27 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
|
|||
|
||||
CPT(TransformState) transform = light.get_transform(trav->get_scene()->get_scene_root().get_parent());
|
||||
LVector3f dir = light_obj->get_direction() * transform->get_mat();
|
||||
_forest.SetLightDir(SpeedTree::Vec3(dir[0], dir[1], dir[2]));
|
||||
_light_dir = SpeedTree::Vec3(dir[0], dir[1], dir[2]);
|
||||
|
||||
} else {
|
||||
// No light. But there's no way to turn off lighting in
|
||||
// SpeedTree. In lieu of this, we just shine a light from
|
||||
// above.
|
||||
_forest.SetLightDir(SpeedTree::Vec3(0.0, 0.0, -1.0));
|
||||
_light_dir = SpeedTree::Vec3(0.0, 0.0, -1.0);
|
||||
}
|
||||
|
||||
_forest_render.SetLightDir(_light_dir);
|
||||
|
||||
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);
|
||||
_forest_render.CullAndComputeLOD(_view, _visible_trees);
|
||||
|
||||
if (has_terrain()) {
|
||||
_terrain_render.CullAndComputeLOD(_view, _visible_terrain);
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse onto the node's children.
|
||||
|
|
@ -921,9 +1057,10 @@ init_node() {
|
|||
return;
|
||||
}
|
||||
|
||||
_forest.SetHint(SpeedTree::CForest::HINT_MAX_NUM_VISIBLE_CELLS, speedtree_max_num_visible_cells);
|
||||
_forest_render.SetHint(SpeedTree::CForest::HINT_MAX_NUM_VISIBLE_CELLS,
|
||||
speedtree_max_num_visible_cells);
|
||||
|
||||
_forest.SetCullCellSize(speedtree_cull_cell_size);
|
||||
_forest_render.SetCullCellSize(speedtree_cull_cell_size);
|
||||
|
||||
_is_valid = true;
|
||||
}
|
||||
|
|
@ -958,7 +1095,7 @@ r_add_instances(PandaNode *node, const TransformState *transform,
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void SpeedTreeNode::
|
||||
repopulate() {
|
||||
_forest.ClearInstances();
|
||||
_forest_render.ClearInstances();
|
||||
|
||||
Trees::iterator ti;
|
||||
for (ti = _trees.begin(); ti != _trees.end(); ++ti) {
|
||||
|
|
@ -973,7 +1110,7 @@ repopulate() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!_forest.AddInstances(tree->get_tree(), &instances[0], instances.size())) {
|
||||
if (!_forest_render.AddInstances(tree->get_tree(), &instances[0], instances.size())) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to add " << instances.size()
|
||||
<< " instances for " << *tree << "\n";
|
||||
|
|
@ -982,7 +1119,7 @@ repopulate() {
|
|||
}
|
||||
}
|
||||
|
||||
_forest.GetPopulationStats(_population_stats);
|
||||
_forest_render.GetPopulationStats(_population_stats);
|
||||
print_forest_stats(_population_stats);
|
||||
|
||||
// setup billboard caps based on instances-per-cell stats
|
||||
|
|
@ -1005,13 +1142,59 @@ repopulate() {
|
|||
max_instances_by_cell = max(max_instances_by_cell, max_instances);
|
||||
}
|
||||
|
||||
_visible_trees.Reserve(_forest.GetBaseTrees(),
|
||||
_forest.GetBaseTrees().size(),
|
||||
_visible_trees.Reserve(_forest_render.GetBaseTrees(),
|
||||
_forest_render.GetBaseTrees().size(),
|
||||
speedtree_max_num_visible_cells,
|
||||
max_instances_by_cell,
|
||||
speedtree_horizontal_billboards);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::update_terrain_cells
|
||||
// Access: Private
|
||||
// Description: Called once a frame to load vertex data for
|
||||
// newly-visible terrain cells.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void SpeedTreeNode::
|
||||
update_terrain_cells() {
|
||||
nassertv(has_terrain());
|
||||
|
||||
SpeedTree::TTerrainCellArray &cells = _visible_terrain.m_aCellsToUpdate;
|
||||
|
||||
int num_tile_res = _terrain_render.GetMaxTileRes();
|
||||
float cell_size = _terrain_render.GetCellSize();
|
||||
|
||||
// A temporary vertex data object for populating terrain.
|
||||
PT(GeomVertexData) vertex_data =
|
||||
new GeomVertexData("terrain", _terrain->get_vertex_format(),
|
||||
GeomEnums::UH_static);
|
||||
int num_vertices = num_tile_res * num_tile_res;
|
||||
vertex_data->set_num_rows(num_vertices);
|
||||
size_t num_bytes = vertex_data->get_array(0)->get_data_size_bytes();
|
||||
|
||||
int num_cells = (int)cells.size();
|
||||
for (int ci = 0; ci < num_cells; ++ci) {
|
||||
SpeedTree::CTerrainCell *cell = cells[ci];
|
||||
nassertv(cell != NULL && cell->GetVbo() != NULL);
|
||||
int cell_yi = cell->Row();
|
||||
int cell_xi = cell->Col();
|
||||
//cerr << "populating cell " << cell_xi << " " << cell_yi << "\n";
|
||||
|
||||
_terrain->fill_vertices(vertex_data,
|
||||
cell_xi * cell_size, cell_yi * cell_size,
|
||||
cell_size, num_tile_res);
|
||||
|
||||
const GeomVertexArrayData *array_data = vertex_data->get_array(0);
|
||||
CPT(GeomVertexArrayDataHandle) handle = array_data->get_handle();
|
||||
const unsigned char *data_pointer = handle->get_read_pointer(true);
|
||||
SpeedTree::CGeometryBuffer *vbo = (SpeedTree::CGeometryBuffer *)cell->GetVbo();
|
||||
|
||||
nassertv(vbo->NumVertices() == num_tile_res * num_tile_res);
|
||||
nassertv(vbo->NumVertices() * vbo->VertexSize() == handle->get_data_size_bytes());
|
||||
vbo->OverwriteVertices(data_pointer, num_vertices, 0);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: SpeedTreeNode::validate_api
|
||||
// Access: Private
|
||||
|
|
@ -1060,15 +1243,42 @@ draw_callback(CallbackData *data) {
|
|||
GraphicsStateGuardian *gsg = DCAST(GraphicsStateGuardian, geom_cbdata->get_gsg());
|
||||
|
||||
setup_for_render(gsg);
|
||||
|
||||
if (_terrain_render.IsEnabled()) {
|
||||
|
||||
// Is this needed for terrain?
|
||||
_terrain_render.UploadShaderConstants
|
||||
(&_forest_render, _light_dir,
|
||||
_forest_render.GetRenderInfo().m_sLightMaterial);
|
||||
|
||||
// set terrain render states
|
||||
//SetTransparentTextureMode(TRANS_TEXTURE_NOTHING);
|
||||
|
||||
// render actual terrain
|
||||
bool terrain = _terrain_render.Render
|
||||
(&_forest_render, _visible_terrain, SpeedTree::RENDER_PASS_STANDARD,
|
||||
_light_dir, _forest_render.GetRenderInfo().m_sLightMaterial,
|
||||
&_forest_render.GetRenderStats());
|
||||
|
||||
if (!terrain) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to render terrain\n";
|
||||
speedtree_cat.warning()
|
||||
<< SpeedTree::CCore::GetError() << "\n";
|
||||
}
|
||||
|
||||
// restore default forest rendering states
|
||||
//SetTransparentTextureMode(ETextureAlphaRenderMode(m_uiTransparentTextureRenderMode));
|
||||
}
|
||||
|
||||
// start the forest render
|
||||
_forest.StartRender();
|
||||
_forest_render.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);
|
||||
bool branches = _forest_render.RenderBranches(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
|
||||
bool fronds = _forest_render.RenderFronds(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
|
||||
bool leaf_meshes = _forest_render.RenderLeafMeshes(_visible_trees, SpeedTree::RENDER_PASS_STANDARD);
|
||||
bool leaf_cards = _forest_render.RenderLeafCards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view);
|
||||
bool billboards = _forest_render.RenderBillboards(_visible_trees, SpeedTree::RENDER_PASS_STANDARD, _view);
|
||||
|
||||
if (!branches || !fronds || !leaf_meshes || !leaf_cards || !billboards) {
|
||||
speedtree_cat.warning()
|
||||
|
|
@ -1078,7 +1288,7 @@ draw_callback(CallbackData *data) {
|
|||
<< SpeedTree::CCore::GetError() << "\n";
|
||||
}
|
||||
|
||||
_forest.EndRender();
|
||||
_forest_render.EndRender();
|
||||
|
||||
// SpeedTree leaves the graphics state indeterminate. But this
|
||||
// doesn't help?
|
||||
|
|
@ -1167,9 +1377,9 @@ setup_for_render(GraphicsStateGuardian *gsg) {
|
|||
#endif
|
||||
}
|
||||
|
||||
if (!_forest.InitTreeGraphics((SpeedTree::CTreeRender *)tree->get_tree(),
|
||||
max_instances, speedtree_horizontal_billboards,
|
||||
os_textures_dir.c_str())) {
|
||||
if (!_forest_render.InitTreeGraphics((SpeedTree::CTreeRender *)tree->get_tree(),
|
||||
max_instances, speedtree_horizontal_billboards,
|
||||
os_textures_dir.c_str())) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to init tree graphics for " << *tree << "\n";
|
||||
speedtree_cat.warning()
|
||||
|
|
@ -1178,7 +1388,7 @@ setup_for_render(GraphicsStateGuardian *gsg) {
|
|||
}
|
||||
|
||||
// Init overall graphics
|
||||
if (!_forest.InitGraphics(false)) {
|
||||
if (!_forest_render.InitGraphics(false)) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to init graphics\n";
|
||||
speedtree_cat.warning()
|
||||
|
|
@ -1190,16 +1400,39 @@ setup_for_render(GraphicsStateGuardian *gsg) {
|
|||
// This call apparently must be made at draw time, not earlier,
|
||||
// because it might attempt to create OpenGL index buffers and
|
||||
// such.
|
||||
_forest.UpdateTreeCellExtents();
|
||||
_forest_render.UpdateTreeCellExtents();
|
||||
|
||||
if (has_terrain()) {
|
||||
// Now initialize the terrain.
|
||||
static const int speedtree_terrain_num_lods = 5; // number of LOD stages
|
||||
static const int speedtree_terrain_resolution = 33; // num vertices per edge of grid cell at highest LOD, must be power-of-two-plus-1
|
||||
static const float speedtree_terrain_cell_size = 800.0f; // spatial size of one edge of grid cell
|
||||
|
||||
if (!_terrain_render.Init(speedtree_terrain_num_lods,
|
||||
speedtree_terrain_resolution,
|
||||
speedtree_terrain_cell_size,
|
||||
_terrain->get_st_vertex_format())) {
|
||||
speedtree_cat.warning()
|
||||
<< "Failed to init terrain\n";
|
||||
speedtree_cat.warning()
|
||||
<< SpeedTree::CCore::GetError() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// If we needed to repopulate, it means we didn't cull in the cull
|
||||
// traversal. Do it now.
|
||||
_forest.CullAndComputeLOD(_view, _visible_trees);
|
||||
_forest_render.CullAndComputeLOD(_view, _visible_trees);
|
||||
if (has_terrain()) {
|
||||
_terrain_render.CullAndComputeLOD(_view, _visible_terrain);
|
||||
}
|
||||
|
||||
_needs_repopulate = false;
|
||||
}
|
||||
if (has_terrain()) {
|
||||
update_terrain_cells();
|
||||
}
|
||||
|
||||
if (!_forest.UploadViewShaderParameters(_view)) {
|
||||
if (!_forest_render.UploadViewShaderParameters(_view)) {
|
||||
speedtree_cat.warning()
|
||||
<< "Couldn't set view parameters\n";
|
||||
speedtree_cat.warning()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "pointerTo.h"
|
||||
#include "stTree.h"
|
||||
#include "stTransform.h"
|
||||
#include "stTerrain.h"
|
||||
#include "callbackObject.h"
|
||||
#include "loaderOptions.h"
|
||||
#include "transformState.h"
|
||||
|
|
@ -68,6 +69,7 @@ PUBLISHED:
|
|||
INLINE int get_num_instances() const;
|
||||
INLINE STTransform get_instance(int n) const;
|
||||
MAKE_SEQ(get_instances, get_num_instances, get_instance);
|
||||
INLINE void set_instance(int n, const STTransform &transform);
|
||||
|
||||
INLINE int add_instance(const STTransform &transform);
|
||||
INLINE void remove_instance(int n);
|
||||
|
|
@ -108,12 +110,20 @@ PUBLISHED:
|
|||
void add_instances(const NodePath &root, const TransformState *transform = TransformState::make_identity());
|
||||
void add_instances_from(const SpeedTreeNode *other);
|
||||
void add_instances_from(const SpeedTreeNode *other, const TransformState *transform);
|
||||
bool add_from_stf(const Filename &pathname,
|
||||
bool add_from_stf(const Filename &stf_filename,
|
||||
const LoaderOptions &options = LoaderOptions());
|
||||
bool add_from_stf(istream &in, const Filename &pathname,
|
||||
const LoaderOptions &options = LoaderOptions(),
|
||||
Loader *loader = NULL);
|
||||
|
||||
bool setup_terrain(const Filename &terrain_file);
|
||||
void set_terrain(STTerrain *terrain);
|
||||
INLINE void clear_terrain();
|
||||
INLINE bool has_terrain() const;
|
||||
INLINE STTerrain *get_terrain() const;
|
||||
|
||||
void snap_to_terrain();
|
||||
|
||||
static bool authorize(const string &license = "");
|
||||
|
||||
public:
|
||||
|
|
@ -145,6 +155,7 @@ private:
|
|||
Thread *current_thread);
|
||||
|
||||
void repopulate();
|
||||
void update_terrain_cells();
|
||||
bool validate_api(GraphicsStateGuardian *gsg);
|
||||
void draw_callback(CallbackData *cbdata);
|
||||
void setup_for_render(GraphicsStateGuardian *gsg);
|
||||
|
|
@ -179,14 +190,16 @@ private:
|
|||
};
|
||||
|
||||
private:
|
||||
string _os_shaders_dir;
|
||||
|
||||
// A list of instances per each unique tree.
|
||||
typedef ov_set<InstanceList *, IndirectLess<InstanceList> > Trees;
|
||||
Trees _trees;
|
||||
|
||||
#ifdef ST_DELETE_FOREST_HACK
|
||||
SpeedTree::CForestRender &_forest;
|
||||
SpeedTree::CForestRender &_forest_render;
|
||||
#else
|
||||
SpeedTree::CForestRender _forest;
|
||||
SpeedTree::CForestRender _forest_render;
|
||||
#endif // ST_DELETE_FOREST_HACK
|
||||
SpeedTree::CView _view;
|
||||
SpeedTree::SForestCullResultsRender _visible_trees;
|
||||
|
|
@ -194,6 +207,12 @@ private:
|
|||
bool _needs_repopulate;
|
||||
bool _is_valid;
|
||||
|
||||
PT(STTerrain) _terrain;
|
||||
SpeedTree::CTerrainRender _terrain_render;
|
||||
SpeedTree::STerrainCullResults _visible_terrain;
|
||||
|
||||
SpeedTree::Vec3 _light_dir;
|
||||
|
||||
static bool _authorized;
|
||||
static bool _done_first_init;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,184 @@
|
|||
// Filename: stBasicTerrain.I
|
||||
// Created by: drose (12Oct10)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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: STBasicTerrain::set_height_map
|
||||
// Access: Published
|
||||
// Description: Specifies the image filename that will define the
|
||||
// height map of the terrain. This will require a
|
||||
// subsequent call to load_data() to actually read the
|
||||
// data.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void STBasicTerrain::
|
||||
set_height_map(const Filename &height_map) {
|
||||
_height_map = height_map;
|
||||
_is_valid = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::get_height_map
|
||||
// Access: Published
|
||||
// Description: Returns the image filename that defines the
|
||||
// height map of the terrain.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const Filename &STBasicTerrain::
|
||||
get_height_map() const {
|
||||
return _height_map;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::get_size
|
||||
// Access: Published
|
||||
// Description: Returns the length, in scene graph units, of one edge
|
||||
// of the heightmap as it is manifested by the terrain.
|
||||
// Increasing this number spreads the heightmap out over
|
||||
// a greater area.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float STBasicTerrain::
|
||||
get_size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::InterpolationData::Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class ValueType>
|
||||
STBasicTerrain::InterpolationData<ValueType>::
|
||||
InterpolationData() : _width(0), _height(0)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::InterpolationData::reset
|
||||
// Access: Public
|
||||
// Description: Resets the array to an empty array of width x height
|
||||
// cells.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class ValueType>
|
||||
void STBasicTerrain::InterpolationData<ValueType>::
|
||||
reset(int width, int height) {
|
||||
_width = width;
|
||||
_height = height;
|
||||
_data.clear();
|
||||
_data.insert(_data.begin(), width * height, ValueType());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::InterpolationData::get_nearest_neighbor
|
||||
// Access: Public
|
||||
// Description: Returns the value nearest to (u, v) in the data.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class ValueType>
|
||||
ValueType STBasicTerrain::InterpolationData<ValueType>::
|
||||
get_nearest_neighbor(float u, float v) const {
|
||||
int u = int(u * _width + 0.5f);
|
||||
int v = int(v * _height + 0.5f);
|
||||
int index = u + v * _width;
|
||||
nassertr(index >= 0 && index < (int)_data.size(), 0);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::InterpolationData::calc_bilnear_interpolation
|
||||
// Access: Public
|
||||
// Description: Interpolates the value at (u, v) between its four
|
||||
// nearest neighbors.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class ValueType>
|
||||
ValueType STBasicTerrain::InterpolationData<ValueType>::
|
||||
calc_bilinear_interpolation(float u, float v) const {
|
||||
u -= cfloor(u);
|
||||
v -= cfloor(v);
|
||||
nassertr((u >= 0.0f) && (u < 1.0f) &&
|
||||
(v >= 0.0f) && (v < 1.0f), 0);
|
||||
|
||||
u *= (float)_width;
|
||||
v *= (float)_height;
|
||||
|
||||
const int lower_x = int(u);
|
||||
const int lower_y = int(v);
|
||||
const int higher_x = (lower_x + 1) % _width;
|
||||
const int higher_y = (lower_y + 1) % _height;
|
||||
|
||||
const float ratio_x = u - float(lower_x);
|
||||
const float ratio_y = v - float(lower_y);
|
||||
const float inv_ratio_x = 1.0f - ratio_x;
|
||||
const float inv_ratio_y = 1.0f - ratio_y;
|
||||
|
||||
nassertr(lower_x + lower_y * _width >= 0 && higher_x + higher_y * _width < (int)_data.size(), 0);
|
||||
|
||||
const ValueType &t1 = _data[lower_x + lower_y * _width];
|
||||
const ValueType &t2 = _data[higher_x + lower_y * _width];
|
||||
const ValueType &t3 = _data[lower_x + higher_y * _width];
|
||||
const ValueType &t4 = _data[higher_x + higher_y * _width];
|
||||
|
||||
return (t1 * inv_ratio_x + t2 * ratio_x) * inv_ratio_y +
|
||||
(t3 * inv_ratio_x + t4 * ratio_x) * ratio_y;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::InterpolationData::calc_smooth
|
||||
// Access: Public
|
||||
// Description: Approximates the average value at (u, v) over the
|
||||
// indicated radius, assuming a polynomial curve.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class ValueType>
|
||||
ValueType STBasicTerrain::InterpolationData<ValueType>::
|
||||
calc_smooth(float u, float v, float radius) const {
|
||||
ValueType retval = 0;
|
||||
|
||||
if (radius <= 0.0f) {
|
||||
retval = calc_bilinear_interpolation(u, v);
|
||||
|
||||
} else {
|
||||
const float test_points[9][2] = {
|
||||
{ 0.0f * radius, 0.0f * radius },
|
||||
{ 0.8f * radius, 0.0f * radius },
|
||||
{ -0.8f * radius, 0.0f * radius },
|
||||
{ 0.0f * radius, 0.8f * radius },
|
||||
{ 0.0f * radius, -0.8f * radius },
|
||||
{ 0.25f * radius, 0.25f * radius },
|
||||
{ 0.25f * radius, -0.25f * radius },
|
||||
{ -0.25f * radius, 0.25f * radius },
|
||||
{ -0.25f * radius, -0.25f * radius }
|
||||
};
|
||||
|
||||
float total_weight = 0.0f;
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
const float *test_point = test_points[i];
|
||||
float weight = (1.0f - sqrt((test_point[0] * test_point[0]) + (test_point[1] * test_point[1])));
|
||||
total_weight += weight;
|
||||
retval += weight * calc_bilinear_interpolation(u + test_point[0], v + test_point[1]);
|
||||
}
|
||||
|
||||
retval /= total_weight;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::InterpolationData::is_present
|
||||
// Access: Public
|
||||
// Description: Returns true if the data is present--that is, reset()
|
||||
// was called with non-zero values--or false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class ValueType>
|
||||
bool STBasicTerrain::InterpolationData<ValueType>::
|
||||
is_present() const {
|
||||
return !_data.empty();
|
||||
}
|
||||
|
|
@ -0,0 +1,424 @@
|
|||
// Filename: stBasicTerrain.cxx
|
||||
// Created by: drose (12Oct10)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "stBasicTerrain.h"
|
||||
#include "geomVertexWriter.h"
|
||||
#include "pnmImage.h"
|
||||
#include "indent.h"
|
||||
|
||||
TypeHandle STBasicTerrain::_type_handle;
|
||||
|
||||
// VERTEX_ATTRIB_END is defined as a macro that must be evaluated
|
||||
// within the SpeedTree namespace.
|
||||
namespace SpeedTree {
|
||||
static const SVertexAttribDesc st_attrib_end = VERTEX_ATTRIB_END();
|
||||
}
|
||||
|
||||
/* Hmm, maybe we want to use this lower-level structure directly
|
||||
instead of the GeomVertexWriter.
|
||||
|
||||
namespace SpeedTree {
|
||||
static const SVertexAttribDesc std_vertex_format[] = {
|
||||
{ VERTEX_ATTRIB_SEMANTIC_POS, VERTEX_ATTRIB_TYPE_FLOAT, 3 },
|
||||
{ VERTEX_ATTRIB_SEMANTIC_TEXCOORD0, VERTEX_ATTRIB_TYPE_FLOAT, 3 },
|
||||
VERTEX_ATTRIB_END( )
|
||||
};
|
||||
static const int std_vertex_format_length =
|
||||
sizeof(std_vertex_format) / sizeof(std_vertex_format[0]);
|
||||
};
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::Constructor
|
||||
// Access: Published
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
STBasicTerrain::
|
||||
STBasicTerrain() {
|
||||
clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::Copy Constructor
|
||||
// Access: Published
|
||||
// Description: Not sure whether any derived classes will implement
|
||||
// the copy constructor, but it's defined here at the
|
||||
// base level just in case.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
STBasicTerrain::
|
||||
STBasicTerrain(const STBasicTerrain ©) :
|
||||
STTerrain(copy),
|
||||
_size(copy._size),
|
||||
_height_scale(copy._height_scale)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::Destructor
|
||||
// Access: Published, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
STBasicTerrain::
|
||||
~STBasicTerrain() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::clear
|
||||
// Access: Published, Virtual
|
||||
// Description: Resets the terrain to its initial, unloaded state.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STBasicTerrain::
|
||||
clear() {
|
||||
STTerrain::clear();
|
||||
|
||||
_height_map = "";
|
||||
_size = 1.0f;
|
||||
_height_scale = 1.0f;
|
||||
|
||||
CPT(GeomVertexFormat) format = GeomVertexFormat::register_format
|
||||
(new GeomVertexArrayFormat(InternalName::get_vertex(), 3,
|
||||
GeomEnums::NT_float32, GeomEnums::C_point,
|
||||
InternalName::get_texcoord(), 3,
|
||||
GeomEnums::NT_float32, GeomEnums::C_texcoord));
|
||||
set_vertex_format(format);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::setup_terrain
|
||||
// Access: Published
|
||||
// Description: Sets up the terrain by reading a terrain.txt file as
|
||||
// defined by SpeedTree. This file names the various
|
||||
// map files that define the terrain, as well as
|
||||
// defining parameters size as its size and color.
|
||||
//
|
||||
// If a relative filename is supplied, the model-path is
|
||||
// searched. If a directory is named, "terrain.txt" is
|
||||
// implicitly appended.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool STBasicTerrain::
|
||||
setup_terrain(const Filename &terrain_filename) {
|
||||
_is_valid = false;
|
||||
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
|
||||
Filename fullpath = Filename::text_filename(terrain_filename);
|
||||
vfs->resolve_filename(fullpath, get_model_path());
|
||||
|
||||
if (!vfs->exists(fullpath)) {
|
||||
speedtree_cat.warning()
|
||||
<< "Couldn't find " << terrain_filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vfs->is_directory(fullpath)) {
|
||||
fullpath = Filename(fullpath, "terrain.txt");
|
||||
}
|
||||
|
||||
istream *in = vfs->open_read_file(fullpath, true);
|
||||
if (in == NULL) {
|
||||
speedtree_cat.warning()
|
||||
<< "Couldn't open " << terrain_filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = setup_terrain(*in, fullpath);
|
||||
vfs->close_read_file(in);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::setup_terrain
|
||||
// Access: Published
|
||||
// Description: Sets up the terrain by reading a terrain.txt file as
|
||||
// defined by SpeedTree. This variant on this method
|
||||
// accepts an istream for an already-opened terrain.txt
|
||||
// file. The filename is provided for reference, to
|
||||
// assist relative file operations. It should name the
|
||||
// terrain.txt file that has been opened.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool STBasicTerrain::
|
||||
setup_terrain(istream &in, const Filename &pathname) {
|
||||
clear();
|
||||
|
||||
Filename dirname = pathname.get_dirname();
|
||||
|
||||
string keyword;
|
||||
in >> keyword;
|
||||
while (in && !in.eof()) {
|
||||
if (keyword == "area") {
|
||||
// area defines the size of the terrain in square kilometers.
|
||||
static const float feet_per_km = 3280.839895013f;
|
||||
float area;
|
||||
in >> area;
|
||||
_size = csqrt(area) * feet_per_km;
|
||||
|
||||
} else if (keyword == "height_scale") {
|
||||
in >> _height_scale;
|
||||
|
||||
} else if (keyword == "normalmap_b_scale") {
|
||||
float normalmap_b_scale;
|
||||
in >> normalmap_b_scale;
|
||||
|
||||
} else if (keyword == "heightmap") {
|
||||
read_quoted_filename(_height_map, in, dirname);
|
||||
|
||||
} else if (keyword == "texture") {
|
||||
SplatLayer splat;
|
||||
read_quoted_filename(splat._filename, in, dirname);
|
||||
in >> splat._tiling;
|
||||
splat._color.set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
_splat_layers.push_back(splat);
|
||||
|
||||
} else if (keyword == "color") {
|
||||
// color means the overall color of the previous texture.
|
||||
float r, g, b;
|
||||
in >> r >> g >> b;
|
||||
if (!_splat_layers.empty()) {
|
||||
_splat_layers.back()._color.set(r, g, b, 1.0f);
|
||||
}
|
||||
|
||||
} else if (keyword == "ambient" || keyword == "diffuse" || keyword == "specular" || keyword == "emissive") {
|
||||
float r, g, b;
|
||||
in >> r >> g >> b;
|
||||
|
||||
} else if (keyword == "shininess") {
|
||||
float s;
|
||||
in >> s;
|
||||
|
||||
} else {
|
||||
speedtree_cat.error()
|
||||
<< "Invalid token " << keyword << " in " << pathname << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
in >> keyword;
|
||||
}
|
||||
|
||||
// Consume any whitespace at the end of the file.
|
||||
in >> ws;
|
||||
|
||||
if (!in.eof()) {
|
||||
// If we didn't read all the way to end-of-file, there was an
|
||||
// error.
|
||||
in.clear();
|
||||
string text;
|
||||
in >> text;
|
||||
speedtree_cat.error()
|
||||
<< "Unexpected text in " << pathname << " at \"" << text << "\"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first two textures are the normal map and splat map,
|
||||
// respectively.
|
||||
if (!_splat_layers.empty()) {
|
||||
_normal_map = _splat_layers[0]._filename;
|
||||
_splat_layers.erase(_splat_layers.begin());
|
||||
}
|
||||
if (!_splat_layers.empty()) {
|
||||
_splat_map = _splat_layers[0]._filename;
|
||||
_splat_layers.erase(_splat_layers.begin());
|
||||
}
|
||||
|
||||
// Now try to load the actual height map data.
|
||||
load_data();
|
||||
|
||||
return _is_valid;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::load_data
|
||||
// Access: Published, Virtual
|
||||
// Description: This will be called at some point after
|
||||
// initialization. It should be overridden by a derived
|
||||
// class to load up the terrain data from its source and
|
||||
// fill in the data members of this class appropriately,
|
||||
// especially _is_valid. After this call, if _is_valid
|
||||
// is true, then get_height() etc. will be called to
|
||||
// query the terrain's data.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STBasicTerrain::
|
||||
load_data() {
|
||||
_is_valid = false;
|
||||
|
||||
if (!read_height_map()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_is_valid = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::get_height
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this should return
|
||||
// the computed height value at point (x, y) of the
|
||||
// terrain, where x and y are unbounded and may refer to
|
||||
// any 2-d point in space.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
float STBasicTerrain::
|
||||
get_height(float x, float y) const {
|
||||
return _height_data.calc_bilinear_interpolation(x / _size, y / _size);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::get_smooth_height
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this should return
|
||||
// the approximate average height value over a circle of
|
||||
// the specified radius, centered at point (x, y) of the
|
||||
// terrain.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
float STBasicTerrain::
|
||||
get_smooth_height(float x, float y, float radius) const {
|
||||
return _height_data.calc_smooth(x / _size, y / _size, radius / _size);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::get_slope
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this should return
|
||||
// the directionless slope at point (x, y) of the
|
||||
// terrain, where 0.0 is flat and 1.0 is vertical. This
|
||||
// is used for determining the legal points to place
|
||||
// trees and grass.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
float STBasicTerrain::
|
||||
get_slope(float x, float y) const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::fill_vertices
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this will be
|
||||
// called occasionally to populate the vertices for a
|
||||
// terrain cell.
|
||||
//
|
||||
// It will be passed a GeomVertexData whose format will
|
||||
// match get_vertex_format(), and already allocated with
|
||||
// num_xy * num_xy rows. This method should fill the
|
||||
// rows of the data with the appropriate vertex data for
|
||||
// the terrain, over the grid described by the corners
|
||||
// (start_x, start_y) up to and including (start_x +
|
||||
// size_x, start_y + size_xy)--a square of the terrain
|
||||
// with num_xy vertices on a size, arranged in row-major
|
||||
// order.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STBasicTerrain::
|
||||
fill_vertices(GeomVertexData *data,
|
||||
float start_x, float start_y,
|
||||
float size_xy, int num_xy) const {
|
||||
nassertv(data->get_format() == _vertex_format);
|
||||
GeomVertexWriter vertex(data, InternalName::get_vertex());
|
||||
GeomVertexWriter texcoord(data, InternalName::get_texcoord());
|
||||
|
||||
float vertex_scale = 1.0 / (float)(num_xy - 1);
|
||||
float texcoord_scale = 1.0 / _size;
|
||||
for (int xi = 0; xi < num_xy; ++xi) {
|
||||
float xt = xi * vertex_scale;
|
||||
float x = start_x + xt * size_xy;
|
||||
for (int yi = 0; yi < num_xy; ++yi) {
|
||||
float yt = yi * vertex_scale;
|
||||
float y = start_y + yt * size_xy;
|
||||
|
||||
float z = get_height(x, y);
|
||||
|
||||
vertex.set_data3f(x, y, z);
|
||||
texcoord.set_data3f(x * texcoord_scale, -y * texcoord_scale, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::output
|
||||
// Access: Published, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STBasicTerrain::
|
||||
output(ostream &out) const {
|
||||
Namable::output(out);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::write
|
||||
// Access: Published, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STBasicTerrain::
|
||||
write(ostream &out, int indent_level) const {
|
||||
indent(out, indent_level)
|
||||
<< *this << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::read_height_map
|
||||
// Access: Protected
|
||||
// Description: Reads the height map image stored in _height_map, and
|
||||
// stores it in _height_data. Returns true on success,
|
||||
// false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool STBasicTerrain::
|
||||
read_height_map() {
|
||||
PNMImage image(_height_map);
|
||||
if (!image.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_height_data.reset(image.get_x_size(), image.get_y_size());
|
||||
_min_height = FLT_MAX;
|
||||
_max_height = FLT_MIN;
|
||||
|
||||
float scalar = _size * _height_scale / image.get_num_channels();
|
||||
int pi = 0;
|
||||
for (int yi = image.get_y_size() - 1; yi >= 0; --yi) {
|
||||
for (int xi = 0; xi < image.get_x_size(); ++xi) {
|
||||
Colord rgba = image.get_xel_a(xi, yi);
|
||||
float v = rgba[0] + rgba[1] + rgba[2] + rgba[3];
|
||||
v *= scalar;
|
||||
_height_data._data[pi] = v;
|
||||
++pi;
|
||||
_min_height = min(_min_height, v);
|
||||
_max_height = max(_max_height, v);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STBasicTerrain::read_quoted_filename
|
||||
// Access: Private, Static
|
||||
// Description: Reads a quoted filename from the input stream, which
|
||||
// is understood to be relative to the indicated
|
||||
// directory.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STBasicTerrain::
|
||||
read_quoted_filename(Filename &result, istream &in, const Filename &dirname) {
|
||||
string filename;
|
||||
in >> filename;
|
||||
|
||||
// The terrain.txt file should, in theory, support spaces, but the
|
||||
// SpeedTree reference application doesn't, so we don't bother
|
||||
// either.
|
||||
if (filename.size() >= 2 && filename[0] == '"' && filename[filename.size() - 1] == '"') {
|
||||
filename = filename.substr(1, filename.size() - 2);
|
||||
}
|
||||
|
||||
result = Filename::from_os_specific(filename);
|
||||
if (result.is_local()) {
|
||||
result = Filename(dirname, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
// Filename: stBasicTerrain.h
|
||||
// Created by: drose (12Oct10)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 STBASICTERRAIN_H
|
||||
#define STBASICTERRAIN_H
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "stTerrain.h"
|
||||
#include "luse.h"
|
||||
#include "pvector.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : STBasicTerrain
|
||||
// Description : A specific implementation of STTerrain that supports
|
||||
// basic heightmaps loaded from an image file, as
|
||||
// described in a terrain.txt file similar to those
|
||||
// provided with the SpeedTree example application.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDASPEEDTREE STBasicTerrain : public STTerrain {
|
||||
PUBLISHED:
|
||||
STBasicTerrain();
|
||||
STBasicTerrain(const STBasicTerrain ©);
|
||||
virtual ~STBasicTerrain();
|
||||
|
||||
void clear();
|
||||
|
||||
bool setup_terrain(const Filename &terrain_filename);
|
||||
bool setup_terrain(istream &in, const Filename &pathname);
|
||||
|
||||
INLINE void set_height_map(const Filename &height_map);
|
||||
INLINE const Filename &get_height_map() const;
|
||||
|
||||
virtual void load_data();
|
||||
|
||||
INLINE float get_size() const;
|
||||
virtual float get_height(float x, float y) const;
|
||||
virtual float get_smooth_height(float x, float y, float radius) const;
|
||||
virtual float get_slope(float x, float y) const;
|
||||
|
||||
virtual void fill_vertices(GeomVertexData *data,
|
||||
float start_x, float start_y,
|
||||
float size_xy, int num_xy) const;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
protected:
|
||||
bool read_height_map();
|
||||
|
||||
private:
|
||||
static void read_quoted_filename(Filename &result, istream &in,
|
||||
const Filename &dirname);
|
||||
|
||||
protected:
|
||||
template<class ValueType>
|
||||
class InterpolationData {
|
||||
public:
|
||||
InterpolationData();
|
||||
void reset(int width, int height);
|
||||
|
||||
ValueType get_nearest_neighbor(float u, float v) const;
|
||||
ValueType calc_bilinear_interpolation(float u, float v) const;
|
||||
ValueType calc_smooth(float u, float v, float radius) const;
|
||||
bool is_present() const;
|
||||
|
||||
int _width;
|
||||
int _height;
|
||||
pvector<ValueType> _data;
|
||||
};
|
||||
|
||||
protected:
|
||||
Filename _height_map;
|
||||
float _size;
|
||||
float _height_scale;
|
||||
|
||||
InterpolationData<float> _height_data;
|
||||
//InterpolationData<LVector3f> _normal_data;
|
||||
InterpolationData<float> _slope_data;
|
||||
//InterpolationData<unsigned char> _ao_data;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
STTerrain::init_type();
|
||||
register_type(_type_handle, "STBasicTerrain",
|
||||
STTerrain::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;
|
||||
};
|
||||
|
||||
#include "stBasicTerrain.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// Filename: stTerrain.I
|
||||
// Created by: drose (11Oct10)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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: STTerrain::is_valid
|
||||
// Access: Published
|
||||
// Description: Returns true if the terrain data is well-defined and
|
||||
// ready to use.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool STTerrain::
|
||||
is_valid() const {
|
||||
return _is_valid && !_st_vertex_attribs.empty();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_normal_map
|
||||
// Access: Published
|
||||
// Description: Returns the normal map that should be applied to the
|
||||
// terrain. This will be loaded and supplied to the
|
||||
// shader.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const Filename &STTerrain::
|
||||
get_normal_map() const {
|
||||
return _normal_map;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_splat_map
|
||||
// Access: Published
|
||||
// Description: Returns the splat map that should be applied to the
|
||||
// terrain. This will be loaded and supplied to the
|
||||
// shader. Presumably, the shader will use the channels
|
||||
// of this map to determine which of the splat layers
|
||||
// are to be rendered at any given point.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const Filename &STTerrain::
|
||||
get_splat_map() const {
|
||||
return _splat_map;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_num_splat_layers
|
||||
// Access: Published
|
||||
// Description: Returns the number of splat layers that are to be
|
||||
// applied to the terrain. This must be consistent with
|
||||
// c_nNumTerrainSplatLayers in SpeedTree's TerrainRI.h.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int STTerrain::
|
||||
get_num_splat_layers() const {
|
||||
return _splat_layers.size();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_splat_layer
|
||||
// Access: Published
|
||||
// Description: Returns the nth splat layer that is to be applied to
|
||||
// the terrain.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const Filename &STTerrain::
|
||||
get_splat_layer(int n) const {
|
||||
nassertr(n >= 0 && n < (int)_splat_layers.size(), _splat_layers[0]._filename);
|
||||
return _splat_layers[n]._filename;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_splat_layer_tiling
|
||||
// Access: Published
|
||||
// Description: Returns the tiling value of the nth splat layer.
|
||||
// This is an arbitrary UV scale that is applied to each
|
||||
// layer individually, by the terrain shader.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float STTerrain::
|
||||
get_splat_layer_tiling(int n) const {
|
||||
nassertr(n >= 0 && n < (int)_splat_layers.size(), 0.0f);
|
||||
return _splat_layers[n]._tiling;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_splat_layer_color
|
||||
// Access: Published
|
||||
// Description: Returns the overall color of the nth splat layer. This
|
||||
// is used just to match the color of the grass to its
|
||||
// terrain.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const LVecBase4f &STTerrain::
|
||||
get_splat_layer_color(int n) const {
|
||||
nassertr(n >= 0 && n < (int)_splat_layers.size(), _splat_layers[0]._color);
|
||||
return _splat_layers[n]._color;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_vertex_format
|
||||
// Access: Published
|
||||
// Description: Returns the vertex format of the vertex array that is
|
||||
// supported by this terrain data. A GeomVertexData of
|
||||
// the requested format will be passed to
|
||||
// fill_vertices().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE const GeomVertexFormat *STTerrain::
|
||||
get_vertex_format() {
|
||||
return _vertex_format;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_min_height
|
||||
// Access: Published
|
||||
// Description: Returns the smallest height value that might be
|
||||
// returned by get_height(). This is used as a culling
|
||||
// optimization.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float STTerrain::
|
||||
get_min_height() const {
|
||||
return _min_height;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_max_height
|
||||
// Access: Published
|
||||
// Description: Returns the largest height value that might be
|
||||
// returned by get_height(). This is used as a culling
|
||||
// optimization.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE float STTerrain::
|
||||
get_max_height() const {
|
||||
return _max_height;
|
||||
}
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
// Filename: stTerrain.cxx
|
||||
// Created by: drose (11Oct10)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "stTerrain.h"
|
||||
#include "indent.h"
|
||||
|
||||
TypeHandle STTerrain::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::Constructor
|
||||
// Access: Protected
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
STTerrain::
|
||||
STTerrain() {
|
||||
_is_valid = false;
|
||||
_min_height = 0.0f;
|
||||
_max_height = 1.0f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::Copy Constructor
|
||||
// Access: Protected
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
STTerrain::
|
||||
STTerrain(const STTerrain ©) :
|
||||
TypedReferenceCount(copy),
|
||||
Namable(copy),
|
||||
_is_valid(copy._is_valid),
|
||||
_normal_map(copy._normal_map),
|
||||
_splat_layers(copy._splat_layers),
|
||||
_min_height(copy._min_height),
|
||||
_max_height(copy._max_height)
|
||||
{
|
||||
set_vertex_format(copy._vertex_format);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::Destructor
|
||||
// Access: Published, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
STTerrain::
|
||||
~STTerrain() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::clear
|
||||
// Access: Published, Virtual
|
||||
// Description: Resets the terrain to its initial, unloaded state.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STTerrain::
|
||||
clear() {
|
||||
_is_valid = false;
|
||||
_min_height = 0.0f;
|
||||
_max_height = 1.0f;
|
||||
|
||||
_normal_map = "";
|
||||
_splat_map = "";
|
||||
_splat_layers.clear();
|
||||
|
||||
set_vertex_format(NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::load_data
|
||||
// Access: Published, Virtual
|
||||
// Description: This will be called at some point after
|
||||
// initialization. It should be overridden by a derived
|
||||
// class to load up the terrain data from its source and
|
||||
// fill in the data members of this class appropriately,
|
||||
// especially _is_valid. After this call, if _is_valid
|
||||
// is true, then get_height() etc. will be called to
|
||||
// query the terrain's data.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STTerrain::
|
||||
load_data() {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_height
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this should return
|
||||
// the computed height value at point (x, y) of the
|
||||
// terrain, where x and y are unbounded and may refer to
|
||||
// any 2-d point in space.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
float STTerrain::
|
||||
get_height(float x, float y) const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_smooth_height
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this should return
|
||||
// the approximate average height value over a circle of
|
||||
// the specified radius, centered at point (x, y) of the
|
||||
// terrain.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
float STTerrain::
|
||||
get_smooth_height(float x, float y, float radius) const {
|
||||
return get_height(x, y);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_slope
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this should return
|
||||
// the directionless slope at point (x, y) of the
|
||||
// terrain, where 0.0 is flat and 1.0 is vertical. This
|
||||
// is used for determining the legal points to place
|
||||
// trees and grass.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
float STTerrain::
|
||||
get_slope(float x, float y) const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::fill_vertices
|
||||
// Access: Published, Virtual
|
||||
// Description: After load_data() has been called, this will be
|
||||
// called occasionally to populate the vertices for a
|
||||
// terrain cell.
|
||||
//
|
||||
// It will be passed a GeomVertexData whose format will
|
||||
// match get_vertex_format(), and already allocated with
|
||||
// num_xy * num_xy rows. This method should fill the
|
||||
// rows of the data with the appropriate vertex data for
|
||||
// the terrain, over the grid described by the corners
|
||||
// (start_x, start_y) up to and including (start_x +
|
||||
// size_x, start_y + size_xy)--a square of the terrain
|
||||
// with num_xy vertices on a side, arranged in row-major
|
||||
// order.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STTerrain::
|
||||
fill_vertices(GeomVertexData *data,
|
||||
float start_x, float start_y,
|
||||
float size_xy, int num_xy) const {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::output
|
||||
// Access: Published, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STTerrain::
|
||||
output(ostream &out) const {
|
||||
Namable::output(out);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::write
|
||||
// Access: Published, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void STTerrain::
|
||||
write(ostream &out, int indent_level) const {
|
||||
indent(out, indent_level)
|
||||
<< *this << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::get_st_vertex_format
|
||||
// Access: Public
|
||||
// Description: Returns a pointer to the SpeedTree array of vertex
|
||||
// attribs that defines the vertex format for SpeedTree.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
const SpeedTree::SVertexAttribDesc *STTerrain::
|
||||
get_st_vertex_format() const {
|
||||
// return SpeedTree::std_vertex_format;
|
||||
nassertr(!_st_vertex_attribs.empty(), NULL);
|
||||
|
||||
return &_st_vertex_attribs[0];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::set_vertex_format
|
||||
// Access: Protected
|
||||
// Description: Should be called in load_data() by a derived class to
|
||||
// fill in the _vertex_format member. This will also
|
||||
// compute and store the appropriate value for
|
||||
// _st_vertex_attribs.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool STTerrain::
|
||||
set_vertex_format(const GeomVertexFormat *format) {
|
||||
if (format == NULL) {
|
||||
_vertex_format = NULL;
|
||||
_st_vertex_attribs.clear();
|
||||
_is_valid = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
_vertex_format = GeomVertexFormat::register_format(format);
|
||||
if (!convert_vertex_format(_st_vertex_attribs, _vertex_format)) {
|
||||
_is_valid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::convert_vertex_format
|
||||
// Access: Protected, Static
|
||||
// Description: Populates the indicated st_vertex_attribs vector with
|
||||
// an array of SpeedTree vertex attribute entries that
|
||||
// corresponds to the requested format. Returns true on
|
||||
// success, or false if the format cannot be represented
|
||||
// in SpeedTree.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool STTerrain::
|
||||
convert_vertex_format(STTerrain::VertexAttribs &st_vertex_attribs,
|
||||
const GeomVertexFormat *format) {
|
||||
st_vertex_attribs.clear();
|
||||
|
||||
if (format->get_num_arrays() != 1) {
|
||||
speedtree_cat.error()
|
||||
<< "Cannot represent multi-array vertex format in SpeedTree.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const GeomVertexArrayFormat *array = format->get_array(0);
|
||||
|
||||
int num_columns = array->get_num_columns();
|
||||
for (int ci = 0; ci < num_columns; ++ci) {
|
||||
const GeomVertexColumn *column = array->get_column(ci);
|
||||
st_vertex_attribs.push_back(SpeedTree::SVertexAttribDesc());
|
||||
SpeedTree::SVertexAttribDesc &attrib = st_vertex_attribs.back();
|
||||
if (!convert_vertex_column(attrib, column)) {
|
||||
st_vertex_attribs.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
st_vertex_attribs.push_back(SpeedTree::st_attrib_end);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: STTerrain::convert_vertex_column
|
||||
// Access: Protected, Static
|
||||
// Description: Converts the indicated vertex column definition to
|
||||
// the corresponding SpeedTree::SVertexAttribDesc
|
||||
// format. Returns true on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool STTerrain::
|
||||
convert_vertex_column(SpeedTree::SVertexAttribDesc &st_attrib,
|
||||
const GeomVertexColumn *column) {
|
||||
switch (column->get_numeric_type()) {
|
||||
case GeomEnums::NT_float32:
|
||||
st_attrib.m_eDataType = SpeedTree::VERTEX_ATTRIB_TYPE_FLOAT;
|
||||
break;
|
||||
|
||||
default:
|
||||
speedtree_cat.error()
|
||||
<< "Unsupported vertex numeric type for " << *column << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
st_attrib.m_uiNumElements = column->get_num_components();
|
||||
|
||||
if (column->get_name() == InternalName::get_vertex()) {
|
||||
st_attrib.m_eSemantic = SpeedTree::VERTEX_ATTRIB_SEMANTIC_POS;
|
||||
|
||||
} else if (column->get_name() == InternalName::get_texcoord()) {
|
||||
st_attrib.m_eSemantic = SpeedTree::VERTEX_ATTRIB_SEMANTIC_TEXCOORD0;
|
||||
|
||||
} else {
|
||||
speedtree_cat.error()
|
||||
<< "Unsupported vertex semantic name for " << *column << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
nassertr(st_attrib.SizeOfAttrib() == column->get_total_bytes(), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
// Filename: stTerrain.h
|
||||
// Created by: drose (11Oct10)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 STTERRAIN_H
|
||||
#define STTERRAIN_H
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "typedReferenceCount.h"
|
||||
#include "namable.h"
|
||||
#include "geomVertexData.h"
|
||||
#include "speedtree_api.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : STTerrain
|
||||
// Description : This is the abstract base class that defines the
|
||||
// interface needed to describe a terrain for rendering
|
||||
// by SpeedTree. To use it, you must subclass and
|
||||
// override the appropriate virtual methods. Or,
|
||||
// consider just using STBasicTerrain.
|
||||
//
|
||||
// A terrain is defined as a 2-d height function over
|
||||
// all space: get_height(x, y) may be called for any
|
||||
// point in space and it should return a reasonable
|
||||
// value. A terrain also provides normal maps and splat
|
||||
// maps, as rendered by SpeedTree's Terrain.hlsl shader
|
||||
// file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDASPEEDTREE STTerrain : public TypedReferenceCount, public Namable {
|
||||
protected:
|
||||
STTerrain();
|
||||
STTerrain(const STTerrain ©);
|
||||
PUBLISHED:
|
||||
virtual ~STTerrain();
|
||||
|
||||
virtual void clear();
|
||||
virtual void load_data()=0;
|
||||
|
||||
INLINE bool is_valid() const;
|
||||
|
||||
INLINE const Filename &get_normal_map() const;
|
||||
INLINE const Filename &get_splat_map() const;
|
||||
|
||||
INLINE int get_num_splat_layers() const;
|
||||
INLINE const Filename &get_splat_layer(int n) const;
|
||||
INLINE float get_splat_layer_tiling(int n) const;
|
||||
INLINE const LVecBase4f &get_splat_layer_color(int n) const;
|
||||
|
||||
INLINE const GeomVertexFormat *get_vertex_format();
|
||||
|
||||
INLINE float get_min_height() const;
|
||||
INLINE float get_max_height() const;
|
||||
|
||||
virtual float get_height(float x, float y) const=0;
|
||||
virtual float get_smooth_height(float x, float y, float radius) const;
|
||||
virtual float get_slope(float x, float y) const;
|
||||
|
||||
virtual void fill_vertices(GeomVertexData *data,
|
||||
float start_x, float start_y,
|
||||
float size_xy, int num_xy) const;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
public:
|
||||
const SpeedTree::SVertexAttribDesc *get_st_vertex_format() const;
|
||||
|
||||
protected:
|
||||
bool set_vertex_format(const GeomVertexFormat *format);
|
||||
|
||||
typedef pvector<SpeedTree::SVertexAttribDesc> VertexAttribs;
|
||||
static bool convert_vertex_format(VertexAttribs &st_vertex_attribs,
|
||||
const GeomVertexFormat *format);
|
||||
static bool convert_vertex_column(SpeedTree::SVertexAttribDesc &st_attrib,
|
||||
const GeomVertexColumn *column);
|
||||
|
||||
protected:
|
||||
class SplatLayer {
|
||||
public:
|
||||
Filename _filename;
|
||||
float _tiling;
|
||||
LVecBase4f _color;
|
||||
};
|
||||
typedef pvector<SplatLayer> SplatLayers;
|
||||
|
||||
protected:
|
||||
bool _is_valid;
|
||||
|
||||
Filename _normal_map;
|
||||
Filename _splat_map;
|
||||
SplatLayers _splat_layers;
|
||||
|
||||
CPT(GeomVertexFormat) _vertex_format;
|
||||
VertexAttribs _st_vertex_attribs;
|
||||
|
||||
float _min_height;
|
||||
float _max_height;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
TypedReferenceCount::init_type();
|
||||
register_type(_type_handle, "STTerrain",
|
||||
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 STTerrain &terrain) {
|
||||
terrain.output(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
#include "stTerrain.I"
|
||||
|
||||
#endif
|
||||
|
|
@ -24,18 +24,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -25,20 +25,12 @@ TypeHandle STTree::_type_handle;
|
|||
// 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) :
|
||||
STTree(const Filename &fullpath) :
|
||||
Namable(fullpath.get_basename_wo_extension()),
|
||||
_fullpath(fullpath),
|
||||
_filename(filename)
|
||||
_fullpath(fullpath)
|
||||
{
|
||||
if (_filename.empty()) {
|
||||
_filename = fullpath;
|
||||
}
|
||||
|
||||
_is_valid = false;
|
||||
|
||||
// Ensure we have a license.
|
||||
|
|
@ -70,7 +62,7 @@ STTree(const Filename &fullpath, const Filename &filename) :
|
|||
}
|
||||
|
||||
speedtree_cat.info()
|
||||
<< "Read " << _filename << "\n";
|
||||
<< "Read " << _fullpath << "\n";
|
||||
_is_valid = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,12 @@ class SpeedTreeNode;
|
|||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDASPEEDTREE STTree : public TypedReferenceCount, public Namable {
|
||||
PUBLISHED:
|
||||
STTree(const Filename &fullpath, const Filename &filename = Filename());
|
||||
STTree(const Filename &fullpath);
|
||||
private:
|
||||
STTree(const STTree ©);
|
||||
|
||||
PUBLISHED:
|
||||
INLINE const Filename &get_fullpath() const;
|
||||
INLINE const Filename &get_filename() const;
|
||||
|
||||
INLINE bool is_valid() const;
|
||||
|
||||
|
|
@ -47,7 +46,6 @@ public:
|
|||
|
||||
private:
|
||||
Filename _fullpath;
|
||||
Filename _filename;
|
||||
bool _is_valid;
|
||||
SpeedTree::CTreeRender _tree;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue