conquer3d 0.2.6__tar.gz → 0.2.7__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 (69) hide show
  1. {conquer3d-0.2.6/conquer3d.egg-info → conquer3d-0.2.7}/PKG-INFO +1 -1
  2. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/_C.pyi +10 -0
  3. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/_C.so +0 -0
  4. conquer3d-0.2.7/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +238 -0
  5. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +89 -2
  6. conquer3d-0.2.7/conquer3d/csrc/data_structure/mesh_bvh.cu +547 -0
  7. conquer3d-0.2.7/conquer3d/csrc/data_structure/mesh_bvh.h +112 -0
  8. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/triangle_mesh.cu +151 -0
  9. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/triangle_mesh.h +29 -1
  10. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/triangles.h +13 -0
  11. {conquer3d-0.2.6 → conquer3d-0.2.7/conquer3d.egg-info}/PKG-INFO +1 -1
  12. {conquer3d-0.2.6 → conquer3d-0.2.7}/pyproject.toml +1 -1
  13. conquer3d-0.2.6/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -44
  14. conquer3d-0.2.6/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -413
  15. conquer3d-0.2.6/conquer3d/csrc/data_structure/mesh_bvh.h +0 -41
  16. {conquer3d-0.2.6 → conquer3d-0.2.7}/LICENSE +0 -0
  17. {conquer3d-0.2.6 → conquer3d-0.2.7}/MANIFEST.in +0 -0
  18. {conquer3d-0.2.6 → conquer3d-0.2.7}/README.md +0 -0
  19. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/__init__.py +0 -0
  20. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/creation/__init__.py +0 -0
  21. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/creation/triangle_creation.py +0 -0
  22. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
  23. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
  24. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
  25. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
  26. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
  27. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
  28. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
  29. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/check.h +0 -0
  30. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/constants.h +0 -0
  31. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/creation/triangle_creation.h +0 -0
  32. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/bvh.cu +0 -0
  33. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/bvh.h +0 -0
  34. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
  35. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
  36. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/kdtree.h +0 -0
  37. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
  38. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/zcurve.h +0 -0
  39. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/f2x2.h +0 -0
  40. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/f3x1.h +0 -0
  41. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/f3x3.h +0 -0
  42. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/f3x4.h +0 -0
  43. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/f4x1.h +0 -0
  44. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/f4x4.h +0 -0
  45. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/maths.h +0 -0
  46. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/maths/ops.h +0 -0
  47. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/aabb.h +0 -0
  48. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/edge.h +0 -0
  49. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs.cu +0 -0
  50. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs.h +0 -0
  51. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
  52. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
  53. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs.cu +0 -0
  54. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs.h +0 -0
  55. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
  56. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
  57. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/primitive/ray.h +0 -0
  58. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/csrc/pybind.cpp +0 -0
  59. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/data_structure/__init__.py +0 -0
  60. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/primitive/__init__.py +0 -0
  61. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/primitive/gs.py +0 -0
  62. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d/primitive/pgs.py +0 -0
  63. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d.egg-info/SOURCES.txt +0 -0
  64. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d.egg-info/dependency_links.txt +0 -0
  65. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d.egg-info/not-zip-safe +0 -0
  66. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d.egg-info/requires.txt +0 -0
  67. {conquer3d-0.2.6 → conquer3d-0.2.7}/conquer3d.egg-info/top_level.txt +0 -0
  68. {conquer3d-0.2.6 → conquer3d-0.2.7}/setup.cfg +0 -0
  69. {conquer3d-0.2.6 → conquer3d-0.2.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conquer3d
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Geometric Cuda Tool Box
5
5
  Author-email: Do Hoang Khoi <khoido8899@gmail.com>
6
6
  License-Expression: MIT
@@ -57,6 +57,10 @@ class MeshBVH(BVH):
57
57
  """
58
58
  Construct MeshBVH from Triangle AABBs.
59
59
  """
60
+ def build_winding_data(self, vertices: torch.Tensor, triangles: torch.Tensor) -> None:
61
+ """
62
+ Computes the hierarchical area-weighted constants for Fast Winding Number queries.
63
+ """
60
64
  def get_ray_intersection(self, ray_origins: torch.Tensor, ray_dirs: torch.Tensor, vertices: torch.Tensor, triangles: torch.Tensor, return_distance: bool = False) -> typing.Any:
61
65
  """
62
66
  Find all ray-triangle intersections. Returns (ray_ids, triangle_ids, intersect_points, [distances])
@@ -97,6 +101,8 @@ class TriangleMesh:
97
101
  ...
98
102
  def compute_triangle_normals(self) -> None:
99
103
  ...
104
+ def compute_vertex_normals(self) -> None:
105
+ ...
100
106
  def compute_vertices_to_triangle_map(self) -> None:
101
107
  ...
102
108
  def get_edges_to_triangle_map(self) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
@@ -129,6 +135,10 @@ class TriangleMesh:
129
135
  """
130
136
  def remove_triangles_by_mask(self, keep_mask: torch.Tensor) -> None:
131
137
  ...
138
+ def sample_points(self, num_points: int, uniform: bool = False, return_normals: bool = False, return_colors: bool = False, use_triangle_normal: bool = True) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor | None, torch.Tensor | None]:
139
+ """
140
+ Sample points on the mesh
141
+ """
132
142
  @property
