conquer3d 0.3.1__tar.gz → 0.3.2__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.3.1/conquer3d.egg-info → conquer3d-0.3.2}/PKG-INFO +3 -1
- {conquer3d-0.3.1 → conquer3d-0.3.2}/README.md +2 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/_C.so +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/__init__.py +2 -1
- conquer3d-0.3.2/conquer3d/conversion/__init__.py +13 -0
- {conquer3d-0.3.1/conquer3d/data_structure → conquer3d-0.3.2/conquer3d/conversion}/grid.py +49 -88
- conquer3d-0.3.2/conquer3d/conversion/mesh.py +120 -0
- conquer3d-0.3.2/conquer3d/data/collate/mesh.py +40 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/dataset/digit3d.py +4 -8
- conquer3d-0.3.2/conquer3d/data/transform/base.py +39 -0
- conquer3d-0.3.2/conquer3d/data/transform/ops.py +62 -0
- conquer3d-0.3.2/conquer3d/data/transform/vertex.py +55 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data_structure/__init__.py +0 -6
- conquer3d-0.3.2/conquer3d/data_structure/bmesh.py +23 -0
- conquer3d-0.3.2/conquer3d/data_structure/grid.py +65 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2/conquer3d.egg-info}/PKG-INFO +3 -1
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/SOURCES.txt +8 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/pyproject.toml +1 -1
- {conquer3d-0.3.1 → conquer3d-0.3.2}/LICENSE +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/MANIFEST.in +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/_C.pyi +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/creation/__init__.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/creation/triangle_creation.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/grid.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/check.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/constants.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/creation/triangle_creation.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/bvh.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/bvh.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/kdtree.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/data_structure/zcurve.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f2x2.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f3x1.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f3x3.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f3x4.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f4x1.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/f4x4.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/maths.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/maths/ops.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/chamfer.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/chamfer.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/mc.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/mc.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/ops/mc_data.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/aabb.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/edge.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/ray.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/primitive/triangles.h +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/csrc/pybind.cpp +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/__init__.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/collate/__init__.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/collate/sparse_tensor.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/dataset/__init__.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/dataset/base_mesh.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/data/transform/__init__.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/__init__.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/diff_marching_cubes.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/distance.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/ops/marching_cubes.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/primitive/__init__.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/primitive/gs.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d/primitive/pgs.py +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/dependency_links.txt +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/not-zip-safe +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/requires.txt +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/conquer3d.egg-info/top_level.txt +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/setup.cfg +0 -0
- {conquer3d-0.3.1 → conquer3d-0.3.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conquer3d
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Geometric Cuda Tool Box
|
|
5
5
|
Author-email: Do Hoang Khoi <khoido8899@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -63,6 +63,8 @@ pip install pybind11-stubgen
|
|
|
63
63
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
64
64
|
|
|
65
65
|
pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
|
|
66
|
+
|
|
67
|
+
pip install rectified-flow-pytorch
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
# Development
|
|
Binary file
|
|
@@ -5,5 +5,6 @@ from . import creation
|
|
|
5
5
|
from . import data_structure
|
|
6
6
|
from . import primitive
|
|
7
7
|
from . import ops
|
|
8
|
+
from . import conversion
|
|
8
9
|
|
|
9
|
-
__all__ = ['_C', 'creation', 'data_structure', 'primitive', 'ops']
|
|
10
|
+
__all__ = ['_C', 'creation', 'data_structure', 'primitive', 'ops', 'conversion']
|
|
@@ -1,68 +1,5 @@
|
|
|
1
1
|
import torch
|
|
2
2
|
from typing import Tuple, List, Union
|
|
3
|
-
from .. import _C
|
|
4
|
-
|
|
5
|
-
def create_voxel_grid(
|
|
6
|
-
grid_min: Union[List[float], Tuple[float, float, float]],
|
|
7
|
-
grid_max: Union[List[float], Tuple[float, float, float]],
|
|
8
|
-
res: Union[List[int], Tuple[int, int, int]],
|
|
9
|
-
device: str = "cuda"
|
|
10
|
-
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
11
|
-
"""
|
|
12
|
-
Creates a structured 3D voxel grid efficiently.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
grid_min (List[float] | Tuple[float, float, float]): The minimum (x, y, z) bounding box coordinates.
|
|
16
|
-
grid_max (List[float] | Tuple[float, float, float]): The maximum (x, y, z) bounding box coordinates.
|
|
17
|
-
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
18
|
-
device (str, optional): Target device for the tensors (e.g., "cuda" or "cpu"). Defaults to "cuda".
|
|
19
|
-
|
|
20
|
-
Returns:
|
|
21
|
-
Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
22
|
-
- grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing all grid coordinates.
|
|
23
|
-
- voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices
|
|
24
|
-
for each voxel, mapped identically to the Marching Cubes convention.
|
|
25
|
-
- idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
|
|
26
|
-
"""
|
|
27
|
-
return _C.create_voxel_grid(list(grid_min), list(grid_max), list(res), device)
|
|
28
|
-
|
|
29
|
-
def compute_grid_normal(
|
|
30
|
-
sdf: torch.Tensor,
|
|
31
|
-
grid_vertices: torch.Tensor,
|
|
32
|
-
idx_grids: torch.Tensor,
|
|
33
|
-
res: Union[List[int], Tuple[int, int, int]]
|
|
34
|
-
) -> torch.Tensor:
|
|
35
|
-
"""
|
|
36
|
-
Computes smooth normals (gradients) for a voxel grid using central differences.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
|
|
40
|
-
grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing the grid coordinates.
|
|
41
|
-
idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) indices, returned by create_voxel_grid.
|
|
42
|
-
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
torch.Tensor: A float32 tensor of shape (N, 3) containing the normalized gradient vectors.
|
|
46
|
-
"""
|
|
47
|
-
return _C.compute_grid_normal(sdf.contiguous(), grid_vertices.contiguous(), idx_grids.contiguous(), list(res))
|
|
48
|
-
|
|
49
|
-
def compute_active_voxels(
|
|
50
|
-
voxels: torch.Tensor,
|
|
51
|
-
sdf: torch.Tensor,
|
|
52
|
-
iso: float = 0.0
|
|
53
|
-
) -> torch.Tensor:
|
|
54
|
-
"""
|
|
55
|
-
Computes the indices of active voxels that intersect the isosurface.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices for each voxel.
|
|
59
|
-
sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
|
|
60
|
-
iso (float): The isosurface value. Defaults to 0.0.
|
|
61
|
-
|
|
62
|
-
Returns:
|
|
63
|
-
torch.Tensor: An int64 tensor containing the indices of the active voxels.
|
|
64
|
-
"""
|
|
65
|
-
return _C.compute_active_voxels(voxels.contiguous(), sdf.contiguous(), iso)
|
|
66
3
|
|
|
67
4
|
def voxel2sparse(
|
|
68
5
|
active_indices: torch.Tensor,
|
|
@@ -104,28 +41,6 @@ def voxel2sparse(
|
|
|
104
41
|
return sparse_coords
|
|
105
42
|
|
|
106
43
|
def sparse2voxel(
|
|
107
|
-
sparse_coords: torch.Tensor,
|
|
108
|
-
res: Union[List[int], Tuple[int, int, int]]
|
|
109
|
-
) -> torch.Tensor:
|
|
110
|
-
"""
|
|
111
|
-
Converts a [N, 4] sparse coordinates tensor back to 1D active voxel indices.
|
|
112
|
-
|
|
113
|
-
Args:
|
|
114
|
-
sparse_coords (torch.Tensor): An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
|
|
115
|
-
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
torch.Tensor: An int64 tensor containing the indices of the active voxels.
|
|
119
|
-
"""
|
|
120
|
-
x = sparse_coords[:, 1].to(torch.int64)
|
|
121
|
-
y = sparse_coords[:, 2].to(torch.int64)
|
|
122
|
-
z = sparse_coords[:, 3].to(torch.int64)
|
|
123
|
-
|
|
124
|
-
rx, ry, rz = res
|
|
125
|
-
active_indices = x * (ry - 1) * (rz - 1) + y * (rz - 1) + z
|
|
126
|
-
return active_indices
|
|
127
|
-
|
|
128
|
-
def sparse2mesh_topology(
|
|
129
44
|
sparse_coords: torch.Tensor,
|
|
130
45
|
sparse_sdfs: torch.Tensor,
|
|
131
46
|
grid_min: Union[List[float], Tuple[float, float, float]],
|
|
@@ -182,9 +97,9 @@ def sparse2mesh_topology(
|
|
|
182
97
|
|
|
183
98
|
# 6. Convert integer coordinates back to global float positions
|
|
184
99
|
rx, ry, rz = res
|
|
185
|
-
x_coords = torch.linspace(grid_min[0], grid_max[0], rx, device=device, dtype=torch.float32)
|
|
186
|
-
y_coords = torch.linspace(grid_min[1], grid_max[1], ry, device=device, dtype=torch.float32)
|
|
187
|
-
z_coords = torch.linspace(grid_min[2], grid_max[2], rz, device=device, dtype=torch.float32)
|
|
100
|
+
x_coords = torch.linspace(grid_min[0], grid_max[0], rx + 1, device=device, dtype=torch.float32)
|
|
101
|
+
y_coords = torch.linspace(grid_min[1], grid_max[1], ry + 1, device=device, dtype=torch.float32)
|
|
102
|
+
z_coords = torch.linspace(grid_min[2], grid_max[2], rz + 1, device=device, dtype=torch.float32)
|
|
188
103
|
|
|
189
104
|
ux = x_coords[unique_corners_int[:, 0]]
|
|
190
105
|
uy = y_coords[unique_corners_int[:, 1]]
|
|
@@ -193,3 +108,49 @@ def sparse2mesh_topology(
|
|
|
193
108
|
unique_vertices = torch.stack([ux, uy, uz], dim=1)
|
|
194
109
|
|
|
195
110
|
return unique_vertices, local_voxels, merged_sdfs
|
|
111
|
+
|
|
112
|
+
def sparse_coo2dense_occ(
|
|
113
|
+
sparse_coords: torch.Tensor,
|
|
114
|
+
batch_size: int,
|
|
115
|
+
res: Union[List[int], Tuple[int, int, int]]
|
|
116
|
+
) -> torch.Tensor:
|
|
117
|
+
"""
|
|
118
|
+
Converts a [N, 4] sparse coordinates tensor into a [B, 1, rx, ry, rz] dense occupancy grid.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
sparse_coords (torch.Tensor): An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
|
|
122
|
+
batch_size (int): The batch size B.
|
|
123
|
+
res (List[int] | Tuple[int, int, int]): The grid resolution (rx, ry, rz).
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
torch.Tensor: A float32 tensor of shape [B, 1, rx, ry, rz] containing 1.0 at active voxels and 0.0 otherwise.
|
|
127
|
+
"""
|
|
128
|
+
rx, ry, rz = res
|
|
129
|
+
dense_occ = torch.zeros((batch_size, 1, rx, ry, rz), dtype=torch.float32, device=sparse_coords.device)
|
|
130
|
+
|
|
131
|
+
b = sparse_coords[:, 0].to(torch.int64)
|
|
132
|
+
x = sparse_coords[:, 1].to(torch.int64)
|
|
133
|
+
y = sparse_coords[:, 2].to(torch.int64)
|
|
134
|
+
z = sparse_coords[:, 3].to(torch.int64)
|
|
135
|
+
|
|
136
|
+
dense_occ[b, 0, x, y, z] = 1.0
|
|
137
|
+
return dense_occ
|
|
138
|
+
|
|
139
|
+
def dense_occ2sparse_coo(
|
|
140
|
+
dense_occ: torch.Tensor,
|
|
141
|
+
threshold: float = 0.0
|
|
142
|
+
) -> torch.Tensor:
|
|
143
|
+
"""
|
|
144
|
+
Converts a [B, C, rx, ry, rz] dense occupancy grid back to a [N, 4] sparse coordinates tensor.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
dense_occ (torch.Tensor): A tensor of shape [B, C, rx, ry, rz].
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
torch.Tensor: An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
|
|
151
|
+
"""
|
|
152
|
+
active_mask = dense_occ > threshold
|
|
153
|
+
indices = torch.argwhere(active_mask)
|
|
154
|
+
# indices has shape [N, 5] for [b, c, x, y, z]. We want [batch_idx, x, y, z]
|
|
155
|
+
sparse_coords = indices[:, [0, 2, 3, 4]].to(torch.int32)
|
|
156
|
+
return sparse_coords
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from conquer3d.data_structure.grid import create_voxel_grid, compute_active_voxels
|
|
3
|
+
from conquer3d.conversion.grid import voxel2sparse
|
|
4
|
+
from conquer3d.data_structure import TriangleMesh
|
|
5
|
+
from conquer3d.data_structure.bmesh import BTriangleMesh
|
|
6
|
+
from typing import Union, List, Tuple
|
|
7
|
+
|
|
8
|
+
def mesh2voxel(vertices: torch.Tensor, res: Union[List[int], Tuple[int, int, int]], grid_bound: float = None):
|
|
9
|
+
"""
|
|
10
|
+
Computes bounding box of mesh and returns a structured voxel grid.
|
|
11
|
+
"""
|
|
12
|
+
if grid_bound is None:
|
|
13
|
+
grid_min = vertices.min(dim=0)[0].tolist()
|
|
14
|
+
grid_max = vertices.max(dim=0)[0].tolist()
|
|
15
|
+
else:
|
|
16
|
+
grid_min = [-grid_bound, -grid_bound, -grid_bound]
|
|
17
|
+
grid_max = [grid_bound, grid_bound, grid_bound]
|
|
18
|
+
|
|
19
|
+
device_str = str(vertices.device)
|
|
20
|
+
if device_str.startswith('cuda'):
|
|
21
|
+
# Fix format if there's an index like cuda:0
|
|
22
|
+
device_str = "cuda"
|
|
23
|
+
|
|
24
|
+
grid_vertices, voxels, idx_grids = create_voxel_grid(grid_min, grid_max, res, device=device_str)
|
|
25
|
+
|
|
26
|
+
# Ensure they are on the exact same device as vertices (especially if multiple GPUs)
|
|
27
|
+
grid_vertices = grid_vertices.to(vertices.device)
|
|
28
|
+
voxels = voxels.to(vertices.device)
|
|
29
|
+
idx_grids = idx_grids.to(vertices.device)
|
|
30
|
+
|
|
31
|
+
return grid_vertices, voxels, idx_grids, grid_min, grid_max
|
|
32
|
+
|
|
33
|
+
def mesh2voxel_sdf(vertices: torch.Tensor, faces: torch.Tensor, grid_vertices: torch.Tensor):
|
|
34
|
+
"""
|
|
35
|
+
Computes SDF for grid vertices against the mesh using C++ TriangleMesh fast winding numbers.
|
|
36
|
+
"""
|
|
37
|
+
mesh = TriangleMesh(vertices, faces.to(torch.int32))
|
|
38
|
+
|
|
39
|
+
# query_points returns: q_ids, tri_ids, prj_pts, sdf
|
|
40
|
+
_, _, _, sdf = mesh.query_points(
|
|
41
|
+
grid_vertices,
|
|
42
|
+
return_sdf=True,
|
|
43
|
+
return_prj_pts=False,
|
|
44
|
+
sign_mode=0,
|
|
45
|
+
distance_mode=0
|
|
46
|
+
)
|
|
47
|
+
return sdf
|
|
48
|
+
|
|
49
|
+
def mesh2sparse(bmesh: BTriangleMesh, res: Union[List[int], Tuple[int, int, int]], grid_bound: float = 1.2, iso: float = 0.0):
|
|
50
|
+
"""
|
|
51
|
+
Batched processing of BTriangleMesh into sparse tensors.
|
|
52
|
+
"""
|
|
53
|
+
all_coords = []
|
|
54
|
+
all_sdfs = []
|
|
55
|
+
|
|
56
|
+
for b in range(bmesh.batch_size):
|
|
57
|
+
# Extract individual mesh
|
|
58
|
+
v_mask = bmesh.vertbids == b
|
|
59
|
+
f_mask = bmesh.facebids == b
|
|
60
|
+
|
|
61
|
+
v = bmesh.vertices[v_mask]
|
|
62
|
+
f = bmesh.faces[f_mask]
|
|
63
|
+
|
|
64
|
+
if len(v) == 0:
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
# Voxelize and Compute SDF
|
|
68
|
+
grid_vertices, voxels, idx_grids, _, _ = mesh2voxel(v, res, grid_bound)
|
|
69
|
+
sdf = mesh2voxel_sdf(v, f, grid_vertices)
|
|
70
|
+
|
|
71
|
+
# Sparse conversion
|
|
72
|
+
active_voxels = compute_active_voxels(voxels, sdf, iso)
|
|
73
|
+
sparse_coords, sparse_sdfs = voxel2sparse(active_voxels, voxels, idx_grids, sdf=sdf, batch_idx=b)
|
|
74
|
+
|
|
75
|
+
all_coords.append(sparse_coords)
|
|
76
|
+
all_sdfs.append(sparse_sdfs)
|
|
77
|
+
|
|
78
|
+
if len(all_coords) > 0:
|
|
79
|
+
return torch.cat(all_coords, dim=0), torch.cat(all_sdfs, dim=0)
|
|
80
|
+
else:
|
|
81
|
+
# Fallback for empty batch
|
|
82
|
+
device = bmesh.vertices.device
|
|
83
|
+
return torch.empty((0, 4), dtype=torch.int32, device=device), torch.empty((0, 8), dtype=torch.float32, device=device)
|
|
84
|
+
|
|
85
|
+
def mesh2sparse_with_dense(bmesh: BTriangleMesh, res: Union[List[int], Tuple[int, int, int]], grid_bound: float = 1.2, iso: float = 0.0):
|
|
86
|
+
"""
|
|
87
|
+
Batched processing of BTriangleMesh into sparse tensors, also returning full dense coords.
|
|
88
|
+
"""
|
|
89
|
+
all_coords = []
|
|
90
|
+
all_sdfs = []
|
|
91
|
+
all_dense_coords = []
|
|
92
|
+
all_dense_sdfs = []
|
|
93
|
+
|
|
94
|
+
for b in range(bmesh.batch_size):
|
|
95
|
+
v_mask = bmesh.vertbids == b
|
|
96
|
+
f_mask = bmesh.facebids == b
|
|
97
|
+
|
|
98
|
+
v = bmesh.vertices[v_mask]
|
|
99
|
+
f = bmesh.faces[f_mask]
|
|
100
|
+
|
|
101
|
+
if len(v) == 0:
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
grid_vertices, voxels, idx_grids, _, _ = mesh2voxel(v, res, grid_bound)
|
|
105
|
+
sdf = mesh2voxel_sdf(v, f, grid_vertices)
|
|
106
|
+
|
|
107
|
+
active_voxels = compute_active_voxels(voxels, sdf, iso)
|
|
108
|
+
sparse_coords, sparse_sdfs = voxel2sparse(active_voxels, voxels, idx_grids, sdf=sdf, batch_idx=b)
|
|
109
|
+
dense_coords, dense_sdfs = voxel2sparse(torch.ones_like(voxels[:, 0], dtype=torch.bool), voxels, idx_grids, sdf=sdf, batch_idx=b)
|
|
110
|
+
|
|
111
|
+
all_coords.append(sparse_coords)
|
|
112
|
+
all_sdfs.append(sparse_sdfs)
|
|
113
|
+
all_dense_coords.append(dense_coords)
|
|
114
|
+
all_dense_sdfs.append(dense_sdfs)
|
|
115
|
+
|
|
116
|
+
if len(all_coords) > 0:
|
|
117
|
+
return torch.cat(all_coords, dim=0), torch.cat(all_sdfs, dim=0), torch.cat(all_dense_coords, dim=0), torch.cat(all_dense_sdfs, dim=0)
|
|
118
|
+
else:
|
|
119
|
+
device = bmesh.vertices.device
|
|
120
|
+
return torch.empty((0, 4), dtype=torch.int32, device=device), torch.empty((0, 8), dtype=torch.float32, device=device), torch.empty((0, 4), dtype=torch.int32, device=device), torch.empty((0, 8), dtype=torch.float32, device=device)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from conquer3d.data_structure.bmesh import BTriangleMesh
|
|
3
|
+
|
|
4
|
+
def bmesh_collate_fn(batch):
|
|
5
|
+
"""
|
|
6
|
+
Collate function for a dataset returning (vertices, faces, label).
|
|
7
|
+
Returns a BTriangleMesh and a batched label tensor.
|
|
8
|
+
"""
|
|
9
|
+
all_vertices = []
|
|
10
|
+
all_faces = []
|
|
11
|
+
all_vertbids = []
|
|
12
|
+
all_facebids = []
|
|
13
|
+
all_labels = []
|
|
14
|
+
|
|
15
|
+
for b, (v, f, l) in enumerate(batch):
|
|
16
|
+
all_vertices.append(v)
|
|
17
|
+
|
|
18
|
+
# We do not offset the face indices because we will process them per-mesh locally
|
|
19
|
+
all_faces.append(f)
|
|
20
|
+
|
|
21
|
+
all_vertbids.append(torch.full((v.shape[0],), b, dtype=torch.int32))
|
|
22
|
+
all_facebids.append(torch.full((f.shape[0],), b, dtype=torch.int32))
|
|
23
|
+
all_labels.append(l)
|
|
24
|
+
|
|
25
|
+
batched_vertices = torch.cat(all_vertices, dim=0)
|
|
26
|
+
batched_faces = torch.cat(all_faces, dim=0)
|
|
27
|
+
batched_vertbids = torch.cat(all_vertbids, dim=0)
|
|
28
|
+
batched_facebids = torch.cat(all_facebids, dim=0)
|
|
29
|
+
|
|
30
|
+
batched_labels = torch.tensor(all_labels, dtype=torch.long)
|
|
31
|
+
|
|
32
|
+
bmesh = BTriangleMesh(
|
|
33
|
+
vertices=batched_vertices,
|
|
34
|
+
faces=batched_faces,
|
|
35
|
+
vertbids=batched_vertbids,
|
|
36
|
+
facebids=batched_facebids,
|
|
37
|
+
batch_size=len(batch)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return bmesh, batched_labels
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import zipfile
|
|
3
3
|
import torch
|
|
4
|
-
import numpy as np
|
|
5
4
|
import conquer3d as c3d
|
|
6
|
-
import trimesh
|
|
7
5
|
import meshlib.mrmeshpy as mr
|
|
8
6
|
import meshlib.mrmeshnumpy as mrnp
|
|
9
7
|
|
|
@@ -50,7 +48,7 @@ class Digit3D(BaseMeshDataset):
|
|
|
50
48
|
if self.cached and idx in self._cache:
|
|
51
49
|
vertices_t, faces_t, label = self._cache[idx]
|
|
52
50
|
if self.transform:
|
|
53
|
-
vertices_t = self.transform(vertices_t.clone())
|
|
51
|
+
vertices_t, faces_t = self.transform(vertices_t.clone(), faces_t.clone())
|
|
54
52
|
return vertices_t, faces_t, label
|
|
55
53
|
|
|
56
54
|
f_path = self.all_files[idx]
|
|
@@ -72,7 +70,6 @@ class Digit3D(BaseMeshDataset):
|
|
|
72
70
|
vertices.append([float(parts[1]), float(parts[2]), float(parts[3])])
|
|
73
71
|
elif line.startswith("f "):
|
|
74
72
|
parts = line.split()
|
|
75
|
-
# Wavefront OBJ faces are 1-indexed, so we subtract 1 for 0-indexed tensors
|
|
76
73
|
faces.append([int(parts[1])-1, int(parts[2])-1, int(parts[3])-1])
|
|
77
74
|
|
|
78
75
|
vertices_t = torch.tensor(vertices, dtype=torch.float32)
|
|
@@ -82,15 +79,14 @@ class Digit3D(BaseMeshDataset):
|
|
|
82
79
|
self._cache[idx] = (vertices_t, faces_t, label)
|
|
83
80
|
|
|
84
81
|
if self.transform:
|
|
85
|
-
vertices_t = self.transform(vertices_t.clone())
|
|
82
|
+
vertices_t, faces_t = self.transform(vertices_t.clone(), faces_t.clone())
|
|
86
83
|
|
|
87
84
|
return vertices_t, faces_t, label
|
|
88
85
|
|
|
89
86
|
|
|
90
87
|
class SparseDigit3D(Digit3D):
|
|
91
88
|
"""
|
|
92
|
-
Digit3D Dataset that constructs a sparse SDF voxel grid from the mesh on-the-fly
|
|
93
|
-
This allows arbitrary geometric augmentations on the mesh before voxelization without CUDA IPC issues.
|
|
89
|
+
Digit3D Dataset that constructs a sparse SDF voxel grid from the mesh on-the-fly.
|
|
94
90
|
"""
|
|
95
91
|
def __init__(self, root: str = "~/.conquer3d/", train: bool = True, transform=None, download: bool = False,
|
|
96
92
|
grid_res: int = 32, grid_bound: float = 1.2, cached: bool = False):
|
|
@@ -126,7 +122,7 @@ class SparseDigit3D(Digit3D):
|
|
|
126
122
|
active_voxel_indices = c3d.data_structure.compute_active_voxels(voxels, sdf, iso=0.0)
|
|
127
123
|
|
|
128
124
|
# 5. Extract purely Voxel-Centric representations using voxel2sparse
|
|
129
|
-
sparse_coords, sparse_sdfs = c3d.
|
|
125
|
+
sparse_coords, sparse_sdfs = c3d.conversion.grid.voxel2sparse(
|
|
130
126
|
active_voxel_indices, voxels, idx_grids, sdf=sdf, batch_idx=0
|
|
131
127
|
)
|
|
132
128
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
class BaseTransform(object):
|
|
4
|
+
def __init__(self, p=1.0):
|
|
5
|
+
self.p = p
|
|
6
|
+
|
|
7
|
+
def __call__(self, **data):
|
|
8
|
+
if random.random() <= self.p:
|
|
9
|
+
return self.apply(**data)
|
|
10
|
+
return data
|
|
11
|
+
|
|
12
|
+
def apply(self, **data):
|
|
13
|
+
raise NotImplementedError("Transform must implement apply")
|
|
14
|
+
|
|
15
|
+
class Sequence(BaseTransform):
|
|
16
|
+
def __init__(self, transforms, shuffle=False, p=1.0):
|
|
17
|
+
super().__init__(p=p)
|
|
18
|
+
self.transforms = transforms
|
|
19
|
+
self.shuffle = shuffle
|
|
20
|
+
|
|
21
|
+
def apply(self, **data):
|
|
22
|
+
transforms_to_apply = self.transforms.copy()
|
|
23
|
+
if self.shuffle:
|
|
24
|
+
random.shuffle(transforms_to_apply)
|
|
25
|
+
|
|
26
|
+
for transform in transforms_to_apply:
|
|
27
|
+
data = transform(**data)
|
|
28
|
+
|
|
29
|
+
return data
|
|
30
|
+
|
|
31
|
+
class MeshSequence(Sequence):
|
|
32
|
+
def __call__(self, vertices, faces):
|
|
33
|
+
if random.random() <= self.p:
|
|
34
|
+
return self.apply(vertices, faces)
|
|
35
|
+
return vertices, faces
|
|
36
|
+
|
|
37
|
+
def apply(self, vertices, faces):
|
|
38
|
+
data = super().apply(vertices=vertices, faces=faces)
|
|
39
|
+
return data.get('vertices', vertices), data.get('faces', faces)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import math
|
|
3
|
+
|
|
4
|
+
def rotation(vertices, rotation_axis, rotation_degree):
|
|
5
|
+
"""
|
|
6
|
+
Rotates vertices around a given axis by a specific degree.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
vertices (torch.Tensor): Shape (N, 3) representing 3D coordinates.
|
|
10
|
+
rotation_axis (str): 'x', 'y', or 'z'.
|
|
11
|
+
rotation_degree (float): Degree to rotate by (in degrees).
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
torch.Tensor: The rotated vertices of the same type.
|
|
15
|
+
"""
|
|
16
|
+
theta = math.radians(rotation_degree)
|
|
17
|
+
cos_t = math.cos(theta)
|
|
18
|
+
sin_t = math.sin(theta)
|
|
19
|
+
|
|
20
|
+
device = vertices.device
|
|
21
|
+
dtype = vertices.dtype
|
|
22
|
+
|
|
23
|
+
# Define rotation matrices based on the axis directly as PyTorch tensors
|
|
24
|
+
if rotation_axis == 'x':
|
|
25
|
+
rot_mat = torch.tensor([
|
|
26
|
+
[1.0, 0.0, 0.0],
|
|
27
|
+
[0.0, cos_t, -sin_t],
|
|
28
|
+
[0.0, sin_t, cos_t]
|
|
29
|
+
], dtype=dtype, device=device)
|
|
30
|
+
elif rotation_axis == 'y':
|
|
31
|
+
rot_mat = torch.tensor([
|
|
32
|
+
[cos_t, 0.0, sin_t],
|
|
33
|
+
[0.0, 1.0, 0.0],
|
|
34
|
+
[-sin_t, 0.0, cos_t]
|
|
35
|
+
], dtype=dtype, device=device)
|
|
36
|
+
elif rotation_axis == 'z':
|
|
37
|
+
rot_mat = torch.tensor([
|
|
38
|
+
[cos_t, -sin_t, 0.0],
|
|
39
|
+
[sin_t, cos_t, 0.0],
|
|
40
|
+
[0.0, 0.0, 1.0]
|
|
41
|
+
], dtype=dtype, device=device)
|
|
42
|
+
else:
|
|
43
|
+
raise ValueError("rotation_axis must be 'x', 'y', or 'z'")
|
|
44
|
+
|
|
45
|
+
# Compute dot product
|
|
46
|
+
return torch.matmul(vertices, rot_mat.T)
|
|
47
|
+
|
|
48
|
+
def scale(vertices, scale_factor):
|
|
49
|
+
"""
|
|
50
|
+
Scales vertices by a given factor.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
vertices (torch.Tensor): Shape (N, 3) representing 3D coordinates.
|
|
54
|
+
scale_factor (float, tuple, or list): The factor(s) to scale by.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
torch.Tensor: The scaled vertices.
|
|
58
|
+
"""
|
|
59
|
+
if isinstance(scale_factor, (list, tuple)):
|
|
60
|
+
scale_factor = torch.tensor(scale_factor, dtype=vertices.dtype, device=vertices.device)
|
|
61
|
+
|
|
62
|
+
return vertices * scale_factor
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import random
|
|
3
|
+
from .base import BaseTransform
|
|
4
|
+
from .ops import rotation, scale
|
|
5
|
+
|
|
6
|
+
class Rotation(BaseTransform):
|
|
7
|
+
def __init__(self, axis='z', degree_range=(-180, 180), p=1.0):
|
|
8
|
+
"""
|
|
9
|
+
Args:
|
|
10
|
+
axis (str or list): The axis or list of axes to rotate around ('x', 'y', 'z').
|
|
11
|
+
degree_range (tuple): Range of degrees to randomly sample from (min, max).
|
|
12
|
+
p (float): Probability of applying the transform.
|
|
13
|
+
"""
|
|
14
|
+
super().__init__(p=p)
|
|
15
|
+
self.axis = axis
|
|
16
|
+
self.degree_range = degree_range
|
|
17
|
+
|
|
18
|
+
def apply(self, **data):
|
|
19
|
+
if 'vertices' not in data:
|
|
20
|
+
raise KeyError("Rotation transform requires 'vertices' in data")
|
|
21
|
+
|
|
22
|
+
vertices = data['vertices']
|
|
23
|
+
|
|
24
|
+
# Support multiple axes in sequence
|
|
25
|
+
axes = self.axis if isinstance(self.axis, (list, tuple)) else [self.axis]
|
|
26
|
+
|
|
27
|
+
for ax in axes:
|
|
28
|
+
# Sample a random degree for each axis
|
|
29
|
+
degree = random.uniform(self.degree_range[0], self.degree_range[1])
|
|
30
|
+
vertices = rotation(vertices, ax, degree)
|
|
31
|
+
|
|
32
|
+
data['vertices'] = vertices
|
|
33
|
+
return data
|
|
34
|
+
|
|
35
|
+
class Scale(BaseTransform):
|
|
36
|
+
def __init__(self, scale_range=(0.8, 1.2), p=1.0):
|
|
37
|
+
"""
|
|
38
|
+
Args:
|
|
39
|
+
scale_range (tuple): Range of scale factors to randomly sample from (min, max).
|
|
40
|
+
p (float): Probability of applying the transform.
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(p=p)
|
|
43
|
+
self.scale_range = scale_range
|
|
44
|
+
|
|
45
|
+
def apply(self, **data):
|
|
46
|
+
if 'vertices' not in data:
|
|
47
|
+
raise KeyError("Scale transform requires 'vertices' in data")
|
|
48
|
+
|
|
49
|
+
vertices = data['vertices']
|
|
50
|
+
|
|
51
|
+
# Sample a uniform random scale factor
|
|
52
|
+
scale_factor = random.uniform(self.scale_range[0], self.scale_range[1])
|
|
53
|
+
|
|
54
|
+
data['vertices'] = scale(vertices, scale_factor)
|
|
55
|
+
return data
|
|
@@ -10,9 +10,6 @@ from .grid import (
|
|
|
10
10
|
create_voxel_grid,
|
|
11
11
|
compute_grid_normal,
|
|
12
12
|
compute_active_voxels,
|
|
13
|
-
voxel2sparse,
|
|
14
|
-
sparse2voxel,
|
|
15
|
-
sparse2mesh_topology
|
|
16
13
|
)
|
|
17
14
|
|
|
18
15
|
spatial_data_structures = ['KDTree', 'BVH', 'GSBVH', 'PGSBVH', 'MeshBVH']
|
|
@@ -21,9 +18,6 @@ grid_data_structures = [
|
|
|
21
18
|
'create_voxel_grid',
|
|
22
19
|
'compute_grid_normal',
|
|
23
20
|
'compute_active_voxels',
|
|
24
|
-
'voxel2sparse',
|
|
25
|
-
'sparse2voxel',
|
|
26
|
-
'sparse2mesh_topology'
|
|
27
21
|
]
|
|
28
22
|
|
|
29
23
|
__all__ = spatial_data_structures + mesh_data_structures + grid_data_structures
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
class BTriangleMesh:
|
|
4
|
+
def __init__(self, vertices: torch.Tensor, faces: torch.Tensor, vertbids: torch.Tensor, facebids: torch.Tensor, batch_size: int):
|
|
5
|
+
self.vertices = vertices
|
|
6
|
+
self.faces = faces
|
|
7
|
+
self.vertbids = vertbids
|
|
8
|
+
self.facebids = facebids
|
|
9
|
+
self.batch_size = batch_size
|
|
10
|
+
|
|
11
|
+
def to(self, device):
|
|
12
|
+
self.vertices = self.vertices.to(device)
|
|
13
|
+
self.faces = self.faces.to(device)
|
|
14
|
+
self.vertbids = self.vertbids.to(device)
|
|
15
|
+
self.facebids = self.facebids.to(device)
|
|
16
|
+
return self
|
|
17
|
+
|
|
18
|
+
def cuda(self, non_blocking=False):
|
|
19
|
+
self.vertices = self.vertices.cuda(non_blocking=non_blocking)
|
|
20
|
+
self.faces = self.faces.cuda(non_blocking=non_blocking)
|
|
21
|
+
self.vertbids = self.vertbids.cuda(non_blocking=non_blocking)
|
|
22
|
+
self.facebids = self.facebids.cuda(non_blocking=non_blocking)
|
|
23
|
+
return self
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
from typing import Tuple, List, Union
|
|
3
|
+
from .. import _C
|
|
4
|
+
|
|
5
|
+
def create_voxel_grid(
|
|
6
|
+
grid_min: Union[List[float], Tuple[float, float, float]],
|
|
7
|
+
grid_max: Union[List[float], Tuple[float, float, float]],
|
|
8
|
+
res: Union[List[int], Tuple[int, int, int]],
|
|
9
|
+
device: str = "cuda"
|
|
10
|
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
11
|
+
"""
|
|
12
|
+
Creates a structured 3D voxel grid efficiently.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
grid_min (List[float] | Tuple[float, float, float]): The minimum (x, y, z) bounding box coordinates.
|
|
16
|
+
grid_max (List[float] | Tuple[float, float, float]): The maximum (x, y, z) bounding box coordinates.
|
|
17
|
+
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
18
|
+
device (str, optional): Target device for the tensors (e.g., "cuda" or "cpu"). Defaults to "cuda".
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
22
|
+
- grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing all grid coordinates.
|
|
23
|
+
- voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices
|
|
24
|
+
for each voxel, mapped identically to the Marching Cubes convention.
|
|
25
|
+
- idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
|
|
26
|
+
"""
|
|
27
|
+
return _C.create_voxel_grid(list(grid_min), list(grid_max), list(res), device)
|
|
28
|
+
|
|
29
|
+
def compute_grid_normal(
|
|
30
|
+
sdf: torch.Tensor,
|
|
31
|
+
grid_vertices: torch.Tensor,
|
|
32
|
+
idx_grids: torch.Tensor,
|
|
33
|
+
res: Union[List[int], Tuple[int, int, int]]
|
|
34
|
+
) -> torch.Tensor:
|
|
35
|
+
"""
|
|
36
|
+
Computes smooth normals (gradients) for a voxel grid using central differences.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
|
|
40
|
+
grid_vertices (torch.Tensor): A float32 tensor of shape (N, 3) containing the grid coordinates.
|
|
41
|
+
idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) indices, returned by create_voxel_grid.
|
|
42
|
+
res (List[int] | Tuple[int, int, int]): The number of vertices along each axis (rx, ry, rz).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
torch.Tensor: A float32 tensor of shape (N, 3) containing the normalized gradient vectors.
|
|
46
|
+
"""
|
|
47
|
+
return _C.compute_grid_normal(sdf.contiguous(), grid_vertices.contiguous(), idx_grids.contiguous(), list(res))
|
|
48
|
+
|
|
49
|
+
def compute_active_voxels(
|
|
50
|
+
voxels: torch.Tensor,
|
|
51
|
+
sdf: torch.Tensor,
|
|
52
|
+
iso: float = 0.0
|
|
53
|
+
) -> torch.Tensor:
|
|
54
|
+
"""
|
|
55
|
+
Computes the indices of active voxels that intersect the isosurface.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices for each voxel.
|
|
59
|
+
sdf (torch.Tensor): A float32 tensor of shape (N,) containing the SDF values.
|
|
60
|
+
iso (float): The isosurface value. Defaults to 0.0.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
torch.Tensor: An int64 tensor containing the indices of the active voxels.
|
|
64
|
+
"""
|
|
65
|
+
return _C.compute_active_voxels(voxels.contiguous(), sdf.contiguous(), iso)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conquer3d
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Geometric Cuda Tool Box
|
|
5
5
|
Author-email: Do Hoang Khoi <khoido8899@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -63,6 +63,8 @@ pip install pybind11-stubgen
|
|
|
63
63
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
64
64
|
|
|
65
65
|
pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
|
|
66
|
+
|
|
67
|
+
pip install rectified-flow-pytorch
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
# Development
|
|
@@ -12,6 +12,9 @@ conquer3d.egg-info/dependency_links.txt
|
|
|
12
12
|
conquer3d.egg-info/not-zip-safe
|
|
13
13
|
conquer3d.egg-info/requires.txt
|
|
14
14
|
conquer3d.egg-info/top_level.txt
|
|
15
|
+
conquer3d/conversion/__init__.py
|
|
16
|
+
conquer3d/conversion/grid.py
|
|
17
|
+
conquer3d/conversion/mesh.py
|
|
15
18
|
conquer3d/creation/__init__.py
|
|
16
19
|
conquer3d/creation/triangle_creation.py
|
|
17
20
|
conquer3d/csrc/check.h
|
|
@@ -68,12 +71,17 @@ conquer3d/csrc/primitive/ray.h
|
|
|
68
71
|
conquer3d/csrc/primitive/triangles.h
|
|
69
72
|
conquer3d/data/__init__.py
|
|
70
73
|
conquer3d/data/collate/__init__.py
|
|
74
|
+
conquer3d/data/collate/mesh.py
|
|
71
75
|
conquer3d/data/collate/sparse_tensor.py
|
|
72
76
|
conquer3d/data/dataset/__init__.py
|
|
73
77
|
conquer3d/data/dataset/base_mesh.py
|
|
74
78
|
conquer3d/data/dataset/digit3d.py
|
|
75
79
|
conquer3d/data/transform/__init__.py
|
|
80
|
+
conquer3d/data/transform/base.py
|
|
81
|
+
conquer3d/data/transform/ops.py
|
|
82
|
+
conquer3d/data/transform/vertex.py
|
|
76
83
|
conquer3d/data_structure/__init__.py
|
|
84
|
+
conquer3d/data_structure/bmesh.py
|
|
77
85
|
conquer3d/data_structure/grid.py
|
|
78
86
|
conquer3d/ops/__init__.py
|
|
79
87
|
conquer3d/ops/diff_marching_cubes.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|