conquer3d 0.3.0__tar.gz → 0.3.1__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.0/conquer3d.egg-info → conquer3d-0.3.1}/PKG-INFO +3 -2
- {conquer3d-0.3.0 → conquer3d-0.3.1}/README.md +1 -1
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/_C.so +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/grid.cpp +1 -2
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/aabb.h +15 -0
- conquer3d-0.3.1/conquer3d/data/collate/__init__.py +0 -0
- conquer3d-0.3.1/conquer3d/data/collate/sparse_tensor.py +43 -0
- conquer3d-0.3.1/conquer3d/data/dataset/__init__.py +0 -0
- conquer3d-0.3.1/conquer3d/data/dataset/base_mesh.py +17 -0
- conquer3d-0.3.1/conquer3d/data/dataset/digit3d.py +136 -0
- conquer3d-0.3.1/conquer3d/data/transform/__init__.py +0 -0
- conquer3d-0.3.1/conquer3d/data_structure/__init__.py +29 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/data_structure/grid.py +83 -2
- conquer3d-0.3.1/conquer3d/primitive/__init__.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1/conquer3d.egg-info}/PKG-INFO +3 -2
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/SOURCES.txt +7 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/requires.txt +1 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/pyproject.toml +2 -1
- conquer3d-0.3.0/conquer3d/data_structure/__init__.py +0 -15
- {conquer3d-0.3.0 → conquer3d-0.3.1}/LICENSE +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/MANIFEST.in +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/_C.pyi +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/__init__.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/creation/__init__.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/creation/triangle_creation.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/check.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/constants.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/creation/triangle_creation.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/bvh.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/bvh.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/kdtree.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/zcurve.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f2x2.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f3x1.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f3x3.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f3x4.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f4x1.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f4x4.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/maths.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/ops.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/chamfer.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/chamfer.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/mc.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/mc.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/mc_data.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/edge.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/ray.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/triangles.h +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/pybind.cpp +0 -0
- {conquer3d-0.3.0/conquer3d/primitive → conquer3d-0.3.1/conquer3d/data}/__init__.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/__init__.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/diff_marching_cubes.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/distance.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/marching_cubes.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/primitive/gs.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/primitive/pgs.py +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/dependency_links.txt +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/not-zip-safe +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/top_level.txt +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/setup.cfg +0 -0
- {conquer3d-0.3.0 → conquer3d-0.3.1}/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.1
|
|
4
4
|
Summary: Geometric Cuda Tool Box
|
|
5
5
|
Author-email: Do Hoang Khoi <khoido8899@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -13,6 +13,7 @@ Requires-Python: >=3.9
|
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Requires-Dist: torch>=2.0.0
|
|
16
|
+
Requires-Dist: gdown
|
|
16
17
|
Provides-Extra: cuda
|
|
17
18
|
Requires-Dist: cuda-toolkit==12.8.2; extra == "cuda"
|
|
18
19
|
Provides-Extra: dev
|
|
@@ -61,7 +62,7 @@ pip install kaolin==0.18.0 -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/t
|
|
|
61
62
|
pip install pybind11-stubgen
|
|
62
63
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
63
64
|
|
|
64
|
-
pip install plotly open3d jupyter trimesh point-cloud-utils
|
|
65
|
+
pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
# Development
|
|
@@ -33,7 +33,7 @@ pip install kaolin==0.18.0 -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/t
|
|
|
33
33
|
pip install pybind11-stubgen
|
|
34
34
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
35
35
|
|
|
36
|
-
pip install plotly open3d jupyter trimesh point-cloud-utils
|
|
36
|
+
pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
# Development
|
|
Binary file
|
|
@@ -122,8 +122,7 @@ torch::Tensor compute_grid_normal(torch::Tensor sdf, torch::Tensor grid_vertices
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
torch::Tensor compute_active_voxels(torch::Tensor voxels, torch::Tensor sdf, float iso) {
|
|
125
|
-
TORCH_CHECK(voxels.
|
|
126
|
-
TORCH_CHECK(sdf.is_cuda(), "sdf must be a CUDA tensor.");
|
|
125
|
+
TORCH_CHECK(voxels.device() == sdf.device(), "voxels and sdf must be on the same device.");
|
|
127
126
|
TORCH_CHECK(voxels.dtype() == torch::kInt32, "voxels must be int32.");
|
|
128
127
|
TORCH_CHECK(sdf.dtype() == torch::kFloat32, "sdf must be float32.");
|
|
129
128
|
|
|
@@ -167,6 +167,21 @@ namespace aabb
|
|
|
167
167
|
(target_aabb_max.y) >= query_aabb_max.y &&
|
|
168
168
|
(target_aabb_max.z) >= query_aabb_max.z);
|
|
169
169
|
}
|
|
170
|
+
|
|
171
|
+
__device__ __forceinline__ bool test_point_inside_aabb(
|
|
172
|
+
const float3 &point,
|
|
173
|
+
const float3 &aabb_min,
|
|
174
|
+
const float3 &aabb_max
|
|
175
|
+
)
|
|
176
|
+
{
|
|
177
|
+
return (
|
|
178
|
+
aabb_min.x <= point.x &&
|
|
179
|
+
aabb_min.y <= point.y &&
|
|
180
|
+
aabb_min.z <= point.z &&
|
|
181
|
+
(aabb_max.x) >= point.x &&
|
|
182
|
+
(aabb_max.y) >= point.y &&
|
|
183
|
+
(aabb_max.z) >= point.z);
|
|
184
|
+
}
|
|
170
185
|
}
|
|
171
186
|
|
|
172
187
|
#endif // AABB_H
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
def sparse_collate_fn(batch):
|
|
4
|
+
"""
|
|
5
|
+
Collate function for sparse tensor dataloaders (e.g., MinkowskiEngine, spconv, torchsparse).
|
|
6
|
+
Takes a batch of samples where each sample is (idx_grids, features, ...).
|
|
7
|
+
Appends the batch index to the idx_grids to create (batch_idx, x, y, z).
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
batch (list): A list of tuples (idx_grids, features, label)
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
tuple: (batched_coords, batched_features, batched_labels)
|
|
14
|
+
batched_coords (torch.Tensor): Shape (Total_N, 4)
|
|
15
|
+
batched_features (torch.Tensor): Shape (Total_N, ...)
|
|
16
|
+
batched_labels (torch.Tensor): Shape (Batch_Size,)
|
|
17
|
+
"""
|
|
18
|
+
batched_coords = []
|
|
19
|
+
batched_features = []
|
|
20
|
+
batched_labels = []
|
|
21
|
+
|
|
22
|
+
for batch_idx, item in enumerate(batch):
|
|
23
|
+
idx_grids, features, label = item
|
|
24
|
+
|
|
25
|
+
# Create a batch index column of shape [N, 1]
|
|
26
|
+
b_col = torch.full((idx_grids.shape[0], 1), batch_idx, dtype=idx_grids.dtype, device=idx_grids.device)
|
|
27
|
+
|
|
28
|
+
# Concatenate to get [N, 4] formatted as (batch_idx, x, y, z)
|
|
29
|
+
coords_with_batch = torch.cat([b_col, idx_grids], dim=1)
|
|
30
|
+
|
|
31
|
+
batched_coords.append(coords_with_batch)
|
|
32
|
+
batched_features.append(features)
|
|
33
|
+
batched_labels.append(label)
|
|
34
|
+
|
|
35
|
+
batched_coords = torch.cat(batched_coords, dim=0)
|
|
36
|
+
batched_features = torch.cat(batched_features, dim=0)
|
|
37
|
+
|
|
38
|
+
if isinstance(batched_labels[0], torch.Tensor):
|
|
39
|
+
batched_labels = torch.stack(batched_labels, dim=0)
|
|
40
|
+
else:
|
|
41
|
+
batched_labels = torch.tensor(batched_labels)
|
|
42
|
+
|
|
43
|
+
return batched_coords, batched_features, batched_labels
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from torch.utils.data import Dataset
|
|
2
|
+
from typing import Callable, Optional
|
|
3
|
+
|
|
4
|
+
class BaseMeshDataset(Dataset):
|
|
5
|
+
"""
|
|
6
|
+
Base PyTorch Dataset class for Mesh datasets.
|
|
7
|
+
"""
|
|
8
|
+
def __init__(self, root: str, transform: Optional[Callable] = None):
|
|
9
|
+
super().__init__()
|
|
10
|
+
self.root = root
|
|
11
|
+
self.transform = transform
|
|
12
|
+
|
|
13
|
+
def __len__(self) -> int:
|
|
14
|
+
raise NotImplementedError("Subclasses must implement __len__")
|
|
15
|
+
|
|
16
|
+
def __getitem__(self, idx: int):
|
|
17
|
+
raise NotImplementedError("Subclasses must implement __getitem__")
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import zipfile
|
|
3
|
+
import torch
|
|
4
|
+
import numpy as np
|
|
5
|
+
import conquer3d as c3d
|
|
6
|
+
import trimesh
|
|
7
|
+
import meshlib.mrmeshpy as mr
|
|
8
|
+
import meshlib.mrmeshnumpy as mrnp
|
|
9
|
+
|
|
10
|
+
from .base_mesh import BaseMeshDataset
|
|
11
|
+
|
|
12
|
+
class Digit3D(BaseMeshDataset):
|
|
13
|
+
"""
|
|
14
|
+
Digit3D Mesh Dataset containing 3D MNIST digits.
|
|
15
|
+
"""
|
|
16
|
+
def __init__(self, root: str = "~/.conquer3d/", train: bool = True, transform=None, download: bool = False, cached: bool = False):
|
|
17
|
+
root = os.path.expanduser(root)
|
|
18
|
+
super().__init__(root, transform)
|
|
19
|
+
self.train = train
|
|
20
|
+
self.zip_path = os.path.join(root, "digit3d.zip")
|
|
21
|
+
self.split_dir = "src/train" if train else "src/test"
|
|
22
|
+
self.cached = cached
|
|
23
|
+
self._cache = {}
|
|
24
|
+
|
|
25
|
+
if download:
|
|
26
|
+
self.download()
|
|
27
|
+
|
|
28
|
+
if not os.path.exists(self.zip_path):
|
|
29
|
+
raise RuntimeError(f"Dataset not found at {self.zip_path}. You can use download=True to download it.")
|
|
30
|
+
|
|
31
|
+
with zipfile.ZipFile(self.zip_path, 'r') as z:
|
|
32
|
+
self.all_files = [f for f in z.namelist() if f.startswith(self.split_dir) and f.endswith(".obj")]
|
|
33
|
+
|
|
34
|
+
def download(self):
|
|
35
|
+
if os.path.exists(self.zip_path):
|
|
36
|
+
return
|
|
37
|
+
os.makedirs(self.root, exist_ok=True)
|
|
38
|
+
url = "https://drive.google.com/uc?id=1Vry0-sflcSmpwZnjn8yBbF2vBfuW1T_W"
|
|
39
|
+
try:
|
|
40
|
+
import gdown
|
|
41
|
+
except ImportError:
|
|
42
|
+
raise ImportError("gdown is required to download the dataset. Please install it using 'pip install gdown'.")
|
|
43
|
+
print(f"Downloading Digit3D dataset to {self.zip_path}...")
|
|
44
|
+
gdown.download(url, self.zip_path, quiet=False)
|
|
45
|
+
|
|
46
|
+
def __len__(self) -> int:
|
|
47
|
+
return len(self.all_files)
|
|
48
|
+
|
|
49
|
+
def __getitem__(self, idx: int):
|
|
50
|
+
if self.cached and idx in self._cache:
|
|
51
|
+
vertices_t, faces_t, label = self._cache[idx]
|
|
52
|
+
if self.transform:
|
|
53
|
+
vertices_t = self.transform(vertices_t.clone())
|
|
54
|
+
return vertices_t, faces_t, label
|
|
55
|
+
|
|
56
|
+
f_path = self.all_files[idx]
|
|
57
|
+
basename = os.path.basename(f_path)
|
|
58
|
+
label = int(basename.split("_")[0])
|
|
59
|
+
|
|
60
|
+
# Read directly from zip stream to avoid file descriptor and extraction I/O overhead
|
|
61
|
+
if getattr(self, '_zip', None) is None:
|
|
62
|
+
self._zip = zipfile.ZipFile(self.zip_path, 'r')
|
|
63
|
+
|
|
64
|
+
with self._zip.open(f_path, 'r') as f:
|
|
65
|
+
content = f.read().decode('utf-8')
|
|
66
|
+
|
|
67
|
+
vertices = []
|
|
68
|
+
faces = []
|
|
69
|
+
for line in content.splitlines():
|
|
70
|
+
if line.startswith("v "):
|
|
71
|
+
parts = line.split()
|
|
72
|
+
vertices.append([float(parts[1]), float(parts[2]), float(parts[3])])
|
|
73
|
+
elif line.startswith("f "):
|
|
74
|
+
parts = line.split()
|
|
75
|
+
# Wavefront OBJ faces are 1-indexed, so we subtract 1 for 0-indexed tensors
|
|
76
|
+
faces.append([int(parts[1])-1, int(parts[2])-1, int(parts[3])-1])
|
|
77
|
+
|
|
78
|
+
vertices_t = torch.tensor(vertices, dtype=torch.float32)
|
|
79
|
+
faces_t = torch.tensor(faces, dtype=torch.int32)
|
|
80
|
+
|
|
81
|
+
if self.cached:
|
|
82
|
+
self._cache[idx] = (vertices_t, faces_t, label)
|
|
83
|
+
|
|
84
|
+
if self.transform:
|
|
85
|
+
vertices_t = self.transform(vertices_t.clone())
|
|
86
|
+
|
|
87
|
+
return vertices_t, faces_t, label
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class SparseDigit3D(Digit3D):
|
|
91
|
+
"""
|
|
92
|
+
Digit3D Dataset that constructs a sparse SDF voxel grid from the mesh on-the-fly using CPU (Open3D).
|
|
93
|
+
This allows arbitrary geometric augmentations on the mesh before voxelization without CUDA IPC issues.
|
|
94
|
+
"""
|
|
95
|
+
def __init__(self, root: str = "~/.conquer3d/", train: bool = True, transform=None, download: bool = False,
|
|
96
|
+
grid_res: int = 32, grid_bound: float = 1.2, cached: bool = False):
|
|
97
|
+
super().__init__(root, train, transform, download, cached=cached)
|
|
98
|
+
self.grid_res = grid_res
|
|
99
|
+
self.grid_bound = grid_bound
|
|
100
|
+
|
|
101
|
+
def __getitem__(self, idx: int):
|
|
102
|
+
# 1. Obtain vertices, faces, and label from Digit3D
|
|
103
|
+
vertices, faces, label = super().__getitem__(idx)
|
|
104
|
+
|
|
105
|
+
# 2. Construct voxel grid in CPU
|
|
106
|
+
grid_vertices, voxels, idx_grids = c3d.data_structure.create_voxel_grid(
|
|
107
|
+
grid_min=[-self.grid_bound] * 3,
|
|
108
|
+
grid_max=[self.grid_bound] * 3,
|
|
109
|
+
res=[self.grid_res] * 3,
|
|
110
|
+
device="cpu"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Construct the mesh using meshlib's numpy interface
|
|
114
|
+
mesh_mr = mrnp.meshFromFacesVerts(faces.numpy(), vertices.numpy())
|
|
115
|
+
|
|
116
|
+
# Construct point cloud from grid vertices for vectorized distance computation
|
|
117
|
+
pc = mrnp.pointCloudFromPoints(grid_vertices.numpy())
|
|
118
|
+
|
|
119
|
+
# Compute the signed distance for all grid vertices at once
|
|
120
|
+
dist_scalars = mr.findSignedDistances(mesh_mr, pc.points)
|
|
121
|
+
|
|
122
|
+
# Convert to tensor
|
|
123
|
+
sdf = torch.tensor(list(dist_scalars), dtype=torch.float32, device="cpu")
|
|
124
|
+
|
|
125
|
+
# 4. Compute active voxels
|
|
126
|
+
active_voxel_indices = c3d.data_structure.compute_active_voxels(voxels, sdf, iso=0.0)
|
|
127
|
+
|
|
128
|
+
# 5. Extract purely Voxel-Centric representations using voxel2sparse
|
|
129
|
+
sparse_coords, sparse_sdfs = c3d.data_structure.voxel2sparse(
|
|
130
|
+
active_voxel_indices, voxels, idx_grids, sdf=sdf, batch_idx=0
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# We only need the x, y, z for the dataset (collate_fn handles batch_idx)
|
|
134
|
+
sparse_idx_grids = sparse_coords[:, 1:]
|
|
135
|
+
|
|
136
|
+
return sparse_idx_grids, sparse_sdfs, label
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .._C import (
|
|
2
|
+
KDTree,
|
|
3
|
+
BVH,
|
|
4
|
+
GSBVH,
|
|
5
|
+
PGSBVH,
|
|
6
|
+
MeshBVH,
|
|
7
|
+
TriangleMesh
|
|
8
|
+
)
|
|
9
|
+
from .grid import (
|
|
10
|
+
create_voxel_grid,
|
|
11
|
+
compute_grid_normal,
|
|
12
|
+
compute_active_voxels,
|
|
13
|
+
voxel2sparse,
|
|
14
|
+
sparse2voxel,
|
|
15
|
+
sparse2mesh_topology
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
spatial_data_structures = ['KDTree', 'BVH', 'GSBVH', 'PGSBVH', 'MeshBVH']
|
|
19
|
+
mesh_data_structures = ['TriangleMesh']
|
|
20
|
+
grid_data_structures = [
|
|
21
|
+
'create_voxel_grid',
|
|
22
|
+
'compute_grid_normal',
|
|
23
|
+
'compute_active_voxels',
|
|
24
|
+
'voxel2sparse',
|
|
25
|
+
'sparse2voxel',
|
|
26
|
+
'sparse2mesh_topology'
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
__all__ = spatial_data_structures + mesh_data_structures + grid_data_structures
|
|
@@ -68,8 +68,9 @@ def voxel2sparse(
|
|
|
68
68
|
active_indices: torch.Tensor,
|
|
69
69
|
voxels: torch.Tensor,
|
|
70
70
|
idx_grids: torch.Tensor,
|
|
71
|
+
sdf: torch.Tensor = None,
|
|
71
72
|
batch_idx: int = 0
|
|
72
|
-
) -> torch.Tensor:
|
|
73
|
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
|
73
74
|
"""
|
|
74
75
|
Converts 1D active voxel indices into the [N, 4] coordinates tensor
|
|
75
76
|
expected by sparse neural networks (like spconv/torchsparse).
|
|
@@ -78,10 +79,16 @@ def voxel2sparse(
|
|
|
78
79
|
active_indices (torch.Tensor): 1D tensor of active voxel indices.
|
|
79
80
|
voxels (torch.Tensor): An int32 tensor of shape (V, 8) containing corner indices for each voxel.
|
|
80
81
|
idx_grids (torch.Tensor): An int64 tensor of shape (N, 3) containing the (i, j, k) 3D coordinate indices for each vertex.
|
|
82
|
+
sdf (torch.Tensor, optional): A float32 tensor of shape (N,) containing the SDF values. If provided, the function will also extract the 8 corner SDFs for each active voxel. Defaults to None.
|
|
81
83
|
batch_idx (int, optional): The batch index to prepend. Defaults to 0.
|
|
82
84
|
|
|
83
85
|
Returns:
|
|
84
|
-
|
|
86
|
+
If sdf is None:
|
|
87
|
+
torch.Tensor: An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
|
|
88
|
+
If sdf is provided:
|
|
89
|
+
Tuple[torch.Tensor, torch.Tensor]:
|
|
90
|
+
- sparse_coords (torch.Tensor): [N, 4] tensor
|
|
91
|
+
- sparse_sdfs (torch.Tensor): [N, 8] tensor containing the 8 corner SDF values for each voxel.
|
|
85
92
|
"""
|
|
86
93
|
active_corners = voxels[active_indices]
|
|
87
94
|
base_vertex_indices = active_corners[:, 0].to(torch.int64)
|
|
@@ -89,6 +96,11 @@ def voxel2sparse(
|
|
|
89
96
|
|
|
90
97
|
batch_col = torch.full((voxel_coords.shape[0], 1), batch_idx, device=voxel_coords.device, dtype=torch.int64)
|
|
91
98
|
sparse_coords = torch.cat([batch_col, voxel_coords], dim=1).to(torch.int32)
|
|
99
|
+
|
|
100
|
+
if sdf is not None:
|
|
101
|
+
sparse_sdfs = sdf[active_corners.to(torch.int64)]
|
|
102
|
+
return sparse_coords, sparse_sdfs
|
|
103
|
+
|
|
92
104
|
return sparse_coords
|
|
93
105
|
|
|
94
106
|
def sparse2voxel(
|
|
@@ -112,3 +124,72 @@ def sparse2voxel(
|
|
|
112
124
|
rx, ry, rz = res
|
|
113
125
|
active_indices = x * (ry - 1) * (rz - 1) + y * (rz - 1) + z
|
|
114
126
|
return active_indices
|
|
127
|
+
|
|
128
|
+
def sparse2mesh_topology(
|
|
129
|
+
sparse_coords: torch.Tensor,
|
|
130
|
+
sparse_sdfs: torch.Tensor,
|
|
131
|
+
grid_min: Union[List[float], Tuple[float, float, float]],
|
|
132
|
+
grid_max: Union[List[float], Tuple[float, float, float]],
|
|
133
|
+
res: Union[List[int], Tuple[int, int, int]]
|
|
134
|
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
135
|
+
"""
|
|
136
|
+
Converts pure voxel-centric sparse representations back into a localized,
|
|
137
|
+
dense-free mesh topology ready for marching cubes.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
sparse_coords (torch.Tensor): [N, 4] tensor of voxel coords [batch_idx, x, y, z].
|
|
141
|
+
sparse_sdfs (torch.Tensor): [N, 8] tensor of corner SDFs.
|
|
142
|
+
grid_min (List[float]): Global minimum (x, y, z) bounds.
|
|
143
|
+
grid_max (List[float]): Global maximum (x, y, z) bounds.
|
|
144
|
+
res (List[int]): Global resolution (rx, ry, rz).
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
|
148
|
+
- unique_vertices (torch.Tensor): [M, 3] float coordinates of all unique mesh vertices.
|
|
149
|
+
- local_voxels (torch.Tensor): [N, 8] int tensor of corner indices mapping to unique_vertices.
|
|
150
|
+
- merged_sdfs (torch.Tensor): [M,] float tensor of averaged SDF values at unique_vertices.
|
|
151
|
+
"""
|
|
152
|
+
device = sparse_coords.device
|
|
153
|
+
|
|
154
|
+
# 1. Base integer coordinates of the voxels
|
|
155
|
+
base_coords = sparse_coords[:, 1:] # [N, 3]
|
|
156
|
+
|
|
157
|
+
# 2. Local offsets for the 8 corners of a cube
|
|
158
|
+
cube_corners = torch.tensor([
|
|
159
|
+
[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0],
|
|
160
|
+
[0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]
|
|
161
|
+
], dtype=torch.int32, device=device)
|
|
162
|
+
|
|
163
|
+
# 3. Compute all corner integer coordinates [N, 8, 3]
|
|
164
|
+
all_corners = base_coords.unsqueeze(1) + cube_corners.unsqueeze(0)
|
|
165
|
+
all_corners_flat = all_corners.reshape(-1, 3) # [N*8, 3]
|
|
166
|
+
|
|
167
|
+
# 4. Find the unique vertices to form our local mesh graph
|
|
168
|
+
unique_corners_int, inverse_indices = torch.unique(all_corners_flat, dim=0, return_inverse=True)
|
|
169
|
+
local_voxels = inverse_indices.reshape(-1, 8).to(torch.int32) # [N, 8]
|
|
170
|
+
|
|
171
|
+
# 5. Average the SDFs at the overlapping corners
|
|
172
|
+
num_unique = unique_corners_int.shape[0]
|
|
173
|
+
merged_sdfs = torch.zeros(num_unique, dtype=sparse_sdfs.dtype, device=device)
|
|
174
|
+
counts = torch.zeros(num_unique, dtype=sparse_sdfs.dtype, device=device)
|
|
175
|
+
|
|
176
|
+
flat_inverse = inverse_indices # [N*8]
|
|
177
|
+
flat_sdfs = sparse_sdfs.flatten() # [N*8]
|
|
178
|
+
|
|
179
|
+
merged_sdfs.scatter_add_(0, flat_inverse, flat_sdfs)
|
|
180
|
+
counts.scatter_add_(0, flat_inverse, torch.ones_like(flat_sdfs))
|
|
181
|
+
merged_sdfs = merged_sdfs / counts
|
|
182
|
+
|
|
183
|
+
# 6. Convert integer coordinates back to global float positions
|
|
184
|
+
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)
|
|
188
|
+
|
|
189
|
+
ux = x_coords[unique_corners_int[:, 0]]
|
|
190
|
+
uy = y_coords[unique_corners_int[:, 1]]
|
|
191
|
+
uz = z_coords[unique_corners_int[:, 2]]
|
|
192
|
+
|
|
193
|
+
unique_vertices = torch.stack([ux, uy, uz], dim=1)
|
|
194
|
+
|
|
195
|
+
return unique_vertices, local_voxels, merged_sdfs
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conquer3d
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Geometric Cuda Tool Box
|
|
5
5
|
Author-email: Do Hoang Khoi <khoido8899@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -13,6 +13,7 @@ Requires-Python: >=3.9
|
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Requires-Dist: torch>=2.0.0
|
|
16
|
+
Requires-Dist: gdown
|
|
16
17
|
Provides-Extra: cuda
|
|
17
18
|
Requires-Dist: cuda-toolkit==12.8.2; extra == "cuda"
|
|
18
19
|
Provides-Extra: dev
|
|
@@ -61,7 +62,7 @@ pip install kaolin==0.18.0 -f https://nvidia-kaolin.s3.us-east-2.amazonaws.com/t
|
|
|
61
62
|
pip install pybind11-stubgen
|
|
62
63
|
pip install git+https://github.com/KhoiDOO/geocutool.git --no-build-isolation
|
|
63
64
|
|
|
64
|
-
pip install plotly open3d jupyter trimesh point-cloud-utils
|
|
65
|
+
pip install plotly open3d jupyter trimesh point-cloud-utils meshlib
|
|
65
66
|
```
|
|
66
67
|
|
|
67
68
|
# Development
|
|
@@ -66,6 +66,13 @@ conquer3d/csrc/primitive/pgs_aabb.cu
|
|
|
66
66
|
conquer3d/csrc/primitive/pgs_math.cuh
|
|
67
67
|
conquer3d/csrc/primitive/ray.h
|
|
68
68
|
conquer3d/csrc/primitive/triangles.h
|
|
69
|
+
conquer3d/data/__init__.py
|
|
70
|
+
conquer3d/data/collate/__init__.py
|
|
71
|
+
conquer3d/data/collate/sparse_tensor.py
|
|
72
|
+
conquer3d/data/dataset/__init__.py
|
|
73
|
+
conquer3d/data/dataset/base_mesh.py
|
|
74
|
+
conquer3d/data/dataset/digit3d.py
|
|
75
|
+
conquer3d/data/transform/__init__.py
|
|
69
76
|
conquer3d/data_structure/__init__.py
|
|
70
77
|
conquer3d/data_structure/grid.py
|
|
71
78
|
conquer3d/ops/__init__.py
|
|
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
|
|
|
9
9
|
|
|
10
10
|
[project]
|
|
11
11
|
name = "conquer3d"
|
|
12
|
-
version = "0.3.
|
|
12
|
+
version = "0.3.1"
|
|
13
13
|
description = "Geometric Cuda Tool Box"
|
|
14
14
|
readme = "README.md"
|
|
15
15
|
license = "MIT"
|
|
@@ -20,6 +20,7 @@ requires-python = ">=3.9"
|
|
|
20
20
|
keywords = ["geometry"]
|
|
21
21
|
dependencies = [
|
|
22
22
|
"torch>=2.0.0",
|
|
23
|
+
"gdown",
|
|
23
24
|
]
|
|
24
25
|
|
|
25
26
|
[project.optional-dependencies]
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from .._C import (
|
|
2
|
-
KDTree,
|
|
3
|
-
BVH,
|
|
4
|
-
GSBVH,
|
|
5
|
-
PGSBVH,
|
|
6
|
-
MeshBVH,
|
|
7
|
-
TriangleMesh
|
|
8
|
-
)
|
|
9
|
-
from .grid import create_voxel_grid, compute_grid_normal, compute_active_voxels, voxel2sparse, sparse2voxel
|
|
10
|
-
|
|
11
|
-
spatial_data_structures = ['KDTree', 'BVH', 'GSBVH', 'PGSBVH', 'MeshBVH']
|
|
12
|
-
mesh_data_structures = ['TriangleMesh']
|
|
13
|
-
grid_data_structures = ['create_voxel_grid', 'compute_grid_normal', 'compute_active_voxels', 'voxel2sparse', 'sparse2voxel']
|
|
14
|
-
|
|
15
|
-
__all__ = spatial_data_structures + mesh_data_structures + grid_data_structures
|
|
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
|