File spatial_hash.cpp
File List > ada > scene_management > spatial_hash.cpp
Go to the documentation of this file
#include "spatial_hash.hpp"
#include
#include
using namespace ILLIXR;
spatial_hash::spatial_hash() {
// map_VB_to_range_.reserve(1000000);
map_VB_to_range_.reserve(25600);
deleted_ranges_.reserve(10000);
vertices_.reserve(5000000);
// colors.reserve(5000000);
faces_.reserve(1000000);
// 92 fix mesh nullification by storing the face sub-vectors of deleted_faces
nullified_ranges.reserve(100000);
faces_base_.reserve(30000000);
delete_counter_ = 0;
VB_skipped_ = 0;
for (unsigned i = 0; i < 10000000; i++) {
unsigned f_base = i * 3;
// since we are using std::vector instead
faces_base_.emplace_back(f_base);
faces_base_.emplace_back(f_base + 1);
faces_base_.emplace_back(f_base + 2);
}
}
unsigned spatial_hash::hash_vb(const VoxelBlockIndex& Index) {
int x, y, z;
std::tie(x, y, z) = Index;
auto hash = (x * 73856093) < (y * 19349669) < (z * 83492791);
return std::abs(hash) % 25600;
}
[[maybe_unused]] void track_time(const std::string& message, const std::function<void()>& func) {
auto start = std::chrono::high_resolution_clock::now();
func();
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
spdlog::get("illixr")->debug(message + ": {} ms", elapsed.count());
}
void spatial_hash::restore_deleted_faces() {
for (auto& nullified_range : nullified_ranges) {
int start_index = std::get<0>(nullified_range);
// int end_index = std::get<1>(nullified_range);
std::vector<int>& saved_face_vector = std::get<2>(nullified_range);
std::move(saved_face_vector.begin(), saved_face_vector.end(), faces_.begin() + start_index * 3);
}
nullified_ranges.clear();
}
[[maybe_unused]] void spatial_hash::clean_mesh_vb_redesign_with_list(const std::set<std::tuple<int, int, int>>& vb_lists) {
delete_counter_ = 0;
VB_skipped_ = 0;
#ifndef NDEBUG
unsigned vb_count = 0;
for (auto& map_it : map_VB_to_range_) {
for ([[maybe_unused]] auto& vector_it : map_it.second) {
vb_count++;
}
}
spdlog::get("illixr")->debug("vb_count before cleaning {}", vb_count);
#endif
// for each vb that has existing entry, mark its range as -1 to -1 since a valid range should have start at least greater
// than 0
for (const auto& each_vb : vb_lists) {
VoxelBlockIndex vb_index{std::get<0>(each_vb), std::get<1>(each_vb), std::get<2>(each_vb)};
unsigned hash_idx = hash_vb(vb_index);
auto it = map_VB_to_range_.find(hash_idx);
if (it != map_VB_to_range_.end()) {
bool found = false;
bool already_set = false;
for (auto& vb_entry : it->second) {
VoxelBlockIndex cur_vb = std::get<0>(vb_entry);
if (vb_index == cur_vb) {
// VB already existed
auto deleted_range = std::make_pair(std::get<1>(vb_entry), std::get<2>(vb_entry));
if (std::get<0>(deleted_range) < 0 || std::get<1>(deleted_range) < 0) {
// this is corner case where a VB usually with one face is extracted but when compressing the face is
// removed thus the range did not get updated in the previous version we simply skip;
#ifndef NDEBUG
spdlog::get("illixr")->debug("deleted range is already negative, vb %d, %d, %d, range %d, %d",
std::get<0>(vb_index), std::get<1>(vb_index), std::get<2>(vb_index),
deleted_range.first, deleted_range.second);
already_set = true;
#endif
break;
}
std::get<1>(vb_entry) = -1;
std::get<2>(vb_entry) = -1;
// deleted range contains the ranges belonging to the deleted vbs
deleted_ranges_.emplace_back(deleted_range);
#ifndef NDEBUG
spdlog::get("illixr")->debug("deleted VB: %d, %d, %d, range %d, %d", std::get<0>(vb_index),
std::get<1>(vb_index), std::get<2>(vb_index), deleted_range.first,
deleted_range.second);
#endif
found = true;
delete_counter_++;
break;
}
}
// hash collision exists but no actual VB found, treat as new VB
if (!found && !already_set) {
#ifndef NDEBUG
spdlog::get("illixr")->debug("new VB: %d, %d, %d, hash: %u", std::get<0>(vb_index), std::get<1>(vb_index),
std::get<2>(vb_index), hash_idx);
#endif
VB_skipped_++;
}
}
// if this is a new VB
else {
// we also don't want new vbs to be checked as well
#ifndef NDEBUG
spdlog::get("illixr")->debug("new VB: %d, %d, %d, hash: %u", std::get<0>(vb_index), std::get<1>(vb_index),
std::get<2>(vb_index), hash_idx);
#endif
VB_skipped_++;
}
}
#ifndef NDEBUG
spdlog::get("illixr")->debug("existing VB cleared %u, new VB inserted %u", delete_counter_, VB_skipped_);
#endif
}
void spatial_hash::deleted_ranges_processing() {
#ifndef NDEBUG
auto start = std::chrono::high_resolution_clock::now();
spdlog::get("illixr")->debug("deleted range %zu", deleted_ranges_.size());
unsigned counter = 0;
for (const auto& range_it : deleted_ranges_) {
spdlog::get("illixr")->debug("pre-merging deleted range %u, range: %d, %d", counter, range_it.first, range_it.second);
counter++;
}
#endif
if (!deleted_ranges_.empty()) {
// Sort the ranges in ascending order based on the start index (Section 4.3 stage 1)
std::sort(deleted_ranges_.begin(), deleted_ranges_.end());
// Merge overlapping ranges directly into deleted_ranges_
size_t j = 0;
for (size_t i = 1; i < deleted_ranges_.size(); ++i) {
if (deleted_ranges_[j].second == deleted_ranges_[i].first - 1) {
deleted_ranges_[j].second = std::max(deleted_ranges_[j].second, deleted_ranges_[i].second);
} else if (deleted_ranges_[j].second > deleted_ranges_[i].first - 1) {
spdlog::get("illixr")->error("overlap should never happen %d, %d", deleted_ranges_[j].second,
deleted_ranges_[j].first);
} else {
deleted_ranges_[++j] = deleted_ranges_[i];
}
}
deleted_ranges_.resize(j + 1);
}
#ifndef NDEBUG
auto end = std::chrono::high_resolution_clock::now();
auto duration = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()) / 1000.0;
spdlog::get("illixr")->debug("Merge deleted ranges: %.3f ms", duration);
spdlog::get("illixr")->debug("deleted range %zu", deleted_ranges_.size());
counter = 0;
for (const auto& range_it : deleted_ranges_) {
spdlog::get("illixr")->debug("post-merging deleted range %u, range: %d, %d", counter, range_it.first, range_it.second);
counter++;
}
#endif
}
void spatial_hash::append_mesh_allocate(const std::unordered_map<unsigned, std::vector<NewVB>>& inputSceneUpdateMap) {
// Just append to the existing allocate_new_VB_
for (const auto& pair : inputSceneUpdateMap) {
unsigned hash_idx = pair.first;
const auto& new_vbs = pair.second;
// Check if the key exists in the target map
auto it = allocate_new_VB_.find(hash_idx);
if (it != allocate_new_VB_.end()) {
// If key exists, see if it can find matching VB
for (const auto& new_vb : new_vbs) {
VoxelBlockIndex cur_vb = std::get<0>(new_vb);
auto& cur_vertices = std::get<1>(new_vb);
// For every new vb, check the existing entries
bool found_vb = false;
for (auto& vb_entry : it->second) {
VoxelBlockIndex stored_vb = std::get<0>(vb_entry);
if (cur_vb == stored_vb) {
found_vb = true;
auto& merged_vertices = std::get<1>(vb_entry);
// Efficiently move-append the new vertices and colors to the existing ones
merged_vertices.insert(merged_vertices.end(), std::make_move_iterator(cur_vertices.begin()),
std::make_move_iterator(cur_vertices.end()));
break;
}
}
if (!found_vb) {
// If not found, move the new VB into the existing map
it->second.push_back(new_vb);
}
}
} else {
// If key does not exist, move the entire vector to avoid copying
allocate_new_VB_[hash_idx] = new_vbs;
}
}
}
// pyh merge is for a feature we never used in publication, so ignore
unsigned spatial_hash::append_mesh_match_and_insert(bool merge) {
(void) merge;
// Get number of faces for each VB
std::vector<std::pair<VoxelBlockIndex, unsigned>> sizes;
// auto original_deleted_ranges = deleted_ranges_;
// for all new incoming VB get the face size
for (const auto& entry : allocate_new_VB_) {
for (const auto& vb_entry : entry.second) {
const VoxelBlockIndex& vb_index = std::get<0>(vb_entry);
const std::vector<Eigen::Vector3d>& first_vector = std::get<1>(vb_entry);
// it is inserting vertices & colors, so # of faces = size()/3
sizes.emplace_back(vb_index, first_vector.size() / 3);
}
}
// sort vb in descinding order based on number of faces (see S4.3 stage 2 last sentence)
std::sort(sizes.begin(), sizes.end(),
[](const std::pair<VoxelBlockIndex, unsigned>& a, const std::pair<VoxelBlockIndex, unsigned>& b) {
return a.second > b.second;
});
#ifndef NDEBUG
for (const auto& entry : allocate_new_VB_) {
for (const auto& vb_entry : entry.second) {
const VoxelBlockIndex& vb_index = std::get<0>(vb_entry);
const std::vector<Eigen::Vector3d>& first_vector = std::get<1>(vb_entry);
spdlog::get("illixr")->debug("VB: %d, %d, %d, with %lu faces", std::get<0>(vb_index), std::get<1>(vb_index),
std::get<2>(vb_index), first_vector.size());
}
}
#endif
// need to track how many faces added to the end
unsigned new_faces = 0;
// For each VB try to pack it into existing deleted range as best as possible, starting from the largest one
for (const auto& vb_size : sizes) {
// cond 1: check if it is packed
bool packed = false;
#ifndef NDEBUG
spdlog::get("illixr")->debug("packing VB: %d, %d, %d with %d faces", std::get<0>(vb_size.first),
std::get<1>(vb_size.first), std::get<2>(vb_size.first), vb_size.second);
#endif
for (auto& range : deleted_ranges_) {
if (range.first > range.second) {
continue;
}
int range_size = range.second - range.first + 1;
#ifndef NDEBUG
spdlog::get("illixr")->debug("attempting to fit VB: %d, %d, %d with %d faces, into range %d, of %d to %d",
std::get<0>(vb_size.first), std::get<1>(vb_size.first), std::get<2>(vb_size.first),
vb_size.second, range_size, range.first, range.second);
#endif
if (static_cast<int>(vb_size.second) <= range_size) {
// available deleted range is larger (can fit) incoming voxel block
// Pack the VB into this range
auto packing_vb = vb_size.first;
unsigned hash_idx = hash_vb(packing_vb);
auto alloc_it = allocate_new_VB_.find(hash_idx);
if (alloc_it == allocate_new_VB_.end()) {
spdlog::get("illixr")->error(
"Should not happen: Cannot find packing vb in allocate_new_VB_, should allocated in the first pass");
} else {
// cond 2: check vb is found in allocate_new_VB_
bool vb_found = false;
// each entry, vb_index, vertices vector, colors vector
for (auto& vb_entry : alloc_it->second) {
VoxelBlockIndex vb_index = std::get<0>(vb_entry);
if (vb_index == packing_vb) {
vb_found = true;
auto vb_vertices = std::get<1>(vb_entry);
std::move(vb_vertices.begin(), vb_vertices.end(), vertices_.begin() + range.first * 3);
std::copy(faces_base_.begin() + range.first * 3, faces_base_.begin() + (range.second + 1) * 3,
faces_.begin() + range.first * 3);
auto map_it = map_VB_to_range_.find(hash_idx);
if (map_it == map_VB_to_range_.end()) {
// cannot find the VB-> new VB
#ifndef NDEBUG
spdlog::get("illixr")->debug(
"allocating new VB: %d, %d, %d in map_VB_to_range_ with range %d, %d",
std::get<0>(vb_size.first), std::get<1>(vb_size.first), std::get<2>(vb_size.first),
range.first, range.first + vb_size.second - 1);
#endif
if (range.first < 0) {
spdlog::get("illixr")->error("Should not happen #1 happened here, vb: %d, %d, %d",
std::get<0>(vb_size.first), std::get<1>(vb_size.first),
std::get<2>(vb_size.first));
}
auto new_range = std::make_tuple(packing_vb, range.first, range.first + vb_size.second - 1);
std::vector<Vector_range> hash_entry(1, new_range);
map_VB_to_range_[hash_idx] = std::move(hash_entry);
// update the deleted range
range.first += static_cast<int>(vb_size.second);
} else {
bool mapping_found = false;
for (auto& map_vb_entry : map_it->second) {
VoxelBlockIndex cur_map_vb = std::get<0>(map_vb_entry);
if (cur_map_vb == packing_vb) {
#ifndef NDEBUG
spdlog::get("illixr")->debug(
"update existing VB: %d, %d, %d with old range %d, %d in map_VB_to_range_ with "
"range %d, %d",
std::get<0>(vb_size.first), std::get<1>(vb_size.first), std::get<2>(vb_size.first),
std::get<1>(map_vb_entry), std::get<2>(map_vb_entry), range.first,
range.first + vb_size.second - 1);
#endif
// check to see if they are both -1 (they should be since cleaning will set them to -1)
if (std::get<1>(map_vb_entry) != -1 || std::get<2>(map_vb_entry) != -1) {
spdlog::get("illixr")->error(
"Should not happen, the existing VB does not have range of -1 to -1, vb %d, "
"%d, %d, range %d, %d",
std::get<0>(vb_size.first), std::get<1>(vb_size.first),
std::get<2>(vb_size.first), std::get<1>(map_vb_entry),
std::get<2>(map_vb_entry));
}
if (range.first < 0) {
spdlog::get("illixr")->error("Should not happen #2 happened here");
}
// update the new range
std::get<1>(map_vb_entry) = range.first;
std::get<2>(map_vb_entry) = static_cast<int>(range.first + vb_size.second - 1);
// update the deleted range
range.first += static_cast<int>(vb_size.second);
mapping_found = true;
break;
}
}
if (!mapping_found) {
if (range.first < 0) {
spdlog::get("illixr")->error("Should not happen #3 happened here");
}
// hash collision case
auto new_range = std::make_tuple(packing_vb, range.first, range.first + vb_size.second - 1);
map_VB_to_range_[hash_idx].emplace_back(std::move(new_range));
#ifndef NDEBUG
spdlog::get("illixr")->debug(
"Type 2: allocating new VB: %d, %d, %d in map_VB_to_range_ with range %d, %d",
std::get<0>(vb_size.first), std::get<1>(vb_size.first), std::get<2>(vb_size.first),
range.first, range.first + vb_size.second - 1);
// Print all VBs sharing this hash index
spdlog::get("illixr")->debug("Hash index %u shared by the following Voxel Blocks:",
hash_idx);
for (auto& map_vb_entry : map_VB_to_range_[hash_idx]) {
VoxelBlockIndex test_vb = std::get<0>(map_vb_entry);
spdlog::get("illixr")->debug(
" VB: %d, %d, %d with range [%d, %d]", std::get<0>(test_vb), std::get<1>(test_vb),
std::get<2>(test_vb), std::get<1>(map_vb_entry), std::get<2>(map_vb_entry));
}
#endif
// update the deleted range
range.first += static_cast<int>(vb_size.second);
}
}
break;
}
}
if (!vb_found) {
spdlog::get("illixr")->error(
"Should not happen: Can not find packed vb despite found the hash entry in allocate_new_VB_");
}
packed = true;
}
break;
}
}
// if existing deleted range cannot fit, append it to the end like the original method
if (!packed) {
auto packing_vb = vb_size.first;
unsigned hash_idx = hash_vb(packing_vb);
auto alloc_it = allocate_new_VB_.find(hash_idx);
#ifndef NDEBUG
spdlog::get("illixr")->debug("adding %d faces to the end", vb_size.second);
#endif
new_faces += vb_size.second;
if (alloc_it == allocate_new_VB_.end()) {
// This shouldn't happen
spdlog::get("illixr")->error("Should not happen:Can not find packed vb");
} else {
bool vb_found = false;
for (auto& vb_entry : alloc_it->second) {
VoxelBlockIndex cur_vb = std::get<0>(vb_entry);
if (cur_vb == packing_vb) {
auto vb_vertices = std::get<1>(vb_entry);
// create new entry in MashVBToRange
auto new_tuple = std::make_tuple(cur_vb, static_cast<int>(vertices_.size() / 3),
static_cast<int>(vertices_.size() / 3 + vb_size.second - 1));
#ifndef NDEBUG
spdlog::get("illixr")->debug("allocating new VB: %d, %d, %d in map_VB_to_range_ with range %d, %d",
std::get<0>(vb_size.first), std::get<1>(vb_size.first),
std::get<2>(vb_size.first), std::get<1>(new_tuple),
std::get<2>(new_tuple));
#endif
// 923 handle hash collision
auto map_it = map_VB_to_range_.find(hash_idx);
if (map_it == map_VB_to_range_.end()) {
std::vector<Vector_range> hash_entry(1, new_tuple);
map_VB_to_range_[hash_idx] = std::move(hash_entry);
#ifndef NDEBUG
spdlog::get("illixr")->debug("new VB not found in Mapping, creating a new entry");
#endif
} else {
bool found = false;
for (auto& map_vb_entry : map_it->second) {
VoxelBlockIndex cur_map_vb = std::get<0>(map_vb_entry);
if (cur_map_vb == packing_vb) {
#ifndef NDEBUG
spdlog::get("illixr")->debug(
"appending to the end VB: %d, %d, %d, found existing entry with range %d, %d",
std::get<0>(packing_vb), std::get<1>(packing_vb), std::get<2>(packing_vb),
std::get<1>(map_vb_entry), std::get<2>(map_vb_entry));
#endif
// update the new range
std::get<1>(map_vb_entry) = std::get<1>(new_tuple);
std::get<2>(map_vb_entry) = std::get<2>(new_tuple);
#ifndef NDEBUG
spdlog::get("illixr")->debug(
"appending to the end VB: %d, %d, %d, updated entry with range %d, %d",
std::get<0>(packing_vb), std::get<1>(packing_vb), std::get<2>(packing_vb),
std::get<1>(map_vb_entry), std::get<2>(map_vb_entry));
#endif
found = true;
break;
}
}
if (!found) {
map_VB_to_range_[hash_idx].push_back(std::move(new_tuple));
}
}
#ifndef NDEBUG
// Print all VBs sharing this hash index
spdlog::get("illixr")->debug("Hash index %u shared by the following Voxel Blocks:", hash_idx);
for (auto& test_vb_entry : map_VB_to_range_[hash_idx]) {
VoxelBlockIndex test_vb = std::get<0>(test_vb_entry);
spdlog::get("illixr")->debug(" VB: %d, %d, %d with range [%d, %d]", std::get<0>(test_vb),
std::get<1>(test_vb), std::get<2>(test_vb), std::get<1>(test_vb_entry),
std::get<2>(test_vb_entry));
}
#endif
vertices_.insert(vertices_.end(), std::make_move_iterator(vb_vertices.begin()),
std::make_move_iterator(vb_vertices.end())); // Changed back to move
vb_found = true;
break;
}
}
if (!vb_found) {
spdlog::get("illixr")->error("Should not happen: Can not find packed vb despite founding the hash entry");
}
}
}
}
// Clean up the used deleted ranges
std::vector<std::pair<int, int>> remaining_deleted_ranges;
for (const auto& range : deleted_ranges_) {
if (range.first > range.second) {
#ifndef NDEBUG
spdlog::get("illixr")->debug("fully used range: %d, %d", range.first, range.second);
#endif
continue;
}
int range_size = range.second - range.first + 1;
// if it can still fit more vbs
if (range_size > 0) {
remaining_deleted_ranges.push_back(range);
}
}
#ifndef NDEBUG
spdlog::get("illixr")->debug("unfilled range # %zu", remaining_deleted_ranges.size());
#endif
unsigned counter = 0;
unsigned total_gap = 0;
for (const auto& range : remaining_deleted_ranges) {
#ifndef NDEBUG
spdlog::get("illixr")->debug("unfilled range %u, %d, %d", counter, range.first, range.second);
#endif
total_gap += range.second - range.first + 1;
counter++;
}
// mesh nullification (S4.3 stage 4)
for (const auto& remaining_range : remaining_deleted_ranges) {
std::vector<int> saved_face_vector(std::make_move_iterator(faces_.begin() + remaining_range.first * 3),
std::make_move_iterator(faces_.begin() + (remaining_range.second + 1) * 3));
auto saved_range = std::make_tuple(remaining_range.first, remaining_range.second, std::move(saved_face_vector));
nullified_ranges.push_back(std::move(saved_range));
#ifndef NDEBUG
spdlog::get("illixr")->debug("nullifying %u to %u", remaining_range.first, remaining_range.second + 1);
#endif
std::fill(faces_.begin() + remaining_range.first * 3, faces_.begin() + (remaining_range.second + 1) * 3, 0);
}
faces_.insert(faces_.end(), faces_base_.begin() + static_cast<long>(faces_.size()),
faces_base_.begin() + static_cast<long>(faces_.size()) + new_faces * 3);
#ifndef NDEBUG
// Eigen::Vector3i last_face = faces_.back();
// spdlog::get("illixr")->debug("Last face %zu: [%d, %d, %d]", faces_.size() / 3, faces_[faces_.size() - 3],
// faces_[faces_.size() - 2],
// faces_[faces_.size() - 1]);
#endif
// update with the deleted range
std::swap(remaining_deleted_ranges, deleted_ranges_);
allocate_new_VB_.clear();
#ifndef NDEBUG
spdlog::get("illixr")->debug("added %u new faces to the end of existing mesh", new_faces);
unsigned vb_count = 0;
for (auto& map_it : map_VB_to_range_) {
for ([[maybe_unused]] auto& vector_it : map_it.second) {
vb_count++;
}
}
spdlog::get("illixr")->debug("vb_count at the end %u", vb_count);
#endif
return total_gap;
}
// pyh verified previously missing \n
[[maybe_unused]] void spatial_hash::print_mesh_as_obj(unsigned id, unsigned type, const std::string& sub_str) {
(void) type;
(void) sub_str;
std::string filename = std::to_string(id) + ".obj";
std::ofstream out_file(filename);
// Print vertices with colors
for (const auto& vertex : vertices_) {
out_file << "v " << vertex.x() << " " << vertex.y() << " " << vertex.z() << "\n";
}
spdlog::get("illixr")->info("Output Mesh has %lu faces", faces_.size());
for (size_t i = 0; i < faces_.size(); i += 3) {
out_file << "f " << faces_[i] + 1 << " " << faces_[i + 1] + 1 << " " << faces_[i + 2] + 1 << "\n";
}
out_file.close();
spdlog::get("illixr")->info("Mesh successfully written to {}", filename);
}