conquer3d 0.2.9__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {conquer3d-0.2.9/conquer3d.egg-info → conquer3d-0.3.0}/PKG-INFO +6 -1
- {conquer3d-0.2.9 → conquer3d-0.3.0}/README.md +5 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/_C.so +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/grid.cpp +21 -0
- conquer3d-0.3.0/conquer3d/csrc/binds/ops/mc.cpp +118 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/mc.cu +152 -8
- conquer3d-0.3.0/conquer3d/csrc/ops/mc.h +39 -0
- conquer3d-0.3.0/conquer3d/data_structure/__init__.py +15 -0
- conquer3d-0.3.0/conquer3d/data_structure/grid.py +114 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/ops/__init__.py +2 -3
- conquer3d-0.3.0/conquer3d/ops/diff_marching_cubes.py +135 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/ops/marching_cubes.py +16 -5
- {conquer3d-0.2.9 → conquer3d-0.3.0/conquer3d.egg-info}/PKG-INFO +6 -1
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/SOURCES.txt +2 -1
- {conquer3d-0.2.9 → conquer3d-0.3.0}/pyproject.toml +1 -1
- conquer3d-0.2.9/conquer3d/csrc/binds/ops/mc.cpp +0 -47
- conquer3d-0.2.9/conquer3d/csrc/ops/mc.h +0 -25
- conquer3d-0.2.9/conquer3d/data_structure/__init__.py +0 -13
- conquer3d-0.2.9/conquer3d/ops/grid.py +0 -47
- {conquer3d-0.2.9 → conquer3d-0.3.0}/LICENSE +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/MANIFEST.in +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/_C.pyi +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/__init__.py +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/creation/__init__.py +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/creation/triangle_creation.py +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/check.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/constants.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/creation/triangle_creation.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/bvh.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/bvh.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/kdtree.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/zcurve.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f2x2.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f3x1.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f3x3.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f3x4.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f4x1.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f4x4.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/maths.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/ops.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/chamfer.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/chamfer.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/mc_data.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/aabb.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/edge.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/ray.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/triangles.h +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/pybind.cpp +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/ops/distance.py +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/primitive/__init__.py +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/primitive/gs.py +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/primitive/pgs.py +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/dependency_links.txt +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/not-zip-safe +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/requires.txt +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/top_level.txt +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/setup.cfg +0 -0
- {conquer3d-0.2.9 → conquer3d-0.3.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conquer3d
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Geometric Cuda Tool Box
|
|
5
5
|
Author-email: Do Hoang Khoi <khoido8899@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -51,8 +51,13 @@ conda activate geocutool
|
|
|
51
51
|
|
|
52
52
|
conda install nvidia::cuda-toolkit==12.8.2 -y
|
|
53
53
|
|
|
54
|
+
pip install setuptools wheel ninja
|
|
54
55
|
pip install torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 --index-url https://download.pytorch.org/whl/cu128
|
|
55
56
|
|
|
57
|
+
pip install git+https://github.com/mit-han-lab/torchsparse.git
|
|
58
|
+
|
|
59
|
+
pip install kaolin==0.18.0 -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html
|
|
60
|
+
|
|
56
61
|
pip install pybind11-stubgen
|
|
57
62
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
58
63
|
|
|
@@ -23,8 +23,13 @@ conda activate geocutool
|
|
|
23
23
|
|
|
24
24
|
conda install nvidia::cuda-toolkit==12.8.2 -y
|
|
25
25
|
|
|
26
|
+
pip install setuptools wheel ninja
|
|
26
27
|
pip install torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 --index-url https://download.pytorch.org/whl/cu128
|
|
27
28
|
|
|
29
|
+
pip install git+https://github.com/mit-han-lab/torchsparse.git
|
|
30
|
+
|
|
31
|
+
pip install kaolin==0.18.0 -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html
|
|
32
|
+
|
|
28
33
|
pip install pybind11-stubgen
|
|
29
34
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
30
35
|
|
|
Binary file
|
|
@@ -121,9 +121,30 @@ torch::Tensor compute_grid_normal(torch::Tensor sdf, torch::Tensor grid_vertices
|
|
|
121
121
|
return torch::nn::functional::normalize(normals, torch::nn::functional::NormalizeFuncOptions().p(2).dim(1));
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
torch::Tensor compute_active_voxels(torch::Tensor voxels, torch::Tensor sdf, float iso) {
|
|
125
|
+
TORCH_CHECK(voxels.is_cuda(), "voxels must be a CUDA tensor.");
|
|
126
|
+
TORCH_CHECK(sdf.is_cuda(), "sdf must be a CUDA tensor.");
|
|
127
|
+
TORCH_CHECK(voxels.dtype() == torch::kInt32, "voxels must be int32.");
|
|
128
|
+
TORCH_CHECK(sdf.dtype() == torch::kFloat32, "sdf must be float32.");
|
|
129
|
+
|
|
130
|
+
auto voxels_flat = voxels.flatten().to(torch::kInt64);
|
|
131
|
+
auto voxel_sdfs = sdf.index_select(0, voxels_flat).view({-1, 8});
|
|
132
|
+
|
|
133
|
+
auto below_iso = voxel_sdfs < iso;
|
|
134
|
+
auto any_below = below_iso.any(1);
|
|
135
|
+
auto any_above = (~below_iso).any(1);
|
|
136
|
+
|
|
137
|
+
auto active_mask = any_below & any_above;
|
|
138
|
+
auto active_indices = torch::nonzero(active_mask).squeeze(1);
|
|
139
|
+
|
|
140
|
+
return active_indices;
|
|
141
|
+
}
|
|
142
|
+
|
|
124
143
|
void bind_ds_grid(py::module_& m) {
|
|
125
144
|
m.def("create_voxel_grid", &create_voxel_grid, "Creates a structured 3D voxel grid.",
|
|
126
145
|
py::arg("grid_min"), py::arg("grid_max"), py::arg("res"), py::arg("device_str") = "cuda");
|
|
127
146
|
m.def("compute_grid_normal", &compute_grid_normal, "Computes surface normals for a grid.",
|
|
128
147
|
py::arg("sdf"), py::arg("grid_vertices"), py::arg("idx_grids"), py::arg("res"));
|
|
148
|
+
m.def("compute_active_voxels", &compute_active_voxels, "Computes active voxels intersecting the surface",
|
|
149
|
+
py::arg("voxels"), py::arg("sdf"), py::arg("iso"));
|
|
129
150
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#include <torch/extension.h>
|
|
2
|
+
#include <pybind11/pybind11.h>
|
|
3
|
+
#include "../../ops/mc.h"
|
|
4
|
+
#include "../../check.h"
|
|
5
|
+
|
|
6
|
+
namespace py = pybind11;
|
|
7
|
+
|
|
8
|
+
std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>, std::optional<torch::Tensor>, std::optional<torch::Tensor>> marching_cubes_wrapper(
|
|
9
|
+
torch::Tensor grid_vertices,
|
|
10
|
+
torch::Tensor voxels,
|
|
11
|
+
torch::Tensor voxel_values,
|
|
12
|
+
std::optional<torch::Tensor> grid_normals,
|
|
13
|
+
std::optional<torch::Tensor> grid_colors,
|
|
14
|
+
float iso,
|
|
15
|
+
bool return_unique_edges
|
|
16
|
+
) {
|
|
17
|
+
CHECK_INPUT(grid_vertices);
|
|
18
|
+
CHECK_INPUT(voxels);
|
|
19
|
+
CHECK_INPUT(voxel_values);
|
|
20
|
+
|
|
21
|
+
uint32_t num_voxels = voxels.size(0);
|
|
22
|
+
|
|
23
|
+
const float3* __restrict__ p_grid_vertices = (float3*)grid_vertices.data_ptr<float>();
|
|
24
|
+
const uint32_t* __restrict__ p_voxels = (uint32_t*)voxels.data_ptr<int32_t>();
|
|
25
|
+
const float* __restrict__ p_voxel_values = voxel_values.data_ptr<float>();
|
|
26
|
+
|
|
27
|
+
const float3* __restrict__ p_grid_normals = nullptr;
|
|
28
|
+
if (grid_normals.has_value()) {
|
|
29
|
+
CHECK_INPUT(grid_normals.value());
|
|
30
|
+
p_grid_normals = (float3*)grid_normals.value().data_ptr<float>();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const float3* __restrict__ p_grid_colors = nullptr;
|
|
34
|
+
if (grid_colors.has_value()) {
|
|
35
|
+
CHECK_INPUT(grid_colors.value());
|
|
36
|
+
p_grid_colors = (float3*)grid_colors.value().data_ptr<float>();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return mc::marching_cubes(
|
|
40
|
+
num_voxels,
|
|
41
|
+
p_grid_vertices,
|
|
42
|
+
p_voxels,
|
|
43
|
+
p_voxel_values,
|
|
44
|
+
p_grid_normals,
|
|
45
|
+
p_grid_colors,
|
|
46
|
+
iso,
|
|
47
|
+
grid_vertices.options(),
|
|
48
|
+
voxels.options(),
|
|
49
|
+
return_unique_edges
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
void marching_cubes_backward_wrapper(
|
|
54
|
+
torch::Tensor unique_edges,
|
|
55
|
+
torch::Tensor grid_vertices,
|
|
56
|
+
std::optional<torch::Tensor> grid_colors,
|
|
57
|
+
torch::Tensor values,
|
|
58
|
+
torch::Tensor adj_verts,
|
|
59
|
+
std::optional<torch::Tensor> adj_colors,
|
|
60
|
+
torch::Tensor adj_values,
|
|
61
|
+
std::optional<torch::Tensor> adj_grid_colors,
|
|
62
|
+
float iso
|
|
63
|
+
) {
|
|
64
|
+
CHECK_INPUT(unique_edges);
|
|
65
|
+
CHECK_INPUT(grid_vertices);
|
|
66
|
+
CHECK_INPUT(values);
|
|
67
|
+
CHECK_INPUT(adj_verts);
|
|
68
|
+
CHECK_INPUT(adj_values);
|
|
69
|
+
|
|
70
|
+
uint32_t n_verts = unique_edges.size(0);
|
|
71
|
+
|
|
72
|
+
const Edge* p_unique_edges = (const Edge*)unique_edges.data_ptr<int32_t>();
|
|
73
|
+
const float3* p_grid_vertices = (const float3*)grid_vertices.data_ptr<float>();
|
|
74
|
+
const float* p_values = values.data_ptr<float>();
|
|
75
|
+
const float3* p_adj_verts = (const float3*)adj_verts.data_ptr<float>();
|
|
76
|
+
float* p_adj_values = adj_values.data_ptr<float>();
|
|
77
|
+
|
|
78
|
+
const float3* p_grid_colors = nullptr;
|
|
79
|
+
if (grid_colors.has_value()) {
|
|
80
|
+
CHECK_INPUT(grid_colors.value());
|
|
81
|
+
p_grid_colors = (const float3*)grid_colors.value().data_ptr<float>();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const float3* p_adj_colors = nullptr;
|
|
85
|
+
if (adj_colors.has_value()) {
|
|
86
|
+
CHECK_INPUT(adj_colors.value());
|
|
87
|
+
p_adj_colors = (const float3*)adj_colors.value().data_ptr<float>();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
float3* p_adj_grid_colors = nullptr;
|
|
91
|
+
if (adj_grid_colors.has_value()) {
|
|
92
|
+
CHECK_INPUT(adj_grid_colors.value());
|
|
93
|
+
p_adj_grid_colors = (float3*)adj_grid_colors.value().data_ptr<float>();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
mc::backward(
|
|
97
|
+
n_verts,
|
|
98
|
+
p_unique_edges,
|
|
99
|
+
p_grid_vertices,
|
|
100
|
+
p_grid_colors,
|
|
101
|
+
p_values,
|
|
102
|
+
p_adj_verts,
|
|
103
|
+
p_adj_colors,
|
|
104
|
+
p_adj_values,
|
|
105
|
+
p_adj_grid_colors,
|
|
106
|
+
iso
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
void bind_ops_mc(py::module_& m) {
|
|
111
|
+
m.def("marching_cubes", &marching_cubes_wrapper, "Marching Cubes",
|
|
112
|
+
py::arg("grid_vertices"), py::arg("voxels"), py::arg("voxel_values"),
|
|
113
|
+
py::arg("grid_normals") = std::nullopt, py::arg("grid_colors") = std::nullopt, py::arg("iso") = 0.0f, py::arg("return_unique_edges") = false);
|
|
114
|
+
|
|
115
|
+
m.def("marching_cubes_backward", &marching_cubes_backward_wrapper, "Marching Cubes Backward",
|
|
116
|
+
py::arg("unique_edges"), py::arg("grid_vertices"), py::arg("grid_colors"), py::arg("values"),
|
|
117
|
+
py::arg("adj_verts"), py::arg("adj_colors"), py::arg("adj_values"), py::arg("adj_grid_colors"), py::arg("iso"));
|
|
118
|
+
}
|
|
@@ -176,9 +176,11 @@ namespace mc
|
|
|
176
176
|
const float3* grid_vertices,
|
|
177
177
|
const float* values,
|
|
178
178
|
const float3* grid_normals,
|
|
179
|
+
const float3* grid_colors,
|
|
179
180
|
const float iso,
|
|
180
181
|
float3* out_verts,
|
|
181
|
-
float3* out_normals
|
|
182
|
+
float3* out_normals,
|
|
183
|
+
float3* out_colors
|
|
182
184
|
)
|
|
183
185
|
{
|
|
184
186
|
uint32_t v_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
@@ -199,24 +201,31 @@ namespace mc
|
|
|
199
201
|
// 3. Interpolate (Differentiable formula)
|
|
200
202
|
float3 p;
|
|
201
203
|
float3 n;
|
|
204
|
+
float3 c;
|
|
202
205
|
bool has_normals = grid_normals != nullptr;
|
|
206
|
+
bool has_colors = grid_colors != nullptr;
|
|
203
207
|
float3 n0 = has_normals ? grid_normals[v0_idx] : make_float3(0, 0, 0);
|
|
204
208
|
float3 n1 = has_normals ? grid_normals[v1_idx] : make_float3(0, 0, 0);
|
|
209
|
+
float3 c0 = has_colors ? grid_colors[v0_idx] : make_float3(0, 0, 0);
|
|
210
|
+
float3 c1 = has_colors ? grid_colors[v1_idx] : make_float3(0, 0, 0);
|
|
205
211
|
|
|
206
212
|
if (fabsf(iso - val0) < EPS)
|
|
207
213
|
{
|
|
208
214
|
p = p0;
|
|
209
215
|
if (has_normals) n = n0;
|
|
216
|
+
if (has_colors) c = c0;
|
|
210
217
|
}
|
|
211
218
|
else if (fabsf(iso - val1) < EPS)
|
|
212
219
|
{
|
|
213
220
|
p = p1;
|
|
214
221
|
if (has_normals) n = n1;
|
|
222
|
+
if (has_colors) c = c1;
|
|
215
223
|
}
|
|
216
224
|
else if (fabsf(val0 - val1) < EPS)
|
|
217
225
|
{
|
|
218
226
|
p = p0;
|
|
219
227
|
if (has_normals) n = n0;
|
|
228
|
+
if (has_colors) c = c0;
|
|
220
229
|
}
|
|
221
230
|
else
|
|
222
231
|
{
|
|
@@ -228,12 +237,18 @@ namespace mc
|
|
|
228
237
|
n = n0 + (n1 - n0) * t;
|
|
229
238
|
n = maths::normalize(n);
|
|
230
239
|
}
|
|
240
|
+
if (has_colors) {
|
|
241
|
+
c = c0 + (c1 - c0) * t;
|
|
242
|
+
}
|
|
231
243
|
}
|
|
232
244
|
|
|
233
245
|
out_verts[v_idx] = p;
|
|
234
246
|
if (has_normals) {
|
|
235
247
|
out_normals[v_idx] = n;
|
|
236
248
|
}
|
|
249
|
+
if (has_colors) {
|
|
250
|
+
out_colors[v_idx] = c;
|
|
251
|
+
}
|
|
237
252
|
}
|
|
238
253
|
|
|
239
254
|
void compute_active_voxels(
|
|
@@ -357,16 +372,18 @@ namespace mc
|
|
|
357
372
|
const float3* grid_vertices,
|
|
358
373
|
const float* values,
|
|
359
374
|
const float3* grid_normals,
|
|
375
|
+
const float3* grid_colors,
|
|
360
376
|
const float iso,
|
|
361
377
|
float3* out_verts,
|
|
362
|
-
float3* out_normals
|
|
378
|
+
float3* out_normals,
|
|
379
|
+
float3* out_colors
|
|
363
380
|
)
|
|
364
381
|
{
|
|
365
382
|
if (num_unique_edges == 0) return;
|
|
366
383
|
int block_size = NTHREADS;
|
|
367
384
|
int grid_size = (num_unique_edges + block_size - 1) / block_size;
|
|
368
385
|
interpolate_vertices_kernel<<<grid_size, block_size>>>(
|
|
369
|
-
num_unique_edges, unique_edges, grid_vertices, values, grid_normals, iso, out_verts, out_normals);
|
|
386
|
+
num_unique_edges, unique_edges, grid_vertices, values, grid_normals, grid_colors, iso, out_verts, out_normals, out_colors);
|
|
370
387
|
}
|
|
371
388
|
|
|
372
389
|
void compute_number_triangles(
|
|
@@ -435,15 +452,17 @@ namespace mc
|
|
|
435
452
|
num_active_voxels, used_voxel_codes, voxel_edge_to_vert_idx, voxel_triangle_prefix_sums, out_triangles);
|
|
436
453
|
}
|
|
437
454
|
|
|
438
|
-
std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> marching_cubes(
|
|
455
|
+
std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>, std::optional<torch::Tensor>, std::optional<torch::Tensor>> marching_cubes(
|
|
439
456
|
const uint32_t num_voxels,
|
|
440
457
|
const float3* __restrict__ grid_vertices,
|
|
441
458
|
const uint32_t* __restrict__ voxels,
|
|
442
459
|
const float* __restrict__ voxel_values,
|
|
443
460
|
const float3* __restrict__ grid_normals,
|
|
461
|
+
const float3* __restrict__ grid_colors,
|
|
444
462
|
const float iso,
|
|
445
463
|
torch::TensorOptions vert_options,
|
|
446
|
-
torch::TensorOptions tri_options
|
|
464
|
+
torch::TensorOptions tri_options,
|
|
465
|
+
bool return_unique_edges
|
|
447
466
|
)
|
|
448
467
|
{
|
|
449
468
|
uint8_t *__restrict__ voxel_codes;
|
|
@@ -457,11 +476,15 @@ namespace mc
|
|
|
457
476
|
if (num_active_voxels == 0) {
|
|
458
477
|
CHECK_CUDA_INTERNAL(cudaFree(voxel_codes));
|
|
459
478
|
std::optional<torch::Tensor> out_n = std::nullopt;
|
|
479
|
+
std::optional<torch::Tensor> out_c = std::nullopt;
|
|
460
480
|
if (grid_normals != nullptr) out_n = torch::empty({0, 3}, vert_options);
|
|
481
|
+
if (grid_colors != nullptr) out_c = torch::empty({0, 3}, vert_options);
|
|
461
482
|
return std::make_tuple(
|
|
462
483
|
torch::empty({0, 3}, vert_options),
|
|
463
484
|
torch::empty({0, 3}, tri_options),
|
|
464
|
-
out_n
|
|
485
|
+
out_n,
|
|
486
|
+
out_c,
|
|
487
|
+
std::nullopt
|
|
465
488
|
);
|
|
466
489
|
}
|
|
467
490
|
|
|
@@ -493,7 +516,15 @@ namespace mc
|
|
|
493
516
|
out_normals_opt = out_normals;
|
|
494
517
|
}
|
|
495
518
|
|
|
496
|
-
|
|
519
|
+
std::optional<torch::Tensor> out_colors_opt = std::nullopt;
|
|
520
|
+
float3* __restrict__ p_out_colors = nullptr;
|
|
521
|
+
if (grid_colors != nullptr) {
|
|
522
|
+
torch::Tensor out_colors = torch::empty({out_num_vertices, 3}, vert_options);
|
|
523
|
+
p_out_colors = (float3*)out_colors.data_ptr<float>();
|
|
524
|
+
out_colors_opt = out_colors;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
interpolate_vertices(out_num_vertices, unique_edges, grid_vertices, voxel_values, grid_normals, grid_colors, iso, p_out_vertices, p_out_normals, p_out_colors);
|
|
497
528
|
|
|
498
529
|
uint32_t out_num_triangles;
|
|
499
530
|
uint32_t *__restrict__ voxel_triangle_prefix_sums;
|
|
@@ -505,6 +536,13 @@ namespace mc
|
|
|
505
536
|
|
|
506
537
|
assemble_triangles(num_active_voxels, used_voxel_codes, voxel_edge_to_vert_idx, voxel_triangle_prefix_sums, p_out_triangles);
|
|
507
538
|
|
|
539
|
+
std::optional<torch::Tensor> out_unique_edges_opt = std::nullopt;
|
|
540
|
+
if (return_unique_edges) {
|
|
541
|
+
torch::Tensor out_unique_edges = torch::empty({out_num_vertices, 2}, torch::dtype(torch::kInt32).device(vert_options.device()));
|
|
542
|
+
CHECK_CUDA_INTERNAL(cudaMemcpy(out_unique_edges.data_ptr(), unique_edges, out_num_vertices * sizeof(Edge), cudaMemcpyDeviceToDevice));
|
|
543
|
+
out_unique_edges_opt = out_unique_edges;
|
|
544
|
+
}
|
|
545
|
+
|
|
508
546
|
// Cleanup
|
|
509
547
|
CHECK_CUDA_INTERNAL(cudaFree(voxel_codes));
|
|
510
548
|
CHECK_CUDA_INTERNAL(cudaFree(used_voxel_index));
|
|
@@ -514,6 +552,112 @@ namespace mc
|
|
|
514
552
|
CHECK_CUDA_INTERNAL(cudaFree(voxel_edge_to_vert_idx));
|
|
515
553
|
CHECK_CUDA_INTERNAL(cudaFree(voxel_triangle_prefix_sums));
|
|
516
554
|
|
|
517
|
-
return std::make_tuple(out_vertices, out_triangles, out_normals_opt);
|
|
555
|
+
return std::make_tuple(out_vertices, out_triangles, out_normals_opt, out_colors_opt, out_unique_edges_opt);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
__global__ void backward_dmc_kernel(
|
|
559
|
+
const uint32_t n_verts,
|
|
560
|
+
const Edge *unique_edges,
|
|
561
|
+
const float *grid_values,
|
|
562
|
+
const float3 *grid_coords,
|
|
563
|
+
const float3 *grid_colors,
|
|
564
|
+
const float3 *adj_verts,
|
|
565
|
+
const float3 *adj_colors,
|
|
566
|
+
const float iso,
|
|
567
|
+
float *adj_values,
|
|
568
|
+
float3 *adj_grid_colors,
|
|
569
|
+
bool with_colors)
|
|
570
|
+
{
|
|
571
|
+
uint32_t v_idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
572
|
+
if (v_idx >= n_verts)
|
|
573
|
+
return;
|
|
574
|
+
|
|
575
|
+
Edge edge_sig = unique_edges[v_idx];
|
|
576
|
+
uint32_t v0_idx = edge_sig.v0;
|
|
577
|
+
uint32_t v1_idx = edge_sig.v1;
|
|
578
|
+
|
|
579
|
+
float v0_val = grid_values[v0_idx];
|
|
580
|
+
float v1_val = grid_values[v1_idx];
|
|
581
|
+
float3 p0 = grid_coords[v0_idx];
|
|
582
|
+
float3 p1 = grid_coords[v1_idx];
|
|
583
|
+
|
|
584
|
+
float3 grad_p_out = adj_verts[v_idx];
|
|
585
|
+
|
|
586
|
+
float3 c0, c1, grad_c_out;
|
|
587
|
+
if (with_colors)
|
|
588
|
+
{
|
|
589
|
+
c0 = grid_colors[v0_idx];
|
|
590
|
+
c1 = grid_colors[v1_idx];
|
|
591
|
+
grad_c_out = adj_colors[v_idx];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
float diff = v1_val - v0_val;
|
|
595
|
+
|
|
596
|
+
if (with_colors)
|
|
597
|
+
{
|
|
598
|
+
float t = 0.5f;
|
|
599
|
+
if (fabsf(iso - v0_val) < EPS)
|
|
600
|
+
t = 0.0f;
|
|
601
|
+
else if (fabsf(iso - v1_val) < EPS)
|
|
602
|
+
t = 1.0f;
|
|
603
|
+
else if (fabsf(diff) >= EPS)
|
|
604
|
+
{
|
|
605
|
+
t = fmaxf(0.0f, fminf(1.0f, (iso - v0_val) / diff));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
float t0 = 1.0f - t;
|
|
609
|
+
float t1 = t;
|
|
610
|
+
|
|
611
|
+
atomicAdd(&(adj_grid_colors[v0_idx].x), grad_c_out.x * t0);
|
|
612
|
+
atomicAdd(&(adj_grid_colors[v0_idx].y), grad_c_out.y * t0);
|
|
613
|
+
atomicAdd(&(adj_grid_colors[v0_idx].z), grad_c_out.z * t0);
|
|
614
|
+
|
|
615
|
+
atomicAdd(&(adj_grid_colors[v1_idx].x), grad_c_out.x * t1);
|
|
616
|
+
atomicAdd(&(adj_grid_colors[v1_idx].y), grad_c_out.y * t1);
|
|
617
|
+
atomicAdd(&(adj_grid_colors[v1_idx].z), grad_c_out.z * t1);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (diff * diff < 1e-14f)
|
|
621
|
+
return;
|
|
622
|
+
|
|
623
|
+
float dot_prod = (p1.x - p0.x) * grad_p_out.x + (p1.y - p0.y) * grad_p_out.y + (p1.z - p0.z) * grad_p_out.z;
|
|
624
|
+
float common = dot_prod / (diff * diff);
|
|
625
|
+
float grad_v0 = common * (iso - v1_val);
|
|
626
|
+
float grad_v1 = common * (v0_val - iso);
|
|
627
|
+
|
|
628
|
+
atomicAdd(&adj_values[v0_idx], grad_v0);
|
|
629
|
+
atomicAdd(&adj_values[v1_idx], grad_v1);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
void backward(
|
|
633
|
+
const uint32_t n_verts,
|
|
634
|
+
const Edge *unique_edges,
|
|
635
|
+
const float3 *grid_vertices,
|
|
636
|
+
const float3 *grid_colors,
|
|
637
|
+
const float *values,
|
|
638
|
+
const float3 *adj_verts,
|
|
639
|
+
const float3 *adj_colors,
|
|
640
|
+
float *adj_values,
|
|
641
|
+
float3 *adj_grid_colors,
|
|
642
|
+
const float iso)
|
|
643
|
+
{
|
|
644
|
+
if (n_verts == 0) return;
|
|
645
|
+
|
|
646
|
+
bool with_colors = (grid_colors != nullptr && adj_colors != nullptr && adj_grid_colors != nullptr);
|
|
647
|
+
int block_size = NTHREADS;
|
|
648
|
+
int grid_size = (n_verts + block_size - 1) / block_size;
|
|
649
|
+
|
|
650
|
+
backward_dmc_kernel<<<grid_size, block_size>>>(
|
|
651
|
+
n_verts,
|
|
652
|
+
unique_edges,
|
|
653
|
+
values,
|
|
654
|
+
grid_vertices,
|
|
655
|
+
grid_colors,
|
|
656
|
+
adj_verts,
|
|
657
|
+
adj_colors,
|
|
658
|
+
iso,
|
|
659
|
+
adj_values,
|
|
660
|
+
adj_grid_colors,
|
|
661
|
+
with_colors);
|
|
518
662
|
}
|
|
519
663
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#ifndef MC_H
|
|
2
|
+
#define MC_H
|
|
3
|
+
|
|
4
|
+
#include <torch/extension.h>
|
|
5
|
+
#include "mc_data.h"
|
|
6
|
+
#include "../check.h"
|
|
7
|
+
#include "../constants.h"
|
|
8
|
+
#include "../primitive/edge.h"
|
|
9
|
+
|
|
10
|
+
#include <optional>
|
|
11
|
+
|
|
12
|
+
namespace mc{
|
|
13
|
+
std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>, std::optional<torch::Tensor>, std::optional<torch::Tensor>> marching_cubes(
|
|
14
|
+
const uint32_t num_voxels,
|
|
15
|
+
const float3* __restrict__ grid_vertices,
|
|
16
|
+
const uint32_t* __restrict__ voxels,
|
|
17
|
+
const float* __restrict__ voxel_values,
|
|
18
|
+
const float3* __restrict__ grid_normals,
|
|
19
|
+
const float3* __restrict__ grid_colors,
|
|
20
|
+
const float iso,
|
|
21
|
+
torch::TensorOptions vert_options,
|
|
22
|
+
torch::TensorOptions tri_options,
|
|
23
|
+
bool return_unique_edges = false
|
|
24
|
+
);
|
|
25
|
+
void backward(
|
|
26
|
+
const uint32_t n_verts,
|
|
27
|
+
const Edge *unique_edges,
|
|
28
|
+
const float3 *grid_vertices,
|
|
29
|
+
const float3 *grid_colors,
|
|
30
|
+
const float *values,
|
|
31
|
+
const float3 *adj_verts,
|
|
32
|
+
const float3 *adj_colors,
|
|
33
|
+
float *adj_values,
|
|
34
|
+
float3 *adj_grid_colors,
|
|
35
|
+
const float iso
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#endif // MC_H
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .._C import (
|
|
2
|
+
KDTree,
|
|
3
|
+
BVH,
|
|
4
|
+
GSBVH,
|
|
5
|
+
PGSBVH,
|
|
6
|
+
MeshBVH,
|
|
7
|
+
TriangleMesh
|
|
8
|
+
)
|
|
9
|
+
from .grid import create_voxel_grid, compute_grid_normal, compute_active_voxels, voxel2sparse, sparse2voxel
|
|
10
|
+
|
|
11
|
+
spatial_data_structures = ['KDTree', 'BVH', 'GSBVH', 'PGSBVH', 'MeshBVH']
|
|
12
|
+
mesh_data_structures = ['TriangleMesh']
|
|
13
|
+
grid_data_structures = ['create_voxel_grid', 'compute_grid_normal', 'compute_active_voxels', 'voxel2sparse', 'sparse2voxel']
|
|
14
|
+
|
|
15
|
+
__all__ = spatial_data_structures + mesh_data_structures + grid_data_structures
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from typing import Tuple, List, Union
|
|
3
|
+
from .. import _C
|
|
4
|
+
|
|
5
|
+
def create_voxel_grid(
|
|
6
|
+
grid_min: Union[List[float], Tuple[float, float, float]],
|
|
7
|
+
grid_max: Union[List[float], Tuple[float, float, float]],
|
|
8
|
+
res: Union[List[int], Tuple[int, int, int]],
|
|
9
|
+
device: str = "cuda"
|
|
10
|
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
11
|
+
"""
|
|
12
|
+
Creates a structured 3D voxel grid efficiently.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
grid_min (List[float] | Tuple[float, float, float]): The minimum (x, y, z) bounding box coordinates.
|
|
16
|
+
grid_max (List[float] | Tuple[float, float, float]): The maximum (x, y, z) bounding box coordinates.
|
|
17
|
+
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
18
|
+
device (str, optional): Target device for the tensors (e.g., "cuda" or "cpu"). Defaults to "cuda".
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
22
|
+
- grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing all grid coordinates.
|
|
23
|
+
- voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices
|
|
24
|
+
for each voxel, mapped identically to the Marching Cubes convention.
|
|
25
|
+
- idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
|
|
26
|
+
"""
|
|
27
|
+
return _C.create_voxel_grid(list(grid_min), list(grid_max), list(res), device)
|
|
28
|
+
|
|
29
|
+
def compute_grid_normal(
|
|
30
|
+
sdf: torch.Tensor,
|
|
31
|
+
grid_vertices: torch.Tensor,
|
|
32
|
+
idx_grids: torch.Tensor,
|
|
33
|
+
res: Union[List[int], Tuple[int, int, int]]
|
|
34
|
+
) -> torch.Tensor:
|
|
35
|
+
"""
|
|
36
|
+
Computes smooth normals (gradients) for a voxel grid using central differences.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
|
|
40
|
+
grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing the grid coordinates.
|
|
41
|
+
idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) indices, returned by create_voxel_grid.
|
|
42
|
+
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
torch.Tensor: A float32 tensor of shape (N, 3) containing the normalized gradient vectors.
|
|
46
|
+
"""
|
|
47
|
+
return _C.compute_grid_normal(sdf.contiguous(), grid_vertices.contiguous(), idx_grids.contiguous(), list(res))
|
|
48
|
+
|
|
49
|
+
def compute_active_voxels(
|
|
50
|
+
voxels: torch.Tensor,
|
|
51
|
+
sdf: torch.Tensor,
|
|
52
|
+
iso: float = 0.0
|
|
53
|
+
) -> torch.Tensor:
|
|
54
|
+
"""
|
|
55
|
+
Computes the indices of active voxels that intersect the isosurface.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices for each voxel.
|
|
59
|
+
sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
|
|
60
|
+
iso (float): The isosurface value. Defaults to 0.0.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
torch.Tensor: An int64 tensor containing the indices of the active voxels.
|
|
64
|
+
"""
|
|
65
|
+
return _C.compute_active_voxels(voxels.contiguous(), sdf.contiguous(), iso)
|
|
66
|
+
|
|
67
|
+
def voxel2sparse(
|
|
68
|
+
active_indices: torch.Tensor,
|
|
69
|
+
voxels: torch.Tensor,
|
|
70
|
+
idx_grids: torch.Tensor,
|
|
71
|
+
batch_idx: int = 0
|
|
72
|
+
) -> torch.Tensor:
|
|
73
|
+
"""
|
|
74
|
+
Converts 1D active voxel indices into the [N, 4] coordinates tensor
|
|
75
|
+
expected by sparse neural networks (like spconv/torchsparse).
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
active_indices (torch.Tensor): 1D tensor of active voxel indices.
|
|
79
|
+
voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices for each voxel.
|
|
80
|
+
idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
|
|
81
|
+
batch_idx (int, optional): The batch index to prepend. Defaults to 0.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
torch.Tensor: An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
|
|
85
|
+
"""
|
|
86
|
+
active_corners = voxels[active_indices]
|
|
87
|
+
base_vertex_indices = active_corners[:, 0].to(torch.int64)
|
|
88
|
+
voxel_coords = idx_grids[base_vertex_indices]
|
|
89
|
+
|
|
90
|
+
batch_col = torch.full((voxel_coords.shape[0], 1), batch_idx, device=voxel_coords.device, dtype=torch.int64)
|
|
91
|
+
sparse_coords = torch.cat([batch_col, voxel_coords], dim=1).to(torch.int32)
|
|
92
|
+
return sparse_coords
|
|
93
|
+
|
|
94
|
+
def sparse2voxel(
|
|
95
|
+
sparse_coords: torch.Tensor,
|
|
96
|
+
res: Union[List[int], Tuple[int, int, int]]
|
|
97
|
+
) -> torch.Tensor:
|
|
98
|
+
"""
|
|
99
|
+
Converts a [N, 4] sparse coordinates tensor back to 1D active voxel indices.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
sparse_coords (torch.Tensor): An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
|
|
103
|
+
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
torch.Tensor: An int64 tensor containing the indices of the active voxels.
|
|
107
|
+
"""
|
|
108
|
+
x = sparse_coords[:, 1].to(torch.int64)
|
|
109
|
+
y = sparse_coords[:, 2].to(torch.int64)
|
|
110
|
+
z = sparse_coords[:, 3].to(torch.int64)
|
|
111
|
+
|
|
112
|
+
rx, ry, rz = res
|
|
113
|
+
active_indices = x * (ry - 1) * (rz - 1) + y * (rz - 1) + z
|
|
114
|
+
return active_indices
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
from .marching_cubes import marching_cubes
|
|
2
|
-
from .
|
|
2
|
+
from .diff_marching_cubes import diff_marching_cubes
|
|
3
3
|
from .distance import one_sided_chamfer_distance, chamfer_distance
|
|
4
4
|
|
|
5
5
|
__all__ = [
|
|
6
6
|
"marching_cubes",
|
|
7
|
-
"
|
|
8
|
-
"compute_grid_normal",
|
|
7
|
+
"diff_marching_cubes",
|
|
9
8
|
"one_sided_chamfer_distance",
|
|
10
9
|
"chamfer_distance"
|
|
11
10
|
]
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from typing import Tuple, Optional
|
|
3
|
+
|
|
4
|
+
# Explicitly import the compiled CMake target
|
|
5
|
+
from .._C import marching_cubes as marching_cubes_func
|
|
6
|
+
from .._C import marching_cubes_backward as marching_cubes_backward_func
|
|
7
|
+
|
|
8
|
+
class DiffMarchingCubes(torch.autograd.Function):
|
|
9
|
+
@staticmethod
|
|
10
|
+
def forward(
|
|
11
|
+
ctx,
|
|
12
|
+
grid_vertices: torch.Tensor,
|
|
13
|
+
voxels: torch.Tensor,
|
|
14
|
+
voxel_values: torch.Tensor,
|
|
15
|
+
grid_normals: Optional[torch.Tensor] = None,
|
|
16
|
+
grid_colors: Optional[torch.Tensor] = None,
|
|
17
|
+
iso: float = 0.0
|
|
18
|
+
):
|
|
19
|
+
# We need return_unique_edges = True for the backward pass
|
|
20
|
+
out_vertices, out_triangles, out_normals, out_colors, unique_edges = marching_cubes_func(
|
|
21
|
+
grid_vertices,
|
|
22
|
+
voxels,
|
|
23
|
+
voxel_values,
|
|
24
|
+
grid_normals,
|
|
25
|
+
grid_colors,
|
|
26
|
+
iso,
|
|
27
|
+
True # return_unique_edges
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
ctx.iso = iso
|
|
31
|
+
ctx.has_colors = grid_colors is not None
|
|
32
|
+
|
|
33
|
+
# Save tensors needed for the backward pass
|
|
34
|
+
if ctx.has_colors:
|
|
35
|
+
ctx.save_for_backward(unique_edges, grid_vertices, voxel_values, grid_colors)
|
|
36
|
+
else:
|
|
37
|
+
ctx.save_for_backward(unique_edges, grid_vertices, voxel_values)
|
|
38
|
+
|
|
39
|
+
return out_vertices, out_triangles, out_normals, out_colors
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def backward(ctx, grad_out_vertices, grad_out_triangles, grad_out_normals, grad_out_colors):
|
|
43
|
+
if ctx.has_colors:
|
|
44
|
+
unique_edges, grid_vertices, voxel_values, grid_colors = ctx.saved_tensors
|
|
45
|
+
else:
|
|
46
|
+
unique_edges, grid_vertices, voxel_values = ctx.saved_tensors
|
|
47
|
+
grid_colors = None
|
|
48
|
+
|
|
49
|
+
iso = ctx.iso
|
|
50
|
+
|
|
51
|
+
# Initialize gradients for inputs
|
|
52
|
+
grad_voxel_values = torch.zeros_like(voxel_values)
|
|
53
|
+
|
|
54
|
+
grad_grid_colors = None
|
|
55
|
+
if ctx.has_colors:
|
|
56
|
+
grad_grid_colors = torch.zeros_like(grid_colors)
|
|
57
|
+
|
|
58
|
+
# Call the backward C++ binding if we have valid output vertices
|
|
59
|
+
if unique_edges is not None and unique_edges.shape[0] > 0 and grad_out_vertices is not None:
|
|
60
|
+
grad_out_vertices = grad_out_vertices.contiguous()
|
|
61
|
+
if grad_out_colors is not None:
|
|
62
|
+
grad_out_colors = grad_out_colors.contiguous()
|
|
63
|
+
|
|
64
|
+
marching_cubes_backward_func(
|
|
65
|
+
unique_edges,
|
|
66
|
+
grid_vertices,
|
|
67
|
+
grid_colors,
|
|
68
|
+
voxel_values,
|
|
69
|
+
grad_out_vertices,
|
|
70
|
+
grad_out_colors,
|
|
71
|
+
grad_voxel_values,
|
|
72
|
+
grad_grid_colors,
|
|
73
|
+
iso
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Return gradients for all inputs in the same order as forward
|
|
77
|
+
# grid_vertices, voxels, voxel_values, grid_normals, grid_colors, iso
|
|
78
|
+
return None, None, grad_voxel_values, None, grad_grid_colors, None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def diff_marching_cubes(
|
|
82
|
+
grid_vertices: torch.Tensor,
|
|
83
|
+
voxels: torch.Tensor,
|
|
84
|
+
voxel_values: torch.Tensor,
|
|
85
|
+
grid_normals: Optional[torch.Tensor] = None,
|
|
86
|
+
grid_colors: Optional[torch.Tensor] = None,
|
|
87
|
+
iso: float = 0.0
|
|
88
|
+
) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
|
|
89
|
+
"""
|
|
90
|
+
Executes the Differentiable Marching Cubes algorithm to extract an isosurface from a voxel grid.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
grid_vertices (torch.Tensor): (V, 3) tensor of global vertex positions.
|
|
94
|
+
voxels (torch.Tensor): (N, 8) tensor of voxel corner indices mapping to `grid_vertices`.
|
|
95
|
+
voxel_values (torch.Tensor): (V,) tensor of SDF/scalar values at each vertex. Requires grad.
|
|
96
|
+
grid_normals (torch.Tensor, optional): (V, 3) optional tensor of SDF normals at each vertex. Defaults to None.
|
|
97
|
+
grid_colors (torch.Tensor, optional): (V, 3) optional tensor of RGB colors at each vertex. Defaults to None. Requires grad.
|
|
98
|
+
iso (float, optional): The isosurface extraction threshold. Defaults to 0.0.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
|
|
102
|
+
- vertices (torch.Tensor): (M, 3) tensor of extracted mesh vertices. Supports gradients.
|
|
103
|
+
- triangles (torch.Tensor): (T, 3) tensor of extracted mesh triangle indices.
|
|
104
|
+
- normals (torch.Tensor, optional): (M, 3) tensor of extracted mesh vertex normals, if `grid_normals` was provided.
|
|
105
|
+
- colors (torch.Tensor, optional): (M, 3) tensor of extracted mesh vertex colors, if `grid_colors` was provided. Supports gradients.
|
|
106
|
+
"""
|
|
107
|
+
if not all(t.is_cuda for t in [grid_vertices, voxels, voxel_values]):
|
|
108
|
+
raise ValueError("All input tensors must be CUDA tensors.")
|
|
109
|
+
|
|
110
|
+
grid_vertices_c = grid_vertices.contiguous().to(torch.float32)
|
|
111
|
+
voxels_c = voxels.contiguous().to(torch.int32)
|
|
112
|
+
voxel_values_c = voxel_values.contiguous().to(torch.float32)
|
|
113
|
+
|
|
114
|
+
grid_normals_c = None
|
|
115
|
+
if grid_normals is not None:
|
|
116
|
+
if not grid_normals.is_cuda:
|
|
117
|
+
raise ValueError("grid_normals must be a CUDA tensor.")
|
|
118
|
+
grid_normals_c = grid_normals.contiguous().to(torch.float32)
|
|
119
|
+
|
|
120
|
+
grid_colors_c = None
|
|
121
|
+
if grid_colors is not None:
|
|
122
|
+
if not grid_colors.is_cuda:
|
|
123
|
+
raise ValueError("grid_colors must be a CUDA tensor.")
|
|
124
|
+
grid_colors_c = grid_colors.contiguous().to(torch.float32)
|
|
125
|
+
|
|
126
|
+
iso_c = float(iso)
|
|
127
|
+
|
|
128
|
+
return DiffMarchingCubes.apply(
|
|
129
|
+
grid_vertices_c,
|
|
130
|
+
voxels_c,
|
|
131
|
+
voxel_values_c,
|
|
132
|
+
grid_normals_c,
|
|
133
|
+
grid_colors_c,
|
|
134
|
+
iso_c
|
|
135
|
+
)
|
|
@@ -9,8 +9,9 @@ def marching_cubes(
|
|
|
9
9
|
voxels: torch.Tensor,
|
|
10
10
|
voxel_values: torch.Tensor,
|
|
11
11
|
grid_normals: Optional[torch.Tensor] = None,
|
|
12
|
+
grid_colors: Optional[torch.Tensor] = None,
|
|
12
13
|
iso: float = 0.0
|
|
13
|
-
) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
|
|
14
|
+
) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
|
|
14
15
|
"""
|
|
15
16
|
Executes the Marching Cubes algorithm to extract an isosurface from a voxel grid.
|
|
16
17
|
|
|
@@ -19,13 +20,15 @@ def marching_cubes(
|
|
|
19
20
|
voxels (torch.Tensor): (N, 8) tensor of voxel corner indices mapping to `grid_vertices`.
|
|
20
21
|
voxel_values (torch.Tensor): (V,) tensor of SDF/scalar values at each vertex.
|
|
21
22
|
grid_normals (torch.Tensor, optional): (V, 3) optional tensor of SDF normals at each vertex. Defaults to None.
|
|
23
|
+
grid_colors (torch.Tensor, optional): (V, 3) optional tensor of RGB colors at each vertex. Defaults to None.
|
|
22
24
|
iso (float, optional): The isosurface extraction threshold. Defaults to 0.0.
|
|
23
25
|
|
|
24
26
|
Returns:
|
|
25
|
-
Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
|
|
27
|
+
Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
|
|
26
28
|
- vertices (torch.Tensor): (M, 3) tensor of extracted mesh vertices.
|
|
27
29
|
- triangles (torch.Tensor): (T, 3) tensor of extracted mesh triangle indices.
|
|
28
30
|
- normals (torch.Tensor, optional): (M, 3) tensor of extracted mesh vertex normals, if `grid_normals` was provided.
|
|
31
|
+
- colors (torch.Tensor, optional): (M, 3) tensor of extracted mesh vertex colors, if `grid_colors` was provided.
|
|
29
32
|
"""
|
|
30
33
|
if not all(t.is_cuda for t in [grid_vertices, voxels, voxel_values]):
|
|
31
34
|
raise ValueError("All input tensors must be CUDA tensors.")
|
|
@@ -40,14 +43,22 @@ def marching_cubes(
|
|
|
40
43
|
raise ValueError("grid_normals must be a CUDA tensor.")
|
|
41
44
|
grid_normals_c = grid_normals.contiguous().to(torch.float32)
|
|
42
45
|
|
|
46
|
+
grid_colors_c = None
|
|
47
|
+
if grid_colors is not None:
|
|
48
|
+
if not grid_colors.is_cuda:
|
|
49
|
+
raise ValueError("grid_colors must be a CUDA tensor.")
|
|
50
|
+
grid_colors_c = grid_colors.contiguous().to(torch.float32)
|
|
51
|
+
|
|
43
52
|
iso_c = float(iso)
|
|
44
53
|
|
|
45
|
-
vertices, triangles, out_normals = marching_cubes_func(
|
|
54
|
+
vertices, triangles, out_normals, out_colors, _ = marching_cubes_func(
|
|
46
55
|
grid_vertices_c,
|
|
47
56
|
voxels_c,
|
|
48
57
|
voxel_values_c,
|
|
49
58
|
grid_normals_c,
|
|
50
|
-
|
|
59
|
+
grid_colors_c,
|
|
60
|
+
iso_c,
|
|
61
|
+
False # return_unique_edges
|
|
51
62
|
)
|
|
52
63
|
|
|
53
|
-
return vertices, triangles, out_normals
|
|
64
|
+
return vertices, triangles, out_normals, out_colors
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conquer3d
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Geometric Cuda Tool Box
|
|
5
5
|
Author-email: Do Hoang Khoi <khoido8899@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -51,8 +51,13 @@ conda activate geocutool
|
|
|
51
51
|
|
|
52
52
|
conda install nvidia::cuda-toolkit==12.8.2 -y
|
|
53
53
|
|
|
54
|
+
pip install setuptools wheel ninja
|
|
54
55
|
pip install torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 --index-url https://download.pytorch.org/whl/cu128
|
|
55
56
|
|
|
57
|
+
pip install git+https://github.com/mit-han-lab/torchsparse.git
|
|
58
|
+
|
|
59
|
+
pip install kaolin==0.18.0 -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/torch-2.8.0_cu128.html
|
|
60
|
+
|
|
56
61
|
pip install pybind11-stubgen
|
|
57
62
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
58
63
|
|
|
@@ -67,9 +67,10 @@ conquer3d/csrc/primitive/pgs_math.cuh
|
|
|
67
67
|
conquer3d/csrc/primitive/ray.h
|
|
68
68
|
conquer3d/csrc/primitive/triangles.h
|
|
69
69
|
conquer3d/data_structure/__init__.py
|
|
70
|
+
conquer3d/data_structure/grid.py
|
|
70
71
|
conquer3d/ops/__init__.py
|
|
72
|
+
conquer3d/ops/diff_marching_cubes.py
|
|
71
73
|
conquer3d/ops/distance.py
|
|
72
|
-
conquer3d/ops/grid.py
|
|
73
74
|
conquer3d/ops/marching_cubes.py
|
|
74
75
|
conquer3d/primitive/__init__.py
|
|
75
76
|
conquer3d/primitive/gs.py
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#include <torch/extension.h>
|
|
2
|
-
#include <pybind11/pybind11.h>
|
|
3
|
-
#include "../../ops/mc.h"
|
|
4
|
-
#include "../../check.h"
|
|
5
|
-
|
|
6
|
-
namespace py = pybind11;
|
|
7
|
-
|
|
8
|
-
std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> marching_cubes_wrapper(
|
|
9
|
-
torch::Tensor grid_vertices,
|
|
10
|
-
torch::Tensor voxels,
|
|
11
|
-
torch::Tensor voxel_values,
|
|
12
|
-
std::optional<torch::Tensor> grid_normals,
|
|
13
|
-
float iso
|
|
14
|
-
) {
|
|
15
|
-
CHECK_INPUT(grid_vertices);
|
|
16
|
-
CHECK_INPUT(voxels);
|
|
17
|
-
CHECK_INPUT(voxel_values);
|
|
18
|
-
|
|
19
|
-
uint32_t num_voxels = voxels.size(0);
|
|
20
|
-
|
|
21
|
-
const float3* __restrict__ p_grid_vertices = (float3*)grid_vertices.data_ptr<float>();
|
|
22
|
-
const uint32_t* __restrict__ p_voxels = (uint32_t*)voxels.data_ptr<int32_t>();
|
|
23
|
-
const float* __restrict__ p_voxel_values = voxel_values.data_ptr<float>();
|
|
24
|
-
|
|
25
|
-
const float3* __restrict__ p_grid_normals = nullptr;
|
|
26
|
-
if (grid_normals.has_value()) {
|
|
27
|
-
CHECK_INPUT(grid_normals.value());
|
|
28
|
-
p_grid_normals = (float3*)grid_normals.value().data_ptr<float>();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return mc::marching_cubes(
|
|
32
|
-
num_voxels,
|
|
33
|
-
p_grid_vertices,
|
|
34
|
-
p_voxels,
|
|
35
|
-
p_voxel_values,
|
|
36
|
-
p_grid_normals,
|
|
37
|
-
iso,
|
|
38
|
-
grid_vertices.options(),
|
|
39
|
-
voxels.options()
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
void bind_ops_mc(py::module_& m) {
|
|
44
|
-
m.def("marching_cubes", &marching_cubes_wrapper, "Marching Cubes",
|
|
45
|
-
py::arg("grid_vertices"), py::arg("voxels"), py::arg("voxel_values"),
|
|
46
|
-
py::arg("grid_normals") = std::nullopt, py::arg("iso") = 0.0f);
|
|
47
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
#ifndef MC_H
|
|
2
|
-
#define MC_H
|
|
3
|
-
|
|
4
|
-
#include <torch/extension.h>
|
|
5
|
-
#include "mc_data.h"
|
|
6
|
-
#include "../check.h"
|
|
7
|
-
#include "../constants.h"
|
|
8
|
-
#include "../primitive/edge.h"
|
|
9
|
-
|
|
10
|
-
#include <optional>
|
|
11
|
-
|
|
12
|
-
namespace mc{
|
|
13
|
-
std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> marching_cubes(
|
|
14
|
-
const uint32_t num_voxels,
|
|
15
|
-
const float3* __restrict__ grid_vertices,
|
|
16
|
-
const uint32_t* __restrict__ voxels,
|
|
17
|
-
const float* __restrict__ voxel_values,
|
|
18
|
-
const float3* __restrict__ grid_normals,
|
|
19
|
-
const float iso,
|
|
20
|
-
torch::TensorOptions vert_options,
|
|
21
|
-
torch::TensorOptions tri_options
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
#endif // MC_H
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from .._C import (
|
|
2
|
-
KDTree,
|
|
3
|
-
BVH,
|
|
4
|
-
GSBVH,
|
|
5
|
-
PGSBVH,
|
|
6
|
-
MeshBVH,
|
|
7
|
-
TriangleMesh
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
spatial_data_structures = ['KDTree', 'BVH', 'GSBVH', 'PGSBVH', 'MeshBVH']
|
|
11
|
-
mesh_data_structures = ['TriangleMesh']
|
|
12
|
-
|
|
13
|
-
__all__ = spatial_data_structures + mesh_data_structures
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import torch
|
|
2
|
-
from typing import Tuple, List, Union
|
|
3
|
-
from .. import _C
|
|
4
|
-
|
|
5
|
-
def create_voxel_grid(
|
|
6
|
-
grid_min: Union[List[float], Tuple[float, float, float]],
|
|
7
|
-
grid_max: Union[List[float], Tuple[float, float, float]],
|
|
8
|
-
res: Union[List[int], Tuple[int, int, int]],
|
|
9
|
-
device: str = "cuda"
|
|
10
|
-
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
11
|
-
"""
|
|
12
|
-
Creates a structured 3D voxel grid efficiently.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
grid_min (List[float] | Tuple[float, float, float]): The minimum (x, y, z) bounding box coordinates.
|
|
16
|
-
grid_max (List[float] | Tuple[float, float, float]): The maximum (x, y, z) bounding box coordinates.
|
|
17
|
-
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
18
|
-
device (str, optional): Target device for the tensors (e.g., "cuda" or "cpu"). Defaults to "cuda".
|
|
19
|
-
|
|
20
|
-
Returns:
|
|
21
|
-
Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
22
|
-
- grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing all grid coordinates.
|
|
23
|
-
- voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices
|
|
24
|
-
for each voxel, mapped identically to the Marching Cubes convention.
|
|
25
|
-
- idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
|
|
26
|
-
"""
|
|
27
|
-
return _C.create_voxel_grid(list(grid_min), list(grid_max), list(res), device)
|
|
28
|
-
|
|
29
|
-
def compute_grid_normal(
|
|
30
|
-
sdf: torch.Tensor,
|
|
31
|
-
grid_vertices: torch.Tensor,
|
|
32
|
-
idx_grids: torch.Tensor,
|
|
33
|
-
res: Union[List[int], Tuple[int, int, int]]
|
|
34
|
-
) -> torch.Tensor:
|
|
35
|
-
"""
|
|
36
|
-
Computes smooth normals (gradients) for a voxel grid using central differences.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
|
|
40
|
-
grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing the grid coordinates.
|
|
41
|
-
idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) indices, returned by create_voxel_grid.
|
|
42
|
-
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
torch.Tensor: A float32 tensor of shape (N, 3) containing the normalized gradient vectors.
|
|
46
|
-
"""
|
|
47
|
-
return _C.compute_grid_normal(sdf.contiguous(), grid_vertices.contiguous(), idx_grids.contiguous(), list(res))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|