From 267114bd09f355340faf3180214c949e6f45b8d9 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 13 Oct 2010 23:08:57 +0000 Subject: [PATCH] shadows --- panda/src/speedtree/config_speedtree.cxx | 30 +- panda/src/speedtree/config_speedtree.h | 6 +- panda/src/speedtree/speedTreeNode.cxx | 442 +++++++++++++++++------ panda/src/speedtree/speedTreeNode.h | 24 +- panda/src/speedtree/stBasicTerrain.cxx | 1 + panda/src/speedtree/stTree.cxx | 3 +- 6 files changed, 371 insertions(+), 135 deletions(-) diff --git a/panda/src/speedtree/config_speedtree.cxx b/panda/src/speedtree/config_speedtree.cxx index 94da5ff245..17c0790d33 100644 --- a/panda/src/speedtree/config_speedtree.cxx +++ b/panda/src/speedtree/config_speedtree.cxx @@ -151,16 +151,6 @@ 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 " @@ -171,16 +161,17 @@ ConfigVariableDouble speedtree_emissive_color 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 " - "shadows.")); - ConfigVariableInt speedtree_shadow_map_resolution ("speedtree-shadow-map-resolution", 0, //1024, PRC_DESC("Specifies the resolution for rendering shadow maps. Should " - "be a power of 2. Specify 0 to disable shadowing in SpeedTree. " - "Currently unsupported.")); + "be a power of 2. Specify 0 to disable shadowing in SpeedTree.")); + +ConfigVariableDouble speedtree_cascading_shadow_splits +("speedtree-cascading-shadow-splits", "200 400 600", + PRC_DESC("Specifies the shadow split distance, in spatial units, for " + "each of shadow map to be rendered. The number of values also " + "implies the number of shadow maps, to a maximum value compiled " + "within SpeedTree (typically 4).")); ConfigVariableBool speedtree_smooth_shadows ("speedtree-smooth-shadows", false, @@ -198,6 +189,11 @@ ConfigVariableBool speedtree_frond_rippling ("speedtree-frond-rippling", true, PRC_DESC("True to allow fronds to respond to the global wind.")); +ConfigVariableBool speedtree_show_overlays +("speedtree-show-overlays", false, + PRC_DESC("True to draw onscreen overlays showing the generated " + "shadow map(s).")); + ConfigVariableInt speedtree_max_num_visible_cells ("speedtree-max-num-visible-cells", 75, PRC_DESC("Specifies the maximum number of cells in a single SpeedTree forest " diff --git a/panda/src/speedtree/config_speedtree.h b/panda/src/speedtree/config_speedtree.h index 9945de156d..00823defe4 100644 --- a/panda/src/speedtree/config_speedtree.h +++ b/panda/src/speedtree/config_speedtree.h @@ -52,17 +52,17 @@ 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 ConfigVariableDouble speedtree_cascading_shadow_splits; extern ConfigVariableBool speedtree_smooth_shadows; extern ConfigVariableBool speedtree_show_shadow_splits_on_terrain; extern ConfigVariableBool speedtree_wind_enabled; extern ConfigVariableBool speedtree_frond_rippling; +extern ConfigVariableBool speedtree_show_overlays; + extern ConfigVariableInt speedtree_max_num_visible_cells; extern ConfigVariableDouble speedtree_cull_cell_size; extern ConfigVariableBool speedtree_5_2_stf; diff --git a/panda/src/speedtree/speedTreeNode.cxx b/panda/src/speedtree/speedTreeNode.cxx index 31707a1e8c..c56b2cd7b4 100644 --- a/panda/src/speedtree/speedTreeNode.cxx +++ b/panda/src/speedtree/speedTreeNode.cxx @@ -29,6 +29,7 @@ #include "textureAttrib.h" #include "lightAttrib.h" #include "directionalLight.h" +#include "ambientLight.h" #include "loader.h" #include "deg_2_rad.h" #include "sceneGraphReducer.h" @@ -68,10 +69,7 @@ SpeedTreeNode(const string &name) : //set_internal_bounds(new OmniBoundingVolume); // set_internal_bounds(new BoundingSphere(LPoint3f::zero(), 10.0f)); - // Intialize the render params. - SpeedTree::SForestRenderInfo render_info; - - // First, get the shader directory. + // Intialize the render params. First, get the shader directory. Filename shaders_dir = speedtree_shaders_dir; // We expect the shader directory to contain at least this one token @@ -107,42 +105,12 @@ SpeedTreeNode(const string &name) : } #endif + SpeedTree::SForestRenderInfo render_info; 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; - render_info.m_bZPrePass = speedtree_z_pre_pass; - 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.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; - render_info.m_bDetailNormalMapping = speedtree_detail_normal_mapping; - render_info.m_bAmbientContrast = speedtree_ambient_contrast; - render_info.m_fTransmissionScalar = speedtree_transmission_scalar; - render_info.m_fFogStartDistance = speedtree_fog_start_distance; - render_info.m_fFogEndDistance = speedtree_fog_end_distance; - render_info.m_vFogColor = SpeedTree::Vec3(speedtree_fog_color[0], speedtree_fog_color[1], speedtree_fog_color[2]); - render_info.m_vSkyColor = SpeedTree::Vec3(speedtree_sky_color[0], speedtree_sky_color[1], speedtree_sky_color[2]); - render_info.m_fSkyFogMin = speedtree_sky_fog_min; - render_info.m_fSkyFogMax = speedtree_sky_fog_max; - render_info.m_vSunColor = SpeedTree::Vec3(speedtree_sun_color[0], speedtree_sun_color[1], speedtree_sun_color[2]); - render_info.m_fSunSize = speedtree_sun_size; - render_info.m_fSunSpreadExponent = speedtree_sun_spread_exponent; - render_info.m_fSunFogBloom = speedtree_sun_fog_bloom; - render_info.m_nNumShadowMaps = speedtree_num_shadow_maps; - render_info.m_nShadowMapResolution = speedtree_shadow_map_resolution; - render_info.m_bSmoothShadows = speedtree_smooth_shadows; - render_info.m_bShowShadowSplitsOnTerrain = speedtree_show_shadow_splits_on_terrain; - render_info.m_bWindEnabled = speedtree_wind_enabled; - render_info.m_bFrondRippling = speedtree_frond_rippling; - _forest_render.SetRenderInfo(render_info); + + // Now apply the rest of the config settings. + reload_config(); } //////////////////////////////////////////////////////////////////// @@ -191,8 +159,7 @@ add_tree(const STTree *tree) { if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { speedtree_cat.warning() << "Failed to register tree " << tree->get_fullpath() << "\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); } } @@ -220,8 +187,7 @@ remove_tree(const STTree *tree) { if (!_forest_render.UnregisterTree(tree->get_tree())) { speedtree_cat.warning() << "Failed to unregister tree " << tree->get_fullpath() << "\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); } _needs_repopulate = true; @@ -249,8 +215,7 @@ remove_all_trees() { if (!_forest_render.UnregisterTree(tree->get_tree())) { speedtree_cat.warning() << "Failed to unregister tree " << tree->get_fullpath() << "\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); } delete instance_list; } @@ -562,6 +527,8 @@ setup_terrain(const Filename &terrain_file) { void SpeedTreeNode:: set_terrain(STTerrain *terrain) { _terrain = NULL; + _needs_repopulate = true; + if (terrain == (STTerrain *)NULL) { return; } @@ -577,33 +544,27 @@ set_terrain(STTerrain *terrain) { _terrain_render.SetShaderLoader(_forest_render.GetShaderLoader()); - SpeedTree::STerrainRenderInfo render_info; - render_info.m_strShaderPath = _os_shaders_dir.c_str(); + SpeedTree::STerrainRenderInfo trender_info; + trender_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(); + trender_info.m_strNormalMap = os_specific.c_str(); os_specific = terrain->get_splat_map().to_os_specific(); - render_info.m_strSplatMap = os_specific.c_str(); + trender_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); + trender_info.m_astrSplatLayers[i] = os_specific.c_str(); + trender_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; + trender_info.m_fNormalMapBlueScale = 1.0f; + trender_info.m_bShadowsEnabled = false; // what does this do? + trender_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.SetRenderInfo(trender_info); _terrain_render.SetHeightHints(terrain->get_min_height(), terrain->get_max_height()); - - _needs_repopulate = true; } //////////////////////////////////////////////////////////////////// @@ -642,6 +603,74 @@ snap_to_terrain() { _needs_repopulate = true; } +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::reload_config +// Access: Published +// Description: Re-reads the current setting of all of the relevant +// config variables and applies them to this node. This +// can be called after changing config settings, to make +// them apply to this particular node. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +reload_config() { + + _shadow_infos.clear(); + int num_shadow_maps = speedtree_cascading_shadow_splits.get_num_words(); + if (num_shadow_maps > SpeedTree::c_nMaxNumShadowMaps) { + speedtree_cat.warning() + << "SpeedTree is current compiled to support a maximum of " + << SpeedTree::c_nMaxNumShadowMaps << " shadow maps.\n"; + num_shadow_maps = SpeedTree::c_nMaxNumShadowMaps; + } + _shadow_infos.insert(_shadow_infos.begin(), num_shadow_maps, ShadowInfo()); + for (int smi = 0; smi < num_shadow_maps; ++smi) { + _shadow_infos[smi]._shadow_split = speedtree_cascading_shadow_splits[smi]; + } + + SpeedTree::SForestRenderInfo render_info = _forest_render.GetRenderInfo(); + + render_info.m_nMaxAnisotropy = speedtree_max_anisotropy; + render_info.m_bHorizontalBillboards = speedtree_horizontal_billboards; + render_info.m_fAlphaTestScalar = speedtree_alpha_test_scalar; + render_info.m_bZPrePass = speedtree_z_pre_pass; + 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.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; + render_info.m_bDetailNormalMapping = speedtree_detail_normal_mapping; + render_info.m_bAmbientContrast = speedtree_ambient_contrast; + render_info.m_fTransmissionScalar = speedtree_transmission_scalar; + render_info.m_fFogStartDistance = speedtree_fog_start_distance; + render_info.m_fFogEndDistance = speedtree_fog_end_distance; + render_info.m_vFogColor = SpeedTree::Vec3(speedtree_fog_color[0], speedtree_fog_color[1], speedtree_fog_color[2]); + render_info.m_vSkyColor = SpeedTree::Vec3(speedtree_sky_color[0], speedtree_sky_color[1], speedtree_sky_color[2]); + render_info.m_fSkyFogMin = speedtree_sky_fog_min; + render_info.m_fSkyFogMax = speedtree_sky_fog_max; + render_info.m_vSunColor = SpeedTree::Vec3(speedtree_sun_color[0], speedtree_sun_color[1], speedtree_sun_color[2]); + render_info.m_fSunSize = speedtree_sun_size; + render_info.m_fSunSpreadExponent = speedtree_sun_spread_exponent; + render_info.m_fSunFogBloom = speedtree_sun_fog_bloom; + render_info.m_nNumShadowMaps = num_shadow_maps; + render_info.m_nShadowMapResolution = speedtree_shadow_map_resolution; + render_info.m_bSmoothShadows = speedtree_smooth_shadows; + render_info.m_bShowShadowSplitsOnTerrain = speedtree_show_shadow_splits_on_terrain; + render_info.m_bWindEnabled = speedtree_wind_enabled; + render_info.m_bFrondRippling = speedtree_frond_rippling; + + _forest_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); + + _needs_repopulate = true; +} + //////////////////////////////////////////////////////////////////// // Function: SpeedTreeNode::authorize // Access: Published, Static @@ -679,7 +708,9 @@ authorize(const string &license) { //////////////////////////////////////////////////////////////////// SpeedTreeNode:: SpeedTreeNode(const SpeedTreeNode ©) : - PandaNode(copy) + PandaNode(copy), + _os_shaders_dir(copy._os_shaders_dir), + _shadow_infos(copy._shadow_infos) #ifdef ST_DELETE_FOREST_HACK // Early versions of SpeedTree don't destruct unused CForestRender // objects correctly. To avoid crashes, we have to leak these @@ -690,6 +721,13 @@ SpeedTreeNode(const SpeedTreeNode ©) : init_node(); _forest_render.SetRenderInfo(copy._forest_render.GetRenderInfo()); + _terrain_render.SetRenderInfo(copy._terrain_render.GetRenderInfo()); + + // No way to copy these parameters, so we just re-assign them. + _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); Trees::const_iterator ti; for (ti = copy._trees.begin(); ti != copy._trees.end(); ++ti) { @@ -698,14 +736,15 @@ SpeedTreeNode(const SpeedTreeNode ©) : if (!_forest_render.RegisterTree((SpeedTree::CTree *)tree->get_tree())) { speedtree_cat.warning() << "Failed to register tree " << tree->get_fullpath() << "\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); } _trees.push_back(new InstanceList(*instance_list)); } _trees.sort(); + set_terrain(copy._terrain); + _needs_repopulate = true; mark_internal_bounds_stale(); } @@ -878,38 +917,75 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) { // Check lighting state. SpeedTree only supports a single // directional light; we look for a directional light in the - // lighting state and pass its direction to SpeedTree. - NodePath light; + // lighting state and pass its direction and color to SpeedTree. We + // also accumulate the ambient light colors. + Colorf ambient_color(0.0f, 0.0f, 0.0f, 0.0f); + DirectionalLight *dlight = NULL; + NodePath dlight_np; + Colorf diffuse_color; + + int diffuse_priority = 0; const LightAttrib *la = DCAST(LightAttrib, state->get_attrib(LightAttrib::get_class_slot())); if (la != (LightAttrib *)NULL) { - light = la->get_most_important_light(); + for (int i = 0; i < la->get_num_on_lights(); ++i) { + NodePath light = la->get_on_light(i); + if (!light.is_empty() && light.node()->is_of_type(DirectionalLight::get_class_type())) { + // A directional light. + DirectionalLight *light_obj = DCAST(DirectionalLight, light.node()); + if (dlight == NULL || light_obj->get_priority() > dlight->get_priority()) { + // Here's the most important directional light. + dlight = light_obj; + dlight_np = light; + } + } else if (!light.is_empty() && light.node()->is_of_type(AmbientLight::get_class_type())) { + // An ambient light. We keep the color only. + AmbientLight *light_obj = DCAST(AmbientLight, light.node()); + ambient_color += light_obj->get_color(); + } + } } - if (!light.is_empty() && light.node()->is_of_type(DirectionalLight::get_class_type())) { - DirectionalLight *light_obj = DCAST(DirectionalLight, light.node()); - - CPT(TransformState) transform = light.get_transform(trav->get_scene()->get_scene_root().get_parent()); - LVector3f dir = light_obj->get_direction() * transform->get_mat(); + + if (dlight != (DirectionalLight *)NULL) { + CPT(TransformState) transform = dlight_np.get_transform(trav->get_scene()->get_scene_root().get_parent()); + LVector3f dir = dlight->get_direction() * transform->get_mat(); _light_dir = SpeedTree::Vec3(dir[0], dir[1], dir[2]); + diffuse_color = dlight->get_color(); } 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. _light_dir = SpeedTree::Vec3(0.0, 0.0, -1.0); + + // Also, we set ambient and diffuse colors to the same full-white + // value. + ambient_color.set(1.0f, 1.0f, 1.0f, 1.0f); + diffuse_color.set(1.0f, 1.0f, 1.0f, 1.0f); } + SpeedTree::SForestRenderInfo render_info = _forest_render.GetRenderInfo(); + render_info.m_sLightMaterial.m_vAmbient = SpeedTree::Vec4(ambient_color[0], ambient_color[1], ambient_color[2], 1.0f); + render_info.m_sLightMaterial.m_vDiffuse = SpeedTree::Vec4(diffuse_color[0], diffuse_color[1], diffuse_color[2], 1.0f); + _forest_render.SetRenderInfo(render_info); + _forest_render.SetLightDir(_light_dir); + SpeedTree::st_float32 updated_splits[SpeedTree::c_nMaxNumShadowMaps]; + memset(updated_splits, 0, sizeof(updated_splits)); + for (int smi = 0; smi < (int)_shadow_infos.size(); ++smi) { + updated_splits[smi] = _shadow_infos[smi]._shadow_split; + }; + static const float shadow_fade = 0.25; + + _forest_render.SetCascadedShadowMapDistances(updated_splits, lens->get_far()); + _forest_render.SetShadowFadePercentage(shadow_fade); + 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_render.CullAndComputeLOD(_view, _visible_trees); - - if (has_terrain()) { - _terrain_render.CullAndComputeLOD(_view, _visible_terrain); - } + cull_forest(); } // Recurse onto the node's children. @@ -1052,6 +1128,52 @@ write(ostream &out, int indent_level) const { */ } + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::write_error +// Access: Public, Static +// Description: Writes the current SpeedTree error message to the +// indicated stream. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +write_error(ostream &out) { + const char *error = SpeedTree::CCore::GetError(); + if (error != (const char *)NULL) { + out << error; + } + out << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::set_transparent_texture_mode +// Access: Protected +// Description: Uses SpeedTree::CRenderState to set the indicated +// transparency mode. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode eMode) const { + // turn all modes off (no telling what render state the client + // application might be in before this call) + SpeedTree::CRenderState::SetBlending(false); + SpeedTree::CRenderState::SetAlphaTesting(false); + SpeedTree::CRenderState::SetAlphaToCoverage(false); + + switch (eMode) { + case SpeedTree::TRANS_TEXTURE_ALPHA_TESTING: + SpeedTree::CRenderState::SetAlphaTesting(true); + break; + case SpeedTree::TRANS_TEXTURE_ALPHA_TO_COVERAGE: + SpeedTree::CRenderState::SetAlphaToCoverage(true); + break; + case SpeedTree::TRANS_TEXTURE_BLENDING: + SpeedTree::CRenderState::SetBlending(true); + break; + default: + // intentionally do nothing (TRANS_TEXTURE_NOTHING) + break; + } +} + //////////////////////////////////////////////////////////////////// // Function: SpeedTreeNode::init_node // Access: Private @@ -1129,8 +1251,7 @@ repopulate() { speedtree_cat.warning() << "Failed to add " << instances.size() << " instances for " << *tree << "\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); } } @@ -1259,15 +1380,34 @@ draw_callback(CallbackData *data) { setup_for_render(gsg); - if (_terrain_render.IsEnabled()) { + // Set some initial state requirements. + SpeedTree::CRenderState::SetAlphaFunction(SpeedTree::ALPHAFUNC_GREATER, 0.0f); + // start the forest render + _forest_render.StartRender(); + + if (_forest_render.ShadowsAreEnabled()) { + // Update the shadow maps. TODO: consider updating these only + // every once in a while, instead of every frame, as a simple + // optimization. + render_forest_into_shadow_maps(); + _forest_render.ClearBoundTextures( ); + } + + if (!_forest_render.UploadViewShaderParameters(_view)) { + speedtree_cat.warning() + << "Couldn't set view parameters\n"; + write_error(speedtree_cat.warning()); + } + + if (has_terrain()) { // Is this needed for terrain? _terrain_render.UploadShaderConstants - (&_forest_render, _light_dir, + (&_forest_render, _light_dir, _forest_render.GetRenderInfo().m_sLightMaterial); // set terrain render states - //SetTransparentTextureMode(TRANS_TEXTURE_NOTHING); + set_transparent_texture_mode(SpeedTree::TRANS_TEXTURE_NOTHING); // render actual terrain bool terrain = _terrain_render.Render @@ -1278,16 +1418,18 @@ draw_callback(CallbackData *data) { if (!terrain) { speedtree_cat.warning() << "Failed to render terrain\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); + + // Clear the terrain so we don't keep spamming error messages. + _terrain = NULL; } - - // restore default forest rendering states - //SetTransparentTextureMode(ETextureAlphaRenderMode(m_uiTransparentTextureRenderMode)); } - - // start the forest render - _forest_render.StartRender(); + + // SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_ALPHA_TESTING; + // SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_ALPHA_TO_COVERAGE; + // SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_BLENDING; + SpeedTree::ETextureAlphaRenderMode mode = SpeedTree::TRANS_TEXTURE_NOTHING; + set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode(mode)); bool branches = _forest_render.RenderBranches(_visible_trees, SpeedTree::RENDER_PASS_STANDARD); bool fronds = _forest_render.RenderFronds(_visible_trees, SpeedTree::RENDER_PASS_STANDARD); @@ -1299,17 +1441,78 @@ draw_callback(CallbackData *data) { speedtree_cat.warning() << "Failed to render forest completely: " << branches << " " << fronds << " " << leaf_meshes << " " << leaf_cards << " " << billboards << "\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); } _forest_render.EndRender(); - // SpeedTree leaves the graphics state indeterminate. But this - // doesn't help? + if (_forest_render.ShadowsAreEnabled() && speedtree_show_overlays) { + _forest_render.RenderOverlays(); + } + + // SpeedTree leaves the graphics state indeterminate. Make sure + // Panda doesn't rely on anything in the state. geom_cbdata->set_lost_state(true); } + +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::render_forest_into_shadow_maps +// Access: Private +// Description: Renders the forest from the point of view of the +// light, to fill up the shadow map(s). +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +render_forest_into_shadow_maps() { + bool success = true; + + // d3d10 allows A2C on render targets, so make sure to turn it off + SpeedTree::CRenderState::SetMultisampling(false); + SpeedTree::CRenderState::SetAlphaToCoverage(false); + +#if defined(SPEEDTREE_OPENGL) + // Ensure the viewport is not constrained. SpeedTree doesn't expect + // that. + glDisable(GL_SCISSOR_TEST); +#endif + + for (int smi = 0; smi < (int)_shadow_infos.size(); ++smi) { + const SpeedTree::CView &light_view = _shadow_infos[smi]._light_view; + const SpeedTree::SForestCullResults &light_cull = _shadow_infos[smi]._light_cull; + + if (_forest_render.BeginShadowMap(smi, light_view)) { + success &= _forest_render.UploadViewShaderParameters(light_view); + + // branch geometry can be rendered with backfacing triangle + // removed, so a closer tolerance can be used + SpeedTree::CRenderState::SetPolygonOffset(1.0f, 0.125f); + + success &= _forest_render.RenderBranches(light_cull, SpeedTree::RENDER_PASS_SHADOW); + + // the remaining geometry types cannot be backface culled, so we + // need a much more aggressive offset + SpeedTree::CRenderState::SetPolygonOffset(10.0f, 1.0f); + + success &= _forest_render.RenderFronds(light_cull, SpeedTree::RENDER_PASS_SHADOW); + success &= _forest_render.RenderLeafMeshes(light_cull, SpeedTree::RENDER_PASS_SHADOW); + success &= _forest_render.RenderLeafCards(light_cull, SpeedTree::RENDER_PASS_SHADOW, light_view); + + // We don't bother to render billboard geometry into the shadow + // map(s). + + success &= _forest_render.EndShadowMap(smi); + } + } + + // SpeedTree::CRenderState::SetMultisampling(m_sUserSettings.m_nSampleCount > 0); + + if (!success) { + speedtree_cat.warning() + << "Failed to render shadow maps\n"; + write_error(speedtree_cat.warning()); + } +} + //////////////////////////////////////////////////////////////////// // Function: SpeedTreeNode::setup_for_render // Access: Private @@ -1395,10 +1598,11 @@ setup_for_render(GraphicsStateGuardian *gsg) { 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() - << SpeedTree::CCore::GetError() << "\n"; + if (speedtree_cat.is_debug()) { + speedtree_cat.debug() + << "Failed to init tree graphics for " << *tree << "\n"; + write_error(speedtree_cat.debug()); + } } } @@ -1406,8 +1610,7 @@ setup_for_render(GraphicsStateGuardian *gsg) { if (!_forest_render.InitGraphics(false)) { speedtree_cat.warning() << "Failed to init graphics\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); _is_valid = false; return; } @@ -1429,32 +1632,49 @@ setup_for_render(GraphicsStateGuardian *gsg) { _terrain->get_st_vertex_format())) { speedtree_cat.warning() << "Failed to init terrain\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + write_error(speedtree_cat.warning()); } } // If we needed to repopulate, it means we didn't cull in the cull // traversal. Do it now. - _forest_render.CullAndComputeLOD(_view, _visible_trees); - if (has_terrain()) { - _terrain_render.CullAndComputeLOD(_view, _visible_terrain); - } - + cull_forest(); _needs_repopulate = false; } if (has_terrain()) { update_terrain_cells(); } +} - if (!_forest_render.UploadViewShaderParameters(_view)) { - speedtree_cat.warning() - << "Couldn't set view parameters\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; +//////////////////////////////////////////////////////////////////// +// Function: SpeedTreeNode::cull_forest +// Access: Private +// Description: Calls the SpeedTree methods to perform the needed +// cull calculations. +//////////////////////////////////////////////////////////////////// +void SpeedTreeNode:: +cull_forest() { + _forest_render.CullAndComputeLOD(_view, _visible_trees); + if (has_terrain()) { + _terrain_render.CullAndComputeLOD(_view, _visible_terrain); + } + + if (_forest_render.ShadowsAreEnabled()) { + for (int smi = 0; smi < (int)_shadow_infos.size(); ++smi) { + SpeedTree::CView &light_view = _shadow_infos[smi]._light_view; + SpeedTree::SForestCullResultsRender &light_cull = _shadow_infos[smi]._light_cull; + + _forest_render.ComputeLightView + (_forest_render.GetLightDir(), _view.GetFrustumPoints(), smi, + light_view, 0.0f); + + light_view.SetLodRefPoint(_view.GetCameraPos()); + _forest_render.CullAndComputeLOD(light_view, light_cull, false); + } } } + //////////////////////////////////////////////////////////////////// // Function: SpeedTreeNode::print_forest_stats // Access: Private diff --git a/panda/src/speedtree/speedTreeNode.h b/panda/src/speedtree/speedTreeNode.h index dbb4f8da63..ea922ccb52 100644 --- a/panda/src/speedtree/speedTreeNode.h +++ b/panda/src/speedtree/speedTreeNode.h @@ -46,8 +46,7 @@ class Loader; // Panda3D scene graph. // // SpeedTree also includes some support for simple -// terrain and grass systems, but that support is not -// (yet) implemented within this layer. +// terrain and grass systems. //////////////////////////////////////////////////////////////////// class EXPCL_PANDASPEEDTREE SpeedTreeNode : public PandaNode { private: @@ -128,6 +127,8 @@ PUBLISHED: void snap_to_terrain(); + void reload_config(); + static bool authorize(const string &license = ""); public: @@ -153,6 +154,11 @@ public: virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level) const; + static void write_error(ostream &out); + +protected: + void set_transparent_texture_mode(SpeedTree::ETextureAlphaRenderMode eMode) const; + private: void init_node(); void r_add_instances(PandaNode *node, const TransformState *transform, @@ -162,7 +168,10 @@ private: void update_terrain_cells(); bool validate_api(GraphicsStateGuardian *gsg); void draw_callback(CallbackData *cbdata); + void render_forest_into_shadow_maps(); void setup_for_render(GraphicsStateGuardian *gsg); + void cull_forest(); + void print_forest_stats(const SpeedTree::CForest::SPopulationStats &forest_stats) const; private: @@ -217,6 +226,17 @@ private: SpeedTree::Vec3 _light_dir; + class ShadowInfo { + public: + ShadowInfo() {}; + + SpeedTree::CView _light_view; + SpeedTree::SForestCullResultsRender _light_cull; + float _shadow_split; + }; + typedef pvector ShadowInfos; + ShadowInfos _shadow_infos; + static bool _authorized; static bool _done_first_init; diff --git a/panda/src/speedtree/stBasicTerrain.cxx b/panda/src/speedtree/stBasicTerrain.cxx index 3ab8b44822..15427e7dbd 100644 --- a/panda/src/speedtree/stBasicTerrain.cxx +++ b/panda/src/speedtree/stBasicTerrain.cxx @@ -109,6 +109,7 @@ clear() { bool STBasicTerrain:: setup_terrain(const Filename &terrain_filename) { _is_valid = false; + set_name(terrain_filename.get_basename()); VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); diff --git a/panda/src/speedtree/stTree.cxx b/panda/src/speedtree/stTree.cxx index f72de48a2c..84e0d2dc00 100644 --- a/panda/src/speedtree/stTree.cxx +++ b/panda/src/speedtree/stTree.cxx @@ -56,8 +56,7 @@ STTree(const Filename &fullpath) : if (!_tree.LoadTree(os_fullpath.c_str())) { speedtree_cat.warning() << "Couldn't read: " << _fullpath << "\n"; - speedtree_cat.warning() - << SpeedTree::CCore::GetError() << "\n"; + SpeedTreeNode::write_error(speedtree_cat.warning()); return; }