conquer3d 0.2.5__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.
- {conquer3d-0.2.5/conquer3d.egg-info → conquer3d-0.2.7}/PKG-INFO +1 -1
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/_C.pyi +24 -2
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/_C.so +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/bvh.cpp +69 -32
- conquer3d-0.2.7/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +238 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +111 -2
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/bvh.cu +93 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/bvh.h +17 -1
- conquer3d-0.2.7/conquer3d/csrc/data_structure/mesh_bvh.cu +547 -0
- conquer3d-0.2.7/conquer3d/csrc/data_structure/mesh_bvh.h +112 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/triangle_mesh.cu +151 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/triangle_mesh.h +36 -1
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/aabb.h +18 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/triangles.h +57 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7/conquer3d.egg-info}/PKG-INFO +1 -1
- {conquer3d-0.2.5 → conquer3d-0.2.7}/pyproject.toml +1 -1
- conquer3d-0.2.5/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -36
- conquer3d-0.2.5/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -205
- conquer3d-0.2.5/conquer3d/csrc/data_structure/mesh_bvh.h +0 -33
- {conquer3d-0.2.5 → conquer3d-0.2.7}/LICENSE +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/MANIFEST.in +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/README.md +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/__init__.py +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/creation/__init__.py +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/creation/triangle_creation.py +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/check.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/constants.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/creation/triangle_creation.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/kdtree.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/data_structure/zcurve.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/f2x2.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/f3x1.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/f3x3.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/f3x4.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/f4x1.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/f4x4.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/maths.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/maths/ops.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/edge.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs.cu +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs.cu +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/primitive/ray.h +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/csrc/pybind.cpp +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/data_structure/__init__.py +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/primitive/__init__.py +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/primitive/gs.py +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d/primitive/pgs.py +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d.egg-info/SOURCES.txt +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d.egg-info/dependency_links.txt +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d.egg-info/not-zip-safe +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d.egg-info/requires.txt +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/conquer3d.egg-info/top_level.txt +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/setup.cfg +0 -0
- {conquer3d-0.2.5 → conquer3d-0.2.7}/setup.py +0 -0
|
@@ -14,9 +14,13 @@ class BVH:
|
|
|
14
14
|
"""
|
|
15
15
|
Query the BVH with bounding boxes or segments. Returns (query_ids, object_ids).
|
|
16
16
|
"""
|
|
17
|
-
def
|
|
17
|
+
def query_point(self, query_points: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
18
18
|
"""
|
|
19
|
-
|
|
19
|
+
Find the closest leaf AABB to each query point. Returns (query_ids, object_ids, distances)
|
|
20
|
+
"""
|
|
21
|
+
def query_ray(self, ray_origins: torch.Tensor, ray_dirs: torch.Tensor, max_capacity: int = 10000000) -> tuple[torch.Tensor, torch.Tensor]:
|
|
22
|
+
"""
|
|
23
|
+
Find all ray-AABB intersections. Returns (ray_ids, object_ids)
|
|
20
24
|
"""
|
|
21
25
|
def query_self(self) -> tuple[torch.Tensor, torch.Tensor]:
|
|
22
26
|
"""
|
|
@@ -53,6 +57,10 @@ class MeshBVH(BVH):
|
|
|
53
57
|
"""
|
|
54
58
|
Construct MeshBVH from Triangle AABBs.
|
|
55
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
|
+
"""
|
|
56
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:
|
|
57
65
|
"""
|
|
58
66
|
Find all ray-triangle intersections. Returns (ray_ids, triangle_ids, intersect_points, [distances])
|
|
@@ -65,6 +73,10 @@ class MeshBVH(BVH):
|
|
|
65
73
|
"""
|
|
66
74
|
Check if there are any self-intersecting triangle pairs
|
|
67
75
|
"""
|
|
76
|
+
def query_point(self, query_points: torch.Tensor, vertices: torch.Tensor, triangles: torch.Tensor, return_sdf: bool = False, return_prj_pts: bool = True, sign_mode: int = 0) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
77
|
+
"""
|
|
78
|
+
Find the closest triangle to each query point. Returns (query_ids, triangle_ids, projected_points, distances)
|
|
79
|
+
"""
|
|
68
80
|
class PGSBVH(BVH):
|
|
69
81
|
def __init__(self, in_aabb_mins: torch.Tensor, in_aabb_maxs: torch.Tensor) -> None:
|
|
70
82
|
"""
|
|
@@ -89,6 +101,8 @@ class TriangleMesh:
|
|
|
89
101
|
...
|
|
90
102
|
def compute_triangle_normals(self) -> None:
|
|
91
103
|
...
|
|
104
|
+
def compute_vertex_normals(self) -> None:
|
|
105
|
+
...
|
|
92
106
|
def compute_vertices_to_triangle_map(self) -> None:
|
|
93
107
|
...
|
|
94
108
|
def get_edges_to_triangle_map(self) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
@@ -115,8 +129,16 @@ class TriangleMesh:
|
|
|
115
129
|
"""
|
|
116
130
|
def is_vertex_manifold(self) -> bool:
|
|
117
131
|
...
|
|
132
|
+
def query_points(self, query_pts: torch.Tensor, return_sdf: bool = False, return_prj_pts: bool = True, sign_mode: int = 0, distance_mode: int = 0) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
133
|
+
"""
|
|
134
|
+
Query points against the mesh. Returns (query_ids, triangle_ids, projected_points, distances)
|
|
135
|
+
"""
|
|
118
136
|
def remove_triangles_by_mask(self, keep_mask: torch.Tensor) -> None:
|
|
119
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
|
+
"""
|
|
120
142
|
@property
|
|
121
143
|
def bvh(self) -> MeshBVH:
|
|
122
144
|
...
|
|
Binary file
|
|
@@ -155,48 +155,81 @@ std::tuple<torch::Tensor, torch::Tensor> BVH::query_self()
|
|
|
155
155
|
|
|
156
156
|
std::tuple<torch::Tensor, torch::Tensor> BVH::query_ray(
|
|
157
157
|
const torch::Tensor &ray_origins,
|
|
158
|
-
const torch::Tensor &ray_dirs
|
|
158
|
+
const torch::Tensor &ray_dirs,
|
|
159
|
+
int64_t max_capacity)
|
|
159
160
|
{
|
|
160
161
|
CHECK_INPUT(ray_origins);
|
|
161
162
|
CHECK_INPUT(ray_dirs);
|
|
162
|
-
TORCH_CHECK(ray_origins.
|
|
163
|
-
TORCH_CHECK(ray_dirs.scalar_type() == torch::kFloat32, "ray_dirs must be float32");
|
|
164
|
-
TORCH_CHECK(ray_origins.size(1) == 3, "ray_origins must have shape (M, 3)");
|
|
165
|
-
TORCH_CHECK(ray_dirs.size(1) == 3, "ray_dirs must have shape (M, 3)");
|
|
163
|
+
TORCH_CHECK(ray_origins.size(0) == ray_dirs.size(0), "ray_origins and ray_dirs must have the same number of rays");
|
|
166
164
|
|
|
167
|
-
|
|
168
|
-
auto options_int64 = ray_origins.options().dtype(torch::kInt64);
|
|
165
|
+
int num_queries = ray_origins.size(0);
|
|
169
166
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
torch::Tensor out_query_ids = torch::empty({BVH_MAX_CAPACITY}, options_int64);
|
|
175
|
-
torch::Tensor out_object_ids = torch::empty({BVH_MAX_CAPACITY}, options_int64);
|
|
176
|
-
torch::Tensor hit_counter = torch::zeros({1}, options_int64);
|
|
167
|
+
auto options_i64 = torch::TensorOptions().dtype(torch::kInt64).device(ray_origins.device());
|
|
168
|
+
torch::Tensor out_query_ids = torch::empty({max_capacity}, options_i64);
|
|
169
|
+
torch::Tensor out_object_ids = torch::empty({max_capacity}, options_i64);
|
|
170
|
+
torch::Tensor hit_counter = torch::zeros({1}, options_i64);
|
|
177
171
|
|
|
178
172
|
bvh::query_ray(
|
|
179
173
|
num_queries,
|
|
180
174
|
this->num_objects,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
175
|
+
(const float3 *)ray_origins.data_ptr<float>(),
|
|
176
|
+
(const float3 *)ray_dirs.data_ptr<float>(),
|
|
177
|
+
(const float3 *)this->aabb_mins.data_ptr<float>(),
|
|
178
|
+
(const float3 *)this->aabb_maxs.data_ptr<float>(),
|
|
179
|
+
(const int2 *)this->bvh_children.data_ptr<int>(),
|
|
180
|
+
this->object_ids.data_ptr<int>(),
|
|
181
|
+
out_query_ids.data_ptr<int64_t>(),
|
|
182
|
+
out_object_ids.data_ptr<int64_t>(),
|
|
183
|
+
hit_counter.data_ptr<int64_t>(),
|
|
184
|
+
max_capacity);
|
|
185
|
+
|
|
186
|
+
int64_t h_hit_counter = hit_counter.item<int64_t>();
|
|
187
|
+
|
|
188
|
+
if (h_hit_counter >= max_capacity)
|
|
189
|
+
{
|
|
190
|
+
TORCH_WARN("BVH ray query capacity exceeded. Some hits were dropped.");
|
|
191
|
+
h_hit_counter = max_capacity;
|
|
192
|
+
}
|
|
192
193
|
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
if (h_hit_counter == 0) {
|
|
195
|
+
return std::make_tuple(
|
|
196
|
+
torch::empty({0}, options_i64),
|
|
197
|
+
torch::empty({0}, options_i64)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
195
200
|
|
|
196
201
|
return std::make_tuple(
|
|
197
|
-
out_query_ids.slice(0, 0,
|
|
198
|
-
out_object_ids.slice(0, 0,
|
|
199
|
-
|
|
202
|
+
out_query_ids.slice(0, 0, h_hit_counter),
|
|
203
|
+
out_object_ids.slice(0, 0, h_hit_counter));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
std::tuple<torch::Tensor, torch::Tensor, torch::Tensor> BVH::query_point(
|
|
207
|
+
const torch::Tensor &query_points)
|
|
208
|
+
{
|
|
209
|
+
CHECK_INPUT(query_points);
|
|
210
|
+
|
|
211
|
+
int num_queries = query_points.size(0);
|
|
212
|
+
|
|
213
|
+
auto options_i64 = torch::TensorOptions().dtype(torch::kInt64).device(query_points.device());
|
|
214
|
+
auto options_f32 = torch::TensorOptions().dtype(torch::kFloat32).device(query_points.device());
|
|
215
|
+
|
|
216
|
+
torch::Tensor out_query_ids = torch::empty({num_queries}, options_i64);
|
|
217
|
+
torch::Tensor out_object_ids = torch::empty({num_queries}, options_i64);
|
|
218
|
+
torch::Tensor out_distances = torch::empty({num_queries}, options_f32);
|
|
219
|
+
|
|
220
|
+
bvh::query_point(
|
|
221
|
+
num_queries,
|
|
222
|
+
this->num_objects,
|
|
223
|
+
(const float3 *)query_points.data_ptr<float>(),
|
|
224
|
+
(const float3 *)this->aabb_mins.data_ptr<float>(),
|
|
225
|
+
(const float3 *)this->aabb_maxs.data_ptr<float>(),
|
|
226
|
+
(const int2 *)this->bvh_children.data_ptr<int>(),
|
|
227
|
+
this->object_ids.data_ptr<int>(),
|
|
228
|
+
out_query_ids.data_ptr<int64_t>(),
|
|
229
|
+
out_object_ids.data_ptr<int64_t>(),
|
|
230
|
+
out_distances.data_ptr<float>());
|
|
231
|
+
|
|
232
|
+
return std::make_tuple(out_query_ids, out_object_ids, out_distances);
|
|
200
233
|
}
|
|
201
234
|
|
|
202
235
|
void bind_ds_bvh(py::module_& m)
|
|
@@ -216,7 +249,11 @@ void bind_ds_bvh(py::module_& m)
|
|
|
216
249
|
"Query the BVH against itself. Returns unique overlapping (query_ids, object_ids).")
|
|
217
250
|
|
|
218
251
|
.def("query_ray", &BVH::query_ray,
|
|
219
|
-
py::arg("ray_origins"),
|
|
252
|
+
py::arg("ray_origins"),
|
|
220
253
|
py::arg("ray_dirs"),
|
|
221
|
-
"
|
|
254
|
+
py::arg("max_capacity") = BVH_MAX_CAPACITY,
|
|
255
|
+
"Find all ray-AABB intersections. Returns (ray_ids, object_ids)")
|
|
256
|
+
.def("query_point", &BVH::query_point,
|
|
257
|
+
py::arg("query_points"),
|
|
258
|
+
"Find the closest leaf AABB to each query point. Returns (query_ids, object_ids, distances)");
|
|
222
259
|
}
|
|
@@ -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()));
|
|
@@ -97,6 +119,21 @@ bool TriangleMesh::is_self_intersection()
|
|
|
97
119
|
return this->bvh.value().is_self_intersection(this->vertices, this->triangles);
|
|
98
120
|
}
|
|
99
121
|
|
|
122
|
+
std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> TriangleMesh::query_points(
|
|
123
|
+
const torch::Tensor &query_pts,
|
|
124
|
+
bool return_sdf,
|
|
125
|
+
bool return_prj_pts,
|
|
126
|
+
int sign_mode,
|
|
127
|
+
int distance_mode)
|
|
128
|
+
{
|
|
129
|
+
if (distance_mode == 0) {
|
|
130
|
+
this->build_bvh();
|
|
131
|
+
return this->bvh.value().query_point(query_pts, this->vertices, this->triangles, return_sdf, return_prj_pts, sign_mode);
|
|
132
|
+
} else {
|
|
133
|
+
throw std::runtime_error("distance_mode != 0 not implemented yet");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
100
137
|
std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> TriangleMesh::get_ray_intersection(
|
|
101
138
|
const torch::Tensor &ray_origins,
|
|
102
139
|
const torch::Tensor &ray_dirs,
|
|
@@ -106,6 +143,69 @@ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor> TriangleM
|
|
|
106
143
|
return this->bvh.value().get_ray_intersection(ray_origins, ray_dirs, this->vertices, this->triangles, return_distance);
|
|
107
144
|
}
|
|
108
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
|
+
|
|
109
209
|
torch::Tensor TriangleMesh::get_triangle_areas()
|
|
110
210
|
{
|
|
111
211
|
if (!this->triangle_areas.defined())
|
|
@@ -338,6 +438,13 @@ void bind_ds_triangle_mesh(py::module_ &m)
|
|
|
338
438
|
py::arg("ray_dirs"),
|
|
339
439
|
py::arg("return_distance") = false,
|
|
340
440
|
"Find all ray-triangle intersections. Returns (ray_ids, triangle_ids, intersect_points, [distances])")
|
|
441
|
+
.def("query_points", &TriangleMesh::query_points,
|
|
442
|
+
py::arg("query_pts"),
|
|
443
|
+
py::arg("return_sdf") = false,
|
|
444
|
+
py::arg("return_prj_pts") = true,
|
|
445
|
+
py::arg("sign_mode") = 0,
|
|
446
|
+
py::arg("distance_mode") = 0,
|
|
447
|
+
"Query points against the mesh. Returns (query_ids, triangle_ids, projected_points, distances)")
|
|
341
448
|
.def_property_readonly("edges", &TriangleMesh::get_edges)
|
|
342
449
|
.def_property_readonly("edge_to_triangle_offsets", &TriangleMesh::get_edge_to_triangle_offsets)
|
|
343
450
|
.def_property_readonly("edge_to_triangle_counts", &TriangleMesh::get_edge_to_triangle_counts)
|
|
@@ -356,6 +463,8 @@ void bind_ds_triangle_mesh(py::module_ &m)
|
|
|
356
463
|
.def("is_manifold", &TriangleMesh::is_manifold, py::arg("allow_boundary_edge") = true)
|
|
357
464
|
.def("get_non_manifold_vertices", &TriangleMesh::get_non_manifold_vertices)
|
|
358
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)
|
|
359
468
|
.def_property_readonly("euler_characteristic", &TriangleMesh::get_euler_characteristic)
|
|
360
469
|
.def_property_readonly("genus", &TriangleMesh::get_genus);
|
|
361
470
|
}
|