From d93aac8ad89c318a54c3bece85e46d28f1f60b0f Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 12 Oct 2010 23:12:51 +0000 Subject: [PATCH] terrain --- panda/src/speedtree/Sources.pp | 18 +- panda/src/speedtree/config_speedtree.cxx | 24 + panda/src/speedtree/config_speedtree.h | 4 + .../speedtree/pandaspeedtree_composite1.cxx | 2 + panda/src/speedtree/speedTreeNode.I | 44 ++ panda/src/speedtree/speedTreeNode.cxx | 339 +++++++++++--- panda/src/speedtree/speedTreeNode.h | 25 +- panda/src/speedtree/stBasicTerrain.I | 184 ++++++++ panda/src/speedtree/stBasicTerrain.cxx | 424 ++++++++++++++++++ panda/src/speedtree/stBasicTerrain.h | 112 +++++ panda/src/speedtree/stTerrain.I | 138 ++++++ panda/src/speedtree/stTerrain.cxx | 293 ++++++++++++ panda/src/speedtree/stTerrain.h | 134 ++++++ panda/src/speedtree/stTree.I | 12 - panda/src/speedtree/stTree.cxx | 14 +- panda/src/speedtree/stTree.h | 4 +- 16 files changed, 1683 insertions(+), 88 deletions(-) create mode 100644 panda/src/speedtree/stBasicTerrain.I create mode 100644 panda/src/speedtree/stBasicTerrain.cxx create mode 100644 panda/src/speedtree/stBasicTerrain.h create mode 100644 panda/src/speedtree/stTerrain.I create mode 100644 panda/src/speedtree/stTerrain.cxx create mode 100644 panda/src/speedtree/stTerrain.h diff --git a/panda/src/speedtree/Sources.pp b/panda/src/speedtree/Sources.pp index a5d2312167..aac4737204 100644 --- a/panda/src/speedtree/Sources.pp +++ b/panda/src/speedtree/Sources.pp @@ -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 diff --git a/panda/src/speedtree/config_speedtree.cxx b/panda/src/speedtree/config_speedtree.cxx index 68e0ff3823..0bfabda029 100644 --- a/panda/src/speedtree/config_speedtree.cxx +++ b/panda/src/speedtree/config_speedtree.cxx @@ -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(); diff --git a/panda/src/speedtree/config_speedtree.h b/panda/src/speedtree/config_speedtree.h index c49e77083e..9945de156d 100644 --- a/panda/src/speedtree/config_speedtree.h +++ b/panda/src/speedtree/config_speedtree.h @@ -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; diff --git a/panda/src/speedtree/pandaspeedtree_composite1.cxx b/panda/src/speedtree/pandaspeedtree_composite1.cxx index 9aa1d92ab0..92767dc231 100644 --- a/panda/src/speedtree/pandaspeedtree_composite1.cxx +++ b/panda/src/speedtree/pandaspeedtree_composite1.cxx @@ -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" diff --git a/panda/src/speedtree/speedTreeNode.I b/panda/src/speedtree/speedTreeNode.I index feaca878a1..e5e3fde7b5 100644 --- a/panda/src/speedtree/speedTreeNode.I +++ b/panda/src/speedtree/speedTreeNode.I @@ -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 diff --git a/panda/src/speedtree/speedTreeNode.cxx b/panda/src/speedtree/speedTreeNode.cxx index f2907fc6f0..30ec983a00 100644 --- a/panda/src/speedtree/speedTreeNode.cxx +++ b/panda/src/speedtree/speedTreeNode.cxx @@ -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() diff --git a/panda/src/speedtree/speedTreeNode.h b/panda/src/speedtree/speedTreeNode.h index eb6a367ef2..475d42cd5c 100644 --- a/panda/src/speedtree/speedTreeNode.h +++ b/panda/src/speedtree/speedTreeNode.h @@ -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 > 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; diff --git a/panda/src/speedtree/stBasicTerrain.I b/panda/src/speedtree/stBasicTerrain.I new file mode 100644 index 0000000000..5d7def1e1f --- /dev/null +++ b/panda/src/speedtree/stBasicTerrain.I @@ -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 +STBasicTerrain::InterpolationData:: +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 +void STBasicTerrain::InterpolationData:: +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 +ValueType STBasicTerrain::InterpolationData:: +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 +ValueType STBasicTerrain::InterpolationData:: +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 +ValueType STBasicTerrain::InterpolationData:: +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 +bool STBasicTerrain::InterpolationData:: +is_present() const { + return !_data.empty(); +} diff --git a/panda/src/speedtree/stBasicTerrain.cxx b/panda/src/speedtree/stBasicTerrain.cxx new file mode 100644 index 0000000000..3ab8b44822 --- /dev/null +++ b/panda/src/speedtree/stBasicTerrain.cxx @@ -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); + } +} + diff --git a/panda/src/speedtree/stBasicTerrain.h b/panda/src/speedtree/stBasicTerrain.h new file mode 100644 index 0000000000..7f787e93d4 --- /dev/null +++ b/panda/src/speedtree/stBasicTerrain.h @@ -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 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 _data; + }; + +protected: + Filename _height_map; + float _size; + float _height_scale; + + InterpolationData _height_data; + //InterpolationData _normal_data; + InterpolationData _slope_data; + //InterpolationData _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 diff --git a/panda/src/speedtree/stTerrain.I b/panda/src/speedtree/stTerrain.I new file mode 100644 index 0000000000..1879c03848 --- /dev/null +++ b/panda/src/speedtree/stTerrain.I @@ -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; +} diff --git a/panda/src/speedtree/stTerrain.cxx b/panda/src/speedtree/stTerrain.cxx new file mode 100644 index 0000000000..5cac592e3b --- /dev/null +++ b/panda/src/speedtree/stTerrain.cxx @@ -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; +} + diff --git a/panda/src/speedtree/stTerrain.h b/panda/src/speedtree/stTerrain.h new file mode 100644 index 0000000000..93a1bf78d8 --- /dev/null +++ b/panda/src/speedtree/stTerrain.h @@ -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 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 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 diff --git a/panda/src/speedtree/stTree.I b/panda/src/speedtree/stTree.I index 26b21adb0e..b0e48d791b 100644 --- a/panda/src/speedtree/stTree.I +++ b/panda/src/speedtree/stTree.I @@ -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 diff --git a/panda/src/speedtree/stTree.cxx b/panda/src/speedtree/stTree.cxx index cd3e4e6918..f72de48a2c 100644 --- a/panda/src/speedtree/stTree.cxx +++ b/panda/src/speedtree/stTree.cxx @@ -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; } diff --git a/panda/src/speedtree/stTree.h b/panda/src/speedtree/stTree.h index 65bc72b097..ed37e569a6 100644 --- a/panda/src/speedtree/stTree.h +++ b/panda/src/speedtree/stTree.h @@ -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;