conquer3d 0.2.8__tar.gz → 0.2.9__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 (80) hide show
  1. {conquer3d-0.2.8/conquer3d.egg-info → conquer3d-0.2.9}/PKG-INFO +2 -2
  2. {conquer3d-0.2.8 → conquer3d-0.2.9}/README.md +1 -1
  3. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/_C.so +0 -0
  4. conquer3d-0.2.9/conquer3d/csrc/binds/data_structure/grid.cpp +129 -0
  5. conquer3d-0.2.9/conquer3d/csrc/binds/ops/chamfer.cpp +42 -0
  6. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/ops/mc.cpp +12 -2
  7. conquer3d-0.2.9/conquer3d/csrc/ops/chamfer.cu +73 -0
  8. conquer3d-0.2.9/conquer3d/csrc/ops/chamfer.h +18 -0
  9. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/ops/mc.cu +42 -12
  10. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/ops/mc.h +3 -2
  11. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/pybind.cpp +2 -0
  12. conquer3d-0.2.9/conquer3d/ops/__init__.py +11 -0
  13. conquer3d-0.2.9/conquer3d/ops/distance.py +28 -0
  14. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/ops/grid.py +23 -2
  15. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/ops/marching_cubes.py +16 -5
  16. {conquer3d-0.2.8 → conquer3d-0.2.9/conquer3d.egg-info}/PKG-INFO +2 -2
  17. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d.egg-info/SOURCES.txt +4 -0
  18. {conquer3d-0.2.8 → conquer3d-0.2.9}/pyproject.toml +1 -1
  19. conquer3d-0.2.8/conquer3d/csrc/binds/data_structure/grid.cpp +0 -66
  20. conquer3d-0.2.8/conquer3d/ops/__init__.py +0 -7
  21. {conquer3d-0.2.8 → conquer3d-0.2.9}/LICENSE +0 -0
  22. {conquer3d-0.2.8 → conquer3d-0.2.9}/MANIFEST.in +0 -0
  23. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/_C.pyi +0 -0
  24. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/__init__.py +0 -0
  25. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/creation/__init__.py +0 -0
  26. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/creation/triangle_creation.py +0 -0
  27. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
  28. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
  29. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
  30. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
  31. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
  32. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
  33. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
  34. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
  35. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
  36. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/check.h +0 -0
  37. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/constants.h +0 -0
  38. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/creation/triangle_creation.h +0 -0
  39. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/bvh.cu +0 -0
  40. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/bvh.h +0 -0
  41. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
  42. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
  43. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/kdtree.h +0 -0
  44. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
  45. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
  46. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
  47. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
  48. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
  49. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/data_structure/zcurve.h +0 -0
  50. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/f2x2.h +0 -0
  51. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/f3x1.h +0 -0
  52. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/f3x3.h +0 -0
  53. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/f3x4.h +0 -0
  54. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/f4x1.h +0 -0
  55. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/f4x4.h +0 -0
  56. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/maths.h +0 -0
  57. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/maths/ops.h +0 -0
  58. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/ops/mc_data.h +0 -0
  59. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/aabb.h +0 -0
  60. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/edge.h +0 -0
  61. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/gs.cu +0 -0
  62. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/gs.h +0 -0
  63. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
  64. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
  65. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/pgs.cu +0 -0
  66. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/pgs.h +0 -0
  67. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
  68. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
  69. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/ray.h +0 -0
  70. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/csrc/primitive/triangles.h +0 -0
  71. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/data_structure/__init__.py +0 -0
  72. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/primitive/__init__.py +0 -0
  73. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/primitive/gs.py +0 -0
  74. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d/primitive/pgs.py +0 -0
  75. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d.egg-info/dependency_links.txt +0 -0
  76. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d.egg-info/not-zip-safe +0 -0
  77. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d.egg-info/requires.txt +0 -0
  78. {conquer3d-0.2.8 → conquer3d-0.2.9}/conquer3d.egg-info/top_level.txt +0 -0
  79. {conquer3d-0.2.8 → conquer3d-0.2.9}/setup.cfg +0 -0
  80. {conquer3d-0.2.8 → conquer3d-0.2.9}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conquer3d
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Geometric Cuda Tool Box
5
5
  Author-email: Do Hoang Khoi <khoido8899@gmail.com>
6
6
  License-Expression: MIT
@@ -56,7 +56,7 @@ pip install torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 --index-url https
56
56
  pip install pybind11-stubgen
57
57
  pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
58
58
 
59
- pip install plotly open3d jupyter trimesh
59
+ pip install plotly open3d jupyter trimesh point-cloud-utils
60
60
  ```
61
61
 
62
62
  # Development
@@ -28,7 +28,7 @@ pip install torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 --index-url https
28
28
  pip install pybind11-stubgen
29
29
  pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
30
30
 
31
- pip install plotly open3d jupyter trimesh
31
+ pip install plotly open3d jupyter trimesh point-cloud-utils
32
32
  ```
33
33
 
34
34
  # Development
