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.
Files changed (87) hide show
  1. {conquer3d-0.3.0/conquer3d.egg-info → conquer3d-0.3.1}/PKG-INFO +3 -2
  2. {conquer3d-0.3.0 → conquer3d-0.3.1}/README.md +1 -1
  3. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/_C.so +0 -0
  4. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/grid.cpp +1 -2
  5. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/aabb.h +15 -0
  6. conquer3d-0.3.1/conquer3d/data/collate/__init__.py +0 -0
  7. conquer3d-0.3.1/conquer3d/data/collate/sparse_tensor.py +43 -0
  8. conquer3d-0.3.1/conquer3d/data/dataset/__init__.py +0 -0
  9. conquer3d-0.3.1/conquer3d/data/dataset/base_mesh.py +17 -0
  10. conquer3d-0.3.1/conquer3d/data/dataset/digit3d.py +136 -0
  11. conquer3d-0.3.1/conquer3d/data/transform/__init__.py +0 -0
  12. conquer3d-0.3.1/conquer3d/data_structure/__init__.py +29 -0
  13. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/data_structure/grid.py +83 -2
  14. conquer3d-0.3.1/conquer3d/primitive/__init__.py +0 -0
  15. {conquer3d-0.3.0 → conquer3d-0.3.1/conquer3d.egg-info}/PKG-INFO +3 -2
  16. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/SOURCES.txt +7 -0
  17. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/requires.txt +1 -0
  18. {conquer3d-0.3.0 → conquer3d-0.3.1}/pyproject.toml +2 -1
  19. conquer3d-0.3.0/conquer3d/data_structure/__init__.py +0 -15
  20. {conquer3d-0.3.0 → conquer3d-0.3.1}/LICENSE +0 -0
  21. {conquer3d-0.3.0 → conquer3d-0.3.1}/MANIFEST.in +0 -0
  22. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/_C.pyi +0 -0
  23. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/__init__.py +0 -0
  24. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/creation/__init__.py +0 -0
  25. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/creation/triangle_creation.py +0 -0
  26. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/creation/triangle_creation.cpp +0 -0
  27. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/bvh.cpp +0 -0
  28. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/gs_bvh.cpp +0 -0
  29. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/kdtree.cpp +0 -0
  30. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/mesh_bvh.cpp +0 -0
  31. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/pgs_bvh.cpp +0 -0
  32. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/data_structure/triangle_mesh.cpp +0 -0
  33. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/ops/chamfer.cpp +0 -0
  34. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/ops/mc.cpp +0 -0
  35. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/primitive/gs.cpp +0 -0
  36. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/binds/primitive/pgs.cpp +0 -0
  37. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/check.h +0 -0
  38. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/constants.h +0 -0
  39. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/creation/triangle_creation.h +0 -0
  40. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/bvh.cu +0 -0
  41. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/bvh.h +0 -0
  42. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/gs_bvh.h +0 -0
  43. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/kdtree.cu +0 -0
  44. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/kdtree.h +0 -0
  45. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/mesh_bvh.cu +0 -0
  46. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/mesh_bvh.h +0 -0
  47. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/pgs_bvh.h +0 -0
  48. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/triangle_mesh.cu +0 -0
  49. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/triangle_mesh.h +0 -0
  50. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/data_structure/zcurve.h +0 -0
  51. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f2x2.h +0 -0
  52. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f3x1.h +0 -0
  53. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f3x3.h +0 -0
  54. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f3x4.h +0 -0
  55. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f4x1.h +0 -0
  56. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/f4x4.h +0 -0
  57. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/maths.h +0 -0
  58. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/maths/ops.h +0 -0
  59. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/chamfer.cu +0 -0
  60. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/chamfer.h +0 -0
  61. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/mc.cu +0 -0
  62. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/mc.h +0 -0
  63. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/ops/mc_data.h +0 -0
  64. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/edge.h +0 -0
  65. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs.cu +0 -0
  66. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs.h +0 -0
  67. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs_aabb.cu +0 -0
  68. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/gs_math.cuh +0 -0
  69. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs.cu +0 -0
  70. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs.h +0 -0
  71. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs_aabb.cu +0 -0
  72. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/pgs_math.cuh +0 -0
  73. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/ray.h +0 -0
  74. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/primitive/triangles.h +0 -0
  75. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/csrc/pybind.cpp +0 -0
  76. {conquer3d-0.3.0/conquer3d/primitive → conquer3d-0.3.1/conquer3d/data}/__init__.py +0 -0
  77. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/__init__.py +0 -0
  78. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/diff_marching_cubes.py +0 -0
  79. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/distance.py +0 -0
  80. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/ops/marching_cubes.py +0 -0
  81. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/primitive/gs.py +0 -0
  82. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d/primitive/pgs.py +0 -0
  83. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/dependency_links.txt +0 -0
  84. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/not-zip-safe +0 -0
  85. {conquer3d-0.3.0 → conquer3d-0.3.1}/conquer3d.egg-info/top_level.txt +0 -0
  86. {conquer3d-0.3.0 → conquer3d-0.3.1}/setup.cfg +0 -0
  87. {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.0
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
@@ -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.is_cuda(), "voxels must be a CUDA tensor.");
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
- torch.Tensor: An int32 tensor of shape (N, 4) in the format [batch_idx, x, y, z].
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.0
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
@@ -1,4 +1,5 @@
1
1
  torch>=2.0.0
2
+ gdown
2
3
 
3
4
  [cuda]
4
5
  cuda-toolkit==12.8.2
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
9
9
 
10
10
  [project]
11
11
  name = "conquer3d"
12
- version = "0.3.0"
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