#include <gvox/gvox.h>
#include <gvox/adapters/parse/voxlap.h>
#include <cstdlib>
#include <cstdint>
#include <bit>
#include <vector>
#include <array>
#include <new>
#include <limits>
struct VoxlapParseUserState {
GvoxVoxlapParseAdapterConfig config{};
size_t offset{};
std::vector<uint32_t> colors{};
std::vector<bool> is_solid{};
};
extern "C" void gvox_parse_adapter_voxlap_create(GvoxAdapterContext *ctx, void const *config) {
auto *user_state_ptr = malloc(sizeof(VoxlapParseUserState));
[[maybe_unused]] auto &user_state = *(new (user_state_ptr) VoxlapParseUserState());
gvox_adapter_set_user_pointer(ctx, user_state_ptr);
if (config != nullptr) {
user_state.config = *static_cast<GvoxVoxlapParseAdapterConfig const *>(config);
if (user_state.config.size_x == std::numeric_limits<uint32_t>::max()) {
user_state.config.size_x = 1024;
}
if (user_state.config.size_y == std::numeric_limits<uint32_t>::max()) {
user_state.config.size_y = 1024;
}
if (user_state.config.make_solid == std::numeric_limits<uint8_t>::max()) {
user_state.config.make_solid = 1;
}
if (user_state.config.is_ace_of_spades == std::numeric_limits<uint8_t>::max()) {
user_state.config.is_ace_of_spades = 0;
}
} else {
user_state.config = GvoxVoxlapParseAdapterConfig{
.size_x = 1024,
.size_y = 1024,
.size_z = 256,
.make_solid = 1,
};
}
}
extern "C" void gvox_parse_adapter_voxlap_destroy(GvoxAdapterContext *ctx) {
auto &user_state = *static_cast<VoxlapParseUserState *>(gvox_adapter_get_user_pointer(ctx));
user_state.~VoxlapParseUserState();
free(&user_state);
}
extern "C" void gvox_parse_adapter_voxlap_blit_begin(GvoxBlitContext *blit_ctx, GvoxAdapterContext *ctx, GvoxRegionRange const * , uint32_t ) {
auto &user_state = *static_cast<VoxlapParseUserState *>(gvox_adapter_get_user_pointer(ctx));
auto temp_i = uint32_t{};
if (user_state.config.is_ace_of_spades == 0) {
gvox_input_read(blit_ctx, user_state.offset, sizeof(temp_i), &temp_i);
user_state.offset += sizeof(temp_i);
if (temp_i != 0x09072000) {
gvox_adapter_push_error(ctx, GVOX_RESULT_ERROR_PARSE_ADAPTER_INVALID_INPUT, "A voxlap data stream must begin with a valid magic number");
return;
}
gvox_input_read(blit_ctx, user_state.offset, sizeof(temp_i), &temp_i);
user_state.offset += sizeof(temp_i);
if (temp_i != 1024) {
gvox_adapter_push_error(ctx, GVOX_RESULT_ERROR_PARSE_ADAPTER_INVALID_INPUT, "Second u32 not 1024");
return;
}
user_state.offset += sizeof(double) * 3;
user_state.offset += sizeof(double) * 3;
user_state.offset += sizeof(double) * 3;
user_state.offset += sizeof(double) * 3;
}
uint32_t x = 0;
uint32_t y = 0;
uint32_t z = 0;
uint32_t const voxel_n = user_state.config.size_x * user_state.config.size_y * user_state.config.size_z;
user_state.colors.resize(voxel_n);
user_state.is_solid.resize(voxel_n);
auto setgeom = [&user_state](uint32_t x, uint32_t y, uint32_t z, bool solid) {
if (z >= user_state.config.size_z) {
return;
}
auto index = x + (user_state.config.size_y - 1 - y) * user_state.config.size_x + (user_state.config.size_z - 1 - z) * user_state.config.size_x * user_state.config.size_y;
user_state.is_solid[index] = solid;
};
auto setcol = [&user_state](uint32_t x, uint32_t y, uint32_t z, uint32_t color) {
if (z >= user_state.config.size_z) {
return;
}
auto index = x + (user_state.config.size_y - 1 - y) * user_state.config.size_x + (user_state.config.size_z - 1 - z) * user_state.config.size_x * user_state.config.size_y;
uint32_t c = 0;
c |= ((color >> 0x10) & 0xff) << 0x00;
c |= ((color >> 0x08) & 0xff) << 0x08;
c |= ((color >> 0x00) & 0xff) << 0x10;
user_state.colors[index] = c;
};
for (y = 0; y < user_state.config.size_y; ++y) {
for (x = 0; x < user_state.config.size_x; ++x) {
for (z = 0; z < user_state.config.size_z; ++z) {
setgeom(x, y, z, user_state.config.make_solid != 0u);
}
z = 0;
auto v = std::array<uint8_t, 4>{};
for (;;) {
gvox_input_read(blit_ctx, user_state.offset, sizeof(v), &v);
uint32_t const number_4byte_chunks = v[0];
uint32_t const top_color_start = v[1];
uint32_t const top_color_end = v[2];
uint32_t bottom_color_start = 0;
uint32_t bottom_color_end = 0;
uint32_t len_top = 0;
uint32_t len_bottom = 0;
if (user_state.config.make_solid != 0u) {
for (auto i = z; i < top_color_start; ++i) {
setgeom(x, y, i, false);
}
}
auto color_offset = user_state.offset + 4;
for (z = top_color_start; z <= top_color_end; ++z) {
if (user_state.config.make_solid == 0u) {
setgeom(x, y, z, true);
}
auto col = uint32_t{};
gvox_input_read(blit_ctx, color_offset, sizeof(col), &col);
color_offset += 4;
setcol(x, y, z, col);
}
len_bottom = top_color_end - top_color_start + 1;
if (number_4byte_chunks == 0) {
user_state.offset += 4 * static_cast<size_t>(len_bottom + 1);
break;
}
len_top = (number_4byte_chunks - 1) - len_bottom;
user_state.offset += static_cast<size_t>(v[0]) * 4;
gvox_input_read(blit_ctx, user_state.offset, sizeof(v), &v);
bottom_color_end = v[3]; bottom_color_start = bottom_color_end - len_top;
for (z = bottom_color_start; z < bottom_color_end; ++z) {
if (user_state.config.make_solid == 0u) {
setgeom(x, y, z, true);
}
auto col = uint32_t{};
gvox_input_read(blit_ctx, color_offset, sizeof(col), &col);
color_offset += 4;
setcol(x, y, z, col);
}
}
}
}
}
extern "C" void gvox_parse_adapter_voxlap_blit_end(GvoxBlitContext * , GvoxAdapterContext * ) {
}
extern "C" auto gvox_parse_adapter_voxlap_query_details() -> GvoxParseAdapterDetails {
return {
.preferred_blit_mode = GVOX_BLIT_MODE_DONT_CARE,
};
}
extern "C" auto gvox_parse_adapter_voxlap_query_parsable_range(GvoxBlitContext * , GvoxAdapterContext *ctx) -> GvoxRegionRange {
auto &user_state = *static_cast<VoxlapParseUserState *>(gvox_adapter_get_user_pointer(ctx));
return {{0, 0, 0}, {user_state.config.size_x, user_state.config.size_y, user_state.config.size_z}};
}
extern "C" auto gvox_parse_adapter_voxlap_sample_region(GvoxBlitContext * , GvoxAdapterContext *ctx, GvoxRegion const * , GvoxOffset3D const *offset, uint32_t channel_id) -> GvoxSample {
auto &user_state = *static_cast<VoxlapParseUserState *>(gvox_adapter_get_user_pointer(ctx));
if (offset->x < 0 || offset->y < 0 || offset->z < 0 ||
static_cast<uint32_t>(offset->x) >= user_state.config.size_x ||
static_cast<uint32_t>(offset->y) >= user_state.config.size_y ||
static_cast<uint32_t>(offset->z) >= user_state.config.size_z) {
return {0u, 0u};
}
auto voxel_index =
static_cast<uint32_t>(offset->x) +
static_cast<uint32_t>(offset->y) * user_state.config.size_x +
static_cast<uint32_t>(offset->z) * user_state.config.size_x * user_state.config.size_y;
switch (channel_id) {
case GVOX_CHANNEL_ID_COLOR: return {user_state.colors[voxel_index], static_cast<uint8_t>(user_state.is_solid[voxel_index])};
case GVOX_CHANNEL_ID_MATERIAL_ID: return {static_cast<uint32_t>(user_state.is_solid[voxel_index]), static_cast<uint8_t>(user_state.is_solid[voxel_index])};
default: break;
}
return {0u, 0u};
}
extern "C" auto gvox_parse_adapter_voxlap_query_region_flags(GvoxBlitContext * , GvoxAdapterContext * , GvoxRegionRange const * , uint32_t ) -> uint32_t {
return 0;
}
extern "C" auto gvox_parse_adapter_voxlap_load_region(GvoxBlitContext * , GvoxAdapterContext *ctx, GvoxRegionRange const *range, uint32_t channel_flags) -> GvoxRegion {
uint32_t const available_channels = GVOX_CHANNEL_BIT_COLOR | GVOX_CHANNEL_BIT_MATERIAL_ID;
if ((channel_flags & ~available_channels) != 0) {
gvox_adapter_push_error(ctx, GVOX_RESULT_ERROR_PARSE_ADAPTER_REQUESTED_CHANNEL_NOT_PRESENT, "Tried loading a region with a channel that wasn't present in the original data");
}
GvoxRegion const region = {
.range = *range,
.channels = channel_flags & available_channels,
.flags = 0u,
.data = nullptr,
};
return region;
}
extern "C" void gvox_parse_adapter_voxlap_unload_region(GvoxBlitContext * , GvoxAdapterContext * , GvoxRegion * ) {
}
extern "C" void gvox_parse_adapter_voxlap_parse_region(GvoxBlitContext *blit_ctx, GvoxAdapterContext *ctx, GvoxRegionRange const *range, uint32_t channel_flags) {
uint32_t const available_channels = GVOX_CHANNEL_BIT_COLOR | GVOX_CHANNEL_BIT_MATERIAL_ID;
if ((channel_flags & ~available_channels) != 0) {
gvox_adapter_push_error(ctx, GVOX_RESULT_ERROR_PARSE_ADAPTER_REQUESTED_CHANNEL_NOT_PRESENT, "Tried loading a region with a channel that wasn't present in the original data");
}
GvoxRegion const region = {
.range = *range,
.channels = channel_flags & available_channels,
.flags = 0u,
.data = nullptr,
};
gvox_emit_region(blit_ctx, ®ion);
}