@@ -0,0 +1,129 @@
1
+ #include <torch/extension.h>
2
+ #include <pybind11/pybind11.h>
3
+ #include <pybind11/stl.h>
4
+ #include <tuple>
5
+ #include <string>
6
+ #include <vector>
7
+
8
+ namespace py = pybind11;
9
+
10
+ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor> create_voxel_grid(
11
+ std::vector<float> grid_min,
12
+ std::vector<float> grid_max,
13
+ std::vector<int64_t> res,
14
+ std::string device_str
15
+ ) {
16
+ TORCH_CHECK(grid_min.size() == 3, "grid_min must have 3 elements.");
17
+ TORCH_CHECK(grid_max.size() == 3, "grid_max must have 3 elements.");
18
+ TORCH_CHECK(res.size() == 3, "res must have 3 elements.");
19
+
20
+ c10::Device device(device_str);
21
+ auto options = torch::TensorOptions().device(device).dtype(torch::kFloat32);
22
+
23
+ int64_t rx = res[0];
24
+ int64_t ry = res[1];
25
+ int64_t rz = res[2];
26
+
27
+ auto x = torch::linspace(grid_min[0], grid_max[0], rx, options);
28
+ auto y = torch::linspace(grid_min[1], grid_max[1], ry, options);
29
+ auto z = torch::linspace(grid_min[2], grid_max[2], rz, options);
30
+
31
+ auto grids = torch::meshgrid({x, y, z}, "ij");
32
+ auto grid_x = grids[0].flatten();
33
+ auto grid_y = grids[1].flatten();
34
+ auto grid_z = grids[2].flatten();
35
+
36
+ auto grid_vertices = torch::stack({grid_x, grid_y, grid_z}, 1).contiguous();
37
+
38
+ auto options_int = options.dtype(torch::kInt64);
39
+ auto i = torch::arange(rx - 1, options_int);
40
+ auto j = torch::arange(ry - 1, options_int);
41
+ auto k = torch::arange(rz - 1, options_int);
42
+
43
+ auto idx_grids = torch::meshgrid({i, j, k}, "ij");
44
+ auto vi = idx_grids[0];
45
+ auto vj = idx_grids[1];
46
+ auto vk = idx_grids[2];
47
+
48
+ // Voxel connectivity indexing mapping matching mc.cu
49
+ auto v0 = vi * ry * rz + vj * rz + vk;
50
+ auto v1 = (vi + 1) * ry * rz + vj * rz + vk;
51
+ auto v2 = (vi + 1) * ry * rz + (vj + 1) * rz + vk;
52
+ auto v3 = vi * ry * rz + (vj + 1) * rz + vk;
53
+ auto v4 = vi * ry * rz + vj * rz + (vk + 1);
54
+ auto v5 = (vi + 1) * ry * rz + vj * rz + (vk + 1);
55
+ auto v6 = (vi + 1) * ry * rz + (vj + 1) * rz + (vk + 1);
56
+ auto v7 = vi * ry * rz + (vj + 1) * rz + (vk + 1);
57
+
58
+ auto voxels = torch::stack({v0, v1, v2, v3, v4, v5, v6, v7}, -1).view({-1, 8}).contiguous().to(torch::kInt32);
59
+
60
+ auto i_v = torch::arange(rx, options_int);
61
+ auto j_v = torch::arange(ry, options_int);
62
+ auto k_v = torch::arange(rz, options_int);
63
+ auto idx_grids_v = torch::meshgrid({i_v, j_v, k_v}, "ij");
64
+ auto out_idx_grids = torch::stack({idx_grids_v[0].flatten(), idx_grids_v[1].flatten(), idx_grids_v[2].flatten()}, -1).contiguous();
65
+
66
+ return std::make_tuple(grid_vertices, voxels, out_idx_grids);
67
+ }
68
+
69
+ torch::Tensor compute_grid_normal(torch::Tensor sdf, torch::Tensor grid_vertices, torch::Tensor idx_grids, std::vector<int64_t> res) {
70
+ TORCH_CHECK(res.size() == 3, "res must have 3 elements.");
71
+
72
+ auto i = idx_grids.select(1, 0);
73
+ auto j = idx_grids.select(1, 1);
74
+ auto k = idx_grids.select(1, 2);
75
+
76
+ auto rx = res[0];
77
+ auto ry = res[1];
78
+ auto rz = res[2];
79
+
80
+ auto i_prev = torch::clamp(i - 1, 0, rx - 1);
81
+ auto i_next = torch::clamp(i + 1, 0, rx - 1);
82
+
83
+ auto j_prev = torch::clamp(j - 1, 0, ry - 1);
84
+ auto j_next = torch::clamp(j + 1, 0, ry - 1);
85
+
86
+ auto k_prev = torch::clamp(k - 1, 0, rz - 1);
87
+ auto k_next = torch::clamp(k + 1, 0, rz - 1);
88
+
89
+ auto idx_x_prev = i_prev * ry * rz + j * rz + k;
90
+ auto idx_x_next = i_next * ry * rz + j * rz + k;
91
+
92
+ auto idx_y_prev = i * ry * rz + j_prev * rz + k;
93
+ auto idx_y_next = i * ry * rz + j_next * rz + k;
94
+
95
+ auto idx_z_prev = i * ry * rz + j * rz + k_prev;
96
+ auto idx_z_next = i * ry * rz + j * rz + k_next;
97
+
98
+ auto sdf_flat = sdf.flatten();
99
+
100
+ auto diff_x = sdf_flat.index_select(0, idx_x_next) - sdf_flat.index_select(0, idx_x_prev);
101
+ auto diff_y = sdf_flat.index_select(0, idx_y_next) - sdf_flat.index_select(0, idx_y_prev);
102
+ auto diff_z = sdf_flat.index_select(0, idx_z_next) - sdf_flat.index_select(0, idx_z_prev);
103
+
104
+ auto vx = grid_vertices.select(1, 0);
105
+ auto vy = grid_vertices.select(1, 1);
106
+ auto vz = grid_vertices.select(1, 2);
107
+
108
+ auto dx = vx.index_select(0, idx_x_next) - vx.index_select(0, idx_x_prev);
109
+ auto dy = vy.index_select(0, idx_y_next) - vy.index_select(0, idx_y_prev);
110
+ auto dz = vz.index_select(0, idx_z_next) - vz.index_select(0, idx_z_prev);
111
+
112
+ dx = torch::where(dx == 0, torch::tensor(1e-5f, dx.options()), dx);
113
+ dy = torch::where(dy == 0, torch::tensor(1e-5f, dy.options()), dy);
114
+ dz = torch::where(dz == 0, torch::tensor(1e-5f, dz.options()), dz);
115
+
116
+ auto grad_x = diff_x / dx;
117
+ auto grad_y = diff_y / dy;
118
+ auto grad_z = diff_z / dz;
119
+
120
+ auto normals = torch::stack({grad_x, grad_y, grad_z}, 1);
121
+ return torch::nn::functional::normalize(normals, torch::nn::functional::NormalizeFuncOptions().p(2).dim(1));
122
+ }
123
+
124
+ void bind_ds_grid(py::module_& m) {
125
+ m.def("create_voxel_grid", &create_voxel_grid, "Creates a structured 3D voxel grid.",
126
+ py::arg("grid_min"), py::arg("grid_max"), py::arg("res"), py::arg("device_str") = "cuda");
127
+ m.def("compute_grid_normal", &compute_grid_normal, "Computes surface normals for a grid.",
128
+ py::arg("sdf"), py::arg("grid_vertices"), py::arg("idx_grids"), py::arg("res"));
129
+ }
@@ -0,0 +1,42 @@
1
+ #include <torch/extension.h>
2
+ #include <pybind11/pybind11.h>
3
+ #include "../../ops/chamfer.h"
4
+ #include "../../check.h"
5
+
6
+ namespace py = pybind11;
7
+
8
+ std::tuple<torch::Tensor, torch::Tensor> one_sided_chamfer_distance_wrapper(
9
+ torch::Tensor query_points,
10
+ torch::Tensor reference_points
11
+ ) {
12
+ CHECK_INPUT(query_points);
13
+ CHECK_INPUT(reference_points);
14
+
15
+ uint32_t num_query_points = query_points.size(0);
16
+ uint32_t num_reference_points = reference_points.size(0);
17
+
18
+ const float3* __restrict__ p_query_points = (float3*)query_points.data_ptr<float>();
19
+ const float3* __restrict__ p_reference_points = (float3*)reference_points.data_ptr<float>();
20
+
21
+ torch::Tensor distances = torch::empty({num_query_points}, query_points.options());
22
+ torch::Tensor indices = torch::empty({num_query_points}, query_points.options().dtype(torch::kInt64));
23
+
24
+ float* __restrict__ p_distances = distances.data_ptr<float>();
25
+ int64_t* __restrict__ p_indices = indices.data_ptr<int64_t>();
26
+
27
+ one_sided_chamfer_distance(
28
+ num_query_points,
29
+ p_query_points,
30
+ num_reference_points,
31
+ p_reference_points,
32
+ p_distances,
33
+ p_indices
34
+ );
35
+
36
+ return std::make_tuple(distances, indices);
37
+ }
38
+
39
+ void bind_ops_chamfer(py::module_& m) {
40
+ m.def("one_sided_chamfer_distance", &one_sided_chamfer_distance_wrapper, "One Sided Chamfer Distance",
41
+ py::arg("query_points"), py::arg("reference_points"));
42
+ }
@@ -5,10 +5,11 @@
5
5
 
