conquer3d 0.4.0__tar.gz → 0.4.2__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.
- {conquer3d-0.4.0/conquer3d.egg-info → conquer3d-0.4.2}/PKG-INFO +1 -1
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/_C.so +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/__init__.py +6 -0
- conquer3d-0.4.2/conquer3d/conversion/tmesh.py +83 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/grid.cpp +71 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +119 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +69 -4
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/mesh_bvh.cu +326 -18
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/mesh_bvh.h +53 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/triangle_mesh.cu +192 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/triangle_mesh.h +14 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/f3x1.h +16 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/aabb.h +1 -1
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/triangle.h +54 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data_structure/__init__.py +2 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data_structure/grid.py +11 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2/conquer3d.egg-info}/PKG-INFO +1 -1
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d.egg-info/SOURCES.txt +1 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/pyproject.toml +1 -1
- {conquer3d-0.4.0 → conquer3d-0.4.2}/setup.py +6 -4
- {conquer3d-0.4.0 → conquer3d-0.4.2}/LICENSE +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/MANIFEST.in +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/README.md +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/_C.pyi +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/conversion/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/conversion/grid.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/conversion/mesh.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/creation/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/creation/triangle_creation.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/data_structure/zcurve.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/primitive/ray.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/binds/primitive/triangle.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/check.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/constants.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/creation/triangle_creation.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/bvh.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/bvh.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/kdtree.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/zcurve.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/data_structure/zcurve.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/f2x2.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/f3x3.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/f3x4.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/f4x1.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/f4x4.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/maths.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/maths/ops.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/ops/chamfer.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/ops/chamfer.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/ops/mc.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/ops/mc.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/ops/mc_data.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/edge.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/gs.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/gs.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/pgs.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/pgs.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/primitive/ray.h +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/csrc/pybind.cpp +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/assets/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/assets/common.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/assets/iphigenia.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/collate/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/collate/mesh.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/collate/sparse_tensor.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/dataset/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/dataset/base_mesh.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/dataset/digit3d.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/transform/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/transform/base.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/transform/ops.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data/transform/vertex.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data_structure/bmesh.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/data_structure/sort.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/io/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/io/obj.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/io/off.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/ops/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/ops/delaunay_triangulation.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/ops/diff_marching_cubes.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/ops/distance.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/ops/marching_cubes.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/ops/marching_tetrahedra.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/primitive/__init__.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/primitive/gs.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d/primitive/pgs.py +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d.egg-info/dependency_links.txt +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d.egg-info/not-zip-safe +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d.egg-info/requires.txt +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/conquer3d.egg-info/top_level.txt +0 -0
- {conquer3d-0.4.0 → conquer3d-0.4.2}/setup.cfg +0 -0
|
Binary file
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import tqdm
|
|
3
|
+
from conquer3d.data_structure.grid import create_voxel_grid, compute_active_voxels
|
|
4
|
+
|
|
5
|
+
def tmesh2voxel(tm, res, grid_min=None, grid_max=None, chunk_size=5000000, device='cuda', show_progress=True):
|
|
6
|
+
"""
|
|
7
|
+
Constructs a dense voxel grid from a TriangleMesh and evaluates its SDF.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
grid_vertices, voxels, idx_grids, sdfs
|
|
11
|
+
"""
|
|
12
|
+
if grid_min is None:
|
|
13
|
+
grid_min = [-1.0, -1.0, -1.0]
|
|
14
|
+
if grid_max is None:
|
|
15
|
+
grid_max = [1.0, 1.0, 1.0]
|
|
16
|
+
|
|
17
|
+
if isinstance(res, int):
|
|
18
|
+
res_list = [res, res, res]
|
|
19
|
+
else:
|
|
20
|
+
res_list = list(res)
|
|
21
|
+
grid_vertices, voxels, idx_grids = create_voxel_grid(grid_min, grid_max, res_list, device=device)
|
|
22
|
+
|
|
23
|
+
num_points = grid_vertices.shape[0]
|
|
24
|
+
sdfs = torch.empty(num_points, dtype=torch.float32, device=device)
|
|
25
|
+
|
|
26
|
+
iterator = range(0, num_points, chunk_size)
|
|
27
|
+
if show_progress:
|
|
28
|
+
iterator = tqdm.tqdm(iterator, desc="Computing SDF")
|
|
29
|
+
|
|
30
|
+
for i in iterator:
|
|
31
|
+
end = min(i + chunk_size, num_points)
|
|
32
|
+
chunk_points = grid_vertices[i:end]
|
|
33
|
+
_, _, _, chunk_sdf = tm.query_points(chunk_points, return_sdf=True, return_prj_pts=False, sign_mode=1)
|
|
34
|
+
sdfs[i:end] = chunk_sdf
|
|
35
|
+
|
|
36
|
+
return grid_vertices, voxels, idx_grids, sdfs
|
|
37
|
+
|
|
38
|
+
def tmesh2sparse(tm, res, grid_min=None, grid_max=None, chunk_size=5000000, iso=0.0, device='cuda', show_progress=True):
|
|
39
|
+
"""
|
|
40
|
+
Computes the SDF and returns only the active voxels (sparse grid) near the surface.
|
|
41
|
+
This now completely bypasses dense memory allocation!
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
grid_vertices, active_voxels, idx_grids, sdfs
|
|
45
|
+
"""
|
|
46
|
+
if grid_min is None:
|
|
47
|
+
grid_min = [-1.0, -1.0, -1.0]
|
|
48
|
+
if grid_max is None:
|
|
49
|
+
grid_max = [1.0, 1.0, 1.0]
|
|
50
|
+
|
|
51
|
+
if isinstance(res, int):
|
|
52
|
+
res_list = [res, res, res]
|
|
53
|
+
else:
|
|
54
|
+
res_list = list(res)
|
|
55
|
+
|
|
56
|
+
from conquer3d.data_structure import create_voxel_grid_from_tmesh
|
|
57
|
+
|
|
58
|
+
if tm.bvh is None:
|
|
59
|
+
tm.build_bvh()
|
|
60
|
+
|
|
61
|
+
grid_vertices, active_voxels, unique_vert_ids = create_voxel_grid_from_tmesh(
|
|
62
|
+
grid_min, grid_max, res_list, tm
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
num_points = grid_vertices.shape[0]
|
|
66
|
+
sdfs = torch.empty(num_points, dtype=torch.float32, device=device)
|
|
67
|
+
|
|
68
|
+
iterator = range(0, num_points, chunk_size)
|
|
69
|
+
if show_progress:
|
|
70
|
+
iterator = tqdm.tqdm(iterator, desc="Computing Sparse SDF")
|
|
71
|
+
|
|
72
|
+
for i in iterator:
|
|
73
|
+
end = min(i + chunk_size, num_points)
|
|
74
|
+
chunk_points = grid_vertices[i:end]
|
|
75
|
+
_, _, _, chunk_sdf = tm.query_points(chunk_points, return_sdf=True, return_prj_pts=False, sign_mode=1)
|
|
76
|
+
sdfs[i:end] = chunk_sdf
|
|
77
|
+
|
|
78
|
+
# Since we strictly extracted surface-intersecting voxels in C++,
|
|
79
|
+
# we don't strictly need to re-filter them, but we can do a quick check to refine the band.
|
|
80
|
+
# However, marching_cubes only uses active_voxels.
|
|
81
|
+
# To keep identical API, we return None for idx_grids since it's dense and unnecessary.
|
|
82
|
+
return grid_vertices, active_voxels.to(torch.int64), None, sdfs
|
|
83
|
+
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
#include <tuple>
|
|
5
5
|
#include <string>
|
|
6
6
|
#include <vector>
|
|
7
|
+
#include "../../data_structure/mesh_bvh.h"
|
|
8
|
+
#include "../../data_structure/triangle_mesh.h"
|
|
7
9
|
|
|
8
10
|
namespace py = pybind11;
|
|
9
11
|
|
|
@@ -139,9 +141,78 @@ torch::Tensor compute_active_voxels(torch::Tensor voxels, torch::Tensor sdf, flo
|
|
|
139
141
|
return active_indices;
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
std::tuple<torch::Tensor, torch::Tensor, torch::Tensor> create_voxel_grid_from_tmesh(
|
|
145
|
+
std::vector<float> grid_min,
|
|
146
|
+
std::vector<float> grid_max,
|
|
147
|
+
std::vector<int64_t> res,
|
|
148
|
+
TriangleMesh &tmesh
|
|
149
|
+
) {
|
|
150
|
+
TORCH_CHECK(grid_min.size() == 3, "grid_min must have 3 elements.");
|
|
151
|
+
TORCH_CHECK(grid_max.size() == 3, "grid_max must have 3 elements.");
|
|
152
|
+
TORCH_CHECK(res.size() == 3, "res must have 3 elements.");
|
|
153
|
+
|
|
154
|
+
int64_t rx = res[0];
|
|
155
|
+
int64_t ry = res[1];
|
|
156
|
+
int64_t rz = res[2];
|
|
157
|
+
|
|
158
|
+
MeshBVH bvh = tmesh.build_bvh();
|
|
159
|
+
auto vertices = tmesh.get_vertices();
|
|
160
|
+
auto triangles = tmesh.get_triangles();
|
|
161
|
+
|
|
162
|
+
auto active_voxel_ids = bvh.get_active_voxel_ids_from_grid(grid_min, grid_max, res, vertices, triangles);
|
|
163
|
+
|
|
164
|
+
if (active_voxel_ids.size(0) == 0) {
|
|
165
|
+
return std::make_tuple(
|
|
166
|
+
torch::empty({0, 3}, torch::TensorOptions().dtype(torch::kFloat32).device(vertices.device())),
|
|
167
|
+
torch::empty({0, 8}, torch::TensorOptions().dtype(torch::kInt32).device(vertices.device())),
|
|
168
|
+
torch::empty({0}, torch::TensorOptions().dtype(torch::kInt64).device(vertices.device()))
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
auto vi = active_voxel_ids.div((ry - 1) * (rz - 1), "trunc");
|
|
173
|
+
auto rem = active_voxel_ids.remainder((ry - 1) * (rz - 1));
|
|
174
|
+
auto vj = rem.div(rz - 1, "trunc");
|
|
175
|
+
auto vk = rem.remainder(rz - 1);
|
|
176
|
+
|
|
177
|
+
auto v0 = vi * ry * rz + vj * rz + vk;
|
|
178
|
+
auto v1 = (vi + 1) * ry * rz + vj * rz + vk;
|
|
179
|
+
auto v2 = (vi + 1) * ry * rz + (vj + 1) * rz + vk;
|
|
180
|
+
auto v3 = vi * ry * rz + (vj + 1) * rz + vk;
|
|
181
|
+
auto v4 = vi * ry * rz + vj * rz + (vk + 1);
|
|
182
|
+
auto v5 = (vi + 1) * ry * rz + vj * rz + (vk + 1);
|
|
183
|
+
auto v6 = (vi + 1) * ry * rz + (vj + 1) * rz + (vk + 1);
|
|
184
|
+
auto v7 = vi * ry * rz + (vj + 1) * rz + (vk + 1);
|
|
185
|
+
|
|
186
|
+
auto active_voxels = torch::stack({v0, v1, v2, v3, v4, v5, v6, v7}, -1);
|
|
187
|
+
|
|
188
|
+
torch::Tensor unique_vert_ids, inverse_indices, counts;
|
|
189
|
+
std::tie(unique_vert_ids, inverse_indices, counts) = torch::_unique2(active_voxels.flatten(), true, true, false);
|
|
190
|
+
|
|
191
|
+
auto remapped_voxels = inverse_indices.view({-1, 8}).to(torch::kInt32);
|
|
192
|
+
|
|
193
|
+
auto u_i = unique_vert_ids.div(ry * rz, "trunc");
|
|
194
|
+
auto u_rem = unique_vert_ids.remainder(ry * rz);
|
|
195
|
+
auto u_j = u_rem.div(rz, "trunc");
|
|
196
|
+
auto u_k = u_rem.remainder(rz);
|
|
197
|
+
|
|
198
|
+
float spacing_x = (grid_max[0] - grid_min[0]) / (rx - 1);
|
|
199
|
+
float spacing_y = (grid_max[1] - grid_min[1]) / (ry - 1);
|
|
200
|
+
float spacing_z = (grid_max[2] - grid_min[2]) / (rz - 1);
|
|
201
|
+
|
|
202
|
+
auto x = grid_min[0] + u_i.to(torch::kFloat32) * spacing_x;
|
|
203
|
+
auto y = grid_min[1] + u_j.to(torch::kFloat32) * spacing_y;
|
|
204
|
+
auto z = grid_min[2] + u_k.to(torch::kFloat32) * spacing_z;
|
|
205
|
+
|
|
206
|
+
auto sparse_grid_vertices = torch::stack({x, y, z}, 1).contiguous();
|
|
207
|
+
|
|
208
|
+
return std::make_tuple(sparse_grid_vertices, remapped_voxels, unique_vert_ids);
|
|
209
|
+
}
|
|
210
|
+
|
|
142
211
|
void bind_ds_grid(py::module_& m) {
|
|
143
212
|
m.def("create_voxel_grid", &create_voxel_grid, "Creates a structured 3D voxel grid.",
|
|
144
213
|
py::arg("grid_min"), py::arg("grid_max"), py::arg("res"), py::arg("device_str") = "cuda");
|
|
214
|
+
m.def("create_voxel_grid_from_tmesh", &create_voxel_grid_from_tmesh, "Creates a sparse 3D voxel grid strictly around the surface.",
|
|
215
|
+
py::arg("grid_min"), py::arg("grid_max"), py::arg("res"), py::arg("tmesh"));
|
|
145
216
|
m.def("compute_grid_normal", &compute_grid_normal, "Computes surface normals for a grid.",
|
|
146
217
|
py::arg("sdf"), py::arg("grid_vertices"), py::arg("idx_grids"), py::arg("res"));
|
|
147
218
|
m.def("compute_active_voxels", &compute_active_voxels, "Computes active voxels intersecting the surface",
|
|
@@ -187,6 +187,112 @@ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> MeshBVH::
|
|
|
187
187
|
return std::make_tuple(out_query_ids, out_object_ids, out_projected_pts, out_distances);
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
torch::Tensor MeshBVH::query_voxel(
|
|
191
|
+
const torch::Tensor &query_mins,
|
|
192
|
+
const torch::Tensor &query_maxs,
|
|
193
|
+
const torch::Tensor &vertices,
|
|
194
|
+
const torch::Tensor &triangles)
|
|
195
|
+
{
|
|
196
|
+
int num_queries = query_mins.size(0);
|
|
197
|
+
int num_objects = this->object_ids.size(0);
|
|
198
|
+
|
|
199
|
+
auto options_bool = torch::TensorOptions().dtype(torch::kBool).device(query_mins.device());
|
|
200
|
+
torch::Tensor out_intersect = torch::empty({num_queries}, options_bool);
|
|
201
|
+
|
|
202
|
+
if (num_queries == 0 || num_objects == 0)
|
|
203
|
+
{
|
|
204
|
+
out_intersect.fill_(false);
|
|
205
|
+
return out_intersect;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
mesh_bvh::query_voxel_mesh_bvh(
|
|
209
|
+
num_queries,
|
|
210
|
+
num_objects,
|
|
211
|
+
(const float3 *)query_mins.data_ptr<float>(),
|
|
212
|
+
(const float3 *)query_maxs.data_ptr<float>(),
|
|
213
|
+
(const float3 *)vertices.data_ptr<float>(),
|
|
214
|
+
(const int3 *)triangles.data_ptr<int>(),
|
|
215
|
+
(const float3 *)this->aabb_mins.data_ptr<float>(),
|
|
216
|
+
(const float3 *)this->aabb_maxs.data_ptr<float>(),
|
|
217
|
+
(const int2 *)this->bvh_children.data_ptr<int>(),
|
|
218
|
+
this->object_ids.data_ptr<int>(),
|
|
219
|
+
out_intersect.data_ptr<bool>());
|
|
220
|
+
|
|
221
|
+
return out_intersect;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
torch::Tensor MeshBVH::get_active_voxel_ids_from_grid(
|
|
225
|
+
std::vector<float> grid_min,
|
|
226
|
+
std::vector<float> grid_max,
|
|
227
|
+
std::vector<int64_t> res,
|
|
228
|
+
const torch::Tensor &vertices,
|
|
229
|
+
const torch::Tensor &triangles)
|
|
230
|
+
{
|
|
231
|
+
int64_t rx = res[0] - 1;
|
|
232
|
+
int64_t ry = res[1] - 1;
|
|
233
|
+
int64_t rz = res[2] - 1;
|
|
234
|
+
int64_t num_queries = rx * ry * rz;
|
|
235
|
+
int num_objects = this->object_ids.size(0);
|
|
236
|
+
|
|
237
|
+
auto options_i64 = torch::TensorOptions().dtype(torch::kInt64).device(vertices.device());
|
|
238
|
+
|
|
239
|
+
if (num_queries <= 0 || num_objects == 0)
|
|
240
|
+
{
|
|
241
|
+
return torch::empty({0}, options_i64);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
float spacing_x = (grid_max[0] - grid_min[0]) / (res[0] - 1);
|
|
245
|
+
float spacing_y = (grid_max[1] - grid_min[1]) / (res[1] - 1);
|
|
246
|
+
float spacing_z = (grid_max[2] - grid_min[2]) / (res[2] - 1);
|
|
247
|
+
|
|
248
|
+
int3 res_int = make_int3(rx, ry, rz);
|
|
249
|
+
float3 g_min = make_float3(grid_min[0], grid_min[1], grid_min[2]);
|
|
250
|
+
float3 v_size = make_float3(spacing_x, spacing_y, spacing_z);
|
|
251
|
+
|
|
252
|
+
// Pass 1: Count active voxels
|
|
253
|
+
torch::Tensor active_counter = torch::zeros({1}, options_i64);
|
|
254
|
+
|
|
255
|
+
mesh_bvh::count_active_voxels_mesh_bvh(
|
|
256
|
+
res_int,
|
|
257
|
+
g_min,
|
|
258
|
+
v_size,
|
|
259
|
+
num_objects,
|
|
260
|
+
(const float3 *)vertices.data_ptr<float>(),
|
|
261
|
+
(const int3 *)triangles.data_ptr<int>(),
|
|
262
|
+
(const float3 *)this->aabb_mins.data_ptr<float>(),
|
|
263
|
+
(const float3 *)this->aabb_maxs.data_ptr<float>(),
|
|
264
|
+
(const int2 *)this->bvh_children.data_ptr<int>(),
|
|
265
|
+
this->object_ids.data_ptr<int>(),
|
|
266
|
+
active_counter.data_ptr<int64_t>());
|
|
267
|
+
|
|
268
|
+
int64_t num_active = active_counter.item<int64_t>();
|
|
269
|
+
|
|
270
|
+
if (num_active == 0)
|
|
271
|
+
{
|
|
272
|
+
return torch::empty({0}, options_i64);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Pass 2: Collect active voxels
|
|
276
|
+
torch::Tensor out_active_ids = torch::empty({num_active}, options_i64);
|
|
277
|
+
active_counter.zero_();
|
|
278
|
+
|
|
279
|
+
mesh_bvh::collect_active_voxels_mesh_bvh(
|
|
280
|
+
res_int,
|
|
281
|
+
g_min,
|
|
282
|
+
v_size,
|
|
283
|
+
num_objects,
|
|
284
|
+
(const float3 *)vertices.data_ptr<float>(),
|
|
285
|
+
(const int3 *)triangles.data_ptr<int>(),
|
|
286
|
+
(const float3 *)this->aabb_mins.data_ptr<float>(),
|
|
287
|
+
(const float3 *)this->aabb_maxs.data_ptr<float>(),
|
|
288
|
+
(const int2 *)this->bvh_children.data_ptr<int>(),
|
|
289
|
+
this->object_ids.data_ptr<int>(),
|
|
290
|
+
active_counter.data_ptr<int64_t>(),
|
|
291
|
+
out_active_ids.data_ptr<int64_t>());
|
|
292
|
+
|
|
293
|
+
return out_active_ids;
|
|
294
|
+
}
|
|
295
|
+
|
|
190
296
|
void MeshBVH::build_winding_data(
|
|
191
297
|
const torch::Tensor &vertices,
|
|
192
298
|
const torch::Tensor &triangles)
|
|
@@ -295,5 +401,18 @@ void bind_ds_mesh_bvh(py::module_ &m)
|
|
|
295
401
|
Args:
|
|
296
402
|
vertices (torch.Tensor): Shape (N, 3) float32 tensor of vertices.
|
|
297
403
|
triangles (torch.Tensor): Shape (M, 3) int32 tensor of triangles.
|
|
404
|
+
)doc")
|
|
405
|
+
.def("query_voxel", &MeshBVH::query_voxel, py::arg("query_mins"), py::arg("query_maxs"), py::arg("vertices"), py::arg("triangles"),
|
|
406
|
+
R"doc(
|
|
407
|
+
Queries whether each voxel intersects the mesh.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
query_mins (torch.Tensor): Shape (Q, 3) float32 tensor of voxel minimums.
|
|
411
|
+
query_maxs (torch.Tensor): Shape (Q, 3) float32 tensor of voxel maximums.
|
|
412
|
+
vertices (torch.Tensor): Shape (N, 3) float32 tensor of vertices.
|
|
413
|
+
triangles (torch.Tensor): Shape (M, 3) int32 tensor of triangles.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
torch.Tensor: Boolean tensor of shape (Q,) indicating intersection.
|
|
298
417
|
)doc");
|
|
299
418
|
}
|
|
@@ -296,6 +296,10 @@ void TriangleMesh::remove_isolated_vertices() {
|
|
|
296
296
|
this->triangle_normals = torch::Tensor();
|
|
297
297
|
this->surface_area = torch::Tensor();
|
|
298
298
|
this->bvh.reset();
|
|
299
|
+
this->opt_edge_manifold = std::nullopt;
|
|
300
|
+
this->opt_edge_manifold_w_boundary = std::nullopt;
|
|
301
|
+
this->opt_vertex_manifold = std::nullopt;
|
|
302
|
+
this->opt_self_intersected = std::nullopt;
|
|
299
303
|
this->edges = torch::Tensor();
|
|
300
304
|
this->edge_to_triangle_offsets = torch::Tensor();
|
|
301
305
|
this->edge_to_triangle_counts = torch::Tensor();
|
|
@@ -347,8 +351,13 @@ torch::Tensor TriangleMesh::get_self_intersection()
|
|
|
347
351
|
|
|
348
352
|
bool TriangleMesh::is_self_intersection()
|
|
349
353
|
{
|
|
354
|
+
if (this->opt_self_intersected.has_value()) {
|
|
355
|
+
return this->opt_self_intersected.value();
|
|
356
|
+
}
|
|
350
357
|
this->build_bvh();
|
|
351
|
-
|
|
358
|
+
bool self_int = this->bvh.value().is_self_intersection(this->vertices, this->triangles);
|
|
359
|
+
this->opt_self_intersected = self_int;
|
|
360
|
+
return self_int;
|
|
352
361
|
}
|
|
353
362
|
|
|
354
363
|
std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> TriangleMesh::query_points(
|
|
@@ -358,6 +367,10 @@ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> TriangleM
|
|
|
358
367
|
int sign_mode,
|
|
359
368
|
int distance_mode)
|
|
360
369
|
{
|
|
370
|
+
if (!this->is_manifold(true)) {
|
|
371
|
+
sign_mode = 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
361
374
|
if (distance_mode == 0)
|
|
362
375
|
{
|
|
363
376
|
this->build_bvh();
|
|
@@ -541,15 +554,26 @@ bool TriangleMesh::is_edge_manifold(bool allow_boundary_edge)
|
|
|
541
554
|
{
|
|
542
555
|
if (this->num_triangles == 0)
|
|
543
556
|
return true;
|
|
557
|
+
|
|
558
|
+
if (allow_boundary_edge && this->opt_edge_manifold_w_boundary.has_value()) {
|
|
559
|
+
return this->opt_edge_manifold_w_boundary.value();
|
|
560
|
+
} else if (!allow_boundary_edge && this->opt_edge_manifold.has_value()) {
|
|
561
|
+
return this->opt_edge_manifold.value();
|
|
562
|
+
}
|
|
563
|
+
|
|
544
564
|
torch::Tensor counts = this->get_edge_to_triangle_counts();
|
|
565
|
+
bool is_manifold;
|
|
545
566
|
if (allow_boundary_edge)
|
|
546
567
|
{
|
|
547
|
-
|
|
568
|
+
is_manifold = (counts <= 2).all().item<bool>();
|
|
569
|
+
this->opt_edge_manifold_w_boundary = is_manifold;
|
|
548
570
|
}
|
|
549
571
|
else
|
|
550
572
|
{
|
|
551
|
-
|
|
573
|
+
is_manifold = (counts == 2).all().item<bool>();
|
|
574
|
+
this->opt_edge_manifold = is_manifold;
|
|
552
575
|
}
|
|
576
|
+
return is_manifold;
|
|
553
577
|
}
|
|
554
578
|
|
|
555
579
|
void TriangleMesh::remove_triangles_by_mask(const torch::Tensor &keep_mask)
|
|
@@ -566,6 +590,10 @@ void TriangleMesh::remove_triangles_by_mask(const torch::Tensor &keep_mask)
|
|
|
566
590
|
this->triangle_normals = torch::Tensor();
|
|
567
591
|
this->surface_area = torch::Tensor();
|
|
568
592
|
this->bvh.reset();
|
|
593
|
+
this->opt_edge_manifold = std::nullopt;
|
|
594
|
+
this->opt_edge_manifold_w_boundary = std::nullopt;
|
|
595
|
+
this->opt_vertex_manifold = std::nullopt;
|
|
596
|
+
this->opt_self_intersected = std::nullopt;
|
|
569
597
|
this->edges = torch::Tensor();
|
|
570
598
|
this->edge_to_triangle_offsets = torch::Tensor();
|
|
571
599
|
this->edge_to_triangle_counts = torch::Tensor();
|
|
@@ -581,6 +609,33 @@ void TriangleMesh::remove_triangles_by_mask(const torch::Tensor &keep_mask)
|
|
|
581
609
|
this->voronoi_areas = torch::Tensor();
|
|
582
610
|
}
|
|
583
611
|
|
|
612
|
+
void TriangleMesh::fix_normals()
|
|
613
|
+
{
|
|
614
|
+
if (this->num_triangles == 0) return;
|
|
615
|
+
|
|
616
|
+
if (!this->vertex_to_triangle_offsets.defined()) {
|
|
617
|
+
this->compute_vertices_to_triangle_map();
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
triangle_mesh::fix_normals(
|
|
621
|
+
this->num_triangles,
|
|
622
|
+
reinterpret_cast<const float3 *>(this->vertices.data_ptr<float>()),
|
|
623
|
+
this->vertex_to_triangle_offsets,
|
|
624
|
+
this->vertex_to_triangle_counts,
|
|
625
|
+
this->vertex_to_triangle_indices,
|
|
626
|
+
reinterpret_cast<int3 *>(this->triangles.data_ptr<int>()));
|
|
627
|
+
|
|
628
|
+
// Invalidate caches
|
|
629
|
+
this->triangle_areas = torch::Tensor();
|
|
630
|
+
this->triangle_normals = torch::Tensor();
|
|
631
|
+
this->vertex_normals = torch::Tensor();
|
|
632
|
+
this->bvh.reset();
|
|
633
|
+
this->opt_edge_manifold = std::nullopt;
|
|
634
|
+
this->opt_edge_manifold_w_boundary = std::nullopt;
|
|
635
|
+
this->opt_vertex_manifold = std::nullopt;
|
|
636
|
+
this->opt_self_intersected = std::nullopt;
|
|
637
|
+
}
|
|
638
|
+
|
|
584
639
|
int32_t TriangleMesh::get_euler_characteristic()
|
|
585
640
|
{
|
|
586
641
|
int32_t V = this->vertices.size(0);
|
|
@@ -648,8 +703,13 @@ torch::Tensor TriangleMesh::get_non_manifold_vertices()
|
|
|
648
703
|
|
|
649
704
|
bool TriangleMesh::is_vertex_manifold()
|
|
650
705
|
{
|
|
706
|
+
if (this->opt_vertex_manifold.has_value()) {
|
|
707
|
+
return this->opt_vertex_manifold.value();
|
|
708
|
+
}
|
|
651
709
|
torch::Tensor nm_vertices = this->get_non_manifold_vertices();
|
|
652
|
-
|
|
710
|
+
bool is_manifold = (nm_vertices.size(0) == 0);
|
|
711
|
+
this->opt_vertex_manifold = is_manifold;
|
|
712
|
+
return is_manifold;
|
|
653
713
|
}
|
|
654
714
|
|
|
655
715
|
bool TriangleMesh::is_manifold(bool allow_boundary_edge)
|
|
@@ -934,6 +994,11 @@ void bind_ds_triangle_mesh(py::module_ &m)
|
|
|
934
994
|
Args:
|
|
935
995
|
keep_mask (torch.Tensor): Shape (M,) boolean tensor indicating which triangles to keep.
|
|
936
996
|
)doc")
|
|
997
|
+
.def("fix_normals", &TriangleMesh::fix_normals, R"doc(
|
|
998
|
+
Fixes the winding order and outward orientation of the mesh normals.
|
|
999
|
+
This uses a CUDA-accelerated BFS to ensure consistent winding,
|
|
1000
|
+
and computes signed volumes to ensure all disconnected components face outward.
|
|
1001
|
+
)doc")
|
|
937
1002
|
.def("sample_points", &TriangleMesh::sample_points, py::arg("num_points"), py::arg("uniform") = false, py::arg("return_normals") = false, py::arg("return_colors") = false, py::arg("use_triangle_normal") = true, R"doc(
|
|
938
1003
|
Samples random points on the surface of the mesh.
|
|
939
1004
|
|