2019-08-17 11:56:21 -07:00
|
|
|
#include "voxel_stream_region_files.h"
|
2021-01-31 08:51:42 -08:00
|
|
|
#include "../../server/voxel_server.h"
|
|
|
|
#include "../../util/macros.h"
|
2021-05-31 09:10:54 -07:00
|
|
|
#include "../../util/math/box3i.h"
|
2021-01-31 08:51:42 -08:00
|
|
|
#include "../../util/profiling.h"
|
2021-02-01 14:25:25 -08:00
|
|
|
|
2019-08-14 12:34:06 -07:00
|
|
|
#include <core/io/json.h>
|
2021-12-29 09:06:24 -08:00
|
|
|
#include <core/os/time.h>
|
2019-08-14 12:34:06 -07:00
|
|
|
#include <algorithm>
|
|
|
|
|
2022-01-09 14:13:10 -08:00
|
|
|
namespace zylann::voxel {
|
2022-01-03 15:14:18 -08:00
|
|
|
|
2019-08-14 12:34:06 -07:00
|
|
|
namespace {
|
2020-09-19 13:50:23 -07:00
|
|
|
const uint8_t FORMAT_VERSION = 3;
|
|
|
|
|
|
|
|
// Version 2 is the same as version 3, except region files use version 3 of their specification.
|
|
|
|
const uint8_t FORMAT_VERSION_LEGACY_2 = 2;
|
|
|
|
|
2020-01-10 10:56:57 -08:00
|
|
|
const uint8_t FORMAT_VERSION_LEGACY_1 = 1;
|
2019-08-14 12:34:06 -07:00
|
|
|
const char *META_FILE_NAME = "meta.vxrm";
|
2021-09-25 20:14:50 -07:00
|
|
|
|
2019-08-14 12:34:06 -07:00
|
|
|
} // namespace
|
|
|
|
|
2021-09-25 20:14:50 -07:00
|
|
|
// Sorts a sequence without modifying it, returning a sorted list of pointers
|
|
|
|
template <typename T, typename Comparer_T>
|
2022-02-06 13:26:48 -08:00
|
|
|
void get_sorted_indices(Span<T> sequence, Comparer_T comparer, std::vector<unsigned int> &out_sorted_indices) {
|
|
|
|
struct Compare {
|
2021-09-25 20:14:50 -07:00
|
|
|
Span<T> sequence;
|
|
|
|
Comparer_T comparer;
|
2022-02-06 13:26:48 -08:00
|
|
|
inline bool operator()(unsigned int ia, unsigned int ib) const {
|
|
|
|
return comparer(sequence[ia], sequence[ib]);
|
2021-09-25 20:14:50 -07:00
|
|
|
}
|
|
|
|
};
|
2022-02-06 13:26:48 -08:00
|
|
|
out_sorted_indices.resize(sequence.size());
|
2021-09-25 20:14:50 -07:00
|
|
|
for (unsigned int i = 0; i < sequence.size(); ++i) {
|
2022-02-06 13:26:48 -08:00
|
|
|
out_sorted_indices[i] = i;
|
2021-09-25 20:14:50 -07:00
|
|
|
}
|
2022-02-06 13:26:48 -08:00
|
|
|
SortArray<unsigned int, Compare> sort_array;
|
2021-09-25 20:14:50 -07:00
|
|
|
sort_array.compare.sequence = sequence;
|
|
|
|
sort_array.compare.comparer = comparer;
|
2022-02-06 13:26:48 -08:00
|
|
|
sort_array.sort(out_sorted_indices.data(), out_sorted_indices.size());
|
2021-09-25 20:14:50 -07:00
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
VoxelStreamRegionFiles::VoxelStreamRegionFiles() {
|
2019-08-14 12:34:06 -07:00
|
|
|
_meta.version = FORMAT_VERSION;
|
2019-08-23 17:44:27 -07:00
|
|
|
_meta.block_size_po2 = 4;
|
|
|
|
_meta.region_size_po2 = 4;
|
2019-08-14 12:34:06 -07:00
|
|
|
_meta.sector_size = 512; // next_power_of_2(_meta.block_size.volume() / 10) // based on compression ratios
|
|
|
|
_meta.lod_count = 1;
|
2021-09-25 20:14:50 -07:00
|
|
|
_meta.channel_depths.fill(VoxelBufferInternal::DEFAULT_CHANNEL_DEPTH);
|
|
|
|
_meta.channel_depths[VoxelBufferInternal::CHANNEL_TYPE] = VoxelBufferInternal::DEFAULT_TYPE_CHANNEL_DEPTH;
|
|
|
|
_meta.channel_depths[VoxelBufferInternal::CHANNEL_SDF] = VoxelBufferInternal::DEFAULT_SDF_CHANNEL_DEPTH;
|
|
|
|
_meta.channel_depths[VoxelBufferInternal::CHANNEL_INDICES] = VoxelBufferInternal::DEFAULT_INDICES_CHANNEL_DEPTH;
|
|
|
|
_meta.channel_depths[VoxelBufferInternal::CHANNEL_WEIGHTS] = VoxelBufferInternal::DEFAULT_WEIGHTS_CHANNEL_DEPTH;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
VoxelStreamRegionFiles::~VoxelStreamRegionFiles() {
|
2019-08-19 15:17:23 -07:00
|
|
|
close_all_regions();
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2022-01-15 20:21:02 -08:00
|
|
|
VoxelStream::Result VoxelStreamRegionFiles::load_voxel_block(
|
2021-12-13 13:38:10 -08:00
|
|
|
VoxelBufferInternal &out_buffer, Vector3i origin_in_voxels, int lod) {
|
2021-09-25 20:14:50 -07:00
|
|
|
VoxelBlockRequest r{ out_buffer, origin_in_voxels, lod };
|
2022-02-06 13:26:48 -08:00
|
|
|
Result result;
|
|
|
|
load_voxel_blocks(Span<VoxelBlockRequest>(&r, 1), Span<Result>(&result, 1));
|
|
|
|
return result;
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
2022-01-15 20:21:02 -08:00
|
|
|
void VoxelStreamRegionFiles::save_voxel_block(VoxelBufferInternal &buffer, Vector3i origin_in_voxels, int lod) {
|
2021-09-25 20:14:50 -07:00
|
|
|
VoxelBlockRequest r{ buffer, origin_in_voxels, lod };
|
2022-01-15 20:21:02 -08:00
|
|
|
save_voxel_blocks(Span<VoxelBlockRequest>(&r, 1));
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
2022-02-06 13:26:48 -08:00
|
|
|
void VoxelStreamRegionFiles::load_voxel_blocks(Span<VoxelBlockRequest> p_blocks, Span<Result> out_results) {
|
2020-08-29 15:20:51 -07:00
|
|
|
VOXEL_PROFILE_SCOPE();
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2019-08-16 12:56:07 -07:00
|
|
|
// In order to minimize opening/closing files, requests are grouped according to their region.
|
|
|
|
|
|
|
|
// Had to copy input to sort it, as some areas in the module break if they get responses in different order
|
2022-02-06 13:26:48 -08:00
|
|
|
std::vector<unsigned int> sorted_block_indices;
|
2021-09-25 20:14:50 -07:00
|
|
|
BlockRequestComparator comparator;
|
|
|
|
comparator.self = this;
|
2022-02-06 13:26:48 -08:00
|
|
|
get_sorted_indices(p_blocks, comparator, sorted_block_indices);
|
2019-08-16 12:56:07 -07:00
|
|
|
|
2022-02-06 13:26:48 -08:00
|
|
|
for (unsigned int i = 0; i < sorted_block_indices.size(); ++i) {
|
|
|
|
const unsigned int bi = sorted_block_indices[i];
|
|
|
|
VoxelBlockRequest &r = p_blocks[bi];
|
2022-01-15 20:21:02 -08:00
|
|
|
const EmergeResult result = _load_block(r.voxel_buffer, r.origin_in_voxels, r.lod);
|
2021-01-17 09:18:05 -08:00
|
|
|
switch (result) {
|
|
|
|
case EMERGE_OK:
|
2022-02-06 13:26:48 -08:00
|
|
|
out_results[bi] = RESULT_BLOCK_FOUND;
|
2021-01-17 09:18:05 -08:00
|
|
|
break;
|
|
|
|
case EMERGE_OK_FALLBACK:
|
2022-02-06 13:26:48 -08:00
|
|
|
out_results[bi] = RESULT_BLOCK_NOT_FOUND;
|
2021-01-17 09:18:05 -08:00
|
|
|
break;
|
|
|
|
case EMERGE_FAILED:
|
2022-02-06 13:26:48 -08:00
|
|
|
out_results[bi] = RESULT_ERROR;
|
2021-01-17 09:18:05 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
CRASH_NOW();
|
|
|
|
break;
|
2019-08-16 12:56:07 -07:00
|
|
|
}
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-15 20:21:02 -08:00
|
|
|
void VoxelStreamRegionFiles::save_voxel_blocks(Span<VoxelBlockRequest> p_blocks) {
|
2020-08-29 15:20:51 -07:00
|
|
|
VOXEL_PROFILE_SCOPE();
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2019-08-16 12:56:07 -07:00
|
|
|
// Had to copy input to sort it, as some areas in the module break if they get responses in different order
|
2022-02-06 13:26:48 -08:00
|
|
|
std::vector<unsigned int> sorted_block_indices;
|
2021-09-25 20:14:50 -07:00
|
|
|
BlockRequestComparator comparator;
|
|
|
|
comparator.self = this;
|
2022-02-06 13:26:48 -08:00
|
|
|
get_sorted_indices(p_blocks, comparator, sorted_block_indices);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2022-02-06 13:26:48 -08:00
|
|
|
for (unsigned int i = 0; i < sorted_block_indices.size(); ++i) {
|
|
|
|
const unsigned int bi = sorted_block_indices[i];
|
|
|
|
VoxelBlockRequest &r = p_blocks[bi];
|
2022-01-15 20:21:02 -08:00
|
|
|
_save_block(r.voxel_buffer, r.origin_in_voxels, r.lod);
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 10:51:19 -07:00
|
|
|
int VoxelStreamRegionFiles::get_used_channels_mask() const {
|
|
|
|
// Assuming all, since that stream can store anything.
|
2021-09-25 20:14:50 -07:00
|
|
|
return VoxelBufferInternal::ALL_CHANNELS_MASK;
|
2021-06-14 10:51:19 -07:00
|
|
|
}
|
|
|
|
|
2022-01-15 20:21:02 -08:00
|
|
|
VoxelStreamRegionFiles::EmergeResult VoxelStreamRegionFiles::_load_block(
|
2021-09-25 20:14:50 -07:00
|
|
|
VoxelBufferInternal &out_buffer, Vector3i origin_in_voxels, int lod) {
|
2020-08-29 15:20:51 -07:00
|
|
|
VOXEL_PROFILE_SCOPE();
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
if (_directory_path.is_empty()) {
|
2019-08-16 12:56:07 -07:00
|
|
|
return EMERGE_OK_FALLBACK;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!_meta_loaded) {
|
2022-01-08 18:21:49 -08:00
|
|
|
const FileResult load_res = load_meta();
|
|
|
|
if (load_res != FILE_OK) {
|
2021-08-01 07:42:24 -07:00
|
|
|
// No block was ever saved
|
|
|
|
return EMERGE_OK_FALLBACK;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
const Vector3i block_size = Vector3iUtil::create(1 << _meta.block_size_po2);
|
|
|
|
const Vector3i region_size = Vector3iUtil::create(1 << _meta.region_size_po2);
|
2019-08-23 17:44:27 -07:00
|
|
|
|
2019-08-14 12:34:06 -07:00
|
|
|
CRASH_COND(!_meta_loaded);
|
2019-08-16 12:56:07 -07:00
|
|
|
ERR_FAIL_COND_V(lod >= _meta.lod_count, EMERGE_FAILED);
|
2021-09-25 20:14:50 -07:00
|
|
|
ERR_FAIL_COND_V(block_size != out_buffer.get_size(), EMERGE_FAILED);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2021-08-01 07:42:24 -07:00
|
|
|
// Configure depths, as they might not be specified in old block data.
|
2020-02-09 09:07:53 -08:00
|
|
|
// Regions are expected to contain such depths, and use those in the buffer to know how much data to read.
|
|
|
|
for (unsigned int channel_index = 0; channel_index < _meta.channel_depths.size(); ++channel_index) {
|
2021-09-25 20:14:50 -07:00
|
|
|
out_buffer.set_channel_depth(channel_index, _meta.channel_depths[channel_index]);
|
2020-02-09 09:07:53 -08:00
|
|
|
}
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
const Vector3i block_pos = get_block_position_from_voxels(origin_in_voxels) >> lod;
|
|
|
|
const Vector3i region_pos = get_region_position_from_blocks(block_pos);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
CachedRegion *cache = open_region(region_pos, lod, false);
|
|
|
|
if (cache == nullptr || !cache->file_exists) {
|
2019-08-16 12:56:07 -07:00
|
|
|
return EMERGE_OK_FALLBACK;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
const Vector3i block_rpos = Vector3iUtil::wrap(block_pos, region_size);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2022-01-15 20:01:22 -08:00
|
|
|
const Error err = cache->region.load_block(block_rpos, out_buffer);
|
2020-09-19 13:50:23 -07:00
|
|
|
switch (err) {
|
|
|
|
case OK:
|
|
|
|
return EMERGE_OK;
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
case ERR_DOES_NOT_EXIST:
|
|
|
|
return EMERGE_OK_FALLBACK;
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
default:
|
|
|
|
return EMERGE_FAILED;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-15 20:21:02 -08:00
|
|
|
void VoxelStreamRegionFiles::_save_block(VoxelBufferInternal &voxel_buffer, Vector3i origin_in_voxels, int lod) {
|
2020-08-29 15:20:51 -07:00
|
|
|
VOXEL_PROFILE_SCOPE();
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND(_directory_path.is_empty());
|
2020-02-09 10:37:44 -08:00
|
|
|
|
|
|
|
if (!_meta_loaded) {
|
|
|
|
// If it's not loaded, always try to load meta file first if it exists already,
|
|
|
|
// because we could want to save blocks without reading any
|
2022-01-08 18:21:49 -08:00
|
|
|
FileResult load_res = load_meta();
|
|
|
|
if (load_res != FILE_OK && load_res != FILE_CANT_OPEN) {
|
2020-02-09 10:37:44 -08:00
|
|
|
// The file is present but there is a problem with it
|
|
|
|
String meta_path = _directory_path.plus_file(META_FILE_NAME);
|
2022-01-09 14:13:10 -08:00
|
|
|
ERR_PRINT(String("Could not read {0}: error {1}").format(varray(meta_path, zylann::to_string(load_res))));
|
2020-02-09 10:37:44 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
if (!_meta_saved) {
|
2020-02-09 10:37:44 -08:00
|
|
|
// First time we save the meta file, initialize it from the first block format
|
|
|
|
for (unsigned int i = 0; i < _meta.channel_depths.size(); ++i) {
|
2021-09-25 20:14:50 -07:00
|
|
|
_meta.channel_depths[i] = voxel_buffer.get_channel_depth(i);
|
2020-02-09 10:37:44 -08:00
|
|
|
}
|
2022-01-08 18:21:49 -08:00
|
|
|
FileResult err = save_meta();
|
|
|
|
ERR_FAIL_COND(err != FILE_OK);
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2020-01-10 10:56:57 -08:00
|
|
|
// Verify format
|
2021-12-13 13:38:10 -08:00
|
|
|
const Vector3i block_size = Vector3iUtil::create(1 << _meta.block_size_po2);
|
2021-09-25 20:14:50 -07:00
|
|
|
ERR_FAIL_COND(voxel_buffer.get_size() != block_size);
|
|
|
|
for (unsigned int i = 0; i < VoxelBufferInternal::MAX_CHANNELS; ++i) {
|
|
|
|
ERR_FAIL_COND(voxel_buffer.get_channel_depth(i) != _meta.channel_depths[i]);
|
2020-01-10 10:56:57 -08:00
|
|
|
}
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
const Vector3i region_size = Vector3iUtil::create(1 << _meta.region_size_po2);
|
2019-08-14 12:34:06 -07:00
|
|
|
Vector3i block_pos = get_block_position_from_voxels(origin_in_voxels) >> lod;
|
|
|
|
Vector3i region_pos = get_region_position_from_blocks(block_pos);
|
2021-12-13 13:38:10 -08:00
|
|
|
Vector3i block_rpos = Vector3iUtil::wrap(block_pos, region_size);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
CachedRegion *cache = open_region(region_pos, lod, true);
|
2020-09-19 13:50:23 -07:00
|
|
|
ERR_FAIL_COND_MSG(cache == nullptr, "Could not save region file data");
|
2022-01-15 20:01:22 -08:00
|
|
|
ERR_FAIL_COND(cache->region.save_block(block_rpos, voxel_buffer) != OK);
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
String VoxelStreamRegionFiles::get_directory() const {
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
2019-08-14 12:34:06 -07:00
|
|
|
return _directory_path;
|
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
void VoxelStreamRegionFiles::set_directory(String dirpath) {
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
2019-08-14 12:34:06 -07:00
|
|
|
if (_directory_path != dirpath) {
|
2021-01-16 05:34:45 -08:00
|
|
|
close_all_regions();
|
2019-08-14 12:34:06 -07:00
|
|
|
_directory_path = dirpath.strip_edges();
|
|
|
|
_meta_loaded = false;
|
2020-02-09 10:37:44 -08:00
|
|
|
_meta_saved = false;
|
2019-08-23 17:44:27 -07:00
|
|
|
load_meta();
|
2021-12-13 13:38:10 -08:00
|
|
|
notify_property_list_changed();
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool u8_from_json_variant(Variant v, uint8_t &i) {
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND_V(v.get_type() != Variant::INT && v.get_type() != Variant::FLOAT, false);
|
2019-08-14 12:34:06 -07:00
|
|
|
int n = v;
|
|
|
|
ERR_FAIL_COND_V(n < 0 || n > 255, false);
|
|
|
|
i = v;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-09-20 08:33:00 -07:00
|
|
|
static bool u32_from_json_variant(Variant v, uint32_t &i) {
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND_V(v.get_type() != Variant::INT && v.get_type() != Variant::FLOAT, false);
|
2020-09-20 08:33:00 -07:00
|
|
|
ERR_FAIL_COND_V(v.operator int64_t() < 0, false);
|
2019-08-14 12:34:06 -07:00
|
|
|
i = v;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-25 20:14:50 -07:00
|
|
|
static bool depth_from_json_variant(Variant &v, VoxelBufferInternal::Depth &d) {
|
2020-01-10 10:56:57 -08:00
|
|
|
uint8_t n;
|
|
|
|
ERR_FAIL_COND_V(!u8_from_json_variant(v, n), false);
|
2021-09-25 20:14:50 -07:00
|
|
|
ERR_FAIL_INDEX_V(n, VoxelBufferInternal::DEPTH_COUNT, false);
|
|
|
|
d = (VoxelBufferInternal::Depth)n;
|
2020-01-10 10:56:57 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-01-08 18:21:49 -08:00
|
|
|
FileResult VoxelStreamRegionFiles::save_meta() {
|
|
|
|
ERR_FAIL_COND_V(_directory_path == "", FILE_CANT_OPEN);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
Dictionary d;
|
|
|
|
d["version"] = _meta.version;
|
2019-08-23 17:44:27 -07:00
|
|
|
d["block_size_po2"] = _meta.block_size_po2;
|
|
|
|
d["region_size_po2"] = _meta.region_size_po2;
|
2019-08-14 12:34:06 -07:00
|
|
|
d["lod_count"] = _meta.lod_count;
|
|
|
|
d["sector_size"] = _meta.sector_size;
|
|
|
|
|
2020-01-10 10:56:57 -08:00
|
|
|
Array channel_depths;
|
|
|
|
channel_depths.resize(_meta.channel_depths.size());
|
|
|
|
for (unsigned int i = 0; i < _meta.channel_depths.size(); ++i) {
|
|
|
|
channel_depths[i] = _meta.channel_depths[i];
|
|
|
|
}
|
|
|
|
d["channel_depths"] = channel_depths;
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
JSON json;
|
|
|
|
String json_string = json.stringify(d, "\t", true);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
// Make sure the directory exists
|
|
|
|
{
|
2022-01-08 20:53:33 -08:00
|
|
|
Error err = check_directory_created_using_file_locker(_directory_path);
|
2019-08-14 12:34:06 -07:00
|
|
|
if (err != OK) {
|
|
|
|
ERR_PRINT("Could not save meta");
|
2022-01-08 18:21:49 -08:00
|
|
|
return FILE_CANT_OPEN;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
String meta_path = _directory_path.plus_file(META_FILE_NAME);
|
|
|
|
|
|
|
|
Error err;
|
2021-01-16 05:34:45 -08:00
|
|
|
VoxelFileLockerWrite file_wlock(meta_path);
|
2021-02-01 14:25:25 -08:00
|
|
|
FileAccessRef f = FileAccess::open(meta_path, FileAccess::WRITE, &err);
|
2019-08-14 12:34:06 -07:00
|
|
|
if (!f) {
|
2020-02-09 10:37:44 -08:00
|
|
|
ERR_PRINT(String("Could not save {0}").format(varray(meta_path)));
|
2022-01-08 18:21:49 -08:00
|
|
|
return FILE_CANT_OPEN;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
f->store_string(json_string);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
_meta_saved = true;
|
|
|
|
_meta_loaded = true;
|
|
|
|
|
2022-01-08 18:21:49 -08:00
|
|
|
return FILE_OK;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2020-01-10 10:56:57 -08:00
|
|
|
static void migrate_region_meta_data(Dictionary &data) {
|
2020-02-09 09:07:53 -08:00
|
|
|
if (data["version"] == Variant(real_t(FORMAT_VERSION_LEGACY_1))) {
|
2020-01-10 10:56:57 -08:00
|
|
|
Array depths;
|
2021-09-25 20:14:50 -07:00
|
|
|
depths.resize(VoxelBufferInternal::MAX_CHANNELS);
|
2020-01-10 10:56:57 -08:00
|
|
|
for (int i = 0; i < depths.size(); ++i) {
|
2021-09-25 20:14:50 -07:00
|
|
|
depths[i] = VoxelBufferInternal::DEFAULT_CHANNEL_DEPTH;
|
2020-01-10 10:56:57 -08:00
|
|
|
}
|
|
|
|
data["channel_depths"] = depths;
|
2020-09-19 13:50:23 -07:00
|
|
|
data["version"] = FORMAT_VERSION_LEGACY_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data["version"] == Variant(real_t(FORMAT_VERSION_LEGACY_2))) {
|
|
|
|
// Nothing for the region forest, but indicates region files may be upgraded to v3.
|
2020-01-10 10:56:57 -08:00
|
|
|
data["version"] = FORMAT_VERSION;
|
|
|
|
}
|
2020-09-19 13:50:23 -07:00
|
|
|
|
|
|
|
//if (data["version"] != Variant(real_t(FORMAT_VERSION))) {
|
|
|
|
// TODO Throw error?
|
|
|
|
//}
|
2020-01-10 10:56:57 -08:00
|
|
|
}
|
|
|
|
|
2022-01-08 18:21:49 -08:00
|
|
|
FileResult VoxelStreamRegionFiles::load_meta() {
|
|
|
|
ERR_FAIL_COND_V(_directory_path == "", FILE_CANT_OPEN);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
// Ensure you cleanup previous world before loading another
|
|
|
|
CRASH_COND(_region_cache.size() > 0);
|
|
|
|
|
|
|
|
String meta_path = _directory_path.plus_file(META_FILE_NAME);
|
2021-12-13 13:38:10 -08:00
|
|
|
String json_string;
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
Error err;
|
2021-01-16 05:34:45 -08:00
|
|
|
VoxelFileLockerRead file_rlock(meta_path);
|
2021-02-01 14:25:25 -08:00
|
|
|
FileAccessRef f = FileAccess::open(meta_path, FileAccess::READ, &err);
|
2019-08-14 12:34:06 -07:00
|
|
|
if (!f) {
|
2022-01-08 18:21:49 -08:00
|
|
|
return FILE_CANT_OPEN;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
2021-12-13 13:38:10 -08:00
|
|
|
json_string = f->get_as_utf8_string();
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Note: I chose JSON purely for debugging purposes. This file is not meant to be edited by hand.
|
|
|
|
// World configuration changes may need a full converter.
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
JSON json;
|
|
|
|
const Error json_err = json.parse(json_string);
|
|
|
|
Variant res = json.get_data();
|
|
|
|
const String json_err_msg = json.get_error_message();
|
|
|
|
const int json_err_line = json.get_error_line();
|
2019-08-14 12:34:06 -07:00
|
|
|
if (json_err != OK) {
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_PRINT(
|
|
|
|
String("Error when parsing {0}: line {1}: {2}").format(varray(meta_path, json_err_line, json_err_msg)));
|
2022-01-08 18:21:49 -08:00
|
|
|
return FILE_INVALID_DATA;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Dictionary d = res;
|
2020-01-10 10:56:57 -08:00
|
|
|
migrate_region_meta_data(d);
|
2019-08-14 12:34:06 -07:00
|
|
|
Meta meta;
|
2022-01-08 18:21:49 -08:00
|
|
|
ERR_FAIL_COND_V(!u8_from_json_variant(d["version"], meta.version), FILE_INVALID_DATA);
|
|
|
|
ERR_FAIL_COND_V(!u8_from_json_variant(d["block_size_po2"], meta.block_size_po2), FILE_INVALID_DATA);
|
|
|
|
ERR_FAIL_COND_V(!u8_from_json_variant(d["region_size_po2"], meta.region_size_po2), FILE_INVALID_DATA);
|
|
|
|
ERR_FAIL_COND_V(!u8_from_json_variant(d["lod_count"], meta.lod_count), FILE_INVALID_DATA);
|
|
|
|
ERR_FAIL_COND_V(!u32_from_json_variant(d["sector_size"], meta.sector_size), FILE_INVALID_DATA);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2022-01-08 18:21:49 -08:00
|
|
|
ERR_FAIL_COND_V(meta.version < 0, FILE_INVALID_DATA);
|
2020-01-10 10:56:57 -08:00
|
|
|
|
|
|
|
Array channel_depths_data = d["channel_depths"];
|
2022-02-02 16:02:10 -08:00
|
|
|
ERR_FAIL_COND_V(channel_depths_data.size() != VoxelBufferInternal::MAX_CHANNELS, FILE_INVALID_DATA);
|
2020-01-10 10:56:57 -08:00
|
|
|
for (int i = 0; i < channel_depths_data.size(); ++i) {
|
2022-01-08 18:21:49 -08:00
|
|
|
ERR_FAIL_COND_V(!depth_from_json_variant(channel_depths_data[i], meta.channel_depths[i]), FILE_INVALID_DATA);
|
2020-01-10 10:56:57 -08:00
|
|
|
}
|
|
|
|
|
2022-01-08 18:21:49 -08:00
|
|
|
ERR_FAIL_COND_V(!check_meta(meta), FILE_INVALID_DATA);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
_meta = meta;
|
|
|
|
_meta_loaded = true;
|
|
|
|
_meta_saved = true;
|
|
|
|
|
2022-01-08 18:21:49 -08:00
|
|
|
return FILE_OK;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2019-08-20 12:50:09 -07:00
|
|
|
bool VoxelStreamRegionFiles::check_meta(const Meta &meta) {
|
2019-08-23 17:44:27 -07:00
|
|
|
ERR_FAIL_COND_V(meta.block_size_po2 < 1 || meta.block_size_po2 > 8, false);
|
|
|
|
ERR_FAIL_COND_V(meta.region_size_po2 < 1 || meta.region_size_po2 > 8, false);
|
2019-08-20 12:50:09 -07:00
|
|
|
ERR_FAIL_COND_V(meta.lod_count <= 0 || meta.lod_count > 32, false);
|
|
|
|
ERR_FAIL_COND_V(meta.sector_size <= 0 || meta.sector_size > 65536, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
Vector3i VoxelStreamRegionFiles::get_block_position_from_voxels(const Vector3i &origin_in_voxels) const {
|
2019-08-23 17:44:27 -07:00
|
|
|
return origin_in_voxels >> _meta.block_size_po2;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
Vector3i VoxelStreamRegionFiles::get_region_position_from_blocks(const Vector3i &block_position) const {
|
2019-08-23 17:44:27 -07:00
|
|
|
return block_position >> _meta.region_size_po2;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2019-08-19 15:17:23 -07:00
|
|
|
void VoxelStreamRegionFiles::close_all_regions() {
|
2019-09-06 08:44:19 -07:00
|
|
|
for (unsigned int i = 0; i < _region_cache.size(); ++i) {
|
2019-08-14 12:34:06 -07:00
|
|
|
CachedRegion *cache = _region_cache[i];
|
|
|
|
close_region(cache);
|
|
|
|
memdelete(cache);
|
|
|
|
}
|
|
|
|
_region_cache.clear();
|
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
String VoxelStreamRegionFiles::get_region_file_path(const Vector3i ®ion_pos, unsigned int lod) const {
|
2019-08-14 12:34:06 -07:00
|
|
|
Array a;
|
|
|
|
a.resize(5);
|
|
|
|
a[0] = lod;
|
|
|
|
a[1] = region_pos.x;
|
|
|
|
a[2] = region_pos.y;
|
|
|
|
a[3] = region_pos.z;
|
2022-01-08 14:49:48 -08:00
|
|
|
a[4] = RegionFormat::FILE_EXTENSION;
|
2019-08-19 15:17:23 -07:00
|
|
|
return _directory_path.plus_file(String("regions/lod{0}/r.{1}.{2}.{3}.{4}").format(a));
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
VoxelStreamRegionFiles::CachedRegion *VoxelStreamRegionFiles::get_region_from_cache(const Vector3i pos, int lod) const {
|
2019-08-14 12:34:06 -07:00
|
|
|
// A linear search might be better than a Map data structure,
|
|
|
|
// because it's unlikely to have more than about 10 regions cached at a time
|
2019-09-06 08:44:19 -07:00
|
|
|
for (unsigned int i = 0; i < _region_cache.size(); ++i) {
|
2019-08-14 12:34:06 -07:00
|
|
|
CachedRegion *r = _region_cache[i];
|
|
|
|
if (r->position == pos && r->lod == lod) {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
VoxelStreamRegionFiles::CachedRegion *VoxelStreamRegionFiles::open_region(
|
|
|
|
const Vector3i region_pos, unsigned int lod, bool create_if_not_found) {
|
2020-08-29 15:20:51 -07:00
|
|
|
VOXEL_PROFILE_SCOPE();
|
2019-08-14 12:34:06 -07:00
|
|
|
ERR_FAIL_COND_V(!_meta_loaded, nullptr);
|
|
|
|
ERR_FAIL_COND_V(lod < 0, nullptr);
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
CachedRegion *cached_region = get_region_from_cache(region_pos, lod);
|
|
|
|
if (cached_region != nullptr) {
|
|
|
|
return cached_region;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
while (_region_cache.size() > _max_open_regions - 1) {
|
|
|
|
close_oldest_region();
|
|
|
|
}
|
2020-09-19 13:50:23 -07:00
|
|
|
// Not in cache, we'll have to open or create it
|
2019-08-14 12:34:06 -07:00
|
|
|
|
|
|
|
String fpath = get_region_file_path(region_pos, lod);
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
cached_region = memnew(CachedRegion);
|
|
|
|
|
|
|
|
// Configure format because we might have to create the file, and some old file versions don't embed format
|
2020-09-20 08:33:00 -07:00
|
|
|
{
|
2022-01-08 14:49:48 -08:00
|
|
|
RegionFormat format;
|
2020-09-20 08:33:00 -07:00
|
|
|
format.block_size_po2 = _meta.block_size_po2;
|
|
|
|
format.channel_depths = _meta.channel_depths;
|
|
|
|
// TODO Palette support
|
|
|
|
format.has_palette = false;
|
2021-12-13 13:38:10 -08:00
|
|
|
format.region_size = Vector3iUtil::create(1 << _meta.region_size_po2);
|
2020-09-20 08:33:00 -07:00
|
|
|
format.sector_size = _meta.sector_size;
|
|
|
|
|
|
|
|
cached_region->region.set_format(format);
|
|
|
|
cached_region->position = region_pos;
|
|
|
|
cached_region->lod = lod;
|
|
|
|
}
|
2020-09-19 13:50:23 -07:00
|
|
|
|
|
|
|
const Error err = cached_region->region.open(fpath, create_if_not_found);
|
|
|
|
|
|
|
|
// Things we could do for optimization:
|
|
|
|
// - Cache the fact the file doesnt exist, so we won't need to do a system call to actually check it every time
|
|
|
|
// - No need to read the header again when it has been read once,
|
|
|
|
// we assume no other process will modify region files
|
|
|
|
|
|
|
|
if (err != OK) {
|
|
|
|
memdelete(cached_region);
|
|
|
|
if (create_if_not_found) {
|
|
|
|
// Could not create it apparently
|
|
|
|
ERR_PRINT(String("Could not open or create region file {0}, error: {1}").format(varray(fpath, err)));
|
2019-08-14 12:34:06 -07:00
|
|
|
return nullptr;
|
2020-09-19 13:50:23 -07:00
|
|
|
} else {
|
|
|
|
// Does not exist, it was probably expected
|
2019-08-14 12:34:06 -07:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
// Make sure it has correct format
|
|
|
|
{
|
2022-01-08 14:49:48 -08:00
|
|
|
const RegionFormat &format = cached_region->region.get_format();
|
2021-12-13 13:38:10 -08:00
|
|
|
if (format.block_size_po2 != _meta.block_size_po2 //
|
|
|
|
|| format.channel_depths != _meta.channel_depths //
|
|
|
|
|| format.region_size != Vector3iUtil::create(1 << _meta.region_size_po2) //
|
|
|
|
|| format.sector_size != _meta.sector_size) {
|
2020-09-19 13:50:23 -07:00
|
|
|
ERR_PRINT("Region file has unexpected format");
|
|
|
|
memdelete(cached_region);
|
|
|
|
return nullptr;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
// TODO Debug check to make sure we did not already cache it
|
|
|
|
_region_cache.push_back(cached_region);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
cached_region->file_exists = true;
|
2021-12-29 09:06:24 -08:00
|
|
|
cached_region->last_opened = Time::get_singleton()->get_ticks_usec();
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
return cached_region;
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
// TODO Get rid of to simplify?
|
2019-08-17 11:56:21 -07:00
|
|
|
void VoxelStreamRegionFiles::close_region(CachedRegion *region) {
|
2020-09-19 13:50:23 -07:00
|
|
|
region->region.close();
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
void VoxelStreamRegionFiles::close_oldest_region() {
|
2019-08-14 12:34:06 -07:00
|
|
|
// Close region assumed to be the least recently used
|
|
|
|
|
|
|
|
if (_region_cache.size() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int oldest_index = -1;
|
|
|
|
uint64_t oldest_time = 0;
|
2021-12-29 09:06:24 -08:00
|
|
|
const uint64_t now = Time::get_singleton()->get_ticks_usec();
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2019-09-06 08:44:19 -07:00
|
|
|
for (unsigned int i = 0; i < _region_cache.size(); ++i) {
|
2020-09-19 13:50:23 -07:00
|
|
|
const CachedRegion *r = _region_cache[i];
|
|
|
|
const uint64_t time = now - r->last_opened;
|
2019-08-14 12:34:06 -07:00
|
|
|
if (time >= oldest_time) {
|
|
|
|
oldest_index = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CachedRegion *region = _region_cache[oldest_index];
|
|
|
|
_region_cache.erase(_region_cache.begin() + oldest_index);
|
|
|
|
|
|
|
|
close_region(region);
|
|
|
|
memdelete(region);
|
|
|
|
}
|
|
|
|
|
2019-08-19 15:17:23 -07:00
|
|
|
static inline int convert_block_coordinate(int p_x, int old_size, int new_size) {
|
2022-01-03 15:14:18 -08:00
|
|
|
return math::floordiv(p_x * old_size, new_size);
|
2019-08-19 15:17:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static Vector3i convert_block_coordinates(Vector3i pos, Vector3i old_size, Vector3i new_size) {
|
2021-12-13 13:38:10 -08:00
|
|
|
return Vector3i(convert_block_coordinate(pos.x, old_size.x, new_size.x),
|
2019-08-19 15:17:23 -07:00
|
|
|
convert_block_coordinate(pos.y, old_size.y, new_size.y),
|
|
|
|
convert_block_coordinate(pos.z, old_size.z, new_size.z));
|
|
|
|
}
|
|
|
|
|
2019-08-23 17:44:27 -07:00
|
|
|
void VoxelStreamRegionFiles::_convert_files(Meta new_meta) {
|
2019-08-20 12:50:09 -07:00
|
|
|
// TODO Converting across different block sizes is untested.
|
2021-12-13 13:38:10 -08:00
|
|
|
// I wrote it because it would be too bad to loose large voxel worlds because of a setting change, so one day we may
|
|
|
|
// need it
|
2019-08-19 15:17:23 -07:00
|
|
|
|
2020-07-08 12:48:52 -07:00
|
|
|
PRINT_VERBOSE("Converting region files");
|
2019-08-19 15:17:23 -07:00
|
|
|
// This can be a very long and slow operation. Better run this in a thread.
|
|
|
|
|
|
|
|
ERR_FAIL_COND(!_meta_saved);
|
|
|
|
ERR_FAIL_COND(!_meta_loaded);
|
|
|
|
|
|
|
|
close_all_regions();
|
|
|
|
|
|
|
|
Ref<VoxelStreamRegionFiles> old_stream;
|
2021-12-13 13:38:10 -08:00
|
|
|
old_stream.instantiate();
|
2019-08-19 15:17:23 -07:00
|
|
|
// Keep file cache to a minimum for the old stream, we'll query all blocks once anyways
|
|
|
|
old_stream->_max_open_regions = MAX(1, FOPEN_MAX);
|
|
|
|
|
|
|
|
// Backup current folder by renaming it, leaving the current name vacant
|
|
|
|
{
|
2019-08-20 12:50:09 -07:00
|
|
|
DirAccessRef da = DirAccess::create_for_path(_directory_path);
|
2019-08-19 15:17:23 -07:00
|
|
|
ERR_FAIL_COND(!da);
|
|
|
|
int i = 0;
|
|
|
|
String old_dir;
|
|
|
|
while (true) {
|
|
|
|
if (i == 0) {
|
|
|
|
old_dir = _directory_path + "_old";
|
|
|
|
} else {
|
|
|
|
old_dir = _directory_path + "_old" + String::num_int64(i);
|
|
|
|
}
|
|
|
|
if (da->exists(old_dir)) {
|
|
|
|
++i;
|
|
|
|
} else {
|
|
|
|
Error err = da->rename(_directory_path, old_dir);
|
2019-08-20 12:50:09 -07:00
|
|
|
ERR_FAIL_COND_MSG(err != OK,
|
|
|
|
String("Failed to rename '{0}' to '{1}', error {2}")
|
|
|
|
.format(varray(_directory_path, old_dir, err)));
|
2019-08-19 15:17:23 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
old_stream->set_directory(old_dir);
|
2020-07-08 12:48:52 -07:00
|
|
|
PRINT_VERBOSE("Data backed up as " + old_dir);
|
2019-08-19 15:17:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct PositionAndLod {
|
|
|
|
Vector3i position;
|
|
|
|
int lod;
|
|
|
|
};
|
|
|
|
|
2022-01-08 18:21:49 -08:00
|
|
|
ERR_FAIL_COND(old_stream->load_meta() != FILE_OK);
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2019-08-19 15:17:23 -07:00
|
|
|
std::vector<PositionAndLod> old_region_list;
|
|
|
|
Meta old_meta = old_stream->_meta;
|
|
|
|
|
|
|
|
// Get list of all regions from the old stream
|
|
|
|
{
|
|
|
|
for (int lod = 0; lod < old_meta.lod_count; ++lod) {
|
2020-09-19 13:50:23 -07:00
|
|
|
const String lod_folder =
|
|
|
|
old_stream->_directory_path.plus_file("regions").plus_file("lod") + String::num_int64(lod);
|
2022-01-08 14:49:48 -08:00
|
|
|
const String ext = String(".") + RegionFormat::FILE_EXTENSION;
|
2019-08-19 15:17:23 -07:00
|
|
|
|
|
|
|
DirAccessRef da = DirAccess::open(lod_folder);
|
|
|
|
if (!da) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
da->list_dir_begin();
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
String fname = da->get_next();
|
|
|
|
if (fname == "") {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (da->current_is_dir()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (fname.ends_with(ext)) {
|
|
|
|
Vector<String> parts = fname.split(".");
|
|
|
|
// r.x.y.z.ext
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND_MSG(
|
|
|
|
parts.size() < 4, String("Found invalid region file: '{0}'").format(varray(fname)));
|
2019-08-19 15:17:23 -07:00
|
|
|
PositionAndLod p;
|
|
|
|
p.position.x = parts[1].to_int();
|
|
|
|
p.position.y = parts[2].to_int();
|
|
|
|
p.position.z = parts[3].to_int();
|
2019-08-20 12:50:09 -07:00
|
|
|
p.lod = lod;
|
2019-08-19 15:17:23 -07:00
|
|
|
old_region_list.push_back(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
da->list_dir_end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_meta = new_meta;
|
2022-01-08 18:21:49 -08:00
|
|
|
ERR_FAIL_COND(save_meta() != FILE_OK);
|
2019-08-19 15:17:23 -07:00
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
const Vector3i old_block_size = Vector3iUtil::create(1 << old_meta.block_size_po2);
|
|
|
|
const Vector3i new_block_size = Vector3iUtil::create(1 << _meta.block_size_po2);
|
2019-08-23 17:44:27 -07:00
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
const Vector3i old_region_size = Vector3iUtil::create(1 << old_meta.region_size_po2);
|
2019-08-23 17:44:27 -07:00
|
|
|
|
2019-08-19 15:17:23 -07:00
|
|
|
// Read all blocks from the old stream and write them into the new one
|
|
|
|
|
2019-09-06 08:44:19 -07:00
|
|
|
for (unsigned int i = 0; i < old_region_list.size(); ++i) {
|
2019-08-19 15:17:23 -07:00
|
|
|
PositionAndLod region_info = old_region_list[i];
|
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
const CachedRegion *old_region = old_stream->open_region(region_info.position, region_info.lod, false);
|
|
|
|
if (old_region == nullptr) {
|
2019-08-19 15:17:23 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-12-13 13:38:10 -08:00
|
|
|
PRINT_VERBOSE(String("Converting region lod{0}/{1}").format(varray(region_info.lod, region_info.position)));
|
2019-08-19 15:17:23 -07:00
|
|
|
|
2020-09-19 13:50:23 -07:00
|
|
|
const unsigned int blocks_count = old_region->region.get_header_block_count();
|
|
|
|
for (unsigned int j = 0; j < blocks_count; ++j) {
|
|
|
|
if (!old_region->region.has_block(j)) {
|
2019-08-19 15:17:23 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-09-25 20:14:50 -07:00
|
|
|
VoxelBufferInternal old_block;
|
|
|
|
old_block.create(old_block_size.x, old_block_size.y, old_block_size.z);
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2021-09-25 20:14:50 -07:00
|
|
|
VoxelBufferInternal new_block;
|
|
|
|
new_block.create(new_block_size.x, new_block_size.y, new_block_size.z);
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2019-08-19 15:17:23 -07:00
|
|
|
// Load block from old stream
|
2020-09-19 13:50:23 -07:00
|
|
|
Vector3i block_rpos = old_region->region.get_block_position_from_index(j);
|
2019-08-23 17:44:27 -07:00
|
|
|
Vector3i block_pos = block_rpos + region_info.position * old_region_size;
|
2022-01-15 20:21:02 -08:00
|
|
|
old_stream->load_voxel_block(old_block, block_pos * old_block_size << region_info.lod, region_info.lod);
|
2019-08-19 15:17:23 -07:00
|
|
|
|
|
|
|
// Save it in the new one
|
2019-08-23 17:44:27 -07:00
|
|
|
if (old_block_size == new_block_size) {
|
2022-01-15 20:21:02 -08:00
|
|
|
save_voxel_block(old_block, block_pos * new_block_size << region_info.lod, region_info.lod);
|
2019-08-19 15:17:23 -07:00
|
|
|
|
|
|
|
} else {
|
2019-08-23 17:44:27 -07:00
|
|
|
Vector3i new_block_pos = convert_block_coordinates(block_pos, old_block_size, new_block_size);
|
2019-08-19 15:17:23 -07:00
|
|
|
|
|
|
|
// TODO Support any size? Assuming cubic blocks here
|
2019-08-23 17:44:27 -07:00
|
|
|
if (old_block_size.x < new_block_size.x) {
|
|
|
|
Vector3i ratio = new_block_size / old_block_size;
|
2019-08-19 15:17:23 -07:00
|
|
|
Vector3i rel = block_pos % ratio;
|
|
|
|
|
|
|
|
// Copy to a sub-area of one block
|
2022-01-15 20:21:02 -08:00
|
|
|
load_voxel_block(new_block, new_block_pos * new_block_size << region_info.lod, region_info.lod);
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2021-09-25 20:14:50 -07:00
|
|
|
Vector3i dst_pos = rel * old_block.get_size();
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2022-02-02 16:02:10 -08:00
|
|
|
for (unsigned int channel_index = 0; channel_index < VoxelBufferInternal::MAX_CHANNELS;
|
|
|
|
++channel_index) {
|
2021-09-25 20:14:50 -07:00
|
|
|
new_block.copy_from(old_block, Vector3i(), old_block.get_size(), dst_pos, channel_index);
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
2021-09-25 20:14:50 -07:00
|
|
|
new_block.compress_uniform_channels();
|
2022-01-15 20:21:02 -08:00
|
|
|
save_voxel_block(new_block, new_block_pos * new_block_size << region_info.lod, region_info.lod);
|
2019-08-19 15:17:23 -07:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// Copy to multiple blocks
|
2019-08-23 17:44:27 -07:00
|
|
|
Vector3i area = new_block_size / old_block_size;
|
2019-08-19 15:17:23 -07:00
|
|
|
Vector3i rpos;
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2019-08-19 15:17:23 -07:00
|
|
|
for (rpos.z = 0; rpos.z < area.z; ++rpos.z) {
|
|
|
|
for (rpos.x = 0; rpos.x < area.x; ++rpos.x) {
|
|
|
|
for (rpos.y = 0; rpos.y < area.y; ++rpos.y) {
|
2021-09-25 20:14:50 -07:00
|
|
|
Vector3i src_min = rpos * new_block.get_size();
|
|
|
|
Vector3i src_max = src_min + new_block.get_size();
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2022-02-02 16:02:10 -08:00
|
|
|
for (unsigned int channel_index = 0; channel_index < VoxelBufferInternal::MAX_CHANNELS;
|
2020-09-19 13:50:23 -07:00
|
|
|
++channel_index) {
|
2021-09-25 20:14:50 -07:00
|
|
|
new_block.copy_from(old_block, src_min, src_max, Vector3i(), channel_index);
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
2022-01-15 20:21:02 -08:00
|
|
|
save_voxel_block(new_block, (new_block_pos + rpos) * new_block_size << region_info.lod,
|
2021-12-13 13:38:10 -08:00
|
|
|
region_info.lod);
|
2019-08-19 15:17:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-20 12:50:09 -07:00
|
|
|
close_all_regions();
|
|
|
|
|
2021-01-29 17:36:37 -08:00
|
|
|
PRINT_VERBOSE("Done converting region files");
|
2019-08-19 15:17:23 -07:00
|
|
|
}
|
|
|
|
|
2019-08-20 12:50:09 -07:00
|
|
|
Vector3i VoxelStreamRegionFiles::get_region_size() const {
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
2021-12-13 13:38:10 -08:00
|
|
|
return Vector3iUtil::create(1 << _meta.region_size_po2);
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Vector3 VoxelStreamRegionFiles::get_region_size_v() const {
|
2021-12-13 13:38:10 -08:00
|
|
|
return get_region_size();
|
2019-08-23 17:44:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelStreamRegionFiles::get_region_size_po2() const {
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
2019-08-23 17:44:27 -07:00
|
|
|
return _meta.region_size_po2;
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
2019-08-23 17:44:27 -07:00
|
|
|
int VoxelStreamRegionFiles::get_block_size_po2() const {
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
2019-08-23 17:44:27 -07:00
|
|
|
return _meta.block_size_po2;
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelStreamRegionFiles::get_lod_count() const {
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
2019-08-20 12:50:09 -07:00
|
|
|
return _meta.lod_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
int VoxelStreamRegionFiles::get_sector_size() const {
|
2021-01-16 05:34:45 -08:00
|
|
|
MutexLock lock(_mutex);
|
2019-08-20 12:50:09 -07:00
|
|
|
return _meta.sector_size;
|
|
|
|
}
|
|
|
|
|
2019-08-23 17:44:27 -07:00
|
|
|
// TODO The following settings are hard to change.
|
|
|
|
// If files already exist, these settings will be ignored.
|
|
|
|
// To be applied, files either need to be wiped out or converted, which is a super-heavy operation.
|
|
|
|
// This can be made easier by adding a button to the inspector to convert existing files just in case
|
|
|
|
|
|
|
|
void VoxelStreamRegionFiles::set_region_size_po2(int p_region_size_po2) {
|
2021-01-16 05:34:45 -08:00
|
|
|
{
|
|
|
|
MutexLock lock(_mutex);
|
|
|
|
if (_meta.region_size_po2 == p_region_size_po2) {
|
|
|
|
return;
|
|
|
|
}
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND_MSG(
|
|
|
|
_meta_loaded, "Can't change existing region size without heavy conversion. Use convert_files().");
|
2021-01-16 05:34:45 -08:00
|
|
|
ERR_FAIL_COND(p_region_size_po2 < 1);
|
|
|
|
ERR_FAIL_COND(p_region_size_po2 > 8);
|
|
|
|
_meta.region_size_po2 = p_region_size_po2;
|
2019-08-23 17:44:27 -07:00
|
|
|
}
|
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelStreamRegionFiles::set_block_size_po2(int p_block_size_po2) {
|
2021-01-16 05:34:45 -08:00
|
|
|
{
|
|
|
|
MutexLock lock(_mutex);
|
|
|
|
if (_meta.block_size_po2 == p_block_size_po2) {
|
|
|
|
return;
|
|
|
|
}
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND_MSG(
|
|
|
|
_meta_loaded, "Can't change existing block size without heavy conversion. Use convert_files().");
|
2021-01-16 05:34:45 -08:00
|
|
|
ERR_FAIL_COND(p_block_size_po2 < 1);
|
|
|
|
ERR_FAIL_COND(p_block_size_po2 > 8);
|
|
|
|
_meta.block_size_po2 = p_block_size_po2;
|
2019-08-23 17:44:27 -07:00
|
|
|
}
|
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelStreamRegionFiles::set_sector_size(int p_sector_size) {
|
2021-01-16 05:34:45 -08:00
|
|
|
{
|
|
|
|
MutexLock lock(_mutex);
|
|
|
|
if (static_cast<int>(_meta.sector_size) == p_sector_size) {
|
|
|
|
return;
|
|
|
|
}
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND_MSG(
|
|
|
|
_meta_loaded, "Can't change existing sector size without heavy conversion. Use convert_files().");
|
2021-01-16 05:34:45 -08:00
|
|
|
ERR_FAIL_COND(p_sector_size < 256);
|
|
|
|
ERR_FAIL_COND(p_sector_size > 65536);
|
|
|
|
_meta.sector_size = p_sector_size;
|
2019-08-23 17:44:27 -07:00
|
|
|
}
|
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelStreamRegionFiles::set_lod_count(int p_lod_count) {
|
2021-01-16 05:34:45 -08:00
|
|
|
{
|
|
|
|
MutexLock lock(_mutex);
|
|
|
|
if (_meta.lod_count == p_lod_count) {
|
|
|
|
return;
|
|
|
|
}
|
2021-12-13 13:38:10 -08:00
|
|
|
ERR_FAIL_COND_MSG(
|
|
|
|
_meta_loaded, "Can't change existing LOD count without heavy conversion. Use convert_files().");
|
2021-01-16 05:34:45 -08:00
|
|
|
ERR_FAIL_COND(p_lod_count < 1);
|
|
|
|
ERR_FAIL_COND(p_lod_count > 32);
|
|
|
|
_meta.lod_count = p_lod_count;
|
2019-08-23 17:44:27 -07:00
|
|
|
}
|
|
|
|
emit_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VoxelStreamRegionFiles::convert_files(Dictionary d) {
|
2019-08-20 12:50:09 -07:00
|
|
|
Meta meta;
|
|
|
|
meta.version = _meta.version;
|
2019-08-23 17:44:27 -07:00
|
|
|
meta.block_size_po2 = int(d["block_size_po2"]);
|
|
|
|
meta.region_size_po2 = int(d["region_size_po2"]);
|
2019-08-20 12:50:09 -07:00
|
|
|
meta.sector_size = int(d["sector_size"]);
|
|
|
|
meta.lod_count = int(d["lod_count"]);
|
|
|
|
|
2021-01-16 05:34:45 -08:00
|
|
|
{
|
|
|
|
MutexLock lock(_mutex);
|
|
|
|
|
|
|
|
ERR_FAIL_COND_MSG(!check_meta(meta), "Invalid setting");
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2021-01-16 05:34:45 -08:00
|
|
|
if (!_meta_loaded) {
|
2022-01-08 18:21:49 -08:00
|
|
|
if (load_meta() != FILE_OK) {
|
2021-01-16 05:34:45 -08:00
|
|
|
// New stream, nothing to convert
|
|
|
|
_meta = meta;
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2021-01-16 05:34:45 -08:00
|
|
|
} else {
|
|
|
|
// Just opened existing stream
|
|
|
|
_convert_files(meta);
|
|
|
|
}
|
2019-08-20 12:50:09 -07:00
|
|
|
|
|
|
|
} else {
|
2021-01-16 05:34:45 -08:00
|
|
|
// That stream was previously used
|
2019-08-23 17:44:27 -07:00
|
|
|
_convert_files(meta);
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
}
|
2019-08-23 17:44:27 -07:00
|
|
|
|
|
|
|
emit_changed();
|
2019-08-20 12:50:09 -07:00
|
|
|
}
|
|
|
|
|
2019-08-17 11:56:21 -07:00
|
|
|
void VoxelStreamRegionFiles::_bind_methods() {
|
|
|
|
ClassDB::bind_method(D_METHOD("set_directory", "directory"), &VoxelStreamRegionFiles::set_directory);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_directory"), &VoxelStreamRegionFiles::get_directory);
|
2019-08-14 12:34:06 -07:00
|
|
|
|
2019-08-23 17:44:27 -07:00
|
|
|
ClassDB::bind_method(D_METHOD("get_block_size_po2"), &VoxelStreamRegionFiles::get_block_size_po2);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_lod_count"), &VoxelStreamRegionFiles::get_lod_count);
|
2019-08-20 12:50:09 -07:00
|
|
|
ClassDB::bind_method(D_METHOD("get_region_size"), &VoxelStreamRegionFiles::get_region_size_v);
|
2019-08-23 17:44:27 -07:00
|
|
|
ClassDB::bind_method(D_METHOD("get_region_size_po2"), &VoxelStreamRegionFiles::get_region_size_po2);
|
2019-08-20 12:50:09 -07:00
|
|
|
ClassDB::bind_method(D_METHOD("get_sector_size"), &VoxelStreamRegionFiles::get_sector_size);
|
|
|
|
|
2019-08-23 17:44:27 -07:00
|
|
|
ClassDB::bind_method(D_METHOD("set_block_size_po2"), &VoxelStreamRegionFiles::set_block_size_po2);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_lod_count"), &VoxelStreamRegionFiles::set_lod_count);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_region_size_po2"), &VoxelStreamRegionFiles::set_region_size_po2);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_sector_size"), &VoxelStreamRegionFiles::set_sector_size);
|
|
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("convert_files", "new_settings"), &VoxelStreamRegionFiles::convert_files);
|
2019-08-20 12:50:09 -07:00
|
|
|
|
2019-08-17 12:22:57 -07:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "directory", PROPERTY_HINT_DIR), "set_directory", "get_directory");
|
2019-08-23 17:44:27 -07:00
|
|
|
|
|
|
|
ADD_GROUP("Dimensions", "");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_count"), "set_lod_count", "get_lod_count");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "region_size_po2"), "set_region_size_po2", "get_region_size_po2");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "block_size_po2"), "set_block_size_po2", "get_region_size_po2");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "sector_size"), "set_sector_size", "get_sector_size");
|
2019-08-14 12:34:06 -07:00
|
|
|
}
|
2022-01-09 14:13:10 -08:00
|
|
|
|
|
|
|
} // namespace zylann::voxel
|