6
6
  namespace py = pybind11;
7
7
 
8
- std::tuple<torch::Tensor, torch::Tensor> marching_cubes_wrapper(
8
+ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> marching_cubes_wrapper(
9
9
  torch::Tensor grid_vertices,
10
10
  torch::Tensor voxels,
11
11
  torch::Tensor voxel_values,
12
+ std::optional<torch::Tensor> grid_normals,
12
13
  float iso
13
14
  ) {
14
15
  CHECK_INPUT(grid_vertices);
@@ -21,11 +22,18 @@ std::tuple<torch::Tensor, torch::Tensor> marching_cubes_wrapper(
21
22
  const uint32_t* __restrict__ p_voxels = (uint32_t*)voxels.data_ptr<int32_t>();
22
23
  const float* __restrict__ p_voxel_values = voxel_values.data_ptr<float>();
23
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
+
24
31
  return mc::marching_cubes(
25
32
  num_voxels,
26
33
  p_grid_vertices,
27
34
  p_voxels,
28
35
  p_voxel_values,
36
+ p_grid_normals,
29
37
  iso,
30
38
  grid_vertices.options(),
31
39
  voxels.options()
@@ -33,5 +41,7 @@ std::tuple<torch::Tensor, torch::Tensor> marching_cubes_wrapper(
33
41
  }
34
42
 
35
43
  void bind_ops_mc(py::module_& m) {
36
- m.def("marching_cubes", &marching_cubes_wrapper, "Marching Cubes");
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);
37
47
  }
@@ -0,0 +1,73 @@
1
+ #include "../data_structure/kdtree.h"
2
+
3
+ #include <cstdint>
4
+ #include <thrust/device_vector.h>
5
+ #include <thrust/sequence.h>
6
+
7
+ __global__ void one_sided_chamfer_distance_kernel(
8
+ const uint32_t num_query_points,
9
+ const float3 *__restrict__ query_points,
10
+ const uint32_t num_reference_points,
11
+ const float3 *__restrict__ tree_points,
12
+ const int64_t *__restrict__ tree_inds,
13
+ float *__restrict__ distances,
14
+ int64_t *__restrict__ indices)
15
+ {
16
+ uint32_t idx = blockIdx.x * blockDim.x + threadIdx.x;
17
+ if (idx >= num_query_points)
18
+ return;
19
+
20
+ float3 query_point = query_points[idx];
21
+
22
+ float best_dists[MAX_K];
23
+ int64_t best_inds[MAX_K];
24
+
25
+ #pragma unroll
26
+ for (int i = 0; i < MAX_K; i++)
27
+ {
28
+ best_dists[i] = FLT_MAX;
29
+ best_inds[i] = -1;
30
+ }
31
+
32
+ kdtree::query_kdtree_loop(
33
+ query_point,
34
+ num_reference_points,
35
+ tree_points,
36
+ tree_inds,
37
+ 1,
38
+ best_dists,
39
+ best_inds);
40
+
41
+ distances[idx] = best_dists[0];
42
+ indices[idx] = best_inds[0];
43
+ }
44
+
45
+ void one_sided_chamfer_distance(
46
+ const uint32_t num_query_points,
47
+ const float3 *__restrict__ query_points,
48
+ const uint32_t num_reference_points,
49
+ const float3 *__restrict__ reference_points,
50
+ float *__restrict__ distances,
51
+ int64_t *__restrict__ indices)
52
+ {
53
+ thrust::device_vector<float3> cloned_ref_points(reference_points, reference_points + num_reference_points);
54
+ thrust::device_vector<int64_t> reference_indices(num_reference_points);
55
+ thrust::sequence(reference_indices.begin(), reference_indices.end());
56
+
57
+ kdtree::build(
58
+ num_reference_points,
59
+ thrust::raw_pointer_cast(cloned_ref_points.data()),
60
+ thrust::raw_pointer_cast(reference_indices.data()));
61
+
62
+ uint32_t threads = NTHREADS;
63
+ uint32_t blocks = (num_query_points + threads - 1) / threads;
64
+
65
+ one_sided_chamfer_distance_kernel<<<blocks, threads>>>(
66
+ num_query_points,
67
+ query_points,
68
+ num_reference_points,
69
+ thrust::raw_pointer_cast(cloned_ref_points.data()),
70
+ thrust::raw_pointer_cast(reference_indices.data()),
71
+ distances,
72
+ indices);
73
+ }
@@ -0,0 +1,18 @@
1
+ #ifndef CHAMFER_H
2
+ #define CHAMFER_H
3
+
4
+ #include "../constants.h"
5
+ #include "../maths/maths.h"
6
+
7
+ #include <cuda_runtime.h>
8
+
9
+ void one_sided_chamfer_distance(
10
+ const uint32_t num_query_points,
11
+ const float3* __restrict__ query_points,
12
+ const uint32_t num_reference_points,
13
+ const float3* __restrict__ reference_points,
14
+ float* __restrict__ distances,
15
+ int64_t* __restrict__ indices
16
+ );
17
+
18
+ #endif // CHAMFER_H
@@ -1,5 +1,5 @@
1
1
  #include "mc.h"
2
-
2
+ #include "../maths/maths.h"
3
3
  #include <cuda_runtime.h>
4
4
  #include <thrust/sort.h>
5
5
  #include <thrust/scan.h>
@@ -25,7 +25,7 @@ namespace mc
25
25
  struct num_triangles_functor
26
26
  {
27
27
  __device__ uint32_t operator()(const uint8_t code) const {
28
- return trinumTable[code + 1] - trinumTable[code];
28
+ return (trinumTable[code + 1] - trinumTable[code]) / 3;
29
29
  }
30
30
  };
31
31
 
@@ -175,8 +175,10 @@ namespace mc
175
175
  const Edge* unique_edges,
176
176
  const float3* grid_vertices,
177
177
  const float* values,
178
+ const float3* grid_normals,
178
179
  const float iso,
179
- float3* out_verts
180
+ float3* out_verts,
181
+ float3* out_normals
180
182
  )
181
183
  {
182
184
  uint32_t v_idx = blockIdx.x * blockDim.x + threadIdx.x;
@@ -196,28 +198,42 @@ namespace mc
196
198
 
197
199
  // 3. Interpolate (Differentiable formula)
198
200
  float3 p;
201
+ float3 n;
202
+ bool has_normals = grid_normals != nullptr;
203
+ float3 n0 = has_normals ? grid_normals[v0_idx] : make_float3(0, 0, 0);
204
+ float3 n1 = has_normals ? grid_normals[v1_idx] : make_float3(0, 0, 0);
199
205
 
200
206
  if (fabsf(iso - val0) < EPS)
201
207
  {
202
208
  p = p0;
209
+ if (has_normals) n = n0;
203
210
  }
204
211
  else if (fabsf(iso - val1) < EPS)
205
212
  {
206
213
  p = p1;
214
+ if (has_normals) n = n1;
207
215
  }
208
216
  else if (fabsf(val0 - val1) < EPS)
209
217
  {
210
218
  p = p0;
219
+ if (has_normals) n = n0;
211
220
  }
212
221
  else
213
222
  {
214
223
  float t = (val1 != val0) ? fmaxf(0.0f, fminf(1.0f, (iso - val0) / (val1 - val0))) : 0.5f;
215
- p.x = p0.x + (p1.x - p0.x) * t;
216
- p.y = p0.y + (p1.y - p0.y) * t;
217
- p.z = p0.z + (p1.z - p0.z) * t;
224
+
225
+ p = p0 + (p1 - p0) * t;
226
+
227
+ if (has_normals) {
228
+ n = n0 + (n1 - n0) * t;
229
+ n = maths::normalize(n);
230
+ }
218
231
  }
219
232
 
220
233
  out_verts[v_idx] = p;
234
+ if (has_normals) {
235
+ out_normals[v_idx] = n;
236
+ }
221
237
  }
222
238
 
223
239
  void compute_active_voxels(
@@ -340,15 +356,17 @@ namespace mc
340
356
  const Edge* unique_edges,
341
357
  const float3* grid_vertices,
342
358
  const float* values,
359
+ const float3* grid_normals,
343
360
  const float iso,
344
- float3* out_verts
361
+ float3* out_verts,
362
+ float3* out_normals
345
363
  )
346
364
  {
347
365
  if (num_unique_edges == 0) return;
348
366
  int block_size = NTHREADS;
349
367
  int grid_size = (num_unique_edges + block_size - 1) / block_size;
350
368
  interpolate_vertices_kernel<<<grid_size, block_size>>>(
351
- num_unique_edges, unique_edges, grid_vertices, values, iso, out_verts);
369
+ num_unique_edges, unique_edges, grid_vertices, values, grid_normals, iso, out_verts, out_normals);
352
370
  }
353
371
 
354
372
  void compute_number_triangles(
@@ -417,11 +435,12 @@ namespace mc
417
435
  num_active_voxels, used_voxel_codes, voxel_edge_to_vert_idx, voxel_triangle_prefix_sums, out_triangles);
418
436
  }
