conquer3d 0.7.4__tar.gz → 0.7.5__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.7.4/conquer3d.egg-info → conquer3d-0.7.5}/PKG-INFO +1 -1
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/_C.so +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/dc.cpp +21 -2
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/dmc.cpp +16 -2
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/qef.h +11 -4
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/dc.cu +32 -4
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/dc.h +3 -1
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/dmc.cu +36 -1
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/dmc.h +3 -1
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/__init__.py +4 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/dual_contouring.py +28 -2
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/dual_marching_cubes.py +29 -2
- conquer3d-0.7.5/conquer3d/ops/hermite.py +263 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5/conquer3d.egg-info}/PKG-INFO +1 -1
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d.egg-info/SOURCES.txt +1 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/pyproject.toml +1 -1
- {conquer3d-0.7.4 → conquer3d-0.7.5}/LICENSE +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/MANIFEST.in +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/README.md +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/_C.pyi +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/conversion/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/conversion/grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/conversion/mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/conversion/tmesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/creation/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/creation/triangle_creation.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/grid.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/data_structure/zcurve.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/flood_fill.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/flood_fill_cf.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/mca.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/mt.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/mtg.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/ops/volint.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/primitive/ray.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/binds/primitive/triangle.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/check.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/constants.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/creation/triangle_creation.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/bvh.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/grid.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/grid.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/kdtree.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/zcurve.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/data_structure/zcurve.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/f2x2.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/f3x1.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/f3x3.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/f3x4.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/f4x1.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/f4x4.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/maths.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/maths/ops.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/chamfer.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/chamfer.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/dc_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/dmc_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/flood_fill.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/flood_fill.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/flood_fill_cf.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/flood_fill_cf.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mc.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mc.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mc_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mca.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mca.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mca_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mt.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mt.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mt_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mtg.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mtg.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/mtg_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/volint.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/ops/volint.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/aabb.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/edge.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/gs.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/gs.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/pgs.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/pgs.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/ray.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/primitive/triangle.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/csrc/pybind.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/assets/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/assets/common.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/assets/iphigenia.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/collate/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/collate/mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/collate/sparse_tensor.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/dataset/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/dataset/base_mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/dataset/digit3d.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/dataset/digit3dmv.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/dataset/mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/dataset/redwood.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/transform/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/transform/base.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/transform/ops.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data/transform/vertex.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data_structure/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data_structure/bmesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data_structure/grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/data_structure/sort.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/io/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/io/obj.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/io/off.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/io/ply.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/delaunay_triangulation.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/diff_marching_cubes.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/diff_marching_tetrahedra_grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/distance.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/dpsr.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/marching_cubes.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/marching_cubes_asymptotic.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/marching_tetrahedra.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/marching_tetrahedra_grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/ops/volint.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/primitive/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/primitive/gs.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d/primitive/pgs.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d.egg-info/dependency_links.txt +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d.egg-info/not-zip-safe +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d.egg-info/requires.txt +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/conquer3d.egg-info/top_level.txt +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/setup.cfg +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.5}/setup.py +0 -0
|
Binary file
|
|
@@ -21,7 +21,9 @@ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> dual_cont
|
|
|
21
21
|
std::optional<torch::Tensor> colors,
|
|
22
22
|
std::optional<torch::Tensor> voxel_vertices,
|
|
23
23
|
float iso,
|
|
24
|
-
bool quad_split
|
|
24
|
+
bool quad_split,
|
|
25
|
+
std::optional<torch::Tensor> edge_points,
|
|
26
|
+
std::optional<torch::Tensor> edge_normals
|
|
25
27
|
) {
|
|
26
28
|
CHECK_INPUT(grid_vertices);
|
|
27
29
|
CHECK_INPUT(voxels);
|
|
@@ -36,6 +38,20 @@ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> dual_cont
|
|
|
36
38
|
if (voxel_vertices.has_value() && voxel_vertices.value().defined()) {
|
|
37
39
|
CHECK_INPUT(voxel_vertices.value());
|
|
38
40
|
}
|
|
41
|
+
if (edge_points.has_value() && edge_points.value().defined()) {
|
|
42
|
+
CHECK_INPUT(edge_points.value());
|
|
43
|
+
TORCH_CHECK(edge_points.value().dim() == 3 && edge_points.value().size(1) == 12 && edge_points.value().size(2) == 3,
|
|
44
|
+
"edge_points must have shape (M, 12, 3)");
|
|
45
|
+
TORCH_CHECK(edge_points.value().size(0) == voxels.size(0),
|
|
46
|
+
"edge_points must have one row per voxel");
|
|
47
|
+
}
|
|
48
|
+
if (edge_normals.has_value() && edge_normals.value().defined()) {
|
|
49
|
+
CHECK_INPUT(edge_normals.value());
|
|
50
|
+
TORCH_CHECK(edge_normals.value().dim() == 3 && edge_normals.value().size(1) == 12 && edge_normals.value().size(2) == 3,
|
|
51
|
+
"edge_normals must have shape (M, 12, 3)");
|
|
52
|
+
TORCH_CHECK(edge_normals.value().size(0) == voxels.size(0),
|
|
53
|
+
"edge_normals must have one row per voxel");
|
|
54
|
+
}
|
|
39
55
|
|
|
40
56
|
return conquer3d::ops::dual_contouring(
|
|
41
57
|
grid_vertices,
|
|
@@ -45,7 +61,9 @@ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> dual_cont
|
|
|
45
61
|
colors,
|
|
46
62
|
voxel_vertices,
|
|
47
63
|
iso,
|
|
48
|
-
quad_split
|
|
64
|
+
quad_split,
|
|
65
|
+
edge_points,
|
|
66
|
+
edge_normals
|
|
49
67
|
);
|
|
50
68
|
}
|
|
51
69
|
|
|
@@ -106,6 +124,7 @@ void bind_ops_dc(py::module &m) {
|
|
|
106
124
|
py::arg("grid_normals") = py::none(), py::arg("colors") = py::none(),
|
|
107
125
|
py::arg("voxel_vertices") = py::none(),
|
|
108
126
|
py::arg("iso") = 0.0f, py::arg("quad_split") = true,
|
|
127
|
+
py::arg("edge_points") = py::none(), py::arg("edge_normals") = py::none(),
|
|
109
128
|
R"pbdoc(
|
|
110
129
|
Extracts a sharp-feature preserving surface mesh using Dual Contouring with GPU QEF solver (Ju et al. 2002) or precomputed voxel vertices.
|
|
111
130
|
|
|
@@ -21,7 +21,9 @@ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> dual_marc
|
|
|
21
21
|
std::optional<torch::Tensor> voxel_vertices,
|
|
22
22
|
float iso,
|
|
23
23
|
bool quad_split,
|
|
24
|
-
int project_iters
|
|
24
|
+
int project_iters,
|
|
25
|
+
std::optional<torch::Tensor> edge_points,
|
|
26
|
+
std::optional<torch::Tensor> edge_normals
|
|
25
27
|
) {
|
|
26
28
|
CHECK_INPUT(grid_vertices);
|
|
27
29
|
CHECK_INPUT(voxels);
|
|
@@ -33,6 +35,15 @@ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> dual_marc
|
|
|
33
35
|
if (voxel_vertices.has_value() && voxel_vertices.value().defined()) {
|
|
34
36
|
CHECK_INPUT(voxel_vertices.value());
|
|
35
37
|
}
|
|
38
|
+
for (auto *t : {&edge_points, &edge_normals}) {
|
|
39
|
+
if (t->has_value() && t->value().defined()) {
|
|
40
|
+
CHECK_INPUT(t->value());
|
|
41
|
+
TORCH_CHECK(t->value().dim() == 3 && t->value().size(1) == 12 && t->value().size(2) == 3,
|
|
42
|
+
"edge_points/edge_normals must have shape (M, 12, 3)");
|
|
43
|
+
TORCH_CHECK(t->value().size(0) == voxels.size(0),
|
|
44
|
+
"edge_points/edge_normals must have one row per voxel");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
36
47
|
|
|
37
48
|
return conquer3d::ops::dual_marching_cubes(
|
|
38
49
|
grid_vertices,
|
|
@@ -42,7 +53,9 @@ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> dual_marc
|
|
|
42
53
|
voxel_vertices,
|
|
43
54
|
iso,
|
|
44
55
|
quad_split,
|
|
45
|
-
project_iters
|
|
56
|
+
project_iters,
|
|
57
|
+
edge_points,
|
|
58
|
+
edge_normals
|
|
46
59
|
);
|
|
47
60
|
}
|
|
48
61
|
|
|
@@ -99,6 +112,7 @@ void bind_ops_dmc(py::module &m) {
|
|
|
99
112
|
py::arg("grid_vertices"), py::arg("voxels"), py::arg("sdf"),
|
|
100
113
|
py::arg("colors") = py::none(), py::arg("voxel_vertices") = py::none(),
|
|
101
114
|
py::arg("iso") = 0.0f, py::arg("quad_split") = true, py::arg("project_iters") = 5,
|
|
115
|
+
py::arg("edge_points") = py::none(), py::arg("edge_normals") = py::none(),
|
|
102
116
|
R"pbdoc(
|
|
103
117
|
Extracts a watertight 2-manifold surface mesh using Differentiable Dual Marching Cubes (Schaefer & Warren 2004) or precomputed voxel vertices.
|
|
104
118
|
|
|
@@ -144,6 +144,7 @@ __host__ __device__ __forceinline__ void symmetric_eigen_3x3(
|
|
|
144
144
|
* @param cell_min Minimum AABB coordinate of the voxel cell.
|
|
145
145
|
* @param cell_max Maximum AABB coordinate of the voxel cell.
|
|
146
146
|
* @param svd_tolerance Relative eigenvalue threshold for pseudoinverse (default: 0.01).
|
|
147
|
+
* @param cell_expand Scale applied to the cell half-extents before clamping (default: 2.0).
|
|
147
148
|
* @return Optimal inner vertex position x*.
|
|
148
149
|
*/
|
|
149
150
|
__host__ __device__ __forceinline__ float3 solve_qef(
|
|
@@ -152,12 +153,18 @@ __host__ __device__ __forceinline__ float3 solve_qef(
|
|
|
152
153
|
int count,
|
|
153
154
|
const float3 &cell_min,
|
|
154
155
|
const float3 &cell_max,
|
|
155
|
-
float svd_tolerance = 0.01f
|
|
156
|
+
float svd_tolerance = 0.01f,
|
|
157
|
+
float cell_expand = 2.0f
|
|
156
158
|
) {
|
|
157
159
|
if (count <= 0) {
|
|
158
160
|
return (cell_min + cell_max) * 0.5f;
|
|
159
161
|
}
|
|
160
162
|
|
|
163
|
+
const float3 cell_centre = (cell_min + cell_max) * 0.5f;
|
|
164
|
+
const float3 cell_half = (cell_max - cell_min) * (0.5f * cell_expand);
|
|
165
|
+
const float3 clamp_min = cell_centre - cell_half;
|
|
166
|
+
const float3 clamp_max = cell_centre + cell_half;
|
|
167
|
+
|
|
161
168
|
// 1. Compute Mass Point (Centroid)
|
|
162
169
|
float3 mass_point = make_float3(0.0f, 0.0f, 0.0f);
|
|
163
170
|
for (int i = 0; i < count; ++i) {
|
|
@@ -166,7 +173,7 @@ __host__ __device__ __forceinline__ float3 solve_qef(
|
|
|
166
173
|
mass_point = mass_point * (1.0f / (float)count);
|
|
167
174
|
|
|
168
175
|
if (count == 1) {
|
|
169
|
-
return maths::clamp(pts[0],
|
|
176
|
+
return maths::clamp(pts[0], clamp_min, clamp_max);
|
|
170
177
|
}
|
|
171
178
|
|
|
172
179
|
// 2. Build Shifted Normal Equation System: (A^T A) y = b_tilde
|
|
@@ -224,9 +231,9 @@ __host__ __device__ __forceinline__ float3 solve_qef(
|
|
|
224
231
|
y = y + v2 * proj;
|
|
225
232
|
}
|
|
226
233
|
|
|
227
|
-
// 5. Unshift and Clamp to Voxel AABB
|
|
234
|
+
// 5. Unshift and Clamp to the (optionally widened) Voxel AABB
|
|
228
235
|
float3 x = mass_point + y;
|
|
229
|
-
x = maths::clamp(x,
|
|
236
|
+
x = maths::clamp(x, clamp_min, clamp_max);
|
|
230
237
|
|
|
231
238
|
return x;
|
|
232
239
|
}
|
|
@@ -138,6 +138,8 @@ __global__ void compute_dual_vertices_kernel(
|
|
|
138
138
|
const int *__restrict__ voxels,
|
|
139
139
|
const float *__restrict__ sdf,
|
|
140
140
|
const float3 *__restrict__ grid_normals,
|
|
141
|
+
const float3 *__restrict__ edge_points,
|
|
142
|
+
const float3 *__restrict__ edge_normals,
|
|
141
143
|
float iso,
|
|
142
144
|
int num_voxels,
|
|
143
145
|
float3 *__restrict__ dual_vertices,
|
|
@@ -184,11 +186,27 @@ __global__ void compute_dual_vertices_kernel(
|
|
|
184
186
|
float t = (iso - s0) / (s1 - s0);
|
|
185
187
|
t = fmaxf(0.0f, fminf(1.0f, t));
|
|
186
188
|
|
|
187
|
-
float3 pt
|
|
189
|
+
float3 pt;
|
|
190
|
+
float3 n;
|
|
191
|
+
bool have_hermite = (edge_points != nullptr && edge_normals != nullptr);
|
|
192
|
+
if (have_hermite) {
|
|
193
|
+
pt = edge_points[m * 12 + e];
|
|
194
|
+
float3 hn = edge_normals[m * 12 + e];
|
|
195
|
+
float hlen = maths::norm(hn);
|
|
196
|
+
if (hlen > 1e-8f) {
|
|
197
|
+
n = hn * (1.0f / hlen);
|
|
198
|
+
} else {
|
|
199
|
+
have_hermite = false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (!have_hermite) {
|
|
203
|
+
pt = p[v0] + (p[v1] - p[v0]) * t;
|
|
204
|
+
}
|
|
188
205
|
pts[count] = pt;
|
|
189
206
|
|
|
190
|
-
|
|
191
|
-
|
|
207
|
+
if (have_hermite) {
|
|
208
|
+
// normal already set from the supplied Hermite data
|
|
209
|
+
} else if (grid_normals != nullptr) {
|
|
192
210
|
float3 n0 = grid_normals[c_idx[v0]];
|
|
193
211
|
float3 n1 = grid_normals[c_idx[v1]];
|
|
194
212
|
float3 n_interp = n0 + (n1 - n0) * t;
|
|
@@ -665,7 +683,9 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_contouring(
|
|
|
665
683
|
const c10::optional<at::Tensor> &colors,
|
|
666
684
|
const c10::optional<at::Tensor> &voxel_vertices,
|
|
667
685
|
float iso,
|
|
668
|
-
bool quad_split
|
|
686
|
+
bool quad_split,
|
|
687
|
+
const c10::optional<at::Tensor> &edge_points,
|
|
688
|
+
const c10::optional<at::Tensor> &edge_normals
|
|
669
689
|
) {
|
|
670
690
|
at::cuda::CUDAGuard device_guard(grid_vertices.device());
|
|
671
691
|
auto allocator = at::cuda::ThrustAllocator();
|
|
@@ -705,12 +725,18 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_contouring(
|
|
|
705
725
|
dual_vertices = at::empty({num_voxels, 3}, grid_vertices.options());
|
|
706
726
|
source_dual_vertices_ptr = reinterpret_cast<const float3*>(dual_vertices.data_ptr<float>());
|
|
707
727
|
const float3 *normals_ptr = grid_normals.has_value() ? reinterpret_cast<const float3*>(grid_normals.value().data_ptr<float>()) : nullptr;
|
|
728
|
+
const float3 *ep_ptr = (edge_points.has_value() && edge_points.value().defined() && edge_points.value().numel() > 0)
|
|
729
|
+
? reinterpret_cast<const float3*>(edge_points.value().data_ptr<float>()) : nullptr;
|
|
730
|
+
const float3 *en_ptr = (edge_normals.has_value() && edge_normals.value().defined() && edge_normals.value().numel() > 0)
|
|
731
|
+
? reinterpret_cast<const float3*>(edge_normals.value().data_ptr<float>()) : nullptr;
|
|
708
732
|
|
|
709
733
|
compute_dual_vertices_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
|
|
710
734
|
reinterpret_cast<const float3*>(grid_vertices.data_ptr<float>()),
|
|
711
735
|
voxels.data_ptr<int>(),
|
|
712
736
|
sdf.data_ptr<float>(),
|
|
713
737
|
normals_ptr,
|
|
738
|
+
ep_ptr,
|
|
739
|
+
en_ptr,
|
|
714
740
|
iso,
|
|
715
741
|
num_voxels,
|
|
716
742
|
reinterpret_cast<float3*>(dual_vertices.data_ptr<float>()),
|
|
@@ -910,6 +936,8 @@ std::tuple<at::Tensor, c10::optional<at::Tensor>> dual_contouring_backward(
|
|
|
910
936
|
voxels.data_ptr<int>(),
|
|
911
937
|
sdf.data_ptr<float>(),
|
|
912
938
|
normals_ptr,
|
|
939
|
+
nullptr,
|
|
940
|
+
nullptr,
|
|
913
941
|
iso,
|
|
914
942
|
num_voxels,
|
|
915
943
|
reinterpret_cast<float3*>(dual_vertices.data_ptr<float>()),
|
|
@@ -30,7 +30,9 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_contouring(
|
|
|
30
30
|
const c10::optional<at::Tensor> &colors = c10::nullopt,
|
|
31
31
|
const c10::optional<at::Tensor> &voxel_vertices = c10::nullopt,
|
|
32
32
|
float iso = 0.0f,
|
|
33
|
-
bool quad_split = true
|
|
33
|
+
bool quad_split = true,
|
|
34
|
+
const c10::optional<at::Tensor> &edge_points = c10::nullopt,
|
|
35
|
+
const c10::optional<at::Tensor> &edge_normals = c10::nullopt
|
|
34
36
|
);
|
|
35
37
|
|
|
36
38
|
/**
|
|
@@ -368,6 +368,8 @@ __global__ void dmc_extract_dual_vertices_and_edges_kernel(
|
|
|
368
368
|
const float *__restrict__ sdf,
|
|
369
369
|
const float *__restrict__ colors,
|
|
370
370
|
const float3 *__restrict__ precomputed_vertices,
|
|
371
|
+
const float3 *__restrict__ edge_points,
|
|
372
|
+
const float3 *__restrict__ edge_normals,
|
|
371
373
|
int num_channels,
|
|
372
374
|
const int *__restrict__ vert_offsets,
|
|
373
375
|
const int *__restrict__ edge_offsets,
|
|
@@ -436,8 +438,22 @@ __global__ void dmc_extract_dual_vertices_and_edges_kernel(
|
|
|
436
438
|
int dual_vert_idx = v_base + c;
|
|
437
439
|
float u_sum = 0.0f, v_sum = 0.0f, w_sum = 0.0f;
|
|
438
440
|
|
|
441
|
+
const bool use_hermite = (edge_points != nullptr && edge_normals != nullptr);
|
|
442
|
+
float3 h_pts[12];
|
|
443
|
+
float3 h_nrm[12];
|
|
444
|
+
int h_count = 0;
|
|
445
|
+
|
|
439
446
|
for (int i = 0; i < sz; ++i) {
|
|
440
447
|
int e = c_edges[c][i];
|
|
448
|
+
if (use_hermite && h_count < 12) {
|
|
449
|
+
float3 hn = edge_normals[m * 12 + e];
|
|
450
|
+
float hlen = maths::norm(hn);
|
|
451
|
+
if (hlen > 1e-8f) {
|
|
452
|
+
h_pts[h_count] = edge_points[m * 12 + e];
|
|
453
|
+
h_nrm[h_count] = hn * (1.0f / hlen);
|
|
454
|
+
h_count++;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
441
457
|
int v0 = dmc_edge_corners[e][0];
|
|
442
458
|
int v1 = dmc_edge_corners[e][1];
|
|
443
459
|
float s0 = s[v0];
|
|
@@ -476,6 +492,16 @@ __global__ void dmc_extract_dual_vertices_and_edges_kernel(
|
|
|
476
492
|
u = fmaxf(0.0f, fminf(1.0f, (pt.x - c_min.x) / dx));
|
|
477
493
|
v = fmaxf(0.0f, fminf(1.0f, (pt.y - c_min.y) / dy));
|
|
478
494
|
w = fmaxf(0.0f, fminf(1.0f, (pt.z - c_min.z) / dz));
|
|
495
|
+
} else if (use_hermite && h_count > 0) {
|
|
496
|
+
// Solve the quadratic error function over THIS contour's Hermite data.
|
|
497
|
+
// Restricting the solve to one contour is what keeps a cell that carries
|
|
498
|
+
// several contours emitting several distinct vertices, so the manifold
|
|
499
|
+
// guarantee survives while each vertex still lands on its own feature.
|
|
500
|
+
float3 pt = maths::solve_qef(h_pts, h_nrm, h_count, c_min, c_max, 0.01f);
|
|
501
|
+
out_vertices[dual_vert_idx] = pt;
|
|
502
|
+
u = fmaxf(0.0f, fminf(1.0f, (pt.x - c_min.x) / dx));
|
|
503
|
+
v = fmaxf(0.0f, fminf(1.0f, (pt.y - c_min.y) / dy));
|
|
504
|
+
w = fmaxf(0.0f, fminf(1.0f, (pt.z - c_min.z) / dz));
|
|
479
505
|
} else {
|
|
480
506
|
// Newton-Raphson Level-Set Projection
|
|
481
507
|
for (int iter = 0; iter < project_iters; ++iter) {
|
|
@@ -802,7 +828,9 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_marching_cube
|
|
|
802
828
|
const c10::optional<at::Tensor> &voxel_vertices,
|
|
803
829
|
float iso,
|
|
804
830
|
bool quad_split,
|
|
805
|
-
int project_iters
|
|
831
|
+
int project_iters,
|
|
832
|
+
const c10::optional<at::Tensor> &edge_points,
|
|
833
|
+
const c10::optional<at::Tensor> &edge_normals
|
|
806
834
|
) {
|
|
807
835
|
TORCH_CHECK(grid_vertices.is_cuda(), "grid_vertices must be a CUDA tensor");
|
|
808
836
|
TORCH_CHECK(voxels.is_cuda(), "voxels must be a CUDA tensor");
|
|
@@ -889,12 +917,19 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_marching_cube
|
|
|
889
917
|
? reinterpret_cast<const float3*>(voxel_vertices->data_ptr<float>())
|
|
890
918
|
: nullptr;
|
|
891
919
|
|
|
920
|
+
const float3 *dmc_ep_ptr = (edge_points.has_value() && edge_points->defined() && edge_points->numel() > 0)
|
|
921
|
+
? reinterpret_cast<const float3*>(edge_points->data_ptr<float>()) : nullptr;
|
|
922
|
+
const float3 *dmc_en_ptr = (edge_normals.has_value() && edge_normals->defined() && edge_normals->numel() > 0)
|
|
923
|
+
? reinterpret_cast<const float3*>(edge_normals->data_ptr<float>()) : nullptr;
|
|
924
|
+
|
|
892
925
|
dmc_extract_dual_vertices_and_edges_kernel<<<grid, block, 0, stream>>>(
|
|
893
926
|
reinterpret_cast<const float3*>(grid_vertices.data_ptr<float>()),
|
|
894
927
|
voxels.data_ptr<int>(),
|
|
895
928
|
sdf.data_ptr<float>(),
|
|
896
929
|
colors_ptr,
|
|
897
930
|
precomputed_ptr,
|
|
931
|
+
dmc_ep_ptr,
|
|
932
|
+
dmc_en_ptr,
|
|
898
933
|
num_channels,
|
|
899
934
|
vert_offsets.data_ptr<int>(),
|
|
900
935
|
edge_offsets.data_ptr<int>(),
|
|
@@ -29,7 +29,9 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_marching_cube
|
|
|
29
29
|
const c10::optional<at::Tensor> &voxel_vertices = c10::nullopt,
|
|
30
30
|
float iso = 0.0f,
|
|
31
31
|
bool quad_split = true,
|
|
32
|
-
int project_iters = 5
|
|
32
|
+
int project_iters = 5,
|
|
33
|
+
const c10::optional<at::Tensor> &edge_points = c10::nullopt,
|
|
34
|
+
const c10::optional<at::Tensor> &edge_normals = c10::nullopt
|
|
33
35
|
);
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -13,6 +13,7 @@ This package provides:
|
|
|
13
13
|
from .marching_cubes import marching_cubes
|
|
14
14
|
from .marching_cubes_asymptotic import marching_cubes_asymptotic, mca
|
|
15
15
|
from .dual_contouring import dual_contouring, dc
|
|
16
|
+
from .hermite import compute_hermite_from_mesh, compute_hermite_from_field, DC_EDGE_CORNERS
|
|
16
17
|
from .dual_marching_cubes import dual_marching_cubes, dmc
|
|
17
18
|
from .diff_marching_cubes import diff_marching_cubes
|
|
18
19
|
|
|
@@ -31,6 +32,9 @@ __all__ = [
|
|
|
31
32
|
"mca",
|
|
32
33
|
"dual_contouring",
|
|
33
34
|
"dc",
|
|
35
|
+
"compute_hermite_from_mesh",
|
|
36
|
+
"compute_hermite_from_field",
|
|
37
|
+
"DC_EDGE_CORNERS",
|
|
34
38
|
"dual_marching_cubes",
|
|
35
39
|
"dmc",
|
|
36
40
|
"diff_marching_cubes",
|
|
@@ -97,7 +97,9 @@ def dual_contouring(
|
|
|
97
97
|
colors: Optional[torch.Tensor] = None,
|
|
98
98
|
voxel_vertices: Optional[torch.Tensor] = None,
|
|
99
99
|
iso: float = 0.0,
|
|
100
|
-
quad_split: bool = True
|
|
100
|
+
quad_split: bool = True,
|
|
101
|
+
edge_points: Optional[torch.Tensor] = None,
|
|
102
|
+
edge_normals: Optional[torch.Tensor] = None
|
|
101
103
|
) -> Union[Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor, torch.Tensor]]:
|
|
102
104
|
"""Extracts sharp surface meshes from volumetric scalar fields using Differentiable Dual Contouring.
|
|
103
105
|
|
|
@@ -123,6 +125,16 @@ def dual_contouring(
|
|
|
123
125
|
quad_split (bool, optional): If True, splits dual quadrilaterals into 2 triangles using
|
|
124
126
|
the optimal Delaunay angle criterion; if False, returns quads of shape `(Q, 4)`.
|
|
125
127
|
Defaults to True.
|
|
128
|
+
edge_points (torch.Tensor, optional): Float32 tensor of shape `(M, 12, 3)` giving the
|
|
129
|
+
exact surface intersection point of each voxel-local edge, in the
|
|
130
|
+
:data:`conquer3d.ops.hermite.DC_EDGE_CORNERS` order. Supplying this together with
|
|
131
|
+
`edge_normals` is what lets Dual Contouring reproduce sharp creases exactly: the
|
|
132
|
+
QEF is then built from true Hermite data instead of from normals interpolated
|
|
133
|
+
between grid vertices, which blend across a crease and round it off. Entries for
|
|
134
|
+
edges that are not sign-crossing are ignored. Defaults to None.
|
|
135
|
+
edge_normals (torch.Tensor, optional): Float32 tensor of shape `(M, 12, 3)` giving the
|
|
136
|
+
exact surface normal at each `edge_points` entry. Must accompany `edge_points`;
|
|
137
|
+
either both are used or neither. Defaults to None.
|
|
126
138
|
|
|
127
139
|
Returns:
|
|
128
140
|
Union[Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor, torch.Tensor]]:
|
|
@@ -165,8 +177,22 @@ def dual_contouring(
|
|
|
165
177
|
# grid_vertices, voxels, sdf, grid_normals, colors, voxel_vertices, iso, quad_split
|
|
166
178
|
# )
|
|
167
179
|
# else:
|
|
180
|
+
if (edge_points is None) != (edge_normals is None):
|
|
181
|
+
raise ValueError("edge_points and edge_normals must be supplied together")
|
|
182
|
+
if edge_points is not None:
|
|
183
|
+
edge_points = edge_points.contiguous().float()
|
|
184
|
+
edge_normals = edge_normals.contiguous().float()
|
|
185
|
+
for name, t in (("edge_points", edge_points), ("edge_normals", edge_normals)):
|
|
186
|
+
if not t.is_cuda:
|
|
187
|
+
raise RuntimeError(f"{name} must be on CUDA")
|
|
188
|
+
if t.shape != (voxels.shape[0], 12, 3):
|
|
189
|
+
raise ValueError(
|
|
190
|
+
f"{name} must have shape ({voxels.shape[0]}, 12, 3), got {tuple(t.shape)}"
|
|
191
|
+
)
|
|
192
|
+
|
|
168
193
|
verts, faces, out_colors = _C.dual_contouring(
|
|
169
|
-
grid_vertices, voxels, sdf, grid_normals, colors, voxel_vertices, iso, quad_split
|
|
194
|
+
grid_vertices, voxels, sdf, grid_normals, colors, voxel_vertices, iso, quad_split,
|
|
195
|
+
edge_points, edge_normals
|
|
170
196
|
)
|
|
171
197
|
|
|
172
198
|
if colors is None:
|
|
@@ -95,7 +95,9 @@ def dual_marching_cubes(
|
|
|
95
95
|
voxel_vertices: Optional[torch.Tensor] = None,
|
|
96
96
|
iso: float = 0.0,
|
|
97
97
|
quad_split: bool = True,
|
|
98
|
-
project_iters: int = 5
|
|
98
|
+
project_iters: int = 5,
|
|
99
|
+
edge_points: Optional[torch.Tensor] = None,
|
|
100
|
+
edge_normals: Optional[torch.Tensor] = None
|
|
99
101
|
) -> Union[Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor, torch.Tensor]]:
|
|
100
102
|
"""Extracts a strictly 2-manifold surface mesh from a voxel grid using Differentiable Dual Marching Cubes.
|
|
101
103
|
|
|
@@ -120,6 +122,17 @@ def dual_marching_cubes(
|
|
|
120
122
|
quad_split (bool, optional): If True, splits dual quadrilaterals into 2 triangles using
|
|
121
123
|
the optimal Delaunay angle criterion; if False, returns quads of shape `(Q, 4)`.
|
|
122
124
|
Defaults to True.
|
|
125
|
+
edge_points (torch.Tensor, optional): Float32 tensor of shape `(M, 12, 3)` giving the
|
|
126
|
+
exact surface intersection of each voxel-local edge, in
|
|
127
|
+
:data:`conquer3d.ops.hermite.DC_EDGE_CORNERS` order. Supplied together with
|
|
128
|
+
`edge_normals`, this replaces the centroid-and-projection placement with a
|
|
129
|
+
quadratic error function solved per contour, which is what lets Dual Marching
|
|
130
|
+
Cubes reproduce sharp creases instead of rounding them. Solving per contour
|
|
131
|
+
rather than per cell keeps a cell that carries several contours emitting
|
|
132
|
+
several distinct vertices, preserving the manifold guarantee. Defaults to None.
|
|
133
|
+
edge_normals (torch.Tensor, optional): Float32 tensor of shape `(M, 12, 3)` giving the
|
|
134
|
+
exact surface normal at each `edge_points` entry. Must accompany `edge_points`.
|
|
135
|
+
Defaults to None.
|
|
123
136
|
project_iters (int, optional): Number of Newton-Raphson level-set projection iterations.
|
|
124
137
|
Defaults to 5.
|
|
125
138
|
|
|
@@ -160,8 +173,22 @@ def dual_marching_cubes(
|
|
|
160
173
|
# grid_vertices, voxels, sdf, colors, voxel_vertices, iso, quad_split, project_iters
|
|
161
174
|
# )
|
|
162
175
|
# else:
|
|
176
|
+
if (edge_points is None) != (edge_normals is None):
|
|
177
|
+
raise ValueError("edge_points and edge_normals must be supplied together")
|
|
178
|
+
if edge_points is not None:
|
|
179
|
+
edge_points = edge_points.contiguous().float()
|
|
180
|
+
edge_normals = edge_normals.contiguous().float()
|
|
181
|
+
for name, t in (("edge_points", edge_points), ("edge_normals", edge_normals)):
|
|
182
|
+
if not t.is_cuda:
|
|
183
|
+
raise RuntimeError(f"{name} must be on CUDA")
|
|
184
|
+
if t.shape != (voxels.shape[0], 12, 3):
|
|
185
|
+
raise ValueError(
|
|
186
|
+
f"{name} must have shape ({voxels.shape[0]}, 12, 3), got {tuple(t.shape)}"
|
|
187
|
+
)
|
|
188
|
+
|
|
163
189
|
verts, faces, out_colors = _C.dual_marching_cubes(
|
|
164
|
-
grid_vertices, voxels, sdf, colors, voxel_vertices, iso, quad_split, project_iters
|
|
190
|
+
grid_vertices, voxels, sdf, colors, voxel_vertices, iso, quad_split, project_iters,
|
|
191
|
+
edge_points, edge_normals
|
|
165
192
|
)
|
|
166
193
|
|
|
167
194
|
if colors is None:
|