godot/servers/rendering/renderer_rd/light_cluster_builder.cpp

253 lines
8.8 KiB
C++

/*************************************************************************/
/* light_cluster_builder.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 "light_cluster_builder.h"
void LightClusterBuilder::begin(const Transform &p_view_transform, const CameraMatrix &p_cam_projection) {
view_xform = p_view_transform;
projection = p_cam_projection;
z_near = -projection.get_z_near();
z_far = -projection.get_z_far();
//reset counts
light_count = 0;
refprobe_count = 0;
decal_count = 0;
item_count = 0;
sort_id_count = 0;
}
void LightClusterBuilder::bake_cluster() {
float slice_depth = (z_near - z_far) / depth;
uint8_t *cluster_dataw = cluster_data.ptrw();
Cell *cluster_data_ptr = (Cell *)cluster_dataw;
//clear the cluster
zeromem(cluster_data_ptr, (width * height * depth * sizeof(Cell)));
/* Step 1, create cell positions and count them */
for (uint32_t i = 0; i < item_count; i++) {
const Item &item = items[i];
int from_slice = Math::floor((z_near - (item.aabb.position.z + item.aabb.size.z)) / slice_depth);
int to_slice = Math::floor((z_near - item.aabb.position.z) / slice_depth);
if (from_slice >= (int)depth || to_slice < 0) {
continue; //sorry no go
}
from_slice = MAX(0, from_slice);
to_slice = MIN((int)depth - 1, to_slice);
for (int j = from_slice; j <= to_slice; j++) {
Vector3 min = item.aabb.position;
Vector3 max = item.aabb.position + item.aabb.size;
float limit_near = MIN((z_near - slice_depth * j), max.z);
float limit_far = MAX((z_near - slice_depth * (j + 1)), min.z);
max.z = limit_near;
min.z = limit_near;
Vector3 proj_min = projection.xform(min);
Vector3 proj_max = projection.xform(max);
int near_from_x = int(Math::floor((proj_min.x * 0.5 + 0.5) * width));
int near_from_y = int(Math::floor((-proj_max.y * 0.5 + 0.5) * height));
int near_to_x = int(Math::floor((proj_max.x * 0.5 + 0.5) * width));
int near_to_y = int(Math::floor((-proj_min.y * 0.5 + 0.5) * height));
max.z = limit_far;
min.z = limit_far;
proj_min = projection.xform(min);
proj_max = projection.xform(max);
int far_from_x = int(Math::floor((proj_min.x * 0.5 + 0.5) * width));
int far_from_y = int(Math::floor((-proj_max.y * 0.5 + 0.5) * height));
int far_to_x = int(Math::floor((proj_max.x * 0.5 + 0.5) * width));
int far_to_y = int(Math::floor((-proj_min.y * 0.5 + 0.5) * height));
//print_line(itos(j) + " near - " + Vector2i(near_from_x, near_from_y) + " -> " + Vector2i(near_to_x, near_to_y));
//print_line(itos(j) + " far - " + Vector2i(far_from_x, far_from_y) + " -> " + Vector2i(far_to_x, far_to_y));
int from_x = MIN(near_from_x, far_from_x);
int from_y = MIN(near_from_y, far_from_y);
int to_x = MAX(near_to_x, far_to_x);
int to_y = MAX(near_to_y, far_to_y);
if (from_x >= (int)width || to_x < 0 || from_y >= (int)height || to_y < 0) {
continue;
}
int sx = MAX(0, from_x);
int sy = MAX(0, from_y);
int dx = MIN((int)width - 1, to_x);
int dy = MIN((int)height - 1, to_y);
//print_line(itos(j) + " - " + Vector2i(sx, sy) + " -> " + Vector2i(dx, dy));
for (int x = sx; x <= dx; x++) {
for (int y = sy; y <= dy; y++) {
uint32_t offset = j * (width * height) + y * width + x;
if (unlikely(sort_id_count == sort_id_max)) {
sort_id_max = nearest_power_of_2_templated(sort_id_max + 1);
sort_ids = (SortID *)memrealloc(sort_ids, sizeof(SortID) * sort_id_max);
if (ids.size()) {
ids.resize(sort_id_max);
RD::get_singleton()->free(items_buffer);
items_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * sort_id_max);
}
}
sort_ids[sort_id_count].cell_index = offset;
sort_ids[sort_id_count].item_index = item.index;
sort_ids[sort_id_count].item_type = item.type;
sort_id_count++;
//for now, only count
cluster_data_ptr[offset].item_pointers[item.type]++;
//print_line("at offset " + itos(offset) + " value: " + itos(cluster_data_ptr[offset].item_pointers[item.type]));
}
}
}
}
/* Step 2, Assign pointers (and reset counters) */
uint32_t offset = 0;
for (uint32_t i = 0; i < (width * height * depth); i++) {
for (int j = 0; j < ITEM_TYPE_MAX; j++) {
uint32_t count = cluster_data_ptr[i].item_pointers[j]; //save count
cluster_data_ptr[i].item_pointers[j] = offset; //replace count by pointer
offset += count; //increase offset by count;
}
}
//print_line("offset: " + itos(offset));
/* Step 3, Place item lists */
uint32_t *ids_ptr = ids.ptrw();
for (uint32_t i = 0; i < sort_id_count; i++) {
const SortID &id = sort_ids[i];
Cell &cell = cluster_data_ptr[id.cell_index];
uint32_t pointer = cell.item_pointers[id.item_type] & POINTER_MASK;
uint32_t counter = cell.item_pointers[id.item_type] >> COUNTER_SHIFT;
ids_ptr[pointer + counter] = id.item_index;
cell.item_pointers[id.item_type] = pointer | ((counter + 1) << COUNTER_SHIFT);
}
RD::get_singleton()->texture_update(cluster_texture, 0, cluster_data, true);
RD::get_singleton()->buffer_update(items_buffer, 0, offset * sizeof(uint32_t), ids_ptr, true);
}
void LightClusterBuilder::setup(uint32_t p_width, uint32_t p_height, uint32_t p_depth) {
if (width == p_width && height == p_height && depth == p_depth) {
return;
}
if (cluster_texture.is_valid()) {
RD::get_singleton()->free(cluster_texture);
}
width = p_width;
height = p_height;
depth = p_depth;
cluster_data.resize(width * height * depth * sizeof(Cell));
{
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R32G32B32A32_UINT;
tf.type = RD::TEXTURE_TYPE_3D;
tf.width = width;
tf.height = height;
tf.depth = depth;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
cluster_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
}
}
RID LightClusterBuilder::get_cluster_texture() const {
return cluster_texture;
}
RID LightClusterBuilder::get_cluster_indices_buffer() const {
return items_buffer;
}
LightClusterBuilder::LightClusterBuilder() {
//initialize accumulators to something
lights = (LightData *)memalloc(sizeof(LightData) * 1024);
light_max = 1024;
refprobes = (OrientedBoxData *)memalloc(sizeof(OrientedBoxData) * 1024);
refprobe_max = 1024;
decals = (OrientedBoxData *)memalloc(sizeof(OrientedBoxData) * 1024);
decal_max = 1024;
items = (Item *)memalloc(sizeof(Item) * 1024);
item_max = 1024;
sort_ids = (SortID *)memalloc(sizeof(SortID) * 1024);
ids.resize(2014);
items_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 1024);
item_max = 1024;
}
LightClusterBuilder::~LightClusterBuilder() {
if (cluster_data.size()) {
RD::get_singleton()->free(cluster_texture);
}
if (lights) {
memfree(lights);
}
if (refprobes) {
memfree(refprobes);
}
if (decals) {
memfree(decals);
}
if (items) {
memfree(items);
}
if (sort_ids) {
memfree(sort_ids);
RD::get_singleton()->free(items_buffer);
}
}