419
437
 
420
- std::tuple<torch::Tensor, torch::Tensor> marching_cubes(
438
+ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> marching_cubes(
421
439
  const uint32_t num_voxels,
422
440
  const float3* __restrict__ grid_vertices,
423
441
  const uint32_t* __restrict__ voxels,
424
442
  const float* __restrict__ voxel_values,
443
+ const float3* __restrict__ grid_normals,
425
444
  const float iso,
426
445
  torch::TensorOptions vert_options,
427
446
  torch::TensorOptions tri_options
@@ -437,9 +456,12 @@ namespace mc
437
456
 
438
457
  if (num_active_voxels == 0) {
439
458
  CHECK_CUDA_INTERNAL(cudaFree(voxel_codes));
459
+ std::optional<torch::Tensor> out_n = std::nullopt;
460
+ if (grid_normals != nullptr) out_n = torch::empty({0, 3}, vert_options);
440
461
  return std::make_tuple(
441
462
  torch::empty({0, 3}, vert_options),
442
- torch::empty({0, 3}, tri_options)
463
+ torch::empty({0, 3}, tri_options),
464
+ out_n
443
465
  );
444
466
  }
445
467
 
@@ -463,7 +485,15 @@ namespace mc
463
485
  torch::Tensor out_vertices = torch::empty({out_num_vertices, 3}, vert_options);
464
486
  float3* __restrict__ p_out_vertices = (float3*)out_vertices.data_ptr<float>();
465
487
 
466
- interpolate_vertices(out_num_vertices, unique_edges, grid_vertices, voxel_values, iso, p_out_vertices);
488
+ std::optional<torch::Tensor> out_normals_opt = std::nullopt;
489
+ float3* __restrict__ p_out_normals = nullptr;
490
+ if (grid_normals != nullptr) {
491
+ torch::Tensor out_normals = torch::empty({out_num_vertices, 3}, vert_options);
492
+ p_out_normals = (float3*)out_normals.data_ptr<float>();
493
+ out_normals_opt = out_normals;
494
+ }
495
+
496
+ interpolate_vertices(out_num_vertices, unique_edges, grid_vertices, voxel_values, grid_normals, iso, p_out_vertices, p_out_normals);
467
497
 
468
498
  uint32_t out_num_triangles;
469
499
  uint32_t *__restrict__ voxel_triangle_prefix_sums;
@@ -484,6 +514,6 @@ namespace mc
484
514
  CHECK_CUDA_INTERNAL(cudaFree(voxel_edge_to_vert_idx));
485
515
  CHECK_CUDA_INTERNAL(cudaFree(voxel_triangle_prefix_sums));
486
516
 
487
- return std::make_tuple(out_vertices, out_triangles);
517
+ return std::make_tuple(out_vertices, out_triangles, out_normals_opt);
488
518
  }
489
519
  }
