158 lines
5.8 KiB
C++
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);
|
|
}
|