EG/RenderPipelineFile/rpcore/native/source/shadow_manager.cpp
2026-02-25 14:56:09 +08:00

158 lines
5.8 KiB
C++

/**
*
* RenderPipeline
*
* Copyright (c) 2014-2016 tobspr <tobias.springer1@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "shadow_manager.h"
NotifyCategoryDef(shadowmanager, "");
/**
* @brief Constructs a new shadow atlas
* @details This constructs a new shadow atlas. There are a set of properties
* which should be set before calling ShadowManager::init, see the set-Methods.
* After all properties are set, ShadowManager::init should get called.
* ShadowManager::update should get called on a per frame basis.
*/
ShadowManager::ShadowManager() {
_max_updates = 10;
_atlas = NULL;
_atlas_size = 4096;
_tag_state_mgr = NULL;
_atlas_graphics_output = NULL;
}
/**
* @brief Destructs the ShadowManager
* @details This destructs the shadow manager, clearing all resources used
*/
ShadowManager::~ShadowManager() {
delete _atlas;
// Todo: Could eventually unregister all shadow cameras. Since the tag state
// manager does this on cleanup already, and we get destructed at the same
// time (if at all), this is not really necessary
}
/**
* @brief Initializes the ShadowManager.
* @details This initializes the ShadowManager. All properties should have
* been set before calling this, otherwise assertions will get triggered.
*
* This setups everything required for rendering shadows, including the
* shadow atlas and the various shadow cameras. After calling this method,
* no properties can be changed anymore.
*/
void ShadowManager::init() {
nassertv(!_scene_parent.is_empty()); // Scene parent not set, call set_scene_parent before init!
nassertv(_tag_state_mgr != NULL); // TagStateManager not set, call set_tag_state_mgr before init!
nassertv(_atlas_graphics_output != NULL); // AtlasGraphicsOutput not set, call set_atlas_graphics_output before init!
_cameras.resize(_max_updates);
_display_regions.resize(_max_updates);
_camera_nps.reserve(_max_updates);
// Create the cameras and regions
for(size_t i = 0; i < _max_updates; ++i) {
// Create the camera
PT(Camera) camera = new Camera("ShadowCam-" + to_string((long long)i));
camera->set_lens(new MatrixLens());
camera->set_active(false);
camera->set_scene(_scene_parent);
_tag_state_mgr->register_camera("shadow", camera);
_camera_nps.push_back(_scene_parent.attach_new_node(camera));
_cameras[i] = camera;
// Create the display region
PT(DisplayRegion) region = _atlas_graphics_output->make_display_region();
region->set_sort(1000);
region->set_clear_depth_active(true);
region->set_clear_depth(1.0);
region->set_clear_color_active(false);
region->set_camera(_camera_nps[i]);
region->set_active(false);
_display_regions[i] = region;
}
// Create the atlas
_atlas = new ShadowAtlas(_atlas_size);
// Reserve enough space for the updates
_queued_updates.reserve(_max_updates);
}
/**
* @brief Updates the ShadowManager
* @details This updates the ShadowManager, processing all shadow sources which
* need to get updated.
*
* This first collects all sources which require an update, sorts them by priority,
* and then processes the first <max_updates> ShadowSources.
*
* This may not get called before ShadowManager::init, or an assertion will be
* thrown.
*/
void ShadowManager::update() {
nassertv(_atlas != NULL); // ShadowManager::init not called yet
nassertv(_queued_updates.size() <= _max_updates); // Internal error, should not happen
// Disable all cameras and regions which will not be used
for (size_t i = _queued_updates.size(); i < _max_updates; ++i) {
_cameras[i]->set_active(false);
_display_regions[i]->set_active(false);
}
// Iterate over all queued updates
for (size_t i = 0; i < _queued_updates.size(); ++i) {
const ShadowSource* source = _queued_updates[i];
// Enable the camera and display region, so they perform a render
_cameras[i]->set_active(true);
_display_regions[i]->set_active(true);
// Set the view projection matrix
DCAST(MatrixLens, _cameras[i]->get_lens())->set_user_mat(source->get_mvp());
// Optional: Show the camera frustum for debugging
// _cameras[i]->show_frustum();
// Set the correct dimensions on the display region
const LVecBase4f& uv = source->get_uv_region();
_display_regions[i]->set_dimensions(
uv.get_x(), // left
uv.get_x() + uv.get_z(), // right
uv.get_y(), // bottom
uv.get_y() + uv.get_w() // top
);
}
// Clear the update list
_queued_updates.clear();
_queued_updates.reserve(_max_updates);
}