@@ -7,14 +7,15 @@
7
7
  #include "../constants.h"
8
8
  #include "../primitive/edge.h"
9
9
 
10
- #include <stdint.h>
10
+ #include <optional>
11
11
 
12
12
  namespace mc{
13
- std::tuple<torch::Tensor, torch::Tensor> marching_cubes(
13
+ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>> marching_cubes(
14
14
  const uint32_t num_voxels,
15
15
  const float3* __restrict__ grid_vertices,
16
16
  const uint32_t* __restrict__ voxels,
17
17
  const float* __restrict__ voxel_values,
18
+ const float3* __restrict__ grid_normals,
18
19
  const float iso,
19
20
  torch::TensorOptions vert_options,
20
21
  torch::TensorOptions tri_options
@@ -17,6 +17,7 @@ void bind_ds_grid(py::module_& m);
17
17
  void bind_creation_triangle_creation(py::module_& m);
18
18
 
19
19
  void bind_ops_mc(py::module_& m);
20
+ void bind_ops_chamfer(py::module_& m);
20
21
 
21
22
  PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
22
23
  m.doc() = "Geocutool Python bindings";
@@ -35,4 +36,5 @@ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
35
36
  bind_creation_triangle_creation(m);
36
37
 
37
38
  bind_ops_mc(m);
39
+ bind_ops_chamfer(m);
38
40
  }
@@ -0,0 +1,11 @@
1
+ from .marching_cubes import marching_cubes
2
+ from .grid import create_voxel_grid, compute_grid_normal
3
+ from .distance import one_sided_chamfer_distance, chamfer_distance
4
+
5
+ __all__ = [
6
+ "marching_cubes",
7
+ "create_voxel_grid",
8
+ "compute_grid_normal",
9
+ "one_sided_chamfer_distance",
10
+ "chamfer_distance"
11
+ ]
@@ -0,0 +1,28 @@
1
+ import torch
2
+ import conquer3d._C as _C
3
+
4
+ def one_sided_chamfer_distance(query_points: torch.Tensor, reference_points: torch.Tensor, squared: bool = True):
5
+ assert query_points.is_cuda and reference_points.is_cuda, "Points must be on CUDA"
6
+ assert query_points.dtype == torch.float32 and reference_points.dtype == torch.float32, "Points must be float32"
7
+ assert query_points.shape[1] == 3 and reference_points.shape[1] == 3, "Points must be 3D"
8
+
9
+ query_points_c = query_points.contiguous()
10
+ reference_points_c = reference_points.contiguous()
11
+
12
+ distances, indices = _C.one_sided_chamfer_distance(query_points_c, reference_points_c)
13
+
14
+ if not squared:
15
+ # clamp to avoid NaN from floating point inaccuracies
16
+ distances = torch.sqrt(torch.clamp(distances, min=1e-12))
17
+
18
+ return distances, indices
19
+
20
+ def chamfer_distance(x: torch.Tensor, y: torch.Tensor, squared: bool = True, return_indices: bool = False):
21
+ dist_x_to_y, idx_x_to_y = one_sided_chamfer_distance(x, y, squared=squared)
22
+ dist_y_to_x, idx_y_to_x = one_sided_chamfer_distance(y, x, squared=squared)
23
+
24
+ loss = dist_x_to_y.mean() + dist_y_to_x.mean()
25
+
26
+ if return_indices:
27
+ return loss, idx_x_to_y, idx_y_to_x
28
+ return loss
@@ -7,7 +7,7 @@ def create_voxel_grid(
7
7
  grid_max: Union[List[float], Tuple[float, float, float]],
8
8
  res: Union[List[int], Tuple[int, int, int]],
9
9
  device: str = "cuda"
10
- ) -> Tuple[torch.Tensor, torch.Tensor]:
10
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
11
11
  """
12
12
  Creates a structured 3D voxel grid efficiently.
13
13
 
@@ -18,9 +18,30 @@ def create_voxel_grid(
18
18
  device (str, optional): Target device for the tensors (e.g., "cuda" or "cpu"). Defaults to "cuda".
19
19
 
20
20
  Returns:
21
- Tuple[torch.Tensor, torch.Tensor]:
21
+ Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
22
22
  - grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing all grid coordinates.
23
23
  - voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices
24
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.
25
26
  """
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))
@@ -1,5 +1,5 @@
1
1
  import torch