133
143
  def bvh(self) -> MeshBVH:
134
144
  ...
@@ -0,0 +1,238 @@
1
+ #include <torch/extension.h>
2
+ #include "../../data_structure/mesh_bvh.h"
3
+ #include <pybind11/pybind11.h>
4
+ #include "../../check.h"
5
+
6
+ namespace py = pybind11;
7
+
8
+ torch::Tensor MeshBVH::get_self_intersection(
9
+ const torch::Tensor &vertices,
10
+ const torch::Tensor &triangles)
11
+ {
12
+ // 1. Broad-phase AABB overlap query
13
+ auto [query_ids, object_ids] = this->query_self();
14
+
15
+ int num_pairs = query_ids.size(0);
16
+ if (num_pairs == 0)
17
+ {
18
+ return torch::empty({0, 2}, torch::TensorOptions().dtype(torch::kInt64).device(query_ids.device()));
19
+ }
20
+
21
+ // 2. Narrow-phase intersection
22
+ auto options_i64 = torch::TensorOptions().dtype(torch::kInt64).device(query_ids.device());
23
+ torch::Tensor out_query_ids = torch::empty({num_pairs}, options_i64);
24
+ torch::Tensor out_object_ids = torch::empty({num_pairs}, options_i64);
25
+ torch::Tensor valid_counter = torch::zeros({1}, options_i64);
26
+
27
+ mesh_bvh::filter_self_intersections(
28
+ num_pairs,
29
+ query_ids.data_ptr<int64_t>(),
30
+ object_ids.data_ptr<int64_t>(),
31
+ (const float3 *)vertices.data_ptr<float>(),
32
+ (const int3 *)triangles.data_ptr<int>(),
33
+ out_query_ids.data_ptr<int64_t>(),
34
+ out_object_ids.data_ptr<int64_t>(),
35
+ valid_counter.data_ptr<int64_t>());
36
+
37
+ int64_t h_valid_counter = valid_counter.item<int64_t>();
38
+
39
+ if (h_valid_counter == 0)
40
+ {
41
+ return torch::empty({0, 2}, options_i64);
42
+ }
43
+
44
+ out_query_ids = out_query_ids.slice(0, 0, h_valid_counter).unsqueeze(1);
45
+ out_object_ids = out_object_ids.slice(0, 0, h_valid_counter).unsqueeze(1);
46
+
47
+ return torch::cat({out_query_ids, out_object_ids}, 1);
48
+ }
49
+
50
+ bool MeshBVH::is_self_intersection(
51
+ const torch::Tensor &vertices,
52
+ const torch::Tensor &triangles)
53
+ {
54
+ torch::Tensor pairs = this->get_self_intersection(vertices, triangles);
55
+ return pairs.size(0) > 0;
56
+ }
57
+
58
+ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> MeshBVH::get_ray_intersection(
59
+ const torch::Tensor &ray_origins,
60
+ const torch::Tensor &ray_dirs,
61
+ const torch::Tensor &vertices,
62
+ const torch::Tensor &triangles,
63
+ bool return_distance)
64
+ {
65
+ // 1. Broad-phase BVH query
66
+ auto [query_ids, object_ids] = this->query_ray(ray_origins, ray_dirs);
67
+
68
+ int num_pairs = query_ids.size(0);
69
+ auto options_i64 = torch::TensorOptions().dtype(torch::kInt64).device(query_ids.device());
70
+ auto options_f32 = torch::TensorOptions().dtype(torch::kFloat32).device(query_ids.device());
71
+
72
+ if (num_pairs == 0)
73
+ {
74
+ return std::make_tuple(
75
+ torch::empty({0}, options_i64),
76
+ torch::empty({0}, options_i64),
77
+ torch::empty({0, 3}, options_f32),
78
+ torch::empty({0}, options_f32));
79
+ }
80
+
81
+ // 2. Narrow-phase intersection
82
+ torch::Tensor out_query_ids = torch::empty({num_pairs}, options_i64);
83
+ torch::Tensor out_object_ids = torch::empty({num_pairs}, options_i64);
84
+ torch::Tensor out_intersect_pts = torch::empty({num_pairs, 3}, options_f32);
85
+
86
+ torch::Tensor out_distances;
87
+ if (return_distance)
88
+ {
89
+ out_distances = torch::empty({num_pairs}, options_f32);
90
+ }
91
+ else
92
+ {
93
+ out_distances = torch::empty({0}, options_f32);
94
+ }
95
+
96
+ torch::Tensor valid_counter = torch::zeros({1}, options_i64);
97
+
98
+ mesh_bvh::filter_ray_triangle_intersections(
99
+ num_pairs,
100
+ query_ids.data_ptr<int64_t>(),
101
+ object_ids.data_ptr<int64_t>(),
102
+ (const float3 *)ray_origins.data_ptr<float>(),
103
+ (const float3 *)ray_dirs.data_ptr<float>(),
104
+ (const float3 *)vertices.data_ptr<float>(),
105
+ (const int3 *)triangles.data_ptr<int>(),
106
+ out_query_ids.data_ptr<int64_t>(),
107
+ out_object_ids.data_ptr<int64_t>(),
108
+ (float3 *)out_intersect_pts.data_ptr<float>(),
109
+ return_distance ? out_distances.data_ptr<float>() : nullptr,
110
+ return_distance,
111
+ valid_counter.data_ptr<int64_t>());
112
+
113
+ int64_t h_valid_counter = valid_counter.item<int64_t>();
114
+
115
+ if (h_valid_counter == 0)
116
+ {
117
+ return std::make_tuple(
118
+ torch::empty({0}, options_i64),
119
+ torch::empty({0}, options_i64),
120
+ torch::empty({0, 3}, options_f32),
121
+ torch::empty({0}, options_f32));
122
+ }
123
+
124
+ return std::make_tuple(
125
+ out_query_ids.slice(0, 0, h_valid_counter),
126
+ out_object_ids.slice(0, 0, h_valid_counter),
127
+ out_intersect_pts.slice(0, 0, h_valid_counter),
128
+ return_distance ? out_distances.slice(0, 0, h_valid_counter) : out_distances);
129
+ }
130
+
131
+ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> MeshBVH::query_point(
132
+ const torch::Tensor &query_points,
133
+ const torch::Tensor &vertices,
134
+ const torch::Tensor &triangles,
135
+ bool return_sdf,
136
+ bool return_prj_pts,
137
+ int sign_mode)
138
+ {
139
+ if (sign_mode != 0 && sign_mode != 1)
140
+ {
141
+ throw std::runtime_error("sign_mode must be 0 (ray casting) or 1 (fast winding number)");
142
+ }
143
+
144
+ if (sign_mode == 1 && !this->has_winding_data)
145
+ {
146
+ this->build_winding_data(vertices, triangles);
147
+ }
148
+
149
+ int num_queries = query_points.size(0);
150
+ int num_objects = this->object_ids.size(0);
151
+
152
+ auto options_i64 = torch::TensorOptions().dtype(torch::kInt64).device(query_points.device());
153
+ auto options_f32 = torch::TensorOptions().dtype(torch::kFloat32).device(query_points.device());
154
+
155
+ torch::Tensor out_query_ids = torch::empty({num_queries}, options_i64);
156
+ torch::Tensor out_object_ids = torch::empty({num_queries}, options_i64);
157
+ torch::Tensor out_projected_pts;
158
+ if (return_prj_pts)
159
+ {
160
+ out_projected_pts = torch::empty({num_queries, 3}, options_f32);
161
+ }
162
+ else
163
+ {
164
+ out_projected_pts = torch::empty({0, 3}, options_f32);
165
+ }
166
+ torch::Tensor out_distances = torch::empty({num_queries}, options_f32);
167
+
168
+ mesh_bvh::query_point_mesh_bvh(
169
+ num_queries,
170
+ num_objects,
171
+ (const float3 *)query_points.data_ptr<float>(),
172
+ (const float3 *)vertices.data_ptr<float>(),
173
+ (const int3 *)triangles.data_ptr<int>(),
174
+ (const float3 *)this->aabb_mins.data_ptr<float>(),
175
+ (const float3 *)this->aabb_maxs.data_ptr<float>(),
176
+ (const int2 *)this->bvh_children.data_ptr<int>(),
177
+ this->object_ids.data_ptr<int>(),
178
+ this->has_winding_data ? (const WindingData *)this->winding_data.data_ptr() : nullptr,
179
+ out_query_ids.data_ptr<int64_t>(),
180
+ out_object_ids.data_ptr<int64_t>(),
181
+ return_prj_pts ? (float3 *)out_projected_pts.data_ptr<float>() : nullptr,
182
+ out_distances.data_ptr<float>(),
183
+ return_sdf,
184
+ return_prj_pts,
185
+ sign_mode);
186
+
187
+ return std::make_tuple(out_query_ids, out_object_ids, out_projected_pts, out_distances);
188
+ }
189
+
190
+ void MeshBVH::build_winding_data(
191
+ const torch::Tensor &vertices,
192
+ const torch::Tensor &triangles)
193
+ {
194
+ int num_objects = this->object_ids.size(0);
195
+ int num_nodes = num_objects * 2 - 1;
196
+
197
+ auto options = torch::TensorOptions().dtype(torch::kByte).device(vertices.device());
198
+ this->winding_data = torch::empty({num_nodes * (int64_t)sizeof(WindingData)}, options);
199
+
200
+ mesh_bvh::bottom_up_winding_data(
201
+ num_objects,
202
+ this->object_ids.data_ptr<int>(),
203
+ (const float3 *)vertices.data_ptr<float>(),
204
+ (const int3 *)triangles.data_ptr<int>(),
205
+ this->bvh_parents.data_ptr<int>(),
206
+ (const int2 *)this->bvh_children.data_ptr<int>(),
207
+ (WindingData *)this->winding_data.data_ptr());
208
+
209
+ cudaDeviceSynchronize();
210
+ this->has_winding_data = true;
211
+ }
212
+
213
+ void bind_ds_mesh_bvh(py::module_ &m)
214
+ {
215
+ py::class_<MeshBVH, BVH>(m, "MeshBVH")
216
+ .def(py::init<const torch::Tensor &, const torch::Tensor &>(),
217
+ py::arg("in_aabb_mins"),
218
+ py::arg("in_aabb_maxs"),
219
+ "Construct MeshBVH from Triangle AABBs.")
220
+ .def("get_self_intersection", &MeshBVH::get_self_intersection,
221
+ py::arg("vertices"),
222
+ py::arg("triangles"),
223
+ "Find all self-intersecting triangle pairs")
224
+ .def("is_self_intersection", &MeshBVH::is_self_intersection,
225
+ py::arg("vertices"),
226
+ py::arg("triangles"),
227
+ "Check if there are any self-intersecting triangle pairs")
228
+ .def("get_ray_intersection", [](MeshBVH &self, const torch::Tensor &ray_origins, const torch::Tensor &ray_dirs, const torch::Tensor &vertices, const torch::Tensor &triangles, bool return_distance) -> py::object
229
+ {
230
+ auto result = self.get_ray_intersection(ray_origins, ray_dirs, vertices, triangles, return_distance);
231
+ if (return_distance) {
232
+ return py::cast(result);
233
+ } else {
234
+ return py::cast(std::make_tuple(std::get<0>(result), std::get<1>(result), std::get<2>(result)));
235
+ } }, py::arg("ray_origins"), py::arg("ray_dirs"), py::arg("vertices"), py::arg("triangles"), py::arg("return_distance") = false, "Find all ray-triangle intersections. Returns (ray_ids, triangle_ids, intersect_points, [distances])")
236
+ .def("query_point", &MeshBVH::query_point, py::arg("query_points"), py::arg("vertices"), py::arg("triangles"), py::arg("return_sdf") = false, py::arg("return_prj_pts") = true, py::arg("sign_mode") = 0, "Find the closest triangle to each query point. Returns (query_ids, triangle_ids, projected_points, distances)")
237
+ .def("build_winding_data", &MeshBVH::build_winding_data, py::arg("vertices"), py::arg("triangles"), "Computes the hierarchical area-weighted constants for Fast Winding Number queries.");
238
+ }
@@ -50,11 +50,33 @@ void TriangleMesh::compute_triangle_normals()
50
50
  this->triangle_normals = torch::empty({static_cast<int64_t>(this->num_triangles), 3}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
51
51
  triangle_mesh::compute_triangle_normals(
52
52
  this->num_triangles,
53
- reinterpret_cast<float3 *>(this->vertices.data_ptr<float>()),
54
- reinterpret_cast<int3 *>(this->triangles.data_ptr<int>()),
53
+ reinterpret_cast<const float3 *>(this->vertices.data_ptr<float>()),
54
+ reinterpret_cast<const int3 *>(this->triangles.data_ptr<int>()),
55
55
  reinterpret_cast<float3 *>(this->triangle_normals.data_ptr<float>()));
56
56
  }
