15 constexpr int k_deflate_level = 5;
40 ActiveSet extract_active(
41 const Kakshya::VolumeField& field,
42 const Kinesis::Lattice3D& lattice,
45 const glm::uvec3 res = lattice.resolution;
48 out.is_vector = field.is_vector();
50 const size_t cells = field.element_count();
51 const size_t guess = cells / 4;
52 out.coords.reserve(guess);
53 out.values.reserve(guess * (
out.is_vector ?
sizeof(glm::vec3) : sizeof(float)));
55 const auto emit = [&](uint32_t x, uint32_t y, uint32_t z,
57 out.coords.emplace_back(
58 static_cast<int32_t
>(x),
static_cast<int32_t
>(y),
static_cast<int32_t
>(z));
59 const auto* bytes =
static_cast<const std::byte*
>(
value);
60 out.values.insert(
out.values.end(), bytes, bytes +
width);
63 if (
const auto* scalars = field.as_scalar()) {
65 for (uint32_t z = 0; z < res.z; ++z) {
66 for (uint32_t y = 0; y < res.y; ++y) {
67 for (uint32_t x = 0; x < res.x; ++x, ++i) {
68 const float v = (*scalars)[i];
70 emit(x, y, z, &v,
sizeof(
float));
78 const auto* vectors = field.as_vector();
82 for (uint32_t z = 0; z < res.z; ++z) {
83 for (uint32_t y = 0; y < res.y; ++y) {
84 for (uint32_t x = 0; x < res.x; ++x, ++i) {
85 const glm::vec3& v = (*vectors)[i];
86 if (glm::dot(v, v) >= threshold_sq) {
87 emit(x, y, z, &v,
sizeof(glm::vec3));
95 uint32_t compression_flags(
const VolumeWriteOptions& options)
97 if (options.compression < 0) {
100 return static_cast<uint32_t
>(options.compression);
106 float threshold_for(
const std::string&
name,
const VolumeWriteOptions& options)
108 auto it = options.field_thresholds.find(
name);
109 return it != options.field_thresholds.end() ? it->second : options.activity_threshold;
112 std::string extension_of(
const std::string& filepath)
114 auto ext = std::filesystem::path(filepath).extension().string();
115 if (!ext.empty() && ext[0] ==
'.') {
118 std::ranges::transform(ext, ext.begin(),
119 [](
unsigned char c) { return std::tolower(c); });
143 return "covariant normalize";
145 return "contravariant relative";
147 return "contravariant absolute";
159 std::vector<std::pair<std::string, std::string>> semantics_metadata(
160 const Kakshya::VolumeField& field)
162 std::vector<std::pair<std::string, std::string>> meta;
165 meta.emplace_back(
"class", class_token(field.semantics.value_class));
166 if (field.is_vector()) {
167 meta.emplace_back(
"vector_type", variance_token(field.semantics.variance));
184 []() -> std::unique_ptr<VolumeWriter> {
185 return std::make_unique<VDBWriter>();
189 "VDBWriter registered for: vdb");
194 return extension_of(filepath) ==
"vdb";
207 const std::string& filepath,
220 const glm::vec3 vector_bg { options.
background };
224 for (
const auto& field : data.
fields) {
225 const ActiveSet active = extract_active(
226 field, data.
lattice, threshold_for(field.name, options));
228 const auto meta = semantics_metadata(field);
230 const auto background = active.is_vector
231 ? std::as_bytes(std::span(&vector_bg, 1))
232 : std::as_bytes(std::span(&scalar_bg, 1));
236 .coords = active.coords,
237 .values = active.values,
238 .background = background,
239 .is_vector = active.is_vector,
250 "VDBWriter: '{}' {} of {} cells active",
251 field.name, active.coords.size(), data.
cell_count());
256 if (!archive.
save(resolved, compression_flags(options), k_deflate_level)) {
263 "VDBWriter: wrote '{}', {} grids", resolved, archive.
grid_count());
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
std::vector< float > * out
std::vector< glm::ivec3 > coords
std::vector< std::byte > values
bool save(const std::string &path, uint32_t compression, int level)
Write every added grid to one file.
std::string_view last_error() const
size_t grid_count() const
bool add_grid(const Kinesis::Lattice3D &lattice, const VDBGridSpec &spec)
Build one grid from active cells and retain it for saving.
RAII boundary around tinyvdb's C read and write paths.
bool write(const std::string &filepath, const Kakshya::VolumeData &data, const VolumeWriteOptions &options={}) override
Write volume data to disk.
std::vector< std::string > get_supported_extensions() const override
File extensions handled by this writer (without dot).
static void register_with_registry()
Register this writer with the VolumeWriterRegistry.
bool can_write(const std::string &filepath) const override
Check whether this writer handles the given filepath.
static VolumeWriterRegistry & instance()
constexpr uint32_t Default
std::string resolve_write_path(const std::string &filepath)
Anchor a relative output path to Config::SOURCE_DIR.
@ FileIO
Filesystem I/O operations.
@ Init
Engine/subsystem initialization.
@ IO
Networking, file handling, streaming.
LatticeValueClass
What the values sampled over a lattice mean geometrically.
@ FogVolume
Density of a participating medium, zero outside it.
@ Staggered
Vector components sampled on cell faces, not centres.
@ LevelSet
Signed distance to a surface at the zero crossing.
VectorVariance
How a vector quantity's components behave under a change of frame.
@ ContravariantAbsolute
Contravariant, treated as a world-space position.
@ ContravariantRelative
Transforms by the frame itself; velocities.
@ CovariantNormalize
Inverse transpose, then renormalized.
@ Covariant
Transforms by the inverse transpose.
One grid's worth of input to VDBArchive::add_grid.
float background
Value assumed by every inactive cell.
Configuration for volume writing.
Kinesis::Lattice3D lattice
bool is_consistent() const
Check that every field is densely populated over the lattice.
size_t cell_count() const
Cells in the lattice, which every field's element_count() must equal.
std::vector< VolumeField > fields
A lattice and every field sampled over it, held in host memory.