Ejemplo n.º 1
0
/**
 * @brief Constructs a new shadow atlas.
 * @details This constructs a new shadow atlas with the given size and tile size.
 *
 *   The size determines the total size of the atlas in pixels. It should be a
 *   power-of-two to favour the GPU.
 *
 *   The tile_size determines the smallest unit of tiles the atlas can store.
 *   If, for example, a tile_size of 32 is used, then every entry stored must
 *   have a resolution of 32 or greater, and the resolution must be a multiple
 *   of 32. This is to optimize the search in the atlas, so the atlas does not
 *   have to check every pixel, and instead can just check whole tiles.
 *
 *   If you want to disable the use of tiles, set the tile_size to 1, which
 *   will make the shadow atlas use pixels instead of tiles.
 *
 * @param size Atlas-size in pixels
 * @param tile_size tile-size in pixels, or 1 to use no tiles.
 */
ShadowAtlas::ShadowAtlas(size_t size, size_t tile_size) {
    nassertv(size > 1 && tile_size >= 1);
    nassertv(tile_size < size && size % tile_size == 0);
    _size = size;
    _tile_size = tile_size;
    _num_used_tiles = 0;
    init_tiles();
}
Ejemplo n.º 2
0
/**
 * @brief Frees a given region
 * @details This frees a given region, marking it as free so that other shadow
 *   maps can use the space again. The region should be the same as returned
 *   by ShadowAtlas::find_and_reserve_region.
 *
 *   If an invalid region is passed, an assertion is triggered. If assertions
 *   are compiled out, undefined behaviour will occur.
 *
 * @param region Region to free
 */
void ShadowAtlas::free_region(const LVecBase4i& region) {
    // Out of bounds check, can't hurt
    nassertv(region.get_x() >= 0 && region.get_y() >= 0);
    nassertv(region.get_x() + region.get_z() <= _num_tiles && region.get_y() + region.get_w() <= _num_tiles);

    _num_used_tiles -= region.get_z() * region.get_w();

    for (size_t x = 0; x < region.get_z(); ++x) {
        for (size_t y = 0; y < region.get_w(); ++y) {
            // Could do an assert here, that the tile should have been used (=true) before
            set_tile(region.get_x() + x, region.get_y() + y, false);
        }
    }
}
Ejemplo n.º 3
0
/**
 * @brief Constructs a new TagStateManager
 * @details This constructs a new TagStateManager. The #main_cam_node should
 *   refer to the main scene camera, and will most likely be base.cam.
 *   It is necessary to pass the camera because the C++ code does not have
 *   access to the showbase.
 *
 * @param main_cam_node The main scene camera
 */
TagStateManager::TagStateManager(NodePath main_cam_node) {
    nassertv(!main_cam_node.is_empty());
    nassertv(DCAST(Camera, main_cam_node.node()) != NULL);
    _main_cam_node = main_cam_node;

    // Set default camera mask
    DCAST(Camera, _main_cam_node.node())->set_camera_mask(BitMask32::bit(1));

    // Init containers
    _containers["shadow"]   = StateContainer("Shadows",  2, false);
    _containers["voxelize"] = StateContainer("Voxelize", 3, false);
    _containers["envmap"]   = StateContainer("Envmap",   4, true);
    _containers["forward"]  = StateContainer("Forward",  5, true);
}
Ejemplo n.º 4
0
/**
 * @brief Writes the light to a GPUCommand
 * @details This writes all of the lights data to the given GPUCommand handle.
 *   Subclasses should first call this method, and then append their own
 *   data. This makes sure that for unpacking a light, no information about
 *   the type of the light is required.
 * 
 * @param cmd The GPUCommand to write to
 */
void RPLight::write_to_command(GPUCommand &cmd) {
    cmd.push_int(_light_type);
    cmd.push_int(_ies_profile);

    if (_casts_shadows) {
        // If we casts shadows, write the index of the first source, we expect
        // them to be consecutive
        nassertv(_shadow_sources.size() >= 0);
        nassertv(_shadow_sources[0]->has_slot());
        cmd.push_int(_shadow_sources[0]->get_slot());
    } else {
        // If we cast no shadows, just push a negative number
        cmd.push_int(-1);
    }

    cmd.push_vec3(_position);

    // Get the lights color by multiplying color with lumens, I hope thats
    // physically correct.
    cmd.push_vec3(_color * _lumens);
}
Ejemplo n.º 5
0
/**
 * @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);
}
Ejemplo n.º 6
0
/**
 * @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);
}
Ejemplo n.º 7
0
/**
 * @brief Internal method to reserve a region in the atlas.
 * @details This reserves a given region in the shadow atlas. The region should
 *   be in tile space.This is called by the ShadowAtlas::find_and_reserve_region.
 *   It sets all flags in that region to true, indicating that those are used.
 *   When an invalid region is passed, an assertion is triggered. If assertions
 *   are optimized out, undefined behaviour occurs.
 *
 * @param x x- start positition of the region
 * @param y y- start position of the region
 * @param w width of the region
 * @param h height of the region
 */
void ShadowAtlas::reserve_region(size_t x, size_t y, size_t w, size_t h) {
    // Check if we are out of bounds, this should be disabled for performance
    // reasons at some point.
    nassertv(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles);

    _num_used_tiles += w * h;

    // Iterate over every tile in the region and mark it as used
    for (size_t cx = 0; cx < w; ++cx) {
        for (size_t cy = 0; cy < h; ++cy) {
            set_tile(cx + x, cy + y, true);
        }
    }
}
Ejemplo n.º 8
0
/**
 * @brief Light destructor
 * @details This destructs the light, cleaning up all resourced used. The light
 *   should be detached at this point, because while the Light is attached,
 *   the InternalLightManager holds a reference to prevent it from being
 *   destructed. 
 */
RPLight::~RPLight() {
    nassertv(!has_slot()); // Light still attached - should never happen
    clear_shadow_sources();
}