2
- from typing import Tuple
2
+ from typing import Tuple, Optional
3
3
 
4
4
  # Explicitly import the compiled CMake target
5
5
  from .._C import marching_cubes as marching_cubes_func
@@ -8,8 +8,9 @@ def marching_cubes(
8
8
  grid_vertices: torch.Tensor,
9
9
  voxels: torch.Tensor,
10
10
  voxel_values: torch.Tensor,
11
+ grid_normals: Optional[torch.Tensor] = None,
11
12
  iso: float = 0.0
12
- ) -> Tuple[torch.Tensor, torch.Tensor]:
13
+ ) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
13
14
  """
14
15
  Executes the Marching Cubes algorithm to extract an isosurface from a voxel grid.
15
16
 
@@ -17,12 +18,14 @@ def marching_cubes(
17
18
  grid_vertices (torch.Tensor): (V, 3) tensor of global vertex positions.
18
19
  voxels (torch.Tensor): (N, 8) tensor of voxel corner indices mapping to `grid_vertices`.
19
20
  voxel_values (torch.Tensor): (V,) tensor of SDF/scalar values at each vertex.
21
+ grid_normals (torch.Tensor, optional): (V, 3) optional tensor of SDF normals at each vertex. Defaults to None.
20
22
  iso (float, optional): The isosurface extraction threshold. Defaults to 0.0.
21
23
 
22
24
  Returns:
23
- Tuple[torch.Tensor, torch.Tensor]:
25
+ Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
24
26
  - vertices (torch.Tensor): (M, 3) tensor of extracted mesh vertices.
25
27
  - triangles (torch.Tensor): (T, 3) tensor of extracted mesh triangle indices.
28
+ - normals (torch.Tensor, optional): (M, 3) tensor of extracted mesh vertex normals, if `grid_normals` was provided.
26
29
  """
