summaryrefslogtreecommitdiff
path: root/servers/visual/rasterizer_rd/light_cluster_builder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'servers/visual/rasterizer_rd/light_cluster_builder.cpp')
-rw-r--r--servers/visual/rasterizer_rd/light_cluster_builder.cpp230
1 files changed, 230 insertions, 0 deletions
diff --git a/servers/visual/rasterizer_rd/light_cluster_builder.cpp b/servers/visual/rasterizer_rd/light_cluster_builder.cpp
new file mode 100644
index 0000000000..5236c5d1f2
--- /dev/null
+++ b/servers/visual/rasterizer_rd/light_cluster_builder.cpp
@@ -0,0 +1,230 @@
+#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;
+ item_count = 0;
+ sort_id_count = 0;
+}
+
+void LightClusterBuilder::bake_cluster() {
+
+ float slice_depth = (z_near - z_far) / depth;
+
+ PoolVector<uint8_t>::Write cluster_dataw = cluster_data.write();
+ Cell *cluster_data_ptr = (Cell *)cluster_dataw.ptr();
+ //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(width - 1, to_x);
+ int dy = MIN(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 */
+
+ PoolVector<uint32_t>::Write idsw = ids.write();
+ uint32_t *ids_ptr = idsw.ptr();
+
+ 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);
+ }
+
+ cluster_dataw = PoolVector<uint8_t>::Write();
+
+ 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);
+
+ idsw = PoolVector<uint32_t>::Write();
+}
+
+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);
+ }
+}