GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
1 / 1
100.0%
Branches:
-
-%
Functions:
-
-%

io/nanovdb.h
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file nanovdb.h
3 /// @brief NanoVDB grids: reading .nvdb/.vdb files and building grids.
4 #pragma once
5
6 #include "ernie/geometry/bounds.h"
7
8 #include <glm/glm.hpp>
9
10 #include <cstdint>
11 #include <filesystem>
12 #include <span>
13 #include <string>
14 #include <string_view>
15 #include <vector>
16
17 namespace ernie {
18
19 /// @brief A NanoVDB grid's value type, numbered as
20 /// `PNANOVDB_GRID_TYPE_*`.
21 /// Volumes sample scalars and float vectors; other types load only.
22 enum class NanoVdbType : std::uint32_t {
23 Unknown = 0,
24 Float = 1,
25 Double = 2,
26 Vec3f = 6,
27 Half = 9,
28 Fp4 = 13,
29 Fp8 = 14,
30 Fp16 = 15,
31 FpN = 16,
32 Vec4f = 17,
33 };
34
35 /// @brief One sparse grid in NanoVDB layout, with its header's facts.
36 /// The GPU walks the buffer in place (PNanoVDB). The grid's map
37 /// takes index space to the grid frame (OpenVDB's "world"), which a
38 /// Volume prim's transform places.
39 ///
40 /// References:
41 /// - K. Museth, "NanoVDB: A GPU-Friendly and Portable VDB Data
42 /// Structure For Real-Time Rendering And Simulation", ACM SIGGRAPH
43 /// 2021 Talks. https://doi.org/10.1145/3450623.3464653
44 /// - K. Museth, "VDB: High-Resolution Sparse Volumes with Dynamic
45 /// Topology", ACM Transactions on Graphics 32(3), 27, 2013.
46 /// https://doi.org/10.1145/2487228.2487235
47 /// - NanoVDB's file layout, nanovdb/io/IO.h and FileMetaData in
48 /// nanovdb/NanoVDB.h, OpenVDB 13.0.0.
49 50 struct NanoVdbGrid {
50 /// The grid's name.
51 std::string name;
52 /// The value type.
53 NanoVdbType type = NanoVdbType::Unknown;
54 /// Index coordinates to the grid frame, from the grid's map.
55 glm::dmat4 indexToGrid{1.0};
56 /// Lower inclusive index bound of active values; min > max when
57 /// there are none.
58 glm::ivec3 indexMin{0};
59 /// Upper inclusive index bound of active values.
60 glm::ivec3 indexMax{-1};
61 /// A voxel's size in the grid frame.
62 glm::dvec3 voxelSize{1.0};
63 /// Whether the grid has value statistics (HAS_MIN_MAX; Warp's
64 /// grids do not).
65 bool hasStatistics = false;
66 /// Smallest active value (vector length for vector grids); 0
67 /// without statistics.
68 double minValue = 0.0;
69 /// Largest active value (vector length for vector grids); 0
70 /// without statistics.
71 double maxValue = 0.0;
72 /// The grid from its GridData header on, padded to 8 bytes;
73 /// empty when only the head was read (`NanoVdbGridHead`).
74 std::vector<std::uint8_t> buffer;
75 /// The grid's size in bytes, per its header.
76 std::uint64_t size = 0;
77
78 /// @return Whether the grid has no active values.
79 bool Empty() const;
80 /// @return Whether volumes sample one value per voxel.
81 bool Scalar() const;
82 /// @return Whether volumes sample a 3-vector per voxel.
83 bool Vector() const;
84 /// @return Active bounds in the grid frame, grown by a voxel on each side for trilinear
85 /// sampling; the grid's origin when it has no active voxels.
86 Box GridBounds() const;
87 };
88
89 /// @brief Reads every grid of a NanoVDB file, in file order.
90 /// Accepts segmented files or a raw grid buffer.
91 /// @param path The .nvdb file.
92 /// @return The grids.
93 /// @throws std::runtime_error If unreadable, not NanoVDB, not major
94 /// version 32, or zip/blosc compressed (save uncompressed:
95 /// `nanovdb_convert` without `-z`/`-b`).
96 std::vector<NanoVdbGrid> ReadNanoVdb(const std::filesystem::path& path);
97
98 /// @brief Reads the headers of a NanoVDB file's grids (names, types, bounds and transforms)
99 /// without their voxels, as `NanoVdbGridHead` does for each grid.
100 /// @throws std::runtime_error As `ReadNanoVdb`.
101 std::vector<NanoVdbGrid> ReadNanoVdbHeads(const std::filesystem::path& path);
102
103 /// @brief Splits a raw in-memory NanoVDB buffer into its grids.
104 /// @param bytes Grids back to back, as Warp's `Volume.array()`.
105 /// @return The grids.
106 /// @throws std::runtime_error If it does not start with a version 32
107 /// NanoVDB grid.
108 std::vector<NanoVdbGrid> ParseNanoVdbGrids(std::span<const std::uint8_t> bytes);
109 /// @brief Reads the headers of a raw buffer's grids, without copying their voxels.
110 /// @throws std::runtime_error As `ParseNanoVdbGrids`.
111 std::vector<NanoVdbGrid> ParseNanoVdbHeads(std::span<const std::uint8_t> bytes);
112
113 /// Bytes at a grid's start holding its header, tree and root, root
114 /// statistics included.
115 inline constexpr std::size_t kNanoVdbHeadBytes = 1024;
116
117 /// @brief Reads a grid's header without its buffer, for grids on a
118 /// device of which only the head reaches the host.
119 /// @param head The first `kNanoVdbHeadBytes`, or fewer for small
120 /// grids.
121 /// @return The grid; its `size` locates the next grid.
122 /// @throws std::runtime_error If `head` is not a version 32 NanoVDB
123 /// grid or ends before its root.
124 NanoVdbGrid NanoVdbGridHead(std::span<const std::uint8_t> head);
125
126 /// @brief Finds the grid a UsdVol OpenVDBAsset names.
127 /// @param grids The file's grids.
128 /// @param name The grid name; empty matches any.
129 /// @param index Which match, 0 the first.
130 /// @return The grid, or null if there is none.
131 const NanoVdbGrid* FindNanoVdbGrid(std::span<const NanoVdbGrid> grids, std::string_view name,
132 int index = 0);
133
134 /// @brief Builds an in-memory NanoVDB float grid (a fog volume) from
135 /// dense values, active where not 0, with bounds and min/max.
136 /// @param values C-order (x, y, z) values, z fastest.
137 /// @param shape The voxel counts along x, y and z.
138 /// @param voxelSize The voxel size in the grid frame; positive.
139 /// @param origin Where index (0, 0, 0) lies in the grid frame.
140 /// @param name The grid's name.
141 /// @return The grid buffer, for a NanoVDBField's array.
142 /// @throws std::invalid_argument If `values` does not match `shape`
143 /// or `voxelSize` is not positive.
144 ///
145 /// References:
146 /// - K. Museth, "NanoVDB: A GPU-Friendly and Portable VDB Data
147 /// Structure For Real-Time Rendering And Simulation", ACM SIGGRAPH
148 /// 2021 Talks. https://doi.org/10.1145/3450623.3464653
149 /// - PNanoVDB.h, OpenVDB 13.0.0: the layout and its setters.
150 std::vector<std::uint8_t> DenseNanoVdbGrid(std::span<const float> values, const glm::ivec3& shape,
151 double voxelSize, const glm::dvec3& origin,
152 std::string_view name = "");
153
154 /// @brief Where a `DenseNanoVdbTopology`'s values go.
155 /// Leaf (a, b, c) starts at firstLeaf + ((a leaves.y + b) leaves.z +
156 /// c) leafBytes; voxel (i, j, k) is at valuesOffset + 4 ((i & 7) 64
157 /// + (j & 7) 8 + (k & 7)) within it.
158 struct DenseNanoVdbLayout {
159 /// Byte offset of the first leaf in the grid.
160 std::uint64_t firstLeaf = 0;
161 /// Bytes per leaf.
162 std::uint32_t leafBytes = 0;
163 /// Byte offset of a leaf's values.
164 std::uint32_t valuesOffset = 0;
165 /// Leaves (8^3 blocks) along x, y and z.
166 glm::ivec3 leaves{0};
167 };
168
169 /// @brief As `DenseNanoVdbGrid` with every voxel active and 0, for
170 /// values written in place later (a DenseField on the GPU).
171 /// No min and max.
172 /// @param shape The voxel counts along x, y and z.
173 /// @param voxelSize The voxel size in the grid frame; positive.
174 /// @param origin Where index (0, 0, 0) lies in the grid frame.
175 /// @param name The grid's name.
176 /// @param layout Receives where the values go.
177 /// @return The grid buffer.
178 /// @throws std::invalid_argument For a negative shape or a voxel
179 /// size that is not positive.
180 std::vector<std::uint8_t> DenseNanoVdbTopology(const glm::ivec3& shape, double voxelSize,
181 const glm::dvec3& origin, std::string_view name,
182 DenseNanoVdbLayout& layout);
183
184 } // namespace ernie
185