27
30
  if not all(t.is_cuda for t in [grid_vertices, voxels, voxel_values]):
28
31
  raise ValueError("All input tensors must be CUDA tensors.")
@@ -30,13 +33,21 @@ def marching_cubes(
30
33
  grid_vertices_c = grid_vertices.contiguous().to(torch.float32)
31
34
  voxels_c = voxels.contiguous().to(torch.int32)
32
35
  voxel_values_c = voxel_values.contiguous().to(torch.float32)
36
+
37
+ grid_normals_c = None
38
+ if grid_normals is not None:
39
+ if not grid_normals.is_cuda:
40
+ raise ValueError("grid_normals must be a CUDA tensor.")
41
+ grid_normals_c = grid_normals.contiguous().to(torch.float32)
42
+
33
43
  iso_c = float(iso)
34
44
 
35
- vertices, triangles = marching_cubes_func(
45
+ vertices, triangles, out_normals = marching_cubes_func(
36
46
  grid_vertices_c,
37
47
  voxels_c,
38
48
  voxel_values_c,
49
+ grid_normals_c,
39
50
  iso_c
40
51
  )
41
52
 
42
- return vertices, triangles
53
+ return vertices, triangles, out_normals
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conquer3d
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Geometric Cuda Tool Box
5
5
  Author-email: Do Hoang Khoi <khoido8899@gmail.com>
6
6
  License-Expression: MIT
@@ -56,7 +56,7 @@ pip install torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 --index-url https
56
56
  pip install pybind11-stubgen
57
57
  pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
58
58
 
59
- pip install plotly open3d jupyter trimesh
59
+ pip install plotly open3d jupyter trimesh point-cloud-utils
60
60
  ```
61
61
 
62
62
  # Development
@@ -25,6 +25,7 @@ conquer3d/csrc/binds/data_structure/kdtree.cpp
25
25
  conquer3d/csrc/binds/data_structure/mesh_bvh.cpp
26
26
  conquer3d/csrc/binds/data_structure/pgs_bvh.cpp
27
27
  conquer3d/csrc/binds/data_structure/triangle_mesh.cpp
28
+ conquer3d/csrc/binds/ops/chamfer.cpp
28
29
  conquer3d/csrc/binds/ops/mc.cpp
29
30
  conquer3d/csrc/binds/primitive/gs.cpp
30
31
  conquer3d/csrc/binds/primitive/pgs.cpp
@@ -48,6 +49,8 @@ conquer3d/csrc/maths/f4x1.h
48
49
  conquer3d/csrc/maths/f4x4.h
49
50
  conquer3d/csrc/maths/maths.h
50
51
  conquer3d/csrc/maths/ops.h
52
+ conquer3d/csrc/ops/chamfer.cu
53
+ conquer3d/csrc/ops/chamfer.h
51
54
  conquer3d/csrc/ops/mc.cu
52
55
  conquer3d/csrc/ops/mc.h
53
56
  conquer3d/csrc/ops/mc_data.h
@@ -65,6 +68,7 @@ conquer3d/csrc/primitive/ray.h
65
68
  conquer3d/csrc/primitive/triangles.h
66
69
  conquer3d/data_structure/__init__.py
67
70
  conquer3d/ops/__init__.py
71
+ conquer3d/ops/distance.py
68
72
  conquer3d/ops/grid.py
69
73
  conquer3d/ops/marching_cubes.py
70
74
  conquer3d/primitive/__init__.py
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "conquer3d"
12
- version = "0.2.8"
12
+ version = "0.2.9"
13
13
  description = "Geometric Cuda Tool Box"
14
14
  readme = "README.md"
15
15
  license = "MIT"
@@ -1,66 +0,0 @@
1
- #include <torch/extension.h>
2
- #include <pybind11/pybind11.h>
3
- #include <pybind11/stl.h>
4
- #include <tuple>
5
- #include <string>
6
- #include <vector>
7
-
8
- namespace py = pybind11;
9
-
10
- std::tuple<torch::Tensor, torch::Tensor> create_voxel_grid(
11
- std::vector<float> grid_min,
12
- std::vector<float> grid_max,
13
- std::vector<int64_t> res,
14
- std::string device_str
15
- ) {
16
- TORCH_CHECK(grid_min.size() == 3, "grid_min must have 3 elements.");
17
- TORCH_CHECK(grid_max.size() == 3, "grid_max must have 3 elements.");
18
- TORCH_CHECK(res.size() == 3, "res must have 3 elements.");
19
-
20
- c10::Device device(device_str);
21
- auto options = torch::TensorOptions().device(device).dtype(torch::kFloat32);
22
-
23
- int64_t rx = res[0];
24
- int64_t ry = res[1];
25
- int64_t rz = res[2];
26
-
27
- auto x = torch::linspace(grid_min[0], grid_max[0], rx, options);
28
- auto y = torch::linspace(grid_min[1], grid_max[1], ry, options);
29
- auto z = torch::linspace(grid_min[2], grid_max[2], rz, options);
30
-
31
- auto grids = torch::meshgrid({x, y, z}, "ij");
32
- auto grid_x = grids[0].flatten();
33
- auto grid_y = grids[1].flatten();
34
- auto grid_z = grids[2].flatten();
35
-
36
- auto grid_vertices = torch::stack({grid_x, grid_y, grid_z}, 1).contiguous();
37
-
38
- auto options_int = options.dtype(torch::kInt64);
39
- auto i = torch::arange(rx - 1, options_int);
40
- auto j = torch::arange(ry - 1, options_int);
41
- auto k = torch::arange(rz - 1, options_int);
42
-
43
- auto idx_grids = torch::meshgrid({i, j, k}, "ij");
44
- auto vi = idx_grids[0];
45
- auto vj = idx_grids[1];
46
- auto vk = idx_grids[2];
47
-
48
- // Voxel connectivity indexing mapping matching mc.cu
49
- auto v0 = vi * ry * rz + vj * rz + vk;
50
- auto v1 = (vi + 1) * ry * rz + vj * rz + vk;
51
- auto v2 = (vi + 1) * ry * rz + (vj + 1) * rz + vk;
52
- auto v3 = vi * ry * rz + (vj + 1) * rz + vk;
53
- auto v4 = vi * ry * rz + vj * rz + (vk + 1);
54
- auto v5 = (vi + 1) * ry * rz + vj * rz + (vk + 1);
55
- auto v6 = (vi + 1) * ry * rz + (vj + 1) * rz + (vk + 1);
56
- auto v7 = vi * ry * rz + (vj + 1) * rz + (vk + 1);
57
-
58
- auto voxels = torch::stack({v0, v1, v2, v3, v4, v5, v6, v7}, -1).view({-1, 8}).contiguous().to(torch::kInt32);
59
-
60
- return std::make_tuple(grid_vertices, voxels);
61
- }
62
-
63
- void bind_ds_grid(py::module_& m) {
64
- m.def("create_voxel_grid", &create_voxel_grid, "Creates a structured 3D voxel grid.",
65
- py::arg("grid_min"), py::arg("grid_max"), py::arg("res"), py::arg("device_str") = "cuda");
66
- }
@@ -1,7 +0,0 @@
1
- from .marching_cubes import marching_cubes
2
- from .grid import create_voxel_grid
3
-
4
- __all__ = [
5
- "marching_cubes",
6
- "create_voxel_grid"
7
- ]
File without changes
File without changes
File without changes
File without changes
File without changes