conquer3d 0.7.4__tar.gz → 0.7.6__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.6}/PKG-INFO +1 -1
- conquer3d-0.7.6/conquer3d/_C.so +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/dc.cpp +21 -2
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/dmc.cpp +16 -2
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/mesh_bvh.cu +5 -13
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/triangle_mesh.cu +3 -3
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/f3x1.h +34 -0
- conquer3d-0.7.6/conquer3d/csrc/maths/ops.h +89 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/qef.h +11 -21
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/dc.cu +44 -22
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/dc.h +3 -1
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/dmc.cu +49 -14
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/dmc.h +3 -1
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mc.cu +5 -5
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mca.cu +4 -4
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mt.cu +5 -5
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mtg.cu +5 -5
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/triangle.h +3 -3
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/__init__.py +4 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/dual_contouring.py +28 -2
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/dual_marching_cubes.py +29 -2
- conquer3d-0.7.6/conquer3d/ops/hermite.py +263 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6/conquer3d.egg-info}/PKG-INFO +1 -1
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d.egg-info/SOURCES.txt +1 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/pyproject.toml +1 -1
- conquer3d-0.7.4/conquer3d/_C.so +0 -0
- conquer3d-0.7.4/conquer3d/csrc/maths/ops.h +0 -43
- {conquer3d-0.7.4 → conquer3d-0.7.6}/LICENSE +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/MANIFEST.in +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/README.md +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/_C.pyi +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/conversion/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/conversion/grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/conversion/mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/conversion/tmesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/creation/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/creation/triangle_creation.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/grid.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/data_structure/zcurve.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/flood_fill.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/flood_fill_cf.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/mca.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/mt.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/mtg.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/ops/volint.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/primitive/ray.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/binds/primitive/triangle.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/check.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/constants.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/creation/triangle_creation.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/bvh.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/grid.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/grid.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/kdtree.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/zcurve.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/data_structure/zcurve.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/f2x2.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/f3x3.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/f3x4.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/f4x1.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/f4x4.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/maths/maths.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/chamfer.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/chamfer.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/dc_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/dmc_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/flood_fill.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/flood_fill.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/flood_fill_cf.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/flood_fill_cf.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mc.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mc_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mca.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mca_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mt.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mt_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mtg.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/mtg_data.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/volint.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/ops/volint.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/aabb.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/edge.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/gs.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/gs.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/pgs.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/pgs.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/primitive/ray.h +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/csrc/pybind.cpp +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/assets/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/assets/common.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/assets/iphigenia.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/collate/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/collate/mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/collate/sparse_tensor.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/dataset/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/dataset/base_mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/dataset/digit3d.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/dataset/digit3dmv.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/dataset/mesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/dataset/redwood.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/transform/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/transform/base.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/transform/ops.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data/transform/vertex.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data_structure/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data_structure/bmesh.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data_structure/grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/data_structure/sort.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/io/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/io/obj.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/io/off.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/io/ply.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/delaunay_triangulation.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/diff_marching_cubes.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/diff_marching_tetrahedra_grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/distance.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/dpsr.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/marching_cubes.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/marching_cubes_asymptotic.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/marching_tetrahedra.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/marching_tetrahedra_grid.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/ops/volint.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/primitive/__init__.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/primitive/gs.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d/primitive/pgs.py +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d.egg-info/dependency_links.txt +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d.egg-info/not-zip-safe +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d.egg-info/requires.txt +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/conquer3d.egg-info/top_level.txt +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/setup.cfg +0 -0
- {conquer3d-0.7.4 → conquer3d-0.7.6}/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
|
|
|
@@ -233,16 +233,8 @@ __global__ void filter_ray_triangle_intersections_kernel(
|
|
|
233
233
|
float3 v2 = vertices[tri.z];
|
|
234
234
|
float3 centroid = (v0 + v1 + v2) / 3.0f;
|
|
235
235
|
|
|
236
|
-
float3 ray_dir =
|
|
237
|
-
|
|
238
|
-
if (dir_len > 1e-6f)
|
|
239
|
-
{
|
|
240
|
-
ray_dir = ray_dir / dir_len;
|
|
241
|
-
}
|
|
242
|
-
else
|
|
243
|
-
{
|
|
244
|
-
ray_dir = make_float3(1.0f, 0.0f, 0.0f);
|
|
245
|
-
}
|
|
236
|
+
float3 ray_dir = maths::normalize_safe(
|
|
237
|
+
centroid - p, make_float3(1.0f, 0.0f, 0.0f), 1e-6f);
|
|
246
238
|
|
|
247
239
|
Ray ray2(p, ray_dir, 0.0f);
|
|
248
240
|
|
|
@@ -452,7 +444,7 @@ __global__ void filter_ray_triangle_intersections_kernel(
|
|
|
452
444
|
float3 u0 = v1 - v0;
|
|
453
445
|
float len2_0 = maths::dot2(u0);
|
|
454
446
|
if (len2_0 > 1e-10f) {
|
|
455
|
-
float t0 =
|
|
447
|
+
float t0 = maths::saturate(maths::dot(best_pt - v0, u0) / len2_0);
|
|
456
448
|
if (maths::dot2(v0 + t0 * u0 - best_pt) <= eps_sq) {
|
|
457
449
|
N = pseudonormal_edges[3 * best_tri_id + 0];
|
|
458
450
|
found = true;
|
|
@@ -463,7 +455,7 @@ __global__ void filter_ray_triangle_intersections_kernel(
|
|
|
463
455
|
float3 u1 = v2 - v1;
|
|
464
456
|
float len2_1 = maths::dot2(u1);
|
|
465
457
|
if (len2_1 > 1e-10f) {
|
|
466
|
-
float t1 =
|
|
458
|
+
float t1 = maths::saturate(maths::dot(best_pt - v1, u1) / len2_1);
|
|
467
459
|
if (maths::dot2(v1 + t1 * u1 - best_pt) <= eps_sq) {
|
|
468
460
|
N = pseudonormal_edges[3 * best_tri_id + 1];
|
|
469
461
|
found = true;
|
|
@@ -474,7 +466,7 @@ __global__ void filter_ray_triangle_intersections_kernel(
|
|
|
474
466
|
float3 u2 = v0 - v2;
|
|
475
467
|
float len2_2 = maths::dot2(u2);
|
|
476
468
|
if (len2_2 > 1e-10f) {
|
|
477
|
-
float t2 =
|
|
469
|
+
float t2 = maths::saturate(maths::dot(best_pt - v2, u2) / len2_2);
|
|
478
470
|
if (maths::dot2(v2 + t2 * u2 - best_pt) <= eps_sq) {
|
|
479
471
|
N = pseudonormal_edges[3 * best_tri_id + 2];
|
|
480
472
|
found = true;
|
|
@@ -524,9 +524,9 @@ __global__ void compute_vertex_normals_kernel(
|
|
|
524
524
|
float3 e1 = maths::normalize(v2 - v1);
|
|
525
525
|
float3 e2 = maths::normalize(v0 - v2);
|
|
526
526
|
|
|
527
|
-
float a0 = acosf(
|
|
528
|
-
float a1 = acosf(
|
|
529
|
-
float a2 = acosf(
|
|
527
|
+
float a0 = acosf(maths::clamp(-maths::dot(e0, e2), -1.0f, 1.0f));
|
|
528
|
+
float a1 = acosf(maths::clamp(-maths::dot(e1, e0), -1.0f, 1.0f));
|
|
529
|
+
float a2 = acosf(maths::clamp(-maths::dot(e2, e1), -1.0f, 1.0f));
|
|
530
530
|
|
|
531
531
|
if (isfinite(a0)) {
|
|
532
532
|
atomicAdd(&vertex_normals[tri.x].x, a0 * n.x);
|
|
@@ -260,6 +260,40 @@ namespace maths
|
|
|
260
260
|
fminf(fmaxf(v.z, min_val.z), max_val.z)
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @brief Linear interpolation between two vectors.
|
|
266
|
+
* @details Overloads the scalar lerp() in ops.h. Interpolating along an edge is the
|
|
267
|
+
* single most repeated operation in the extraction kernels -- a crossing point, an
|
|
268
|
+
* interpolated normal, a blended colour -- and naming it keeps those sites readable.
|
|
269
|
+
* @param[in] a Vector returned at $t = 0$.
|
|
270
|
+
* @param[in] b Vector returned at $t = 1$.
|
|
271
|
+
* @param[in] t Interpolation parameter; not clamped.
|
|
272
|
+
* @return $\mathbf{a} + t\,(\mathbf{b} - \mathbf{a})$.
|
|
273
|
+
*/
|
|
274
|
+
static inline __host__ __device__ float3 lerp(float3 a, float3 b, float t) {
|
|
275
|
+
return a + (b - a) * t;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @brief Normalises a vector, falling back when it is too short to normalise.
|
|
280
|
+
* @details normalize() divides unconditionally and yields NaN or an infinity for a
|
|
281
|
+
* degenerate input. Kernels therefore guard the division by hand, and did so with three
|
|
282
|
+
* different epsilons across the tree. This states the policy once: below @p eps the
|
|
283
|
+
* direction is meaningless, so @p fallback is returned instead.
|
|
284
|
+
* @param[in] v Vector to normalise.
|
|
285
|
+
* @param[in] fallback Direction returned when @p v is shorter than @p eps.
|
|
286
|
+
* @param[in] eps Length below which @p v is treated as degenerate.
|
|
287
|
+
* @return The unit vector along @p v, or @p fallback.
|
|
288
|
+
*/
|
|
289
|
+
static inline __host__ __device__ float3 normalize_safe(
|
|
290
|
+
float3 v,
|
|
291
|
+
float3 fallback = make_float3(0.0f, 0.0f, 1.0f),
|
|
292
|
+
float eps = 1e-8f)
|
|
293
|
+
{
|
|
294
|
+
float len = norm(v);
|
|
295
|
+
return (len > eps) ? (v * (1.0f / len)) : fallback;
|
|
296
|
+
}
|
|
263
297
|
}
|
|
264
298
|
|
|
265
299
|
/**
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#ifndef OPS_H
|
|
2
|
+
#define OPS_H
|
|
3
|
+
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
#include <cmath>
|
|
6
|
+
#include <vector_types.h>
|
|
7
|
+
#include <vector_functions.h>
|
|
8
|
+
/**
|
|
9
|
+
* @file ops.h
|
|
10
|
+
* @brief Host fallbacks for CUDA math intrinsics, and scalar interpolation helpers.
|
|
11
|
+
*
|
|
12
|
+
* @details `rsqrt` and `rsqrtf` are device intrinsics that do not exist when a translation
|
|
13
|
+
* unit is compiled by the host compiler alone. These definitions appear only outside
|
|
14
|
+
* `__CUDACC__`, so headers shared between host and device code compile either way without
|
|
15
|
+
* `#ifdef` guards at every call site.
|
|
16
|
+
*
|
|
17
|
+
* The scalar helpers below are the counterparts of the `float3` routines in f3x1.h and
|
|
18
|
+
* overload on the same names, so `maths::clamp` and `maths::lerp` read identically whether
|
|
19
|
+
* the operand is a scalar or a vector. They live here rather than in f3x1.h because this
|
|
20
|
+
* header is included first and must not depend on the vector operators.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
#include <math_constants.h>
|
|
24
|
+
|
|
25
|
+
#ifndef __CUDACC__
|
|
26
|
+
/**
|
|
27
|
+
* @brief Reciprocal square root, double precision (host fallback).
|
|
28
|
+
* @param[in] a Value whose reciprocal square root is taken; must be positive.
|
|
29
|
+
* @return The value $1 / \sqrt{a}$.
|
|
30
|
+
* @note Defined only when not compiling with nvcc, which supplies the intrinsic.
|
|
31
|
+
*/
|
|
32
|
+
static inline __host__ __device__ double rsqrt(double a) {
|
|
33
|
+
return 1. / sqrt(a);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @brief Reciprocal square root, single precision (host fallback).
|
|
38
|
+
* @param[in] a Value whose reciprocal square root is taken; must be positive.
|
|
39
|
+
* @return The value $1 / \sqrt{a}$.
|
|
40
|
+
* @note Defined only when not compiling with nvcc. The device intrinsic is an
|
|
41
|
+
* approximation, so host and device results may differ in the last bits.
|
|
42
|
+
*/
|
|
43
|
+
static inline __host__ __device__ float rsqrtf(float a) {
|
|
44
|
+
return 1. / sqrtf(a);
|
|
45
|
+
}
|
|
46
|
+
#endif
|
|
47
|
+
|
|
48
|
+
namespace maths
|
|
49
|
+
{
|
|
50
|
+
/**
|
|
51
|
+
* @brief Confines a scalar to a range.
|
|
52
|
+
* @details Scalar overload of the `float3` clamp in f3x1.h. Spelling the two branches
|
|
53
|
+
* as one call keeps the intent legible where the bound is itself an expression.
|
|
54
|
+
* @param[in] v Value to clamp.
|
|
55
|
+
* @param[in] min_val Lower bound.
|
|
56
|
+
* @param[in] max_val Upper bound.
|
|
57
|
+
* @return @p v confined to $[\text{min\_val}, \text{max\_val}]$.
|
|
58
|
+
* @note Ordered `fmaxf(lo, fminf(hi, v))` to match the form these call sites already
|
|
59
|
+
* used. The order is irrelevant for finite input but decides which bound a NaN
|
|
60
|
+
* collapses to, so keeping it preserves the previous behaviour exactly.
|
|
61
|
+
*/
|
|
62
|
+
static inline __host__ __device__ float clamp(float v, float min_val, float max_val) {
|
|
63
|
+
return fmaxf(min_val, fminf(max_val, v));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @brief Confines a scalar to the unit interval.
|
|
68
|
+
* @details The overwhelmingly common case of clamp(), used wherever an interpolation
|
|
69
|
+
* parameter or a normalised cell coordinate must not escape $[0, 1]$ through rounding.
|
|
70
|
+
* @param[in] v Value to clamp.
|
|
71
|
+
* @return @p v confined to $[0, 1]$.
|
|
72
|
+
*/
|
|
73
|
+
static inline __host__ __device__ float saturate(float v) {
|
|
74
|
+
return fmaxf(0.0f, fminf(1.0f, v));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @brief Linear interpolation between two scalars.
|
|
79
|
+
* @param[in] a Value returned at $t = 0$.
|
|
80
|
+
* @param[in] b Value returned at $t = 1$.
|
|
81
|
+
* @param[in] t Interpolation parameter; not clamped.
|
|
82
|
+
* @return $a + t\,(b - a)$.
|
|
83
|
+
*/
|
|
84
|
+
static inline __host__ __device__ float lerp(float a, float b, float t) {
|
|
85
|
+
return a + (b - a) * t;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#endif // OPS_H
|
|
@@ -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,30 +231,13 @@ __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
|
}
|
|
233
240
|
|
|
234
|
-
/**
|
|
235
|
-
* @brief Evaluates the residual Quadratic Error Function (QEF) value at point x.
|
|
236
|
-
*/
|
|
237
|
-
__host__ __device__ __forceinline__ float evaluate_qef_error(
|
|
238
|
-
const float3 *pts,
|
|
239
|
-
const float3 *normals,
|
|
240
|
-
int count,
|
|
241
|
-
const float3 &x
|
|
242
|
-
) {
|
|
243
|
-
float error = 0.0f;
|
|
244
|
-
for (int i = 0; i < count; ++i) {
|
|
245
|
-
float d = maths::dot(normals[i], x - pts[i]);
|
|
246
|
-
error += d * d;
|
|
247
|
-
}
|
|
248
|
-
return error;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
241
|
} // namespace maths
|
|
252
242
|
|
|
253
243
|
#endif // QEF_H
|
|
@@ -56,11 +56,7 @@ __device__ __forceinline__ float3 compute_trilinear_normal(
|
|
|
56
56
|
v * ((1.0f - u) * (s[7] - s[3]) + u * (s[6] - s[2]));
|
|
57
57
|
|
|
58
58
|
float3 grad = make_float3(du / dx, dv / dy, dw / dz);
|
|
59
|
-
|
|
60
|
-
if (len > 1e-8f) {
|
|
61
|
-
return grad * (1.0f / len);
|
|
62
|
-
}
|
|
63
|
-
return make_float3(0.0f, 0.0f, 1.0f);
|
|
59
|
+
return maths::normalize_safe(grad);
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
// Compute minimum angle of a 3D triangle in radians
|
|
@@ -91,9 +87,9 @@ __device__ __forceinline__ float triangle_min_angle(
|
|
|
91
87
|
float cos1 = -maths::dot(e0, e1) / (l0 * l1);
|
|
92
88
|
float cos2 = -maths::dot(e1, e2) / (l1 * l2);
|
|
93
89
|
|
|
94
|
-
cos0 =
|
|
95
|
-
cos1 =
|
|
96
|
-
cos2 =
|
|
90
|
+
cos0 = maths::clamp(cos0, -1.0f, 1.0f);
|
|
91
|
+
cos1 = maths::clamp(cos1, -1.0f, 1.0f);
|
|
92
|
+
cos2 = maths::clamp(cos2, -1.0f, 1.0f);
|
|
97
93
|
|
|
98
94
|
return fminf(acosf(cos0), fminf(acosf(cos1), acosf(cos2)));
|
|
99
95
|
}
|
|
@@ -138,6 +134,8 @@ __global__ void compute_dual_vertices_kernel(
|
|
|
138
134
|
const int *__restrict__ voxels,
|
|
139
135
|
const float *__restrict__ sdf,
|
|
140
136
|
const float3 *__restrict__ grid_normals,
|
|
137
|
+
const float3 *__restrict__ edge_points,
|
|
138
|
+
const float3 *__restrict__ edge_normals,
|
|
141
139
|
float iso,
|
|
142
140
|
int num_voxels,
|
|
143
141
|
float3 *__restrict__ dual_vertices,
|
|
@@ -182,22 +180,36 @@ __global__ void compute_dual_vertices_kernel(
|
|
|
182
180
|
// Bipolar test
|
|
183
181
|
if ((s0 < iso && s1 >= iso) || (s0 >= iso && s1 < iso)) {
|
|
184
182
|
float t = (iso - s0) / (s1 - s0);
|
|
185
|
-
t =
|
|
183
|
+
t = maths::saturate(t);
|
|
186
184
|
|
|
187
|
-
float3 pt
|
|
185
|
+
float3 pt;
|
|
186
|
+
float3 n;
|
|
187
|
+
bool have_hermite = (edge_points != nullptr && edge_normals != nullptr);
|
|
188
|
+
if (have_hermite) {
|
|
189
|
+
pt = edge_points[m * 12 + e];
|
|
190
|
+
float3 hn = edge_normals[m * 12 + e];
|
|
191
|
+
float hlen = maths::norm(hn);
|
|
192
|
+
if (hlen > 1e-8f) {
|
|
193
|
+
n = hn * (1.0f / hlen);
|
|
194
|
+
} else {
|
|
195
|
+
have_hermite = false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (!have_hermite) {
|
|
199
|
+
pt = maths::lerp(p[v0], p[v1], t);
|
|
200
|
+
}
|
|
188
201
|
pts[count] = pt;
|
|
189
202
|
|
|
190
|
-
|
|
191
|
-
|
|
203
|
+
if (have_hermite) {
|
|
204
|
+
// normal already set from the supplied Hermite data
|
|
205
|
+
} else if (grid_normals != nullptr) {
|
|
192
206
|
float3 n0 = grid_normals[c_idx[v0]];
|
|
193
207
|
float3 n1 = grid_normals[c_idx[v1]];
|
|
194
|
-
|
|
195
|
-
float len = maths::norm(n_interp);
|
|
196
|
-
n = (len > 1e-8f) ? (n_interp * (1.0f / len)) : make_float3(0, 0, 1);
|
|
208
|
+
n = maths::normalize_safe(maths::lerp(n0, n1, t));
|
|
197
209
|
} else {
|
|
198
|
-
float u = dc_corner_uvw[v0][0]
|
|
199
|
-
float v = dc_corner_uvw[v0][1]
|
|
200
|
-
float w = dc_corner_uvw[v0][2]
|
|
210
|
+
float u = maths::lerp(dc_corner_uvw[v0][0], dc_corner_uvw[v1][0], t);
|
|
211
|
+
float v = maths::lerp(dc_corner_uvw[v0][1], dc_corner_uvw[v1][1], t);
|
|
212
|
+
float w = maths::lerp(dc_corner_uvw[v0][2], dc_corner_uvw[v1][2], t);
|
|
201
213
|
n = compute_trilinear_normal(u, v, w, s, dx, dy, dz);
|
|
202
214
|
}
|
|
203
215
|
normals[count] = n;
|
|
@@ -489,9 +501,9 @@ __global__ void compact_dual_vertices_and_colors_kernel(
|
|
|
489
501
|
float dy = fmaxf(c_max.y - c_min.y, 1e-6f);
|
|
490
502
|
float dz = fmaxf(c_max.z - c_min.z, 1e-6f);
|
|
491
503
|
|
|
492
|
-
float u =
|
|
493
|
-
float val_v =
|
|
494
|
-
float w =
|
|
504
|
+
float u = maths::saturate((v.x - c_min.x) / dx);
|
|
505
|
+
float val_v = maths::saturate((v.y - c_min.y) / dy);
|
|
506
|
+
float w = maths::saturate((v.z - c_min.z) / dz);
|
|
495
507
|
|
|
496
508
|
#pragma unroll
|
|
497
509
|
for (int ch = 0; ch < num_channels; ++ch) {
|
|
@@ -665,7 +677,9 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_contouring(
|
|
|
665
677
|
const c10::optional<at::Tensor> &colors,
|
|
666
678
|
const c10::optional<at::Tensor> &voxel_vertices,
|
|
667
679
|
float iso,
|
|
668
|
-
bool quad_split
|
|
680
|
+
bool quad_split,
|
|
681
|
+
const c10::optional<at::Tensor> &edge_points,
|
|
682
|
+
const c10::optional<at::Tensor> &edge_normals
|
|
669
683
|
) {
|
|
670
684
|
at::cuda::CUDAGuard device_guard(grid_vertices.device());
|
|
671
685
|
auto allocator = at::cuda::ThrustAllocator();
|
|
@@ -705,12 +719,18 @@ std::tuple<at::Tensor, at::Tensor, c10::optional<at::Tensor>> dual_contouring(
|
|
|
705
719
|
dual_vertices = at::empty({num_voxels, 3}, grid_vertices.options());
|
|
706
720
|
source_dual_vertices_ptr = reinterpret_cast<const float3*>(dual_vertices.data_ptr<float>());
|
|
707
721
|
const float3 *normals_ptr = grid_normals.has_value() ? reinterpret_cast<const float3*>(grid_normals.value().data_ptr<float>()) : nullptr;
|
|
722
|
+
const float3 *ep_ptr = (edge_points.has_value() && edge_points.value().defined() && edge_points.value().numel() > 0)
|
|
723
|
+
? reinterpret_cast<const float3*>(edge_points.value().data_ptr<float>()) : nullptr;
|
|
724
|
+
const float3 *en_ptr = (edge_normals.has_value() && edge_normals.value().defined() && edge_normals.value().numel() > 0)
|
|
725
|
+
? reinterpret_cast<const float3*>(edge_normals.value().data_ptr<float>()) : nullptr;
|
|
708
726
|
|
|
709
727
|
compute_dual_vertices_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
|
|
710
728
|
reinterpret_cast<const float3*>(grid_vertices.data_ptr<float>()),
|
|
711
729
|
voxels.data_ptr<int>(),
|
|
712
730
|
sdf.data_ptr<float>(),
|
|
713
731
|
normals_ptr,
|
|
732
|
+
ep_ptr,
|
|
733
|
+
en_ptr,
|
|
714
734
|
iso,
|
|
715
735
|
num_voxels,
|
|
716
736
|
reinterpret_cast<float3*>(dual_vertices.data_ptr<float>()),
|
|
@@ -910,6 +930,8 @@ std::tuple<at::Tensor, c10::optional<at::Tensor>> dual_contouring_backward(
|
|
|
910
930
|
voxels.data_ptr<int>(),
|
|
911
931
|
sdf.data_ptr<float>(),
|
|
912
932
|
normals_ptr,
|
|
933
|
+
nullptr,
|
|
934
|
+
nullptr,
|
|
913
935
|
iso,
|
|
914
936
|
num_voxels,
|
|
915
937
|
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
|
/**
|
|
@@ -111,9 +111,9 @@ __device__ __forceinline__ float triangle_min_angle(
|
|
|
111
111
|
float cos1 = -maths::dot(e0, e1) / (l0 * l1);
|
|
112
112
|
float cos2 = -maths::dot(e1, e2) / (l1 * l2);
|
|
113
113
|
|
|
114
|
-
cos0 =
|
|
115
|
-
cos1 =
|
|
116
|
-
cos2 =
|
|
114
|
+
cos0 = maths::clamp(cos0, -1.0f, 1.0f);
|
|
115
|
+
cos1 = maths::clamp(cos1, -1.0f, 1.0f);
|
|
116
|
+
cos2 = maths::clamp(cos2, -1.0f, 1.0f);
|
|
117
117
|
|
|
118
118
|
float a0 = acosf(cos0);
|
|
119
119
|
float a1 = acosf(cos1);
|
|
@@ -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,18 +438,32 @@ __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];
|
|
444
460
|
float s1 = s[v1];
|
|
445
461
|
float t = (iso - s0) / (s1 - s0);
|
|
446
|
-
t =
|
|
462
|
+
t = maths::saturate(t);
|
|
447
463
|
|
|
448
|
-
float u_e = dmc_corner_uvw[v0][0]
|
|
449
|
-
float v_e = dmc_corner_uvw[v0][1]
|
|
450
|
-
float w_e = dmc_corner_uvw[v0][2]
|
|
464
|
+
float u_e = maths::lerp(dmc_corner_uvw[v0][0], dmc_corner_uvw[v1][0], t);
|
|
465
|
+
float v_e = maths::lerp(dmc_corner_uvw[v0][1], dmc_corner_uvw[v1][1], t);
|
|
466
|
+
float w_e = maths::lerp(dmc_corner_uvw[v0][2], dmc_corner_uvw[v1][2], t);
|
|
451
467
|
|
|
452
468
|
u_sum += u_e;
|
|
453
469
|
v_sum += v_e;
|
|
@@ -473,9 +489,19 @@ __global__ void dmc_extract_dual_vertices_and_edges_kernel(
|
|
|
473
489
|
// Fast path: use precomputed inside-voxel vertex directly
|
|
474
490
|
out_vertices[dual_vert_idx] = precomputed_vertices[m];
|
|
475
491
|
float3 pt = precomputed_vertices[m];
|
|
476
|
-
u =
|
|
477
|
-
v =
|
|
478
|
-
w =
|
|
492
|
+
u = maths::saturate((pt.x - c_min.x) / dx);
|
|
493
|
+
v = maths::saturate((pt.y - c_min.y) / dy);
|
|
494
|
+
w = maths::saturate((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 = maths::saturate((pt.x - c_min.x) / dx);
|
|
503
|
+
v = maths::saturate((pt.y - c_min.y) / dy);
|
|
504
|
+
w = maths::saturate((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) {
|
|
@@ -484,9 +510,9 @@ __global__ void dmc_extract_dual_vertices_and_edges_kernel(
|
|
|
484
510
|
float len_sq = grad.x * grad.x + grad.y * grad.y + grad.z * grad.z;
|
|
485
511
|
if (len_sq > 1e-8f) {
|
|
486
512
|
float step = 0.5f * (iso - f_val) / len_sq;
|
|
487
|
-
u =
|
|
488
|
-
v =
|
|
489
|
-
w =
|
|
513
|
+
u = maths::saturate(u + step * grad.x);
|
|
514
|
+
v = maths::saturate(v + step * grad.y);
|
|
515
|
+
w = maths::saturate(w + step * grad.z);
|
|
490
516
|
}
|
|
491
517
|
}
|
|
492
518
|
|
|
@@ -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
|
/**
|