18 void* sys_malloc(
size_t size,
void* )
20 return std::malloc(size);
23 void* sys_realloc(
void*
ptr,
size_t ,
size_t size,
void* )
25 return std::realloc(
ptr, size);
28 void sys_free(
void*
ptr,
size_t ,
void* )
42 tvdb_allocator_t make_allocator()
44 tvdb_allocator_t alloc;
45 std::memset(&alloc, 0,
sizeof(alloc));
46 alloc.malloc_fn = &sys_malloc;
47 alloc.realloc_fn = &sys_realloc;
48 alloc.free_fn = &sys_free;
49 alloc.user_ctx =
nullptr;
53 constexpr const char* grid_type_token(
bool is_vector)
55 return is_vector ?
"Tree_vec3s_5_4_3" :
"Tree_float_5_4_3";
65 tvdb_value_type_t leaf_value_type(
const tvdb_grid_t& grid)
67 const int levels = grid.tree.layout.num_levels;
69 return TVDB_VALUE_NULL;
71 return grid.tree.layout.levels[levels - 1].value_type;
74 constexpr bool is_vector_type(tvdb_value_type_t
vt)
76 return vt == TVDB_VALUE_VEC3F ||
vt == TVDB_VALUE_VEC3D ||
vt == TVDB_VALUE_VEC3I;
79 constexpr bool needs_narrowing(tvdb_value_type_t
vt)
81 return vt != TVDB_VALUE_FLOAT &&
vt != TVDB_VALUE_VEC3F;
93 glm::vec3 grid_voxel_size(
const tvdb_transform_t& t)
96 case TVDB_TRANSFORM_UNIFORM_SCALE:
97 case TVDB_TRANSFORM_UNIFORM_SCALE_TRANSLATE:
98 return glm::vec3(
static_cast<float>(t.voxel_size[0]));
99 case TVDB_TRANSFORM_SCALE:
100 case TVDB_TRANSFORM_SCALE_TRANSLATE:
102 static_cast<float>(t.voxel_size[0]),
103 static_cast<float>(t.voxel_size[1]),
104 static_cast<float>(t.voxel_size[2]),
106 case TVDB_TRANSFORM_AFFINE:
108 static_cast<float>(t.matrix[0][0]),
109 static_cast<float>(t.matrix[1][1]),
110 static_cast<float>(t.matrix[2][2]),
112 case TVDB_TRANSFORM_TRANSLATION:
114 return glm::vec3(1.0F);
126 glm::vec3 grid_translation(
const tvdb_transform_t& t)
129 case TVDB_TRANSFORM_UNIFORM_SCALE_TRANSLATE:
130 case TVDB_TRANSFORM_SCALE_TRANSLATE:
131 case TVDB_TRANSFORM_TRANSLATION:
133 static_cast<float>(t.translation[0]),
134 static_cast<float>(t.translation[1]),
135 static_cast<float>(t.translation[2]),
137 case TVDB_TRANSFORM_AFFINE:
139 static_cast<float>(t.matrix[0][3]),
140 static_cast<float>(t.matrix[1][3]),
141 static_cast<float>(t.matrix[2][3]),
144 return glm::vec3(0.0F);
154 float half_to_float(uint16_t
h)
156 const uint32_t sign =
static_cast<uint32_t
>(
h & 0x8000U) << 16;
157 uint32_t exp = (
h >> 10) & 0x1FU;
158 uint32_t mant =
h & 0x3FFU;
166 while ((mant & 0x400U) == 0) {
171 bits = sign | ((exp + 112U) << 23) | (mant << 13);
173 }
else if (exp == 0x1FU) {
174 bits = sign | 0x7F800000U | (mant << 13);
176 bits = sign | ((exp + 112U) << 23) | (mant << 13);
180 std::memcpy(&f, &bits,
sizeof(f));
193 CastResult<float> narrow_scalar(tvdb_value_type_t
vt,
const void* bytes)
196 case TVDB_VALUE_FLOAT: {
198 std::memcpy(&v, bytes,
sizeof(v));
199 return try_convert<float>(v);
201 case TVDB_VALUE_DOUBLE: {
203 std::memcpy(&v, bytes,
sizeof(v));
204 return try_convert<float>(v);
206 case TVDB_VALUE_INT32: {
208 std::memcpy(&v, bytes,
sizeof(v));
209 return try_convert<float>(v);
211 case TVDB_VALUE_INT64: {
213 std::memcpy(&v, bytes,
sizeof(v));
214 return try_convert<float>(v);
216 case TVDB_VALUE_BOOL: {
218 std::memcpy(&v, bytes,
sizeof(v));
219 return try_convert<float>(v != 0);
221 case TVDB_VALUE_HALF: {
223 std::memcpy(&v, bytes,
sizeof(v));
224 CastResult<float> result;
225 result.value = half_to_float(v);
229 CastResult<float> result;
246 struct VectorNarrowResult {
251 VectorNarrowResult narrow_vector(tvdb_value_type_t
vt,
const void* bytes)
254 case TVDB_VALUE_VEC3F: {
256 std::memcpy(v, bytes,
sizeof(v));
257 return { { v[0], v[1], v[2] },
false };
259 case TVDB_VALUE_VEC3D: {
261 std::memcpy(v, bytes,
sizeof(v));
262 const auto cx = try_convert<float>(v[0]);
263 const auto cy = try_convert<float>(v[1]);
264 const auto cz = try_convert<float>(v[2]);
266 { cx.value.value_or(0.0F), cy.value.value_or(0.0F), cz.value.value_or(0.0F) },
267 cx.precision_loss || cy.precision_loss || cz.precision_loss,
270 case TVDB_VALUE_VEC3I: {
272 std::memcpy(v, bytes,
sizeof(v));
273 const auto cx = try_convert<float>(v[0]);
274 const auto cy = try_convert<float>(v[1]);
275 const auto cz = try_convert<float>(v[2]);
277 { cx.value.value_or(0.0F), cy.value.value_or(0.0F), cz.value.value_or(0.0F) },
278 cx.precision_loss || cy.precision_loss || cz.precision_loss,
297 float grid_background_scalar(
const tvdb_grid_t& grid)
299 if (grid.tree.num_nodes == 0) {
302 const tvdb_value_t& bg = grid.tree.nodes[0].u.root.background;
303 return narrow_scalar(bg.type, &bg.u).value.value_or(0.0F);
306 glm::vec3 grid_background_vector(
const tvdb_grid_t& grid)
308 if (grid.tree.num_nodes == 0) {
309 return glm::vec3(0.0F);
311 const tvdb_value_t& bg = grid.tree.nodes[0].u.root.background;
312 return narrow_vector(bg.type, &bg.u).value;
326 int bbox_visit(
const tvdb_leaf_view_t* leaf,
void* user)
328 auto* acc =
static_cast<BBoxAcc*
>(user);
329 const int32_t dim = 1 << leaf->log2dim;
330 const glm::ivec3
lo(leaf->origin[0], leaf->origin[1], leaf->origin[2]);
331 const glm::ivec3
hi =
lo + glm::ivec3(dim);
338 acc->min = glm::min(acc->min,
lo);
339 acc->max = glm::max(acc->max,
hi);
356 tvdb_value_type_t
vt;
361 struct DenseScalarCtx : DenseCtx {
365 struct DenseVectorCtx : DenseCtx {
366 std::vector<glm::vec3>*
out;
373 glm::ivec3 leaf_slot_coord(
const tvdb_leaf_view_t& leaf, int32_t s)
375 const int32_t log2dim = leaf.log2dim;
376 const int32_t mask = (1 << log2dim) - 1;
378 leaf.origin[0] + ((s >> (2 * log2dim)) & mask),
379 leaf.origin[1] + ((s >> log2dim) & mask),
380 leaf.origin[2] + (s & mask),
384 int dense_scalar_visit(
const tvdb_leaf_view_t* leaf,
void* user)
386 auto* ctx =
static_cast<DenseScalarCtx*
>(user);
387 const int32_t nslots = 1 << (3 * leaf->log2dim);
388 const auto* base =
reinterpret_cast<const uint8_t*
>(leaf->data);
389 const glm::ivec3 res = ctx->region_max - ctx->region_min;
391 for (int32_t s = 0; s < nslots; ++s) {
392 if (!tvdb_nodemask_is_on(leaf->value_mask, s)) {
395 const glm::ivec3 world = leaf_slot_coord(*leaf, s);
396 if (glm::any(glm::lessThan(world, ctx->region_min))
397 || glm::any(glm::greaterThanEqual(world, ctx->region_max))) {
400 const glm::ivec3 local = world - ctx->region_min;
401 const size_t index = (
static_cast<size_t>(local.z) * res.y + local.y) * res.x + local.x;
402 const auto converted = narrow_scalar(ctx->vt, base +
static_cast<size_t>(s) * ctx->elem_size);
403 (*ctx->out)[
index] = converted.value.value_or(0.0F);
404 if (converted.precision_loss && ctx->precision_lost) {
405 *ctx->precision_lost =
true;
411 int dense_vector_visit(
const tvdb_leaf_view_t* leaf,
void* user)
413 auto* ctx =
static_cast<DenseVectorCtx*
>(user);
414 const int32_t nslots = 1 << (3 * leaf->log2dim);
415 const auto* base =
reinterpret_cast<const uint8_t*
>(leaf->data);
416 const glm::ivec3 res = ctx->region_max - ctx->region_min;
418 for (int32_t s = 0; s < nslots; ++s) {
419 if (!tvdb_nodemask_is_on(leaf->value_mask, s)) {
422 const glm::ivec3 world = leaf_slot_coord(*leaf, s);
423 if (glm::any(glm::lessThan(world, ctx->region_min))
424 || glm::any(glm::greaterThanEqual(world, ctx->region_max))) {
427 const glm::ivec3 local = world - ctx->region_min;
428 const size_t index = (
static_cast<size_t>(local.z) * res.y + local.y) * res.x + local.x;
429 const auto converted = narrow_vector(ctx->vt, base +
static_cast<size_t>(s) * ctx->elem_size);
430 (*ctx->out)[
index] = converted.value;
431 if (converted.precision_loss && ctx->precision_lost) {
432 *ctx->precision_lost =
true;
438 constexpr size_t element_width(
bool is_vector)
440 return is_vector ?
sizeof(glm::vec3) : sizeof(float);
450 tvdb_grid_t make_template(
451 const Kinesis::Lattice3D& lattice,
bool is_vector,
char* grid_type)
454 std::memset(&tmpl, 0,
sizeof(tmpl));
456 std::strcpy(grid_type, grid_type_token(
is_vector));
457 tmpl.descriptor.grid_type = grid_type;
459 const tvdb_value_type_t
vt =
is_vector ? TVDB_VALUE_VEC3F : TVDB_VALUE_FLOAT;
461 tmpl.tree.layout.num_levels = 4;
462 tmpl.tree.layout.levels[0].node_type = TVDB_NODE_ROOT;
463 tmpl.tree.layout.levels[1].node_type = TVDB_NODE_INTERNAL;
464 tmpl.tree.layout.levels[2].node_type = TVDB_NODE_INTERNAL;
465 tmpl.tree.layout.levels[3].node_type = TVDB_NODE_LEAF;
466 tmpl.tree.layout.levels[0].log2dim = 0;
467 tmpl.tree.layout.levels[1].log2dim = 5;
468 tmpl.tree.layout.levels[2].log2dim = 4;
469 tmpl.tree.layout.levels[3].log2dim = 3;
470 for (
int lv = 0; lv < 4; ++lv) {
471 tmpl.tree.layout.levels[lv].value_type =
vt;
474 const glm::vec3 cell = lattice.cell_size();
475 const glm::vec3 origin = lattice.bounds.min + 0.5F * cell;
477 tmpl.transform.type = TVDB_TRANSFORM_SCALE_TRANSLATE;
478 for (
int axis = 0; axis < 3; ++axis) {
479 tmpl.transform.scale_values[axis] = cell[axis];
480 tmpl.transform.voxel_size[axis] = cell[axis];
481 tmpl.transform.translation[axis] = origin[axis];
510 std::deque<std::vector<tvdb_meta_entry_t>>
entries;
518 : m_state(
std::make_unique<
State>())
524 for (
auto& grid :
m_state->grids) {
525 grid.metadata.entries =
nullptr;
526 grid.metadata.count = 0;
527 grid.metadata.capacity = 0;
528 tvdb_grid_destroy_owned(&grid);
532 tvdb_file_close(&
m_state->read_file);
547 +
"': value bytes do not match coordinate count";
552 +
"': background is the wrong width";
556 static_assert(
sizeof(glm::ivec3) ==
sizeof(tvdb_vec3i),
557 "glm::ivec3 and tvdb_vec3i must be layout compatible for the "
558 "coordinate span to be passed through without a copy");
560 auto& type_token =
m_state->type_tokens.emplace_back();
561 tvdb_grid_t tmpl = make_template(lattice, spec.
is_vector, type_token.data());
566 const bool ok = tvdb_grid_from_sparse_typed_using_template(
568 reinterpret_cast<const tvdb_vec3i*
>(spec.
coords.data()),
571 spec.
is_vector ? TVDB_VALUE_VEC3F : TVDB_VALUE_FLOAT,
581 auto& list =
m_state->entries.emplace_back();
582 list.reserve(spec.
metadata.size() + 1);
584 auto add_entry = [&](std::string_view key, std::string_view
value) {
585 std::string&
k =
m_state->strings.emplace_back(key);
586 std::string& t =
m_state->strings.emplace_back(
"string");
589 tvdb_meta_entry_t entry;
590 std::memset(&entry, 0,
sizeof(entry));
591 entry.name =
k.data();
592 entry.type_name = t.data();
593 entry.value.type = TVDB_VALUE_STRING;
594 entry.value.u.s.str = v.data();
595 entry.value.u.s.len = v.size();
596 list.push_back(entry);
601 add_entry(
"name", spec.
name);
604 add_entry(key,
value);
607 grid.metadata.entries = list.data();
608 grid.metadata.count = list.size();
609 grid.metadata.capacity = list.size();
610 grid.metadata.alloc =
nullptr;
612 m_state->grids.push_back(grid);
624 std::memset(&
out, 0,
sizeof(
out));
625 out.alloc = make_allocator();
630 std::memset(&err, 0,
sizeof(err));
632 const tvdb_status_t st = tvdb_file_save(
633 &
out, path.c_str(), compression, level, 0, &err);
637 + (err.message[0] !=
'\0' ? err.message :
"unknown");
651 tvdb_file_close(&
m_state->read_file);
652 m_state->read_file_open =
false;
656 std::memset(&err, 0,
sizeof(err));
658 if (tvdb_file_open(&
m_state->read_file, path.c_str(),
nullptr, &err) != TVDB_OK) {
660 + (err.message[0] !=
'\0' ? err.message :
"unknown");
663 m_state->read_file_open =
true;
665 if (tvdb_read_all_grids(&
m_state->read_file, &err) != TVDB_OK) {
667 + (err.message[0] !=
'\0' ? err.message :
"unknown");
668 tvdb_file_close(&
m_state->read_file);
669 m_state->read_file_open =
false;
678 return m_state->read_file_open ? tvdb_grid_count(&
m_state->read_file) : 0;
689 const tvdb_grid_t& grid =
m_state->read_file.grids[
index];
694 const tvdb_value_type_t
vt = leaf_value_type(grid);
695 out.is_vector = is_vector_type(
vt);
696 out.narrowed = needs_narrowing(
vt);
698 out.voxel_size = grid_voxel_size(grid.transform);
699 out.translation = grid_translation(grid.transform);
702 tvdb_grid_visit_leaves(&grid, bbox_visit, &acc);
703 out.has_active = acc.has_any;
704 out.active_min = acc.min;
705 out.active_max = acc.max;
707 out.background_scalar = grid_background_scalar(grid);
708 out.background_vector = grid_background_vector(grid);
719 const tvdb_grid_t& grid =
m_state->read_file.grids[
index];
721 for (
size_t i = 0; i < grid.metadata.count; ++i) {
722 const tvdb_meta_entry_t& entry = grid.metadata.entries[i];
723 if (!entry.name || key != entry.name) {
726 if (entry.value.type != TVDB_VALUE_STRING || !entry.value.u.s.str) {
729 return { entry.value.u.s.str, entry.value.u.s.len };
737 const glm::uvec3& resolution,
739 std::vector<float>&
out,
743 m_last_error =
"read_dense_scalar: grid index out of range";
746 if (resolution.x == 0 || resolution.y == 0 || resolution.z == 0) {
751 const tvdb_grid_t& grid =
m_state->read_file.grids[
index];
752 const tvdb_value_type_t
vt = leaf_value_type(grid);
754 if (is_vector_type(
vt)) {
756 m_last_error =
"read_dense_scalar: grid '" + std::string(
name ?
name :
"") +
"' is vector-typed";
761 m_last_error =
"read_dense_scalar: grid has an unsupported leaf value type";
769 out.assign(
static_cast<size_t>(resolution.x) * resolution.y * resolution.z, background);
771 DenseScalarCtx ctx {};
773 ctx.region_max =
region_min + glm::ivec3(resolution);
779 tvdb_grid_visit_leaves(&grid, dense_scalar_visit, &ctx);
786 const glm::uvec3& resolution,
787 const glm::vec3& background,
788 std::vector<glm::vec3>&
out,
792 m_last_error =
"read_dense_vector: grid index out of range";
795 if (resolution.x == 0 || resolution.y == 0 || resolution.z == 0) {
800 const tvdb_grid_t& grid =
m_state->read_file.grids[
index];
801 const tvdb_value_type_t
vt = leaf_value_type(grid);
803 if (!is_vector_type(
vt)) {
805 m_last_error =
"read_dense_vector: grid '" + std::string(
name ?
name :
"") +
"' is scalar-typed";
810 m_last_error =
"read_dense_vector: grid has an unsupported leaf value type";
818 out.assign(
static_cast<size_t>(resolution.x) * resolution.y * resolution.z, background);
820 DenseVectorCtx ctx {};
822 ctx.region_max =
region_min + glm::ivec3(resolution);
828 tvdb_grid_visit_leaves(&grid, dense_vector_visit, &ctx);