mesh2sdf2 2.2.0__tar.gz → 2.3.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.
- mesh2sdf2-2.3.0/PKG-INFO +187 -0
- mesh2sdf2-2.3.0/README.md +161 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/csrc/array3.h +2 -47
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/csrc/geometry.h +40 -16
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/csrc/makelevelset3.cpp +62 -97
- mesh2sdf2-2.3.0/csrc/makelevelset3.h +13 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/csrc/parallel.cpp +56 -85
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/csrc/parallel.h +4 -6
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/csrc/pybind.cpp +16 -8
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/csrc/vec.h +0 -1
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/mesh2sdf/compute.py +1 -1
- mesh2sdf2-2.3.0/mesh2sdf2.egg-info/PKG-INFO +187 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/pyproject.toml +1 -1
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/tests/test_mesh2sdf.py +3 -75
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/tests/test_sweep_equivalence.py +2 -12
- mesh2sdf2-2.2.0/PKG-INFO +0 -118
- mesh2sdf2-2.2.0/README.md +0 -92
- mesh2sdf2-2.2.0/csrc/makelevelset3.h +0 -21
- mesh2sdf2-2.2.0/mesh2sdf2.egg-info/PKG-INFO +0 -118
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/LICENSE +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/MANIFEST.in +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/mesh2sdf/__init__.py +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/mesh2sdf2.egg-info/SOURCES.txt +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/mesh2sdf2.egg-info/dependency_links.txt +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/mesh2sdf2.egg-info/requires.txt +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/mesh2sdf2.egg-info/top_level.txt +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/setup.cfg +0 -0
- {mesh2sdf2-2.2.0 → mesh2sdf2-2.3.0}/setup.py +0 -0
mesh2sdf2-2.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mesh2sdf2
|
|
3
|
+
Version: 2.3.0
|
|
4
|
+
Summary: Compute the signed distance field from an input mesh
|
|
5
|
+
Author-email: Lucas Mosquera <25126199+LuckJMG@users.noreply.github.com>, Peng-Shuai Wang <wangps@hotmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/LuckJMG/mesh2sdf2
|
|
8
|
+
Project-URL: Source, https://github.com/LuckJMG/mesh2sdf2
|
|
9
|
+
Project-URL: Issues, https://github.com/LuckJMG/mesh2sdf2/issues
|
|
10
|
+
Project-URL: Upstream, https://github.com/wang-ps/mesh2sdf
|
|
11
|
+
Keywords: sdf,mesh,signed-distance-field,pybind11,3d
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: numpy
|
|
23
|
+
Requires-Dist: trimesh
|
|
24
|
+
Requires-Dist: scikit-image
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
[](https://pepy.tech/project/mesh2sdf2) [](https://pepy.tech/project/mesh2sdf2/month) [](https://pypi.org/project/mesh2sdf2/)
|
|
28
|
+
|
|
29
|
+
# mesh2sdf2
|
|
30
|
+
|
|
31
|
+
Repository: <https://github.com/LuckJMG/mesh2sdf2>. Maintainer: Lucas Mosquera.
|
|
32
|
+
|
|
33
|
+
mesh2sdf2 is a community-maintained fork of [mesh2sdf](https://github.com/wang-ps/mesh2sdf) by Peng-Shuai Wang. The fork keeps the algorithm and the public API of the original project, so the import name is still `mesh2sdf`.
|
|
34
|
+
|
|
35
|
+
## Project description
|
|
36
|
+
|
|
37
|
+
With mesh2sdf2 you can convert a triangle mesh to a signed distance field (SDF) on a regular grid. The library also works with **non-watertight** meshes, such as the meshes of ShapeNet, since it repairs the surface before it computes the sign.
|
|
38
|
+
|
|
39
|
+
Compared with the original project, mesh2sdf2 supports Python 3.10 to 3.14, builds with current versions of setuptools and pybind11, and computes the SDF about 26 times faster on a 12-thread CPU.
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+
|
|
43
|
+
## Who this project is for
|
|
44
|
+
|
|
45
|
+
This project is for researchers and developers who need signed distance fields from 3D meshes, for example to generate training data for 3D deep learning. The original mesh2sdf generated the training data of the paper [Dual Octree Graph Networks (SIGGRAPH 2022)](https://wang-ps.github.io/dualocnn).
|
|
46
|
+
|
|
47
|
+
## Project dependencies
|
|
48
|
+
|
|
49
|
+
Before you use mesh2sdf2, make sure that you have:
|
|
50
|
+
|
|
51
|
+
- Python 3.10 or later.
|
|
52
|
+
- A C++ compiler that [pybind11 supports](https://github.com/pybind/pybind11#supported-compilers), if no wheel exists for your platform or you install from source.
|
|
53
|
+
|
|
54
|
+
pip installs the Python dependencies (NumPy, trimesh, and scikit-image) automatically.
|
|
55
|
+
|
|
56
|
+
## Instructions for using mesh2sdf2
|
|
57
|
+
|
|
58
|
+
Get started with mesh2sdf2 by installing it from PyPI.
|
|
59
|
+
|
|
60
|
+
### Install mesh2sdf2
|
|
61
|
+
|
|
62
|
+
1. Install the package from PyPI.
|
|
63
|
+
|
|
64
|
+
```shell
|
|
65
|
+
pip install mesh2sdf2
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
2. Alternatively, install it from the source code.
|
|
69
|
+
|
|
70
|
+
```shell
|
|
71
|
+
git clone https://github.com/LuckJMG/mesh2sdf2.git
|
|
72
|
+
cd mesh2sdf2
|
|
73
|
+
pip install .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Run mesh2sdf2
|
|
77
|
+
|
|
78
|
+
1. Scale the vertices of your mesh to the range `[-1, 1]` and call `mesh2sdf.compute`.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import trimesh
|
|
82
|
+
import mesh2sdf
|
|
83
|
+
|
|
84
|
+
mesh = trimesh.creation.icosphere(radius=0.8)
|
|
85
|
+
size = 128
|
|
86
|
+
sdf = mesh2sdf.compute(mesh.vertices, mesh.faces, size, fix=True, level=2 / size)
|
|
87
|
+
print(sdf.shape) # (128, 128, 128)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
2. To process the example mesh of the repository, run the demo script from the repository root.
|
|
91
|
+
|
|
92
|
+
```shell
|
|
93
|
+
python example/demo.py
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The script writes `example/data/plane.fixed.obj` with the repaired mesh and `example/data/plane.npy` with the SDF. To process another mesh, pass its path, for example `python example/demo.py path/to/mesh.obj`.
|
|
97
|
+
|
|
98
|
+
The signature of the function is `mesh2sdf.compute(vertices, faces, size=128, fix=False, level=0.015, return_mesh=False)`.
|
|
99
|
+
|
|
100
|
+
| Parameter | Description |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `vertices` | Array of shape `(N, 3)`. The values must be in the range `[-1, 1]`. |
|
|
103
|
+
| `faces` | Array of shape `(M, 3)` with the vertex indices of each triangle. |
|
|
104
|
+
| `size` | Resolution of the grid. The grid has `size` samples per axis and spans `[-1, 1]`, both ends included. |
|
|
105
|
+
| `fix` | If `True`, repairs the mesh before it computes the sign. Use it for non-watertight meshes. |
|
|
106
|
+
| `level` | Level-set value for the repair. The recommended value is `2 / size`. |
|
|
107
|
+
| `return_mesh` | If `True`, also returns the repaired mesh as a `trimesh.Trimesh`. |
|
|
108
|
+
|
|
109
|
+
The function returns a `float32` array of shape `(size, size, size)`.
|
|
110
|
+
|
|
111
|
+
### Troubleshoot mesh2sdf2
|
|
112
|
+
|
|
113
|
+
| Issue | Solution |
|
|
114
|
+
|---|---|
|
|
115
|
+
| The SDF has wrong values and no error appears. | Scale the vertices to the range `[-1, 1]`, since the function does not check the range. |
|
|
116
|
+
| The sign of the SDF is wrong on an open mesh. | Call `compute` with `fix=True`. |
|
|
117
|
+
| `compute` raises `ValueError`. | Use a `size` of 2 or more. |
|
|
118
|
+
| `pip install` fails while it builds the extension. | Install a C++ compiler that pybind11 supports. |
|
|
119
|
+
|
|
120
|
+
## Performance
|
|
121
|
+
|
|
122
|
+
mesh2sdf2 2.3.0 computes the SDF about **26x faster** than the original mesh2sdf 1.1.0 on a 12-thread CPU, and about 5.6x faster on one thread. Both numbers are the geometric mean over five workloads, which are the four in the next table and a cube with `size=64`. Each time is the median of 3 runs with Python 3.14.
|
|
123
|
+
|
|
124
|
+
| Workload | mesh2sdf 1.1.0 | 2.3.0, 12 threads | 2.3.0, 1 thread |
|
|
125
|
+
|---|---|---|---|
|
|
126
|
+
| cube | 6066 ms | 116 ms (52.5x) | 402 ms (15.1x) |
|
|
127
|
+
| plane | 7245 ms | 277 ms (26.2x) | 1195 ms (6.1x) |
|
|
128
|
+
| icosphere, 5k triangles | 7442 ms | 405 ms (18.4x) | 2815 ms (2.6x) |
|
|
129
|
+
| icosphere, 20k triangles | 9040 ms | 634 ms (14.3x) | 4626 ms (2.0x) |
|
|
130
|
+
|
|
131
|
+
The next table gives the cumulative gain of each improvement on the cube, with `size=128` and 12 threads.
|
|
132
|
+
|
|
133
|
+
| Stage | Time | Speedup |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| mesh2sdf 1.1.0 | 6066 ms | 1.00x |
|
|
136
|
+
| cache triangle distance data | 4011 ms | 1.51x |
|
|
137
|
+
| wavefront parallel sweep | 1321 ms | 4.59x |
|
|
138
|
+
| Orthodox C++ port | 1167 ms | 5.20x |
|
|
139
|
+
| geometry and parallel split (2.2.0) | 1077 ms | 5.63x |
|
|
140
|
+
| skip repeated distances, row wavefront (2.3.0) | 116 ms | 52.5x |
|
|
141
|
+
|
|
142
|
+
The C++ core releases the Python global interpreter lock (GIL), so several Python threads can call `compute` at the same time. With `OMP_NUM_THREADS=1`, four Python threads that each process the cube finish 57x faster than with the original project, from 24.3 s to 0.43 s. The release of the GIL gives 4x of that gain. In 2.2.0, the sweep took almost all the time, so the parallel sign, memcpy, and parallel init steps gave no measurable gain on these meshes.
|
|
143
|
+
|
|
144
|
+
To reproduce the numbers, run `just bench-versions`. The run takes about 30 minutes, and the speedup depends on the number of CPU cores.
|
|
145
|
+
|
|
146
|
+
## How it works
|
|
147
|
+
|
|
148
|
+
1. mesh2sdf2 computes the **unsigned** distance field with the fast sweeping algorithm of [SDFGen](https://github.com/christopherbatty/SDFGen) by Christopher Batty. This step is accurate even for non-watertight meshes, since the unsigned distance does not depend on the inside or the outside of the surface.
|
|
149
|
+
2. If `fix=True`, the marching cubes algorithm extracts the level set at the small value `level`. The result is a manifold triangle mesh.
|
|
150
|
+
3. The extracted mesh can have several connected components, so mesh2sdf2 keeps the component with the largest bounding box.
|
|
151
|
+
4. mesh2sdf2 computes the signed distance field again on the kept mesh and returns it as the final output.
|
|
152
|
+
|
|
153
|
+
## Contributing guidelines
|
|
154
|
+
|
|
155
|
+
To report a bug or propose a change, open an issue or a pull request in the [GitHub repository](https://github.com/LuckJMG/mesh2sdf2).
|
|
156
|
+
|
|
157
|
+
## Additional documentation
|
|
158
|
+
|
|
159
|
+
For more information:
|
|
160
|
+
|
|
161
|
+
- [Original mesh2sdf repository](https://github.com/wang-ps/mesh2sdf), with the paper and the algorithm.
|
|
162
|
+
- [Dual Octree Graph Networks](https://wang-ps.github.io/dualocnn), the project page of the paper that uses mesh2sdf.
|
|
163
|
+
- [SDFGen](https://github.com/christopherbatty/SDFGen), the source of the fast sweeping algorithm.
|
|
164
|
+
|
|
165
|
+
## How to get help
|
|
166
|
+
|
|
167
|
+
- [GitHub issues](https://github.com/LuckJMG/mesh2sdf2/issues), for bugs and questions about the fork.
|
|
168
|
+
|
|
169
|
+
## Citation
|
|
170
|
+
|
|
171
|
+
If you use the code in your research, please cite the paper of the original authors.
|
|
172
|
+
|
|
173
|
+
```bibtex
|
|
174
|
+
@article {Wang-Sig2022,
|
|
175
|
+
title = {Dual Octree Graph Networks for Learning Adaptive Volumetric
|
|
176
|
+
Shape Representations},
|
|
177
|
+
author = {Wang, Peng-Shuai and Liu, Yang and Tong, Xin},
|
|
178
|
+
journal = {ACM Transactions on Graphics (SIGGRAPH)},
|
|
179
|
+
volume = {41},
|
|
180
|
+
number = {4},
|
|
181
|
+
year = {2022},
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Terms of use
|
|
186
|
+
|
|
187
|
+
mesh2sdf2 is licensed under the MIT License. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
[](https://pepy.tech/project/mesh2sdf2) [](https://pepy.tech/project/mesh2sdf2/month) [](https://pypi.org/project/mesh2sdf2/)
|
|
2
|
+
|
|
3
|
+
# mesh2sdf2
|
|
4
|
+
|
|
5
|
+
Repository: <https://github.com/LuckJMG/mesh2sdf2>. Maintainer: Lucas Mosquera.
|
|
6
|
+
|
|
7
|
+
mesh2sdf2 is a community-maintained fork of [mesh2sdf](https://github.com/wang-ps/mesh2sdf) by Peng-Shuai Wang. The fork keeps the algorithm and the public API of the original project, so the import name is still `mesh2sdf`.
|
|
8
|
+
|
|
9
|
+
## Project description
|
|
10
|
+
|
|
11
|
+
With mesh2sdf2 you can convert a triangle mesh to a signed distance field (SDF) on a regular grid. The library also works with **non-watertight** meshes, such as the meshes of ShapeNet, since it repairs the surface before it computes the sign.
|
|
12
|
+
|
|
13
|
+
Compared with the original project, mesh2sdf2 supports Python 3.10 to 3.14, builds with current versions of setuptools and pybind11, and computes the SDF about 26 times faster on a 12-thread CPU.
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## Who this project is for
|
|
18
|
+
|
|
19
|
+
This project is for researchers and developers who need signed distance fields from 3D meshes, for example to generate training data for 3D deep learning. The original mesh2sdf generated the training data of the paper [Dual Octree Graph Networks (SIGGRAPH 2022)](https://wang-ps.github.io/dualocnn).
|
|
20
|
+
|
|
21
|
+
## Project dependencies
|
|
22
|
+
|
|
23
|
+
Before you use mesh2sdf2, make sure that you have:
|
|
24
|
+
|
|
25
|
+
- Python 3.10 or later.
|
|
26
|
+
- A C++ compiler that [pybind11 supports](https://github.com/pybind/pybind11#supported-compilers), if no wheel exists for your platform or you install from source.
|
|
27
|
+
|
|
28
|
+
pip installs the Python dependencies (NumPy, trimesh, and scikit-image) automatically.
|
|
29
|
+
|
|
30
|
+
## Instructions for using mesh2sdf2
|
|
31
|
+
|
|
32
|
+
Get started with mesh2sdf2 by installing it from PyPI.
|
|
33
|
+
|
|
34
|
+
### Install mesh2sdf2
|
|
35
|
+
|
|
36
|
+
1. Install the package from PyPI.
|
|
37
|
+
|
|
38
|
+
```shell
|
|
39
|
+
pip install mesh2sdf2
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2. Alternatively, install it from the source code.
|
|
43
|
+
|
|
44
|
+
```shell
|
|
45
|
+
git clone https://github.com/LuckJMG/mesh2sdf2.git
|
|
46
|
+
cd mesh2sdf2
|
|
47
|
+
pip install .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Run mesh2sdf2
|
|
51
|
+
|
|
52
|
+
1. Scale the vertices of your mesh to the range `[-1, 1]` and call `mesh2sdf.compute`.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import trimesh
|
|
56
|
+
import mesh2sdf
|
|
57
|
+
|
|
58
|
+
mesh = trimesh.creation.icosphere(radius=0.8)
|
|
59
|
+
size = 128
|
|
60
|
+
sdf = mesh2sdf.compute(mesh.vertices, mesh.faces, size, fix=True, level=2 / size)
|
|
61
|
+
print(sdf.shape) # (128, 128, 128)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. To process the example mesh of the repository, run the demo script from the repository root.
|
|
65
|
+
|
|
66
|
+
```shell
|
|
67
|
+
python example/demo.py
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The script writes `example/data/plane.fixed.obj` with the repaired mesh and `example/data/plane.npy` with the SDF. To process another mesh, pass its path, for example `python example/demo.py path/to/mesh.obj`.
|
|
71
|
+
|
|
72
|
+
The signature of the function is `mesh2sdf.compute(vertices, faces, size=128, fix=False, level=0.015, return_mesh=False)`.
|
|
73
|
+
|
|
74
|
+
| Parameter | Description |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `vertices` | Array of shape `(N, 3)`. The values must be in the range `[-1, 1]`. |
|
|
77
|
+
| `faces` | Array of shape `(M, 3)` with the vertex indices of each triangle. |
|
|
78
|
+
| `size` | Resolution of the grid. The grid has `size` samples per axis and spans `[-1, 1]`, both ends included. |
|
|
79
|
+
| `fix` | If `True`, repairs the mesh before it computes the sign. Use it for non-watertight meshes. |
|
|
80
|
+
| `level` | Level-set value for the repair. The recommended value is `2 / size`. |
|
|
81
|
+
| `return_mesh` | If `True`, also returns the repaired mesh as a `trimesh.Trimesh`. |
|
|
82
|
+
|
|
83
|
+
The function returns a `float32` array of shape `(size, size, size)`.
|
|
84
|
+
|
|
85
|
+
### Troubleshoot mesh2sdf2
|
|
86
|
+
|
|
87
|
+
| Issue | Solution |
|
|
88
|
+
|---|---|
|
|
89
|
+
| The SDF has wrong values and no error appears. | Scale the vertices to the range `[-1, 1]`, since the function does not check the range. |
|
|
90
|
+
| The sign of the SDF is wrong on an open mesh. | Call `compute` with `fix=True`. |
|
|
91
|
+
| `compute` raises `ValueError`. | Use a `size` of 2 or more. |
|
|
92
|
+
| `pip install` fails while it builds the extension. | Install a C++ compiler that pybind11 supports. |
|
|
93
|
+
|
|
94
|
+
## Performance
|
|
95
|
+
|
|
96
|
+
mesh2sdf2 2.3.0 computes the SDF about **26x faster** than the original mesh2sdf 1.1.0 on a 12-thread CPU, and about 5.6x faster on one thread. Both numbers are the geometric mean over five workloads, which are the four in the next table and a cube with `size=64`. Each time is the median of 3 runs with Python 3.14.
|
|
97
|
+
|
|
98
|
+
| Workload | mesh2sdf 1.1.0 | 2.3.0, 12 threads | 2.3.0, 1 thread |
|
|
99
|
+
|---|---|---|---|
|
|
100
|
+
| cube | 6066 ms | 116 ms (52.5x) | 402 ms (15.1x) |
|
|
101
|
+
| plane | 7245 ms | 277 ms (26.2x) | 1195 ms (6.1x) |
|
|
102
|
+
| icosphere, 5k triangles | 7442 ms | 405 ms (18.4x) | 2815 ms (2.6x) |
|
|
103
|
+
| icosphere, 20k triangles | 9040 ms | 634 ms (14.3x) | 4626 ms (2.0x) |
|
|
104
|
+
|
|
105
|
+
The next table gives the cumulative gain of each improvement on the cube, with `size=128` and 12 threads.
|
|
106
|
+
|
|
107
|
+
| Stage | Time | Speedup |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| mesh2sdf 1.1.0 | 6066 ms | 1.00x |
|
|
110
|
+
| cache triangle distance data | 4011 ms | 1.51x |
|
|
111
|
+
| wavefront parallel sweep | 1321 ms | 4.59x |
|
|
112
|
+
| Orthodox C++ port | 1167 ms | 5.20x |
|
|
113
|
+
| geometry and parallel split (2.2.0) | 1077 ms | 5.63x |
|
|
114
|
+
| skip repeated distances, row wavefront (2.3.0) | 116 ms | 52.5x |
|
|
115
|
+
|
|
116
|
+
The C++ core releases the Python global interpreter lock (GIL), so several Python threads can call `compute` at the same time. With `OMP_NUM_THREADS=1`, four Python threads that each process the cube finish 57x faster than with the original project, from 24.3 s to 0.43 s. The release of the GIL gives 4x of that gain. In 2.2.0, the sweep took almost all the time, so the parallel sign, memcpy, and parallel init steps gave no measurable gain on these meshes.
|
|
117
|
+
|
|
118
|
+
To reproduce the numbers, run `just bench-versions`. The run takes about 30 minutes, and the speedup depends on the number of CPU cores.
|
|
119
|
+
|
|
120
|
+
## How it works
|
|
121
|
+
|
|
122
|
+
1. mesh2sdf2 computes the **unsigned** distance field with the fast sweeping algorithm of [SDFGen](https://github.com/christopherbatty/SDFGen) by Christopher Batty. This step is accurate even for non-watertight meshes, since the unsigned distance does not depend on the inside or the outside of the surface.
|
|
123
|
+
2. If `fix=True`, the marching cubes algorithm extracts the level set at the small value `level`. The result is a manifold triangle mesh.
|
|
124
|
+
3. The extracted mesh can have several connected components, so mesh2sdf2 keeps the component with the largest bounding box.
|
|
125
|
+
4. mesh2sdf2 computes the signed distance field again on the kept mesh and returns it as the final output.
|
|
126
|
+
|
|
127
|
+
## Contributing guidelines
|
|
128
|
+
|
|
129
|
+
To report a bug or propose a change, open an issue or a pull request in the [GitHub repository](https://github.com/LuckJMG/mesh2sdf2).
|
|
130
|
+
|
|
131
|
+
## Additional documentation
|
|
132
|
+
|
|
133
|
+
For more information:
|
|
134
|
+
|
|
135
|
+
- [Original mesh2sdf repository](https://github.com/wang-ps/mesh2sdf), with the paper and the algorithm.
|
|
136
|
+
- [Dual Octree Graph Networks](https://wang-ps.github.io/dualocnn), the project page of the paper that uses mesh2sdf.
|
|
137
|
+
- [SDFGen](https://github.com/christopherbatty/SDFGen), the source of the fast sweeping algorithm.
|
|
138
|
+
|
|
139
|
+
## How to get help
|
|
140
|
+
|
|
141
|
+
- [GitHub issues](https://github.com/LuckJMG/mesh2sdf2/issues), for bugs and questions about the fork.
|
|
142
|
+
|
|
143
|
+
## Citation
|
|
144
|
+
|
|
145
|
+
If you use the code in your research, please cite the paper of the original authors.
|
|
146
|
+
|
|
147
|
+
```bibtex
|
|
148
|
+
@article {Wang-Sig2022,
|
|
149
|
+
title = {Dual Octree Graph Networks for Learning Adaptive Volumetric
|
|
150
|
+
Shape Representations},
|
|
151
|
+
author = {Wang, Peng-Shuai and Liu, Yang and Tong, Xin},
|
|
152
|
+
journal = {ACM Transactions on Graphics (SIGGRAPH)},
|
|
153
|
+
volume = {41},
|
|
154
|
+
number = {4},
|
|
155
|
+
year = {2022},
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Terms of use
|
|
160
|
+
|
|
161
|
+
mesh2sdf2 is licensed under the MIT License. See [LICENSE](LICENSE).
|
|
@@ -16,21 +16,6 @@ template <class T> struct Array3 {
|
|
|
16
16
|
storage = NULL;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
Array3(int ni_, int nj_, int nk_) {
|
|
20
|
-
assert(ni_ >= 0 && nj_ >= 0 && nk_ >= 0);
|
|
21
|
-
|
|
22
|
-
ni = ni_;
|
|
23
|
-
nj = nj_;
|
|
24
|
-
nk = nk_;
|
|
25
|
-
storage = NULL;
|
|
26
|
-
|
|
27
|
-
size_t n = (size_t)ni_ * nj_ * nk_;
|
|
28
|
-
if (n > 0) {
|
|
29
|
-
storage = (T *)malloc(n * sizeof(T));
|
|
30
|
-
assert(storage != NULL);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
19
|
Array3(int ni_, int nj_, int nk_, const T &value) {
|
|
35
20
|
assert(ni_ >= 0 && nj_ >= 0 && nk_ >= 0);
|
|
36
21
|
|
|
@@ -54,49 +39,19 @@ template <class T> struct Array3 {
|
|
|
54
39
|
Array3(const Array3 &) = delete;
|
|
55
40
|
Array3 &operator=(const Array3 &) = delete;
|
|
56
41
|
|
|
57
|
-
Array3(Array3 &&o) noexcept {
|
|
58
|
-
ni = o.ni;
|
|
59
|
-
nj = o.nj;
|
|
60
|
-
nk = o.nk;
|
|
61
|
-
storage = o.storage;
|
|
62
|
-
|
|
63
|
-
o.ni = 0;
|
|
64
|
-
o.nj = 0;
|
|
65
|
-
o.nk = 0;
|
|
66
|
-
o.storage = NULL;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
Array3 &operator=(Array3 &&o) noexcept {
|
|
70
|
-
if (this != &o) {
|
|
71
|
-
free(storage);
|
|
72
|
-
|
|
73
|
-
ni = o.ni;
|
|
74
|
-
nj = o.nj;
|
|
75
|
-
nk = o.nk;
|
|
76
|
-
storage = o.storage;
|
|
77
|
-
|
|
78
|
-
o.ni = 0;
|
|
79
|
-
o.nj = 0;
|
|
80
|
-
o.nk = 0;
|
|
81
|
-
o.storage = NULL;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return *this;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
42
|
inline size_t index(int i, int j, int k) const {
|
|
88
43
|
return (size_t)i + (size_t)ni * ((size_t)j + (size_t)nj * (size_t)k);
|
|
89
44
|
}
|
|
90
45
|
|
|
91
46
|
const T &operator()(int i, int j, int k) const {
|
|
92
47
|
assert(i >= 0 && i < ni && j >= 0 && j < nj && k >= 0 && k < nk);
|
|
93
|
-
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
|
48
|
+
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn,clang-analyzer-security.ArrayBound)
|
|
94
49
|
return storage[index(i, j, k)];
|
|
95
50
|
}
|
|
96
51
|
|
|
97
52
|
T &operator()(int i, int j, int k) {
|
|
98
53
|
assert(i >= 0 && i < ni && j >= 0 && j < nj && k >= 0 && k < nk);
|
|
99
|
-
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
|
54
|
+
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn,clang-analyzer-security.ArrayBound)
|
|
100
55
|
return storage[index(i, j, k)];
|
|
101
56
|
}
|
|
102
57
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
#define GEOMETRY_H
|
|
3
3
|
|
|
4
4
|
#include <assert.h>
|
|
5
|
-
#include <math.h>
|
|
6
5
|
|
|
7
6
|
#include "vec.h"
|
|
8
7
|
|
|
@@ -18,6 +17,14 @@ struct TriangleData {
|
|
|
18
17
|
SegmentData edge12, edge13, edge23;
|
|
19
18
|
};
|
|
20
19
|
|
|
20
|
+
struct Grid {
|
|
21
|
+
Vec3f origin;
|
|
22
|
+
float dx;
|
|
23
|
+
int ni, nj, nk;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
static constexpr int exact_band = 1;
|
|
27
|
+
|
|
21
28
|
static inline int clamp_int(int v, int lo, int hi) {
|
|
22
29
|
if (v < lo) {
|
|
23
30
|
return lo;
|
|
@@ -28,8 +35,6 @@ static inline int clamp_int(int v, int lo, int hi) {
|
|
|
28
35
|
return v;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
static inline float min_f(float a, float b) { return a < b ? a : b; }
|
|
32
|
-
|
|
33
38
|
static inline double min3(double a, double b, double c) {
|
|
34
39
|
double m = a;
|
|
35
40
|
if (b < m) {
|
|
@@ -52,6 +57,25 @@ static inline double max3(double a, double b, double c) {
|
|
|
52
57
|
return m;
|
|
53
58
|
}
|
|
54
59
|
|
|
60
|
+
struct IndexRange {
|
|
61
|
+
int lo, hi;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// grid cells within exact_band of a triangle's extent [min(p,q,r), max(p,q,r)]
|
|
65
|
+
static inline IndexRange band_range(double p, double q, double r, int n) {
|
|
66
|
+
IndexRange range = {
|
|
67
|
+
clamp_int((int)min3(p, q, r) - exact_band, 0, n - 1),
|
|
68
|
+
clamp_int((int)max3(p, q, r) + exact_band + 1, 0, n - 1)};
|
|
69
|
+
return range;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// grid rows inside a triangle's extent [min(p,q,r), max(p,q,r)]
|
|
73
|
+
static inline IndexRange row_range(double p, double q, double r, int n) {
|
|
74
|
+
IndexRange range = {clamp_int((int)ceil(min3(p, q, r)), 0, n - 1),
|
|
75
|
+
clamp_int((int)floor(max3(p, q, r)), 0, n - 1)};
|
|
76
|
+
return range;
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
// find distance x0 is from segment x1-x2
|
|
56
80
|
static inline float point_segment_distance(const Vec3f &x0, const Vec3f &x1,
|
|
57
81
|
const Vec3f &x2,
|
|
@@ -172,15 +196,15 @@ struct TriSetup {
|
|
|
172
196
|
double r_i, r_j, r_k;
|
|
173
197
|
};
|
|
174
198
|
|
|
175
|
-
static inline TriSetup setup_triangle(const Vec3ui &t, const Vec3f *
|
|
199
|
+
static inline TriSetup setup_triangle(const Vec3ui &t, const Vec3f *vertices,
|
|
176
200
|
const Vec3f &origin, float dx) {
|
|
177
201
|
unsigned int p = t.x, q = t.y, r = t.z;
|
|
178
202
|
TriSetup s;
|
|
179
203
|
|
|
180
204
|
TriangleData &triangle = s.triangle;
|
|
181
|
-
triangle.x1 =
|
|
182
|
-
triangle.x2 =
|
|
183
|
-
triangle.x3 =
|
|
205
|
+
triangle.x1 = vertices[p];
|
|
206
|
+
triangle.x2 = vertices[q];
|
|
207
|
+
triangle.x3 = vertices[r];
|
|
184
208
|
triangle.x13 = triangle.x1 - triangle.x3;
|
|
185
209
|
triangle.x23 = triangle.x2 - triangle.x3;
|
|
186
210
|
triangle.m13 = mag2(triangle.x13);
|
|
@@ -199,15 +223,15 @@ static inline TriSetup setup_triangle(const Vec3ui &t, const Vec3f *x,
|
|
|
199
223
|
triangle.edge23 = {triangle.x3 - triangle.x2,
|
|
200
224
|
(double)mag2(triangle.x3 - triangle.x2)};
|
|
201
225
|
|
|
202
|
-
s.p_i = ((double)
|
|
203
|
-
s.p_j = ((double)
|
|
204
|
-
s.p_k = ((double)
|
|
205
|
-
s.q_i = ((double)
|
|
206
|
-
s.q_j = ((double)
|
|
207
|
-
s.q_k = ((double)
|
|
208
|
-
s.r_i = ((double)
|
|
209
|
-
s.r_j = ((double)
|
|
210
|
-
s.r_k = ((double)
|
|
226
|
+
s.p_i = ((double)vertices[p].x - origin.x) / dx;
|
|
227
|
+
s.p_j = ((double)vertices[p].y - origin.y) / dx;
|
|
228
|
+
s.p_k = ((double)vertices[p].z - origin.z) / dx;
|
|
229
|
+
s.q_i = ((double)vertices[q].x - origin.x) / dx;
|
|
230
|
+
s.q_j = ((double)vertices[q].y - origin.y) / dx;
|
|
231
|
+
s.q_k = ((double)vertices[q].z - origin.z) / dx;
|
|
232
|
+
s.r_i = ((double)vertices[r].x - origin.x) / dx;
|
|
233
|
+
s.r_j = ((double)vertices[r].y - origin.y) / dx;
|
|
234
|
+
s.r_k = ((double)vertices[r].z - origin.z) / dx;
|
|
211
235
|
return s;
|
|
212
236
|
}
|
|
213
237
|
|