/*************************************************************************/ /* 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); } }