57
57
 
58
+ void TriangleMesh::compute_vertex_normals()
59
+ {
60
+ uint32_t num_vertices = this->vertices.size(0);
61
+ this->vertex_normals = torch::zeros({static_cast<int64_t>(num_vertices), 3}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
62
+
63
+ triangle_mesh::compute_vertex_normals(
64
+ num_vertices,
65
+ this->num_triangles,
66
+ reinterpret_cast<const int3 *>(this->triangles.data_ptr<int>()),
67
+ reinterpret_cast<const float3 *>(this->get_triangle_normals().data_ptr<float>()),
68
+ reinterpret_cast<float3 *>(this->vertex_normals.data_ptr<float>()));
69
+ }
70
+
71
+ torch::Tensor TriangleMesh::get_vertex_normals()
72
+ {
73
+ if (!this->vertex_normals.defined())
74
+ {
75
+ this->compute_vertex_normals();
76
+ }
77
+ return this->vertex_normals;
78
+ }
79
+
58
80
  void TriangleMesh::compute_triangle_areas()
59
81
  {
60
82
  this->triangle_areas = torch::empty({static_cast<int64_t>(this->num_triangles)}, torch::dtype(torch::kFloat32).device(this->vertices.device()));
@@ -121,6 +143,69 @@ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> TriangleM
121
143
  return this->bvh.value().get_ray_intersection(ray_origins, ray_dirs, this->vertices, this->triangles, return_distance);
122
144
  }
123
145
 
146
+ std::tuple<torch::Tensor, torch::Tensor, std::optional<torch::Tensor>, std::optional<torch::Tensor>> TriangleMesh::sample_points(
147
+ int num_points, bool uniform, bool return_normals, bool return_colors, bool use_triangle_normal)
148
+ {
149
+ if (this->num_triangles == 0) {
150
+ throw std::runtime_error("Cannot sample points from an empty mesh.");
151
+ }
152
+
153
+ torch::Tensor tri_indices;
154
+ if (!uniform) {
155
+ tri_indices = torch::randint(0, this->num_triangles, {num_points}, torch::TensorOptions().device(this->vertices.device()).dtype(torch::kInt64));
156
+ } else {
157
+ torch::Tensor areas = this->get_triangle_areas();
158
+ tri_indices = torch::multinomial(areas, num_points, true);
159
+ }
160
+
161
+ torch::Tensor r1_r2 = torch::rand({num_points, 2}, torch::TensorOptions().device(this->vertices.device()).dtype(torch::kFloat32));
162
+ torch::Tensor out_points = torch::empty({num_points, 3}, torch::TensorOptions().device(this->vertices.device()).dtype(torch::kFloat32));
163
+
164
+ torch::Tensor out_normals, out_colors;
165
+ const float3* d_vertex_normals = nullptr;
166
+ const float3* d_triangle_normals = nullptr;
167
+ const float3* d_vertex_colors = nullptr;
168
+ float3* d_out_normals = nullptr;
169
+ float3* d_out_colors = nullptr;
170
+
171
+ if (return_normals) {
172
+ out_normals = torch::empty({num_points, 3}, torch::TensorOptions().device(this->vertices.device()).dtype(torch::kFloat32));
173
+ d_out_normals = reinterpret_cast<float3 *>(out_normals.data_ptr<float>());
174
+ if (use_triangle_normal) {
175
+ d_triangle_normals = reinterpret_cast<const float3 *>(this->get_triangle_normals().data_ptr<float>());
176
+ } else {
177
+ d_vertex_normals = reinterpret_cast<const float3 *>(this->get_vertex_normals().data_ptr<float>());
178
+ }
179
+ }
180
+
181
+ if (return_colors) {
182
+ if (!this->vertex_colors.defined()) {
183
+ throw std::runtime_error("Cannot sample colors because vertex_colors is not defined.");
184
+ }
185
+ out_colors = torch::empty({num_points, 3}, torch::TensorOptions().device(this->vertices.device()).dtype(torch::kFloat32));
186
+ d_out_colors = reinterpret_cast<float3 *>(out_colors.data_ptr<float>());
187
+ d_vertex_colors = reinterpret_cast<const float3 *>(this->vertex_colors.data_ptr<float>());
188
+ }
189
+
190
+ triangle_mesh::sample_points_triangle_mesh(
191
+ num_points,
192
+ reinterpret_cast<const float3 *>(this->vertices.data_ptr<float>()),
193
+ reinterpret_cast<const int3 *>(this->triangles.data_ptr<int>()),
194
+ reinterpret_cast<const int64_t *>(tri_indices.data_ptr<int64_t>()),
195
+ reinterpret_cast<const float2 *>(r1_r2.data_ptr<float>()),
196
+ d_vertex_normals,
197
+ d_triangle_normals,
198
+ d_vertex_colors,
199
+ reinterpret_cast<float3 *>(out_points.data_ptr<float>()),
200
+ d_out_normals,
201
+ d_out_colors);
202
+
203
+ std::optional<torch::Tensor> opt_normals = return_normals ? std::make_optional(out_normals) : std::nullopt;
204
+ std::optional<torch::Tensor> opt_colors = return_colors ? std::make_optional(out_colors) : std::nullopt;
205
+
206
+ return std::make_tuple(out_points, tri_indices, opt_normals, opt_colors);
207
+ }
208
+
124
209
  torch::Tensor TriangleMesh::get_triangle_areas()
125
210
  {
126
211
  if (!this->triangle_areas.defined())
@@ -378,6 +463,8 @@ void bind_ds_triangle_mesh(py::module_ &m)
378
463
  .def("is_manifold", &TriangleMesh::is_manifold, py::arg("allow_boundary_edge") = true)
379
464
  .def("get_non_manifold_vertices", &TriangleMesh::get_non_manifold_vertices)
380
465
  .def("remove_triangles_by_mask", &TriangleMesh::remove_triangles_by_mask, py::arg("keep_mask"))
466
+ .def("sample_points", &TriangleMesh::sample_points, py::arg("num_points"), py::arg("uniform") = false, py::arg("return_normals") = false, py::arg("return_colors") = false, py::arg("use_triangle_normal") = true, "Sample points on the mesh")
467
+ .def("compute_vertex_normals", &TriangleMesh::compute_vertex_normals)
381
468
  .def_property_readonly("euler_characteristic", &TriangleMesh::get_euler_characteristic)
382
469
  .def_property_readonly("genus", &TriangleMesh::get_genus);
383
470
  }