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.
Files changed (83) hide show
  1. {conquer3d-0.2.9/conquer3d.egg-info → conquer3d-0.3.0}/PKG-INFO +6 -1
  2. {conquer3d-0.2.9 → conquer3d-0.3.0}/README.md +5 -0
  3. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/_C.so +0 -0
  4. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/grid.cpp +21 -0
  5. conquer3d-0.3.0/conquer3d/csrc/binds/ops/mc.cpp +118 -0
  6. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/mc.cu +152 -8
  7. conquer3d-0.3.0/conquer3d/csrc/ops/mc.h +39 -0
  8. conquer3d-0.3.0/conquer3d/data_structure/__init__.py +15 -0
  9. conquer3d-0.3.0/conquer3d/data_structure/grid.py +114 -0
  10. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/ops/__init__.py +2 -3
  11. conquer3d-0.3.0/conquer3d/ops/diff_marching_cubes.py +135 -0
  12. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/ops/marching_cubes.py +16 -5
  13. {conquer3d-0.2.9 → conquer3d-0.3.0/conquer3d.egg-info}/PKG-INFO +6 -1
  14. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/SOURCES.txt +2 -1
  15. {conquer3d-0.2.9 → conquer3d-0.3.0}/pyproject.toml +1 -1
  16. conquer3d-0.2.9/conquer3d/csrc/binds/ops/mc.cpp +0 -47
  17. conquer3d-0.2.9/conquer3d/csrc/ops/mc.h +0 -25
  18. conquer3d-0.2.9/conquer3d/data_structure/__init__.py +0 -13
  19. conquer3d-0.2.9/conquer3d/ops/grid.py +0 -47
  20. {conquer3d-0.2.9 → conquer3d-0.3.0}/LICENSE +0 -0
  21. {conquer3d-0.2.9 → conquer3d-0.3.0}/MANIFEST.in +0 -0
  22. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/_C.pyi +0 -0
  23. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/__init__.py +0 -0
  24. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/creation/__init__.py +0 -0
  25. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/creation/triangle_creation.py +0 -0
  26. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
  27. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
  28. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
  29. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
  30. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
  31. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
  32. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
  33. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
  34. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
  35. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
  36. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/check.h +0 -0
  37. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/constants.h +0 -0
  38. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/creation/triangle_creation.h +0 -0
  39. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/bvh.cu +0 -0
  40. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/bvh.h +0 -0
  41. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
  42. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
  43. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/kdtree.h +0 -0
  44. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
  45. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
  46. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
  47. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
  48. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
  49. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/data_structure/zcurve.h +0 -0
  50. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f2x2.h +0 -0
  51. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f3x1.h +0 -0
  52. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f3x3.h +0 -0
  53. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f3x4.h +0 -0
  54. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f4x1.h +0 -0
  55. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/f4x4.h +0 -0
  56. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/maths.h +0 -0
  57. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/maths/ops.h +0 -0
  58. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/chamfer.cu +0 -0
  59. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/chamfer.h +0 -0
  60. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/ops/mc_data.h +0 -0
  61. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/aabb.h +0 -0
  62. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/edge.h +0 -0
  63. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs.cu +0 -0
  64. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs.h +0 -0
  65. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
  66. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
  67. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs.cu +0 -0
  68. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs.h +0 -0
  69. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
  70. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
  71. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/ray.h +0 -0
  72. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/primitive/triangles.h +0 -0
  73. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/csrc/pybind.cpp +0 -0
  74. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/ops/distance.py +0 -0
  75. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/primitive/__init__.py +0 -0
  76. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/primitive/gs.py +0 -0
  77. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d/primitive/pgs.py +0 -0
  78. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/dependency_links.txt +0 -0
  79. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/not-zip-safe +0 -0
  80. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/requires.txt +0 -0
  81. {conquer3d-0.2.9 → conquer3d-0.3.0}/conquer3d.egg-info/top_level.txt +0 -0
  82. {conquer3d-0.2.9 → conquer3d-0.3.0}/setup.cfg +0 -0
  83. {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.2.9
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
 
@@ -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
- interpolate_vertices(out_num_vertices, unique_edges, grid_vertices, voxel_values, grid_normals, iso, p_out_vertices, p_out_normals);
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 .grid import create_voxel_grid, compute_grid_normal
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
- "create_voxel_grid",
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
- iso_c
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.2.9
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
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "conquer3d"
12
- version = "0.2.9"
12
+ version = "0.3.0"
13
13
  description = "Geometric Cuda Tool Box"
14
14
  readme = "README.md"
15
15
  license = "MIT"
@@ -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