This commit is contained in:
David Rose 2010-10-12 23:12:51 +00:00
parent 66f1acc32f
commit d93aac8ad8
16 changed files with 1683 additions and 88 deletions

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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"

View File

@ -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

View File

@ -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 &copy) :
// 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 &copy) :
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()

View File

@ -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;

View File

@ -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();
}

View File

@ -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 &copy) :
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);
}
}

View File

@ -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 &copy);
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

View File

@ -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;
}

View File

@ -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 &copy) :
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;
}

View File

@ -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 &copy);
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

View File

@ -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

View File

@ -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;
}

View File

@ -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 &copy);
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;