Extracted some functions from VoxelBuffer, optimized copy/paste a little
This commit is contained in:
parent
0de2b1b9d0
commit
4cc47b9036
@ -63,6 +63,7 @@
|
||||
Copies values from a channel's sub-region of another [VoxelBuffer] into the same channel for the current buffer, at a specific location. The depth formats must match.
|
||||
If corners of the area represent a negative-size area, they will be sorted back.
|
||||
If coordinates are entirely or partially out of bounds, they will be clipped automatically.
|
||||
Copying across the same buffer to overlapping areas is not supported. You may use an intermediary buffer in this case.
|
||||
</description>
|
||||
</method>
|
||||
<method name="copy_voxel_metadata_in_area">
|
||||
@ -80,6 +81,7 @@
|
||||
Copies per-voxel metadata from a sub-region of another [VoxelBuffer] into the the current buffer, at a specific location. Values will be a shallow copy.
|
||||
If corners of the area represent a negative-size area, they will be sorted back.
|
||||
If coordinates are entirely or partially out of bounds, they will be clipped automatically.
|
||||
Copying across the same buffer to overlapping areas is not supported. You may use an intermediary buffer in this case.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create">
|
||||
|
51
storage/funcs.cpp
Normal file
51
storage/funcs.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "funcs.h"
|
||||
#include "../util/math/rect3i.h"
|
||||
|
||||
void copy_3d_region_zxy(
|
||||
ArraySlice<uint8_t> dst, Vector3i dst_size, Vector3i dst_min,
|
||||
ArraySlice<const uint8_t> src, Vector3i src_size, Vector3i src_min, Vector3i src_max,
|
||||
size_t item_size) {
|
||||
//
|
||||
Vector3i::sort_min_max(src_min, src_max);
|
||||
clip_copy_region(src_min, src_max, src_size, dst_min, dst_size);
|
||||
const Vector3i area_size = src_max - src_min;
|
||||
if (area_size.x <= 0 || area_size.y <= 0 || area_size.z <= 0) {
|
||||
// Degenerate area, we'll not copy anything.
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (src.data() == dst.data()) {
|
||||
ERR_FAIL_COND_MSG(
|
||||
Rect3i::from_min_max(src_min, src_max).intersects(Rect3i::from_min_max(dst_min, dst_min + area_size)),
|
||||
"Copy across the same buffer to an overlapping area is not supported");
|
||||
}
|
||||
ERR_FAIL_COND(area_size.volume() * item_size > dst.size());
|
||||
ERR_FAIL_COND(area_size.volume() * item_size > src.size());
|
||||
#endif
|
||||
|
||||
if (area_size == src_size && area_size == dst_size) {
|
||||
// Copy everything
|
||||
memcpy(dst.data(), src.data(), dst.size() * item_size);
|
||||
|
||||
} else {
|
||||
// Copy area row by row:
|
||||
// This offset is how much to move in order to advance by one row (row direction is Y),
|
||||
// essentially doing y+1
|
||||
const unsigned int src_row_offset = src_size.y * item_size;
|
||||
const unsigned int dst_row_offset = dst_size.y * item_size;
|
||||
Vector3i pos;
|
||||
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
|
||||
pos.x = 0;
|
||||
unsigned int src_ri = Vector3i(src_min + pos).get_zxy_index(src_size) * item_size;
|
||||
unsigned int dst_ri = Vector3i(dst_min + pos).get_zxy_index(dst_size) * item_size;
|
||||
for (; pos.x < area_size.x; ++pos.x) {
|
||||
// TODO Cast src and dst to `restrict` so the optimizer can assume adresses don't overlap,
|
||||
// which might allow to write as a for loop (which may compile as a `memcpy`)?
|
||||
memcpy(&dst[dst_ri], &src[src_ri], area_size.y * item_size);
|
||||
src_ri += src_row_offset;
|
||||
dst_ri += dst_row_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
139
storage/funcs.h
Normal file
139
storage/funcs.h
Normal file
@ -0,0 +1,139 @@
|
||||
#ifndef VOXEL_STORAGE_FUNCS_H
|
||||
#define VOXEL_STORAGE_FUNCS_H
|
||||
|
||||
#include "../util/array_slice.h"
|
||||
#include "../util/math/vector3i.h"
|
||||
|
||||
inline void clip_copy_region_coord(int &src_min, int &src_max, const int src_size, int &dst_min, const int dst_size) {
|
||||
// Clamp source and shrink destination for moved borders
|
||||
if (src_min < 0) {
|
||||
dst_min += -src_min;
|
||||
src_min = 0;
|
||||
}
|
||||
if (src_max > src_size) {
|
||||
src_max = src_size;
|
||||
}
|
||||
// Clamp destination and shrink source for moved borders
|
||||
if (dst_min < 0) {
|
||||
src_min += -dst_min;
|
||||
dst_min = 0;
|
||||
}
|
||||
const int dst_w = src_max - src_min;
|
||||
const int dst_max = dst_min + dst_w;
|
||||
if (dst_max > dst_size) {
|
||||
src_max -= dst_max - dst_size;
|
||||
}
|
||||
// It is possible the source has negative size at this point, which means there is nothing to copy.
|
||||
// This must be checked by the caller.
|
||||
}
|
||||
|
||||
// Clips coordinates that may be used to copy a sub-region of a 3D container into another 3D container.
|
||||
// The result can have zero or negative size, so it must be checked before proceeding.
|
||||
inline void clip_copy_region(
|
||||
Vector3i &src_min, Vector3i &src_max, const Vector3i &src_size, Vector3i &dst_min, const Vector3i &dst_size) {
|
||||
clip_copy_region_coord(src_min.x, src_max.x, src_size.x, dst_min.x, dst_size.x);
|
||||
clip_copy_region_coord(src_min.y, src_max.y, src_size.y, dst_min.y, dst_size.y);
|
||||
clip_copy_region_coord(src_min.z, src_max.z, src_size.z, dst_min.z, dst_size.z);
|
||||
}
|
||||
|
||||
void copy_3d_region_zxy(
|
||||
ArraySlice<uint8_t> dst, Vector3i dst_size, Vector3i dst_min,
|
||||
ArraySlice<const uint8_t> src, Vector3i src_size, Vector3i src_min, Vector3i src_max,
|
||||
size_t item_size);
|
||||
|
||||
template <typename T>
|
||||
inline void copy_3d_region_zxy(
|
||||
ArraySlice<T> dst, Vector3i dst_size, Vector3i dst_min,
|
||||
ArraySlice<const T> src, Vector3i src_size, Vector3i src_min, Vector3i src_max) {
|
||||
copy_3d_region_zxy(
|
||||
dst.reinterpret_cast_to<uint8_t>(), dst_size, dst_min,
|
||||
src.reinterpret_cast_to<const uint8_t>(), src_size, src_min, src_max,
|
||||
sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void fill_3d_region_zxy(ArraySlice<T> dst, Vector3i dst_size, Vector3i dst_min, Vector3i dst_max, const T value) {
|
||||
Vector3i::sort_min_max(dst_min, dst_max);
|
||||
dst_min.x = clamp(dst_min.x, 0, dst_size.x);
|
||||
dst_min.y = clamp(dst_min.y, 0, dst_size.y);
|
||||
dst_min.z = clamp(dst_min.z, 0, dst_size.z);
|
||||
dst_max.x = clamp(dst_max.x, 0, dst_size.x);
|
||||
dst_max.y = clamp(dst_max.y, 0, dst_size.y);
|
||||
dst_max.z = clamp(dst_max.z, 0, dst_size.z);
|
||||
const Vector3i area_size = src_max - src_min;
|
||||
if (area_size.x <= 0 || area_size.y <= 0 || area_size.z <= 0) {
|
||||
// Degenerate area, we'll not copy anything.
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
ERR_FAIL_COND(area_size.volume() > dst.size());
|
||||
#endif
|
||||
|
||||
if (area_size == dst_size) {
|
||||
for (unsigned int i = 0; i < dst.size(); ++i) {
|
||||
dst[i] = value;
|
||||
}
|
||||
|
||||
} else {
|
||||
const unsigned int dst_row_offset = dst_size.y;
|
||||
Vector3i pos;
|
||||
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
|
||||
unsigned int dst_ri = Vector3i(dst_min + pos).get_zxy_index(src_size);
|
||||
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
|
||||
// Fill row
|
||||
for (pos.y = 0; pos.y < area_size.y; ++pos.y) {
|
||||
dst[dst_ri + pos.y] = value;
|
||||
}
|
||||
dst_ri += dst_row_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline FixedArray<uint8_t, 4> decode_weights_from_packed_u16(uint16_t packed_weights) {
|
||||
FixedArray<uint8_t, 4> weights;
|
||||
// SIMDable?
|
||||
// weights[0] = ((packed_weights >> 0) & 0x0f) << 4;
|
||||
// weights[1] = ((packed_weights >> 4) & 0x0f) << 4;
|
||||
// weights[2] = ((packed_weights >> 8) & 0x0f) << 4;
|
||||
// weights[3] = ((packed_weights >> 12) & 0x0f) << 4;
|
||||
|
||||
// Reduced but not SIMDable
|
||||
weights[0] = (packed_weights & 0x0f) << 4;
|
||||
weights[1] = packed_weights & 0xf0;
|
||||
weights[2] = (packed_weights >> 4) & 0xf0;
|
||||
weights[3] = (packed_weights >> 8) & 0xf0;
|
||||
return weights;
|
||||
}
|
||||
|
||||
inline FixedArray<uint8_t, 4> decode_indices_from_packed_u16(uint16_t packed_indices) {
|
||||
FixedArray<uint8_t, 4> indices;
|
||||
// SIMDable?
|
||||
indices[0] = (packed_indices >> 0) & 0x0f;
|
||||
indices[1] = (packed_indices >> 4) & 0x0f;
|
||||
indices[2] = (packed_indices >> 8) & 0x0f;
|
||||
indices[3] = (packed_indices >> 12) & 0x0f;
|
||||
return indices;
|
||||
}
|
||||
|
||||
inline uint16_t encode_indices_to_packed_u16(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
|
||||
return (a & 0xf) | ((b & 0xf) << 4) | ((c & 0xf) << 8) | ((d & 0xf) << 12);
|
||||
}
|
||||
|
||||
inline uint16_t encode_weights_to_packed_u16(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
|
||||
return (a >> 4) | ((b >> 4) << 4) | ((c >> 4) << 8) | ((d >> 4) << 12);
|
||||
}
|
||||
|
||||
// Checks if there are no duplicate indices in any voxel
|
||||
inline void debug_check_texture_indices(FixedArray<uint8_t, 4> indices) {
|
||||
FixedArray<bool, 16> checked;
|
||||
checked.fill(false);
|
||||
for (int i = 0; i < indices.size(); ++i) {
|
||||
unsigned int ti = indices[i];
|
||||
CRASH_COND(checked[ti]);
|
||||
checked[ti] = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VOXEL_STORAGE_FUNCS_H
|
@ -33,6 +33,9 @@ inline void free_channel_data(uint8_t *data, uint32_t size) {
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t g_depth_byte_counts[] = {
|
||||
1, 2, 4, 8
|
||||
};
|
||||
uint32_t g_depth_bit_counts[] = {
|
||||
8, 16, 32, 64
|
||||
};
|
||||
@ -48,6 +51,11 @@ inline uint32_t get_depth_bit_count(VoxelBuffer::Depth d) {
|
||||
return g_depth_bit_counts[d];
|
||||
}
|
||||
|
||||
inline uint32_t get_depth_byte_count(VoxelBuffer::Depth d) {
|
||||
CRASH_COND(d < 0 || d >= VoxelBuffer::DEPTH_COUNT);
|
||||
return g_depth_byte_counts[d];
|
||||
}
|
||||
|
||||
inline uint64_t get_max_value_for_depth(VoxelBuffer::Depth d) {
|
||||
CRASH_COND(d < 0 || d >= VoxelBuffer::DEPTH_COUNT);
|
||||
return g_depth_max_values[d];
|
||||
@ -345,11 +353,9 @@ void VoxelBuffer::fill_area(uint64_t defval, Vector3i min, Vector3i max, unsigne
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
|
||||
Vector3i::sort_min_max(min, max);
|
||||
|
||||
min.clamp_to(Vector3i(0, 0, 0), _size + Vector3i(1, 1, 1));
|
||||
max.clamp_to(Vector3i(0, 0, 0), _size + Vector3i(1, 1, 1));
|
||||
const Vector3i area_size = max - min;
|
||||
|
||||
if (area_size.x == 0 || area_size.y == 0 || area_size.z == 0) {
|
||||
return;
|
||||
}
|
||||
@ -513,39 +519,9 @@ void VoxelBuffer::copy_from(const VoxelBuffer &other, unsigned int channel_index
|
||||
channel.depth = other_channel.depth;
|
||||
}
|
||||
|
||||
inline void clip_copy_region_coord(int &src_min, int &src_max, const int src_size, int &dst_min, const int dst_size) {
|
||||
// Clamp source and shrink destination for moved borders
|
||||
if (src_min < 0) {
|
||||
dst_min += -src_min;
|
||||
src_min = 0;
|
||||
}
|
||||
if (src_max > src_size) {
|
||||
src_max = src_size;
|
||||
}
|
||||
// Clamp destination and shrink source for moved borders
|
||||
if (dst_min < 0) {
|
||||
src_min += -dst_min;
|
||||
dst_min = 0;
|
||||
}
|
||||
const int dst_w = src_max - src_min;
|
||||
const int dst_max = dst_min + dst_w;
|
||||
if (dst_max > dst_size) {
|
||||
src_max -= dst_max - dst_size;
|
||||
}
|
||||
// It is possible the source has negative size at this point, which means there is nothing to copy.
|
||||
// This must be checked by the caller.
|
||||
}
|
||||
|
||||
inline void clip_copy_region(
|
||||
Vector3i &src_min, Vector3i &src_max, const Vector3i &src_size, Vector3i &dst_min, const Vector3i &dst_size) {
|
||||
clip_copy_region_coord(src_min.x, src_max.x, src_size.x, dst_min.x, dst_size.x);
|
||||
clip_copy_region_coord(src_min.y, src_max.y, src_size.y, dst_min.y, dst_size.y);
|
||||
clip_copy_region_coord(src_min.z, src_max.z, src_size.z, dst_min.z, dst_size.z);
|
||||
}
|
||||
|
||||
// TODO Disallow copying from overlapping areas of the same buffer
|
||||
void VoxelBuffer::copy_from(const VoxelBuffer &other, Vector3i src_min, Vector3i src_max, Vector3i dst_min,
|
||||
unsigned int channel_index) {
|
||||
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
|
||||
Channel &channel = _channels[channel_index];
|
||||
@ -558,74 +534,28 @@ void VoxelBuffer::copy_from(const VoxelBuffer &other, Vector3i src_min, Vector3i
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3i::sort_min_max(src_min, src_max);
|
||||
|
||||
clip_copy_region(src_min, src_max, other._size, dst_min, _size);
|
||||
|
||||
const Vector3i area_size = src_max - src_min;
|
||||
|
||||
if (area_size.x <= 0 || area_size.y <= 0 || area_size.z <= 0) {
|
||||
// Degenerate area, we'll not copy anything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (area_size == _size && area_size == other._size) {
|
||||
// Equivalent of full copy between two blocks of same size
|
||||
copy_from(other, channel_index);
|
||||
|
||||
} else {
|
||||
if (other_channel.data != nullptr) {
|
||||
|
||||
if (channel.data == nullptr) {
|
||||
create_channel(channel_index, _size, channel.defval);
|
||||
}
|
||||
|
||||
if (channel.depth == DEPTH_8_BIT) {
|
||||
// Native format
|
||||
// Copy row by row
|
||||
Vector3i pos;
|
||||
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
|
||||
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
|
||||
// Row direction is Y
|
||||
const unsigned int src_ri =
|
||||
other.get_index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
|
||||
const unsigned int dst_ri = get_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
|
||||
memcpy(&channel.data[dst_ri], &other_channel.data[src_ri], area_size.y * sizeof(uint8_t));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (channel.depth == DEPTH_16_BIT) {
|
||||
Vector3i pos;
|
||||
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
|
||||
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
|
||||
const unsigned int src_ri =
|
||||
2 * other.get_index(pos.x + src_min.x, pos.y + src_min.y, pos.z + src_min.z);
|
||||
const unsigned int dst_ri =
|
||||
2 * get_index(pos.x + dst_min.x, pos.y + dst_min.y, pos.z + dst_min.z);
|
||||
memcpy(&channel.data[dst_ri], &other_channel.data[src_ri], area_size.y * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
// TODO Optimized versions
|
||||
Vector3i pos;
|
||||
for (pos.z = 0; pos.z < area_size.z; ++pos.z) {
|
||||
for (pos.x = 0; pos.x < area_size.x; ++pos.x) {
|
||||
for (pos.y = 0; pos.y < area_size.y; ++pos.y) {
|
||||
const uint64_t v = other.get_voxel(src_min + pos, channel_index);
|
||||
set_voxel(v, dst_min + pos, channel_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (channel.defval != other_channel.defval) {
|
||||
if (channel.data == nullptr) {
|
||||
create_channel(channel_index, _size, channel.defval);
|
||||
}
|
||||
fill_area(other_channel.defval, dst_min, dst_min + area_size, channel_index);
|
||||
if (other_channel.data != nullptr) {
|
||||
if (channel.data == nullptr) {
|
||||
// Note, we do this even if the pasted data happens to be all the same value as our current channel.
|
||||
// We assume that this case is not frequent enough to bother, and compression can happen later
|
||||
create_channel(channel_index, _size, channel.defval);
|
||||
}
|
||||
const unsigned int item_size = get_depth_byte_count(channel.depth);
|
||||
ArraySlice<const uint8_t> src(other_channel.data, other_channel.size_in_bytes);
|
||||
ArraySlice<uint8_t> dst(channel.data, channel.size_in_bytes);
|
||||
copy_3d_region_zxy(dst, _size, dst_min, src, other._size, src_min, src_max, item_size);
|
||||
|
||||
} else if (channel.defval != other_channel.defval) {
|
||||
// This logic is still required due to how source and destination regions can be specified.
|
||||
// The actual size of the destination area must be determined from the source area, after it has been clipped.
|
||||
Vector3i::sort_min_max(src_min, src_max);
|
||||
clip_copy_region(src_min, src_max, other._size, dst_min, _size);
|
||||
const Vector3i area_size = src_max - src_min;
|
||||
if (area_size.x <= 0 || area_size.y <= 0 || area_size.z <= 0) {
|
||||
// Degenerate area, we'll not copy anything.
|
||||
return;
|
||||
}
|
||||
fill_area(other_channel.defval, dst_min, dst_min + area_size, channel_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "../util/array_slice.h"
|
||||
#include "../util/fixed_array.h"
|
||||
#include "../util/math/rect3i.h"
|
||||
#include "funcs.h"
|
||||
|
||||
#include <core/map.h>
|
||||
#include <core/reference.h>
|
||||
@ -74,6 +75,19 @@ public:
|
||||
// Limit was made explicit for serialization reasons, and also because there must be a reasonable one
|
||||
static const uint32_t MAX_SIZE = 65535;
|
||||
|
||||
struct Channel {
|
||||
// Allocated when the channel is populated.
|
||||
// Flat array, in order [z][x][y] because it allows faster vertical-wise access (the engine is Y-up).
|
||||
uint8_t *data = nullptr;
|
||||
|
||||
// Default value when data is null
|
||||
uint64_t defval = 0;
|
||||
|
||||
Depth depth = DEFAULT_CHANNEL_DEPTH;
|
||||
|
||||
uint32_t size_in_bytes = 0;
|
||||
};
|
||||
|
||||
VoxelBuffer();
|
||||
~VoxelBuffer();
|
||||
|
||||
@ -123,6 +137,55 @@ public:
|
||||
void copy_from(const VoxelBuffer &other, Vector3i src_min, Vector3i src_max, Vector3i dst_min,
|
||||
unsigned int channel_index);
|
||||
|
||||
// Copy a region from a box of values, passed as a raw array.
|
||||
// `src_size` is the total 3D size of the source box.
|
||||
// `src_min` and `src_max` are the sub-region of that box we want to copy.
|
||||
// `dst_min` is the lower corner where we want the data to be copied into the destination.
|
||||
template <typename T>
|
||||
void copy_from(ArraySlice<const T> src, Vector3i src_size, Vector3i src_min, Vector3i src_max, Vector3i dst_min,
|
||||
unsigned int channel_index) {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
|
||||
const Channel &channel = _channels[channel_index];
|
||||
#ifdef DEBUG_ENABLED
|
||||
// Size of source and destination values must match
|
||||
ERR_FAIL_COND(channel.depth != get_depth_from_size(sizeof(T)));
|
||||
#endif
|
||||
|
||||
// This function always decompresses the destination.
|
||||
// To keep it compressed, either check what you are about to copy,
|
||||
// or schedule a recompression for later.
|
||||
decompress_channel(channel_index);
|
||||
|
||||
ArraySlice<T> dst(static_cast<T *>(channel.data), channel.size_in_bytes / sizeof(T));
|
||||
copy_3d_region_zxy<T>(dst, _size, dst_min, src, src_size, src_min, src_max);
|
||||
}
|
||||
|
||||
// Copy a region of the data into a dense buffer.
|
||||
// If the source is compressed, it is decompressed.
|
||||
// `dst` is a raw array storing grid values in a box.
|
||||
// `dst_size` is the total size of the box.
|
||||
// `dst_min` is the lower corner of where we want the source data to be stored.
|
||||
// `src_min` and `src_max` is the sub-region of the source we want to copy.
|
||||
template <typename T>
|
||||
void copy_to(ArraySlice<T> dst, Vector3i dst_size, Vector3i dst_min, Vector3i src_min, Vector3i src_max,
|
||||
unsigned int channel_index) const {
|
||||
ERR_FAIL_INDEX(channel_index, MAX_CHANNELS);
|
||||
|
||||
const Channel &channel = _channels[channel_index];
|
||||
#ifdef DEBUG_ENABLED
|
||||
// Size of source and destination values must match
|
||||
ERR_FAIL_COND(channel.depth != get_depth_from_size(sizeof(T)));
|
||||
#endif
|
||||
|
||||
if (channel.data == nullptr) {
|
||||
fill_3d_region_zxy<T>(dst, dst_size, dst_min, dst_min + (src_max - src_min), channel.defval);
|
||||
} else {
|
||||
ArraySlice<const T> src(static_cast<const T *>(channel.data), channel.size_in_bytes / sizeof(T));
|
||||
copy_3d_region_zxy<T>(dst, dst_size, dst_min, src, _size, src_min, src_max);
|
||||
}
|
||||
}
|
||||
|
||||
// Executes a read-write action on all cells of the provided box that intersect with this buffer.
|
||||
// `action_func` receives a voxel value from the channel, and returns a modified value.
|
||||
// if the returned value is different, it will be applied to the buffer.
|
||||
@ -300,19 +363,6 @@ private:
|
||||
void _b_copy_voxel_metadata_in_area(Ref<VoxelBuffer> src_buffer, Vector3 src_min_pos, Vector3 src_max_pos, Vector3 dst_pos);
|
||||
|
||||
private:
|
||||
struct Channel {
|
||||
// Allocated when the channel is populated.
|
||||
// Flat array, in order [z][x][y] because it allows faster vertical-wise access (the engine is Y-up).
|
||||
uint8_t *data = nullptr;
|
||||
|
||||
// Default value when data is null
|
||||
uint64_t defval = 0;
|
||||
|
||||
Depth depth = DEFAULT_CHANNEL_DEPTH;
|
||||
|
||||
uint32_t size_in_bytes = 0;
|
||||
};
|
||||
|
||||
// Each channel can store arbitary data.
|
||||
// For example, you can decide to store colors (R, G, B, A), gameplay types (type, state, light) or both.
|
||||
FixedArray<Channel, MAX_CHANNELS> _channels;
|
||||
@ -326,53 +376,6 @@ private:
|
||||
RWLock _rw_lock;
|
||||
};
|
||||
|
||||
// TODO Maybe find a better place to put these functions other than global space
|
||||
|
||||
inline FixedArray<uint8_t, 4> decode_weights_from_packed_u16(uint16_t packed_weights) {
|
||||
FixedArray<uint8_t, 4> weights;
|
||||
// SIMDable?
|
||||
// weights[0] = ((packed_weights >> 0) & 0x0f) << 4;
|
||||
// weights[1] = ((packed_weights >> 4) & 0x0f) << 4;
|
||||
// weights[2] = ((packed_weights >> 8) & 0x0f) << 4;
|
||||
// weights[3] = ((packed_weights >> 12) & 0x0f) << 4;
|
||||
|
||||
// Reduced but not SIMDable
|
||||
weights[0] = (packed_weights & 0x0f) << 4;
|
||||
weights[1] = packed_weights & 0xf0;
|
||||
weights[2] = (packed_weights >> 4) & 0xf0;
|
||||
weights[3] = (packed_weights >> 8) & 0xf0;
|
||||
return weights;
|
||||
}
|
||||
|
||||
inline FixedArray<uint8_t, 4> decode_indices_from_packed_u16(uint16_t packed_indices) {
|
||||
FixedArray<uint8_t, 4> indices;
|
||||
// SIMDable?
|
||||
indices[0] = (packed_indices >> 0) & 0x0f;
|
||||
indices[1] = (packed_indices >> 4) & 0x0f;
|
||||
indices[2] = (packed_indices >> 8) & 0x0f;
|
||||
indices[3] = (packed_indices >> 12) & 0x0f;
|
||||
return indices;
|
||||
}
|
||||
|
||||
inline uint16_t encode_indices_to_packed_u16(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
|
||||
return (a & 0xf) | ((b & 0xf) << 4) | ((c & 0xf) << 8) | ((d & 0xf) << 12);
|
||||
}
|
||||
|
||||
inline uint16_t encode_weights_to_packed_u16(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
|
||||
return (a >> 4) | ((b >> 4) << 4) | ((c >> 4) << 8) | ((d >> 4) << 12);
|
||||
}
|
||||
|
||||
// Checks if there are no duplicate indices in any voxel
|
||||
inline void debug_check_texture_indices(FixedArray<uint8_t, 4> indices) {
|
||||
FixedArray<bool, 16> checked;
|
||||
checked.fill(false);
|
||||
for (int i = 0; i < indices.size(); ++i) {
|
||||
unsigned int ti = indices[i];
|
||||
CRASH_COND(checked[ti]);
|
||||
checked[ti] = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline void debug_check_texture_indices_packed_u16(const VoxelBuffer &voxels) {
|
||||
for (int z = 0; z < voxels.get_size().z; ++z) {
|
||||
for (int x = 0; x < voxels.get_size().x; ++x) {
|
||||
|
@ -208,6 +208,68 @@ void test_encode_weights_packed_u16() {
|
||||
ERR_FAIL_COND(weights != decoded_weights);
|
||||
}
|
||||
|
||||
void test_copy_3d_region_zxy() {
|
||||
std::vector<uint16_t> src;
|
||||
std::vector<uint16_t> dst;
|
||||
const Vector3i src_size(8, 8, 8);
|
||||
const Vector3i dst_size(3, 4, 5);
|
||||
src.resize(src_size.volume(), 0);
|
||||
dst.resize(src_size.volume(), 0);
|
||||
for (unsigned int i = 0; i < src.size(); ++i) {
|
||||
src[i] = i;
|
||||
}
|
||||
|
||||
ArraySlice<const uint16_t> srcs = to_slice_const(src);
|
||||
ArraySlice<uint16_t> dsts = to_slice(dst);
|
||||
const Vector3i dst_min(0, 0, 0);
|
||||
const Vector3i src_min(2, 1, 0);
|
||||
const Vector3i src_max(5, 4, 3);
|
||||
copy_3d_region_zxy(dsts, dst_size, dst_min, srcs, src_size, src_min, src_max);
|
||||
|
||||
/*for (pos.y = src_min.y; pos.y < src_max.y; ++pos.y) {
|
||||
String s;
|
||||
for (pos.x = src_min.x; pos.x < src_max.x; ++pos.x) {
|
||||
const uint16_t v = srcs[pos.get_zxy_index(src_size)];
|
||||
if (v < 10) {
|
||||
s += String("{0} ").format(varray(v));
|
||||
} else if (v < 100) {
|
||||
s += String("{0} ").format(varray(v));
|
||||
} else {
|
||||
s += String("{0} ").format(varray(v));
|
||||
}
|
||||
}
|
||||
print_line(s);
|
||||
}
|
||||
print_line("----");
|
||||
const Vector3i dst_max = dst_min + (src_max - src_min);
|
||||
pos = Vector3i();
|
||||
for (pos.y = dst_min.y; pos.y < dst_max.y; ++pos.y) {
|
||||
String s;
|
||||
for (pos.x = dst_min.x; pos.x < dst_max.x; ++pos.x) {
|
||||
const uint16_t v = dsts[pos.get_zxy_index(dst_size)];
|
||||
if (v < 10) {
|
||||
s += String("{0} ").format(varray(v));
|
||||
} else if (v < 100) {
|
||||
s += String("{0} ").format(varray(v));
|
||||
} else {
|
||||
s += String("{0} ").format(varray(v));
|
||||
}
|
||||
}
|
||||
print_line(s);
|
||||
}*/
|
||||
|
||||
Vector3i pos;
|
||||
for (pos.z = src_min.z; pos.z < src_max.z; ++pos.z) {
|
||||
for (pos.x = src_min.x; pos.x < src_max.x; ++pos.x) {
|
||||
for (pos.y = src_min.y; pos.y < src_max.y; ++pos.y) {
|
||||
const uint16_t srcv = srcs[pos.get_zxy_index(src_size)];
|
||||
const uint16_t dstv = dsts[(pos - src_min + dst_min).get_zxy_index(dst_size)];
|
||||
ERR_FAIL_COND(srcv != dstv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define VOXEL_TEST(fname) \
|
||||
print_line(String("Running {0}").format(varray(#fname))); \
|
||||
fname()
|
||||
@ -220,6 +282,7 @@ void run_voxel_tests() {
|
||||
VOXEL_TEST(test_voxel_data_map_paste_mask);
|
||||
VOXEL_TEST(test_voxel_data_map_copy);
|
||||
VOXEL_TEST(test_encode_weights_packed_u16);
|
||||
VOXEL_TEST(test_copy_3d_region_zxy);
|
||||
|
||||
print_line("------------ Voxel tests end -------------");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user