xuvdb 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xuvdb-0.1.0/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 xuvdb contributors
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
xuvdb-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: xuvdb
3
+ Version: 0.1.0
4
+ Summary: Editable sparse voxel volumes with OpenVDB interop, built on quadrants kernels
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: <3.14,>=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: numpy
10
+ Requires-Dist: quadrants
11
+ Provides-Extra: genesis
12
+ Requires-Dist: genesis-world; extra == "genesis"
13
+ Provides-Extra: openvdb
14
+ Requires-Dist: pyopenvdb; extra == "openvdb"
15
+ Provides-Extra: test
16
+ Requires-Dist: pytest; extra == "test"
17
+ Dynamic: license-file
18
+
19
+ # XUVDB 太虚 — genesis 自研稀疏体素格式(可编辑 · 与 OpenVDB 互转)
20
+
21
+ > 「不游乎太虚。」——《庄子·知北游》
22
+ > 「太虚无形,气之本体。」——张载《正蒙·太和》
23
+
24
+ `xuvdb` 取「太虚」之音:这是中文里对 VDB「无界而稀疏的索引域」最准确的翻译——无界
25
+ (root 哈希域不设上限),无形(未分配的空间没有形态,采样即得背景值)。
26
+
27
+ `xuvdb` 基于 quadrants 内核:**自己的 `.xuvdb` 格式**(可修改、可在内核里写值),
28
+ 以及**与 OpenVDB 的双向互转**(原生 `.vdb` 文件读写,无需安装 OpenVDB;有 `pyopenvdb`
29
+ 绑定时还能内存级互转)。
30
+
31
+ ## 命名规范(三层名字,各归其位)
32
+
33
+ | 层 | 名字 | 规则 |
34
+ |---|---|---|
35
+ | 项目名 | `xuvdb` | 拼音;不用 Open 前缀(ASWF 语境下暗示基金会血统) |
36
+ | 命名空间 / 扩展名 | `xuvdb` / `.xuvdb` | API 标识符一律英文:`xuvdb.prune()`、`xuvdb.VdbGrid`,绝不是 `xuvdb.sunyi()`。道家词只活在概念层(文档题词、日志、可视化标签) |
37
+ | 互转文件 | `.vdb` | **自有格式绝不写 `.vdb` 后缀**(Houdini/Blender/Cycles/Arnold 按扩展名当 OpenVDB 解析,格式不符时静默出错或崩溃,极难定位;`save()` 对 `.vdb` 路径直接拒绝)。要互通就单独导出:`write_vdb()` 产出真正的 OpenVDB 流 |
38
+
39
+ ## 为什么是它
40
+
41
+ | | OpenVDB | NanoVDB | XUVDB |
42
+ |---|---|---|---|
43
+ | 结构 | 5 层 B+树,CPU C++ | 同构只读缓冲,GPU | 两级:dict 叶根 + 稠密叶块 |
44
+ | 可修改 | ✅(CPU) | ❌ GPU 只读 | ✅ Python 端任意结构编辑;**内核端可写值** |
45
+ | 可微分包差 | ❌ | ❌ | 值缓冲可被 quadrants 内核读写(拓扑固定) |
46
+ | Python 依赖 | pyopenvdb(需自行构建) | — | 仅 numpy + quadrants |
47
+
48
+ 定位不是替换任何求解器,而是补齐 README 物理栈背后的**空间表示层**(落点见下文)。
49
+
50
+ ## 安装
51
+
52
+ 独立 Python 包(src 布局,`import xuvdb` 即用):
53
+
54
+ ```bash
55
+ uv pip install . # 或 pip install .
56
+ uv pip install -e ".[test]" # 开发模式 + pytest
57
+ ```
58
+
59
+ 可选 extras:`[openvdb]`(pyopenvdb 内存级互转)、`[genesis]`(运行引擎侧示例需要 genesis-world)、
60
+ `[test]`(pytest)。
61
+
62
+ ## 快速上手
63
+
64
+ ```python
65
+ import numpy as np
66
+ import xuvdb
67
+
68
+ # 1) 编辑:窄带 level set 球(体素 0.05,带宽 3 体素)
69
+ grid = xuvdb.VdbGrid(background=3 * 0.05, voxel_size=0.05, leaf_log2=4,
70
+ name="shield", grid_class="level set")
71
+ grid.stamp_sphere((0.3, 0.2, 0.1), radius=0.25, band=3.0)
72
+ grid.stamp_sphere((0.5, 0.2, 0.1), radius=0.10) # CSG:并入第二个球
73
+ grid.fill_box((-2, -2, -2), (2, 2, 2), value=0.0) # 任意稠密填充(示例)
74
+ grid.prune()
75
+
76
+ # 2) 自有格式落盘 / 读回(多网格、f32/f64/vec3)
77
+ xuvdb.save("scene.xuvdb", [grid])
78
+ grids = xuvdb.load("scene.xuvdb")
79
+
80
+ # 3) 与 OpenVDB 互通(显式导出:真正的 OpenVDB 流,Houdini/Blender 直接打开)
81
+ xuvdb.write_vdb("scene.vdb", [grid])
82
+ back = xuvdb.read_vdb("scene.vdb", grid_name="shield")
83
+
84
+ # 4) 内核采样 / 写值(等同 Warp example_nvdb 的用法,但值可写)
85
+ vol = xuvdb.GpuVolume(grid)
86
+ pts = np.array([[0.3, 0.2, 0.36]], dtype=np.float32)
87
+ d = vol.sample(pts, linear=True) # SDF 距离
88
+ n = vol.sdf_normal(pts) # 有限差分表面法向
89
+ vol.write_voxels(pts, np.array([-0.01], np.float32)); vol.sync_to_host()
90
+
91
+ # 5) 粒子 ⇄ 体积(液体/油,见落点④)
92
+ drops = np.array([[0.1, 0.0, 0.0], [0.2, 0.0, 0.0]])
93
+ fog = xuvdb.VdbGrid(voxel_size=0.05, name="liquid", grid_class="fog volume")
94
+ fog.scatter_particles(drops, h=4 * 0.05, weights=1.0) # SPH cubic 核密度 splat
95
+ surf = xuvdb.VdbGrid(background=3 * 0.05, voxel_size=0.05, grid_class="level set")
96
+ surf.union_spheres(drops, radius=0.03) # particle level set 表面代理
97
+
98
+ # 6) DDA 射线(空叶块按块跳过,交叉点二分细化到亚体素)
99
+ t, point, value = xuvdb.ray_surface_hit(grid, (0.3, 0.2, 2.0), (0, 0, -1))
100
+ ```
101
+
102
+ 与引擎稠密场的桥:
103
+
104
+ ```python
105
+ # 稠密 qd.field / numpy SDF(如 rigid geom 的 sdf_val)→ 稀疏
106
+ sparse = xuvdb.VdbGrid.from_dense(dense_sdf, origin=ijk_min, voxel_size=h,
107
+ background=band_h, grid_class="level set")
108
+ dense, ijk_min = sparse.to_dense() # 反向:渲染器 / 求解器输入
109
+ ```
110
+
111
+ ## 格式
112
+
113
+ ### `.xuvdb`(自有格式,小端)
114
+
115
+ ```
116
+ "XUVDB" | u8 version=1 | u8 flags | u16 n_grids
117
+ per grid:
118
+ str name | u8 type(0=f32,1=f64,2=vec3f) | u8 leaf_log2 | u8 class | u8 rsv
119
+ f64[3] voxel_size | f64[3] origin_world | background
120
+ u32 n_leaves
121
+ per leaf(按叶原点排序): i32[3] origin | u64[dim³/64] active mask | 值稠密数组
122
+ ```
123
+
124
+ - 叶内线性序 `n = x·dim² + y·dim + z`(z 最快),**与 OpenVDB leaf 序一致**,互转零转置。
125
+ - 叶块与 OpenVDB LeafNode 同为稠密缓冲:level set 内部体素的 `-background` 值在
126
+ save/load 后保留。
127
+ - 变换约定与 OpenVDB 线性映射一致:`world = index · voxel_size + origin_world`,
128
+ 体素中心在整数索引处。
129
+
130
+ ### `.vdb`(OpenVDB 官方流格式,仅显式导出用)
131
+
132
+ 字节布局逐一对照 OpenVDB 源码实现(`io/Archive.cc`、`GridDescriptor.cc`、`Compression.h`、
133
+ `tree/*.h`、`math/Maps.h`、`Metadata.h`):
134
+
135
+ - 头 57B:`int64 magic 0x56444220`、u32 文件版本、u32 库主/次版本、u8 offsets 标志、36 字符 UUID;
136
+ - 文件级元数据表 → i32 网格数 → **描述符与网格流交错**(描述符、i64×3 偏移、网格流、下一描述符…);
137
+ - 网格流:u32 压缩标志 → 元数据表(name/class/file_* 统计)→ 变换(ScaleTranslate 家族
138
+ = 类型字符串 + 6×Vec3d)→ 树(`i32 buffer_count`、root 背景 + tiles + 子节点);
139
+ - 树:root → InternalNode(5)(32³ 桌、512×u64 双掩码、值表)→ InternalNode(4)(64 项)→
140
+ LeafNode(8³)(拓扑段只有值掩码,origin 由树路径隐含;缓冲段掩码重写一遍 + 值块);
141
+ - 值块:`io::writeCompressedValues` 语义 —— 1 字节 metadata(0=惰性值全为 +bg、1=-bg、
142
+ 2/4/5=带 1~2 个惰性值/选择掩码、6=全量数组)+ 值(按 ACTIVE_MASK 只存 active)。
143
+
144
+ 写侧:文件版本 224、压缩 = `COMPRESS_ACTIVE_MASK`(无 zip/blosc),任何 OpenVDB ≥ 9 可读。
145
+ 读侧:支持 `COMPRESS_NONE` / `COMPRESS_ZIP`(stdlib zlib)/ `COMPRESS_ACTIVE_MASK` /
146
+ `_HalfFloat` 网格;Blosc 抛出明确错误;root/internode 活动 tile 物化为稠密叶
147
+ (受 `max_tile_voxels` 上限保护)。
148
+
149
+ ## 已知边界
150
+
151
+ - `GpuVolume` 只支持 f32 标量网格;写入只改值不改拓扑、不动 active 掩码(掩码是宿主侧
152
+ 状态)。结构性编辑后需重新打包。
153
+ - `scatter_particles`/`union_spheres` 是 Python 循环 + 叶切片向量化:千级粒子适用,
154
+ 大规模生产需按叶批处理(未做)。`union_spheres` 的 min-of-spheres 距离在重叠粒子间
155
+ 的凹桥区是真实距离的上界(Lipschitz 精确),做碰撞/渲染代理足够,精确表面请离线
156
+ 用正规表面重建精修。
157
+ - `.vdb` 读侧不支持:Blosc 压缩、实例化网格(instance parent)、点云网格(PointDataGrid)、
158
+ `5_4_3` 以外的树形。写侧不产生 root tile(全部以叶表达)。
159
+ - 与求解器自动微分的边界:XUVDB 提供的是**采样/写入原语**;把 VDB 值直接接入反传图需要
160
+ 包一层自定义求导规则(这正是 FastSweeping 等算子不可微的同一边界)。
161
+
162
+ ## 与引擎四个落点的对接
163
+
164
+ 对应《Genesis × OpenVDB 重合度报告》(`genesis_openvdb_overlap.html`)的结论:
165
+
166
+ 1. **刚性 SDF(`utils/sdf.py`,风险最低)**:`geom.sdf_val` 稠密体 → `VdbGrid.from_dense`
167
+ 窄带化 → `GpuVolume.sample/sdf_normal` 做内核内碰撞采样;粗块最小值下界的带宽门控
168
+ 对应这里"空叶块直接跳过"——稀疏性免费获得。维护/雕刻工装(`csg` + `stamp_sphere`)
169
+ 可离线改碰撞体。
170
+ 2. **MPM 背景网格(解除 1e9 上限)**:`use_sparse_grid` 被移除的原因在 GPU 端动态拓扑;
171
+ XUVDB 的分工是"拓扑宿主端冻结 + 值内核端可写"。粒子覆盖块用 `fill_box` 声明、每步
172
+ `write_voxels` 回写网格值,是向稀疏 MPM 过渡的最小代价路径(需自行验证与现有
173
+ dense reset 的性能对比)。
174
+ 3. **烟尘 / 稳定流体(收益上限最高)**:压力投影每帧回写全网格,短期不建议动求解器;
175
+ 现实路径是**出口侧**:每 N 步 `from_dense(density_field)` → `write_vdb` 交给
176
+ Houdini/Blender 体渲染;进口侧用 Houdini 烘的 `.vdb` 作初始条件(`read_vdb` →
177
+ `to_dense`)。
178
+ 4. **液体与油(SPH,粒子 ⇄ 体积)**:Genesis 的液体是 Lagrangian SPH(hash grid 邻域,
179
+ VDB 不做邻居搜索),与稀疏体积的接口在两端——
180
+ - **出口(每步/每 N 步)**:`scatter_particles(pos, h, mass)` 把粒子 splat 成密度
181
+ fog 网格(SPH cubic 核、单位积分,质量守恒已测)→ `write_vdb` 给 Houdini/Blender
182
+ 体渲染液体,比逐粒子渲染便宜得多;要表面就 `union_spheres(pos, r, band)` 出
183
+ particle level set 表面代理(喷雾/液滴场景),离线可用 OpenVDB 生态精修。
184
+ - **进口**:Houdini 烘的液面/容器 `.vdb` level set → `read_vdb` → `GpuVolume.sample`
185
+ 做容器碰撞 SDF 或装液初始条件(与落点①同一套采样机制,SPH 边界碰撞即刚体 SDF
186
+ 碰撞的复用)。
187
+ - **油(高黏/两相)**:黏性在 SPH 求解器侧,体积层只管表征——两相液体(油-水、
188
+ 油-气)每相一个 fog 网格,即混合分数场 α:`scatter_particles` 按相内粒子各 splat
189
+ 一份,导出双网格 `.vdb`,渲染端做 α 混合;界面 SDF 用两相 `union_spheres` 之差
190
+ (`csg 'diff'`)。
191
+ - **在线更新**:`GpuVolume.write_voxels` 可增量回写密度值做实时可视化;拓扑(叶
192
+ 集合)按粒子包围盒周期性重打包(拓扑宿主端冻结的同一分工)。
193
+
194
+ ## 许可证
195
+
196
+ Apache-2.0(与上游 quadrants、genesis-world 一致),见 [LICENSE](LICENSE)。
197
+
198
+ ## 测试
199
+
200
+ ```
201
+ pytest tests/ -q
202
+ ```
203
+
204
+ 覆盖:树编辑/CSG/稠密互转、粒子 splat(质量守恒、可加性、vec3 速度场、双核函数)、
205
+ `union_spheres`(表面/窄带/逐粒子半径/射线命中/与 stamp 复合)、`.xuvdb` 多网格多类型
206
+ 往返、`.vdb` 头部字节与偏移校验、f32/f64/vec3/fog/level-set 往返、多叶尺寸重分块、
207
+ 负坐标、惰性值压缩路径、`save()` 拒绝 `.vdb` 后缀、GPU 采样对齐宿主三线性、内核写值
208
+ 往返、DDA 射线(含空块跳跃、内部出发、tmax 截断、变换偏移)。
xuvdb-0.1.0/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # XUVDB 太虚 — genesis 自研稀疏体素格式(可编辑 · 与 OpenVDB 互转)
2
+
3
+ > 「不游乎太虚。」——《庄子·知北游》
4
+ > 「太虚无形,气之本体。」——张载《正蒙·太和》
5
+
6
+ `xuvdb` 取「太虚」之音:这是中文里对 VDB「无界而稀疏的索引域」最准确的翻译——无界
7
+ (root 哈希域不设上限),无形(未分配的空间没有形态,采样即得背景值)。
8
+
9
+ `xuvdb` 基于 quadrants 内核:**自己的 `.xuvdb` 格式**(可修改、可在内核里写值),
10
+ 以及**与 OpenVDB 的双向互转**(原生 `.vdb` 文件读写,无需安装 OpenVDB;有 `pyopenvdb`
11
+ 绑定时还能内存级互转)。
12
+
13
+ ## 命名规范(三层名字,各归其位)
14
+
15
+ | 层 | 名字 | 规则 |
16
+ |---|---|---|
17
+ | 项目名 | `xuvdb` | 拼音;不用 Open 前缀(ASWF 语境下暗示基金会血统) |
18
+ | 命名空间 / 扩展名 | `xuvdb` / `.xuvdb` | API 标识符一律英文:`xuvdb.prune()`、`xuvdb.VdbGrid`,绝不是 `xuvdb.sunyi()`。道家词只活在概念层(文档题词、日志、可视化标签) |
19
+ | 互转文件 | `.vdb` | **自有格式绝不写 `.vdb` 后缀**(Houdini/Blender/Cycles/Arnold 按扩展名当 OpenVDB 解析,格式不符时静默出错或崩溃,极难定位;`save()` 对 `.vdb` 路径直接拒绝)。要互通就单独导出:`write_vdb()` 产出真正的 OpenVDB 流 |
20
+
21
+ ## 为什么是它
22
+
23
+ | | OpenVDB | NanoVDB | XUVDB |
24
+ |---|---|---|---|
25
+ | 结构 | 5 层 B+树,CPU C++ | 同构只读缓冲,GPU | 两级:dict 叶根 + 稠密叶块 |
26
+ | 可修改 | ✅(CPU) | ❌ GPU 只读 | ✅ Python 端任意结构编辑;**内核端可写值** |
27
+ | 可微分包差 | ❌ | ❌ | 值缓冲可被 quadrants 内核读写(拓扑固定) |
28
+ | Python 依赖 | pyopenvdb(需自行构建) | — | 仅 numpy + quadrants |
29
+
30
+ 定位不是替换任何求解器,而是补齐 README 物理栈背后的**空间表示层**(落点见下文)。
31
+
32
+ ## 安装
33
+
34
+ 独立 Python 包(src 布局,`import xuvdb` 即用):
35
+
36
+ ```bash
37
+ uv pip install . # 或 pip install .
38
+ uv pip install -e ".[test]" # 开发模式 + pytest
39
+ ```
40
+
41
+ 可选 extras:`[openvdb]`(pyopenvdb 内存级互转)、`[genesis]`(运行引擎侧示例需要 genesis-world)、
42
+ `[test]`(pytest)。
43
+
44
+ ## 快速上手
45
+
46
+ ```python
47
+ import numpy as np
48
+ import xuvdb
49
+
50
+ # 1) 编辑:窄带 level set 球(体素 0.05,带宽 3 体素)
51
+ grid = xuvdb.VdbGrid(background=3 * 0.05, voxel_size=0.05, leaf_log2=4,
52
+ name="shield", grid_class="level set")
53
+ grid.stamp_sphere((0.3, 0.2, 0.1), radius=0.25, band=3.0)
54
+ grid.stamp_sphere((0.5, 0.2, 0.1), radius=0.10) # CSG:并入第二个球
55
+ grid.fill_box((-2, -2, -2), (2, 2, 2), value=0.0) # 任意稠密填充(示例)
56
+ grid.prune()
57
+
58
+ # 2) 自有格式落盘 / 读回(多网格、f32/f64/vec3)
59
+ xuvdb.save("scene.xuvdb", [grid])
60
+ grids = xuvdb.load("scene.xuvdb")
61
+
62
+ # 3) 与 OpenVDB 互通(显式导出:真正的 OpenVDB 流,Houdini/Blender 直接打开)
63
+ xuvdb.write_vdb("scene.vdb", [grid])
64
+ back = xuvdb.read_vdb("scene.vdb", grid_name="shield")
65
+
66
+ # 4) 内核采样 / 写值(等同 Warp example_nvdb 的用法,但值可写)
67
+ vol = xuvdb.GpuVolume(grid)
68
+ pts = np.array([[0.3, 0.2, 0.36]], dtype=np.float32)
69
+ d = vol.sample(pts, linear=True) # SDF 距离
70
+ n = vol.sdf_normal(pts) # 有限差分表面法向
71
+ vol.write_voxels(pts, np.array([-0.01], np.float32)); vol.sync_to_host()
72
+
73
+ # 5) 粒子 ⇄ 体积(液体/油,见落点④)
74
+ drops = np.array([[0.1, 0.0, 0.0], [0.2, 0.0, 0.0]])
75
+ fog = xuvdb.VdbGrid(voxel_size=0.05, name="liquid", grid_class="fog volume")
76
+ fog.scatter_particles(drops, h=4 * 0.05, weights=1.0) # SPH cubic 核密度 splat
77
+ surf = xuvdb.VdbGrid(background=3 * 0.05, voxel_size=0.05, grid_class="level set")
78
+ surf.union_spheres(drops, radius=0.03) # particle level set 表面代理
79
+
80
+ # 6) DDA 射线(空叶块按块跳过,交叉点二分细化到亚体素)
81
+ t, point, value = xuvdb.ray_surface_hit(grid, (0.3, 0.2, 2.0), (0, 0, -1))
82
+ ```
83
+
84
+ 与引擎稠密场的桥:
85
+
86
+ ```python
87
+ # 稠密 qd.field / numpy SDF(如 rigid geom 的 sdf_val)→ 稀疏
88
+ sparse = xuvdb.VdbGrid.from_dense(dense_sdf, origin=ijk_min, voxel_size=h,
89
+ background=band_h, grid_class="level set")
90
+ dense, ijk_min = sparse.to_dense() # 反向:渲染器 / 求解器输入
91
+ ```
92
+
93
+ ## 格式
94
+
95
+ ### `.xuvdb`(自有格式,小端)
96
+
97
+ ```
98
+ "XUVDB" | u8 version=1 | u8 flags | u16 n_grids
99
+ per grid:
100
+ str name | u8 type(0=f32,1=f64,2=vec3f) | u8 leaf_log2 | u8 class | u8 rsv
101
+ f64[3] voxel_size | f64[3] origin_world | background
102
+ u32 n_leaves
103
+ per leaf(按叶原点排序): i32[3] origin | u64[dim³/64] active mask | 值稠密数组
104
+ ```
105
+
106
+ - 叶内线性序 `n = x·dim² + y·dim + z`(z 最快),**与 OpenVDB leaf 序一致**,互转零转置。
107
+ - 叶块与 OpenVDB LeafNode 同为稠密缓冲:level set 内部体素的 `-background` 值在
108
+ save/load 后保留。
109
+ - 变换约定与 OpenVDB 线性映射一致:`world = index · voxel_size + origin_world`,
110
+ 体素中心在整数索引处。
111
+
112
+ ### `.vdb`(OpenVDB 官方流格式,仅显式导出用)
113
+
114
+ 字节布局逐一对照 OpenVDB 源码实现(`io/Archive.cc`、`GridDescriptor.cc`、`Compression.h`、
115
+ `tree/*.h`、`math/Maps.h`、`Metadata.h`):
116
+
117
+ - 头 57B:`int64 magic 0x56444220`、u32 文件版本、u32 库主/次版本、u8 offsets 标志、36 字符 UUID;
118
+ - 文件级元数据表 → i32 网格数 → **描述符与网格流交错**(描述符、i64×3 偏移、网格流、下一描述符…);
119
+ - 网格流:u32 压缩标志 → 元数据表(name/class/file_* 统计)→ 变换(ScaleTranslate 家族
120
+ = 类型字符串 + 6×Vec3d)→ 树(`i32 buffer_count`、root 背景 + tiles + 子节点);
121
+ - 树:root → InternalNode(5)(32³ 桌、512×u64 双掩码、值表)→ InternalNode(4)(64 项)→
122
+ LeafNode(8³)(拓扑段只有值掩码,origin 由树路径隐含;缓冲段掩码重写一遍 + 值块);
123
+ - 值块:`io::writeCompressedValues` 语义 —— 1 字节 metadata(0=惰性值全为 +bg、1=-bg、
124
+ 2/4/5=带 1~2 个惰性值/选择掩码、6=全量数组)+ 值(按 ACTIVE_MASK 只存 active)。
125
+
126
+ 写侧:文件版本 224、压缩 = `COMPRESS_ACTIVE_MASK`(无 zip/blosc),任何 OpenVDB ≥ 9 可读。
127
+ 读侧:支持 `COMPRESS_NONE` / `COMPRESS_ZIP`(stdlib zlib)/ `COMPRESS_ACTIVE_MASK` /
128
+ `_HalfFloat` 网格;Blosc 抛出明确错误;root/internode 活动 tile 物化为稠密叶
129
+ (受 `max_tile_voxels` 上限保护)。
130
+
131
+ ## 已知边界
132
+
133
+ - `GpuVolume` 只支持 f32 标量网格;写入只改值不改拓扑、不动 active 掩码(掩码是宿主侧
134
+ 状态)。结构性编辑后需重新打包。
135
+ - `scatter_particles`/`union_spheres` 是 Python 循环 + 叶切片向量化:千级粒子适用,
136
+ 大规模生产需按叶批处理(未做)。`union_spheres` 的 min-of-spheres 距离在重叠粒子间
137
+ 的凹桥区是真实距离的上界(Lipschitz 精确),做碰撞/渲染代理足够,精确表面请离线
138
+ 用正规表面重建精修。
139
+ - `.vdb` 读侧不支持:Blosc 压缩、实例化网格(instance parent)、点云网格(PointDataGrid)、
140
+ `5_4_3` 以外的树形。写侧不产生 root tile(全部以叶表达)。
141
+ - 与求解器自动微分的边界:XUVDB 提供的是**采样/写入原语**;把 VDB 值直接接入反传图需要
142
+ 包一层自定义求导规则(这正是 FastSweeping 等算子不可微的同一边界)。
143
+
144
+ ## 与引擎四个落点的对接
145
+
146
+ 对应《Genesis × OpenVDB 重合度报告》(`genesis_openvdb_overlap.html`)的结论:
147
+
148
+ 1. **刚性 SDF(`utils/sdf.py`,风险最低)**:`geom.sdf_val` 稠密体 → `VdbGrid.from_dense`
149
+ 窄带化 → `GpuVolume.sample/sdf_normal` 做内核内碰撞采样;粗块最小值下界的带宽门控
150
+ 对应这里"空叶块直接跳过"——稀疏性免费获得。维护/雕刻工装(`csg` + `stamp_sphere`)
151
+ 可离线改碰撞体。
152
+ 2. **MPM 背景网格(解除 1e9 上限)**:`use_sparse_grid` 被移除的原因在 GPU 端动态拓扑;
153
+ XUVDB 的分工是"拓扑宿主端冻结 + 值内核端可写"。粒子覆盖块用 `fill_box` 声明、每步
154
+ `write_voxels` 回写网格值,是向稀疏 MPM 过渡的最小代价路径(需自行验证与现有
155
+ dense reset 的性能对比)。
156
+ 3. **烟尘 / 稳定流体(收益上限最高)**:压力投影每帧回写全网格,短期不建议动求解器;
157
+ 现实路径是**出口侧**:每 N 步 `from_dense(density_field)` → `write_vdb` 交给
158
+ Houdini/Blender 体渲染;进口侧用 Houdini 烘的 `.vdb` 作初始条件(`read_vdb` →
159
+ `to_dense`)。
160
+ 4. **液体与油(SPH,粒子 ⇄ 体积)**:Genesis 的液体是 Lagrangian SPH(hash grid 邻域,
161
+ VDB 不做邻居搜索),与稀疏体积的接口在两端——
162
+ - **出口(每步/每 N 步)**:`scatter_particles(pos, h, mass)` 把粒子 splat 成密度
163
+ fog 网格(SPH cubic 核、单位积分,质量守恒已测)→ `write_vdb` 给 Houdini/Blender
164
+ 体渲染液体,比逐粒子渲染便宜得多;要表面就 `union_spheres(pos, r, band)` 出
165
+ particle level set 表面代理(喷雾/液滴场景),离线可用 OpenVDB 生态精修。
166
+ - **进口**:Houdini 烘的液面/容器 `.vdb` level set → `read_vdb` → `GpuVolume.sample`
167
+ 做容器碰撞 SDF 或装液初始条件(与落点①同一套采样机制,SPH 边界碰撞即刚体 SDF
168
+ 碰撞的复用)。
169
+ - **油(高黏/两相)**:黏性在 SPH 求解器侧,体积层只管表征——两相液体(油-水、
170
+ 油-气)每相一个 fog 网格,即混合分数场 α:`scatter_particles` 按相内粒子各 splat
171
+ 一份,导出双网格 `.vdb`,渲染端做 α 混合;界面 SDF 用两相 `union_spheres` 之差
172
+ (`csg 'diff'`)。
173
+ - **在线更新**:`GpuVolume.write_voxels` 可增量回写密度值做实时可视化;拓扑(叶
174
+ 集合)按粒子包围盒周期性重打包(拓扑宿主端冻结的同一分工)。
175
+
176
+ ## 许可证
177
+
178
+ Apache-2.0(与上游 quadrants、genesis-world 一致),见 [LICENSE](LICENSE)。
179
+
180
+ ## 测试
181
+
182
+ ```
183
+ pytest tests/ -q
184
+ ```
185
+
186
+ 覆盖:树编辑/CSG/稠密互转、粒子 splat(质量守恒、可加性、vec3 速度场、双核函数)、
187
+ `union_spheres`(表面/窄带/逐粒子半径/射线命中/与 stamp 复合)、`.xuvdb` 多网格多类型
188
+ 往返、`.vdb` 头部字节与偏移校验、f32/f64/vec3/fog/level-set 往返、多叶尺寸重分块、
189
+ 负坐标、惰性值压缩路径、`save()` 拒绝 `.vdb` 后缀、GPU 采样对齐宿主三线性、内核写值
190
+ 往返、DDA 射线(含空块跳跃、内部出发、tmax 截断、变换偏移)。
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "xuvdb"
7
+ description = "Editable sparse voxel volumes with OpenVDB interop, built on quadrants kernels"
8
+ readme = "README.md"
9
+ dynamic = ["version"]
10
+ requires-python = ">=3.10,<3.14"
11
+ license = "Apache-2.0"
12
+ license-files = ["LICENSE"]
13
+ dependencies = [
14
+ "numpy",
15
+ "quadrants",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ # engine-side example (examples/test_packages.py) drives a genesis-world simulation
20
+ genesis = ["genesis-world"]
21
+ # in-memory OpenVDB bridge (to_openvdb / from_openvdb) via the official bindings
22
+ openvdb = ["pyopenvdb"]
23
+ test = ["pytest"]
24
+
25
+ [tool.setuptools.dynamic]
26
+ version = { attr = "xuvdb.__version__" }
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
30
+
31
+ [tool.pytest.ini_options]
32
+ testpaths = ["tests"]
xuvdb-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+