Skip to content

Commit 7f46a75

Browse files
authored
Merge branch 'main' into fw/jt_docs
Signed-off-by: Francis Williams <fwilliams@users.noreply.github.com>
2 parents ef4ebc3 + 85d37dc commit 7f46a75

File tree

13 files changed

+1325
-1111
lines changed

13 files changed

+1325
-1111
lines changed

.github/workflows/docs.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,22 @@ jobs:
100100
micromamba activate fvdb
101101
./build.sh install verbose --cuda-arch-list '8.9+PTX'
102102
103+
- name: Clone and build fvdb-reality-capture
104+
run: |
105+
echo "Cloning fvdb-reality-capture"
106+
pwd
107+
git clone https://github.com/openvdb/fvdb-reality-capture.git
108+
pushd fvdb-reality-capture
109+
pip install -e .
110+
popd
111+
103112
- name: Build docs
104113
run: |
105114
micromamba activate fvdb
106115
which sphinx-build
107116
sphinx-build docs/ -E -a _docs_build
117+
sphinx-build fvdb-reality-capture/docs -E -a _docs_build/reality-capture
118+
108119
109120
- name: Upload static files as artifact
110121
id: deployment

Dockerfile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
FROM nvidia/cuda:12.8.1-cudnn-devel-ubuntu22.04
1+
FROM nvidia/cuda:12.9.1-cudnn-devel-ubuntu22.04
22

33
# Set environment variables to prevent interactive prompts during installation.
44
ENV DEBIAN_FRONTEND=noninteractive
55

6-
RUN sed -i 's/archive.ubuntu.com/mirrors.ocf.berkeley.edu/g' /etc/apt/sources.list
7-
86
# Install Python
97
RUN apt-get update && \
108
apt-get install -y python3-pip python3-dev python3-venv python-is-python3 wget git ninja-build vim libxcb1-dev libx11-dev libgl-dev pkgconf && \

README.md

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,53 @@ Lastly, our [documentation](docs) provides deeper details on the concepts as wel
2121

2222
## Installing *f*VDB
2323

24-
During the project's initial development stages, it is necessary to [run the build steps](#building-fvdb-from-source) to install ƒVDB. Eventually, ƒVDB will be provided as a pre-built, installable package. We support building the latest ƒVDB version for the following library configurations:
24+
The `fvdb_core` Python package can be installed either using published packages with pip or built
25+
from source.
2526

26-
| PyTorch | Python | CUDA |
27-
| -------------- | ----------- | ------------ |
28-
| 2.8.0-2.9.0 | 3.10 - 3.13 | 12.8 - 13.0 |
27+
### Platform Requirements
2928

29+
#### Software
3030

31+
fVDB is currently supported on the matrix of dependencies in the following table.
3132

32-
** Notes:**
33-
* Linux is the only platform currently supported (Ubuntu >= 22.04 recommended).
34-
* A CUDA-capable GPU with Ampere architecture or newer (i.e. compute capability >=8.0) is recommended to run the CUDA-accelerated operations in ƒVDB. A GPU with compute capabililty >=7.0 (Volta architecture) is the minimum requirement but some operations and data types are not supported.
33+
| OS | PyTorch | Python | CUDA |
34+
| ---------- | ----------- | ----------- | ----------- |
35+
| Linux Only | 2.8.0-2.9.0 | 3.10 - 3.13 | 12.8 - 13.0 |
3536

37+
#### Hardware
38+
39+
A CUDA-capable GPU with Ampere architecture or newer (i.e. compute capability >=8.0) is recommended
40+
to run the CUDA-accelerated operations in ƒVDB. A GPU with compute capabililty >=7.0 (Volta) is the
41+
minimum requirement but some operations and data types are not supported.
42+
43+
### Package Installation with pip
44+
45+
Currently, pip wheels are built with PyTorch 2.8.0 and CUDA 12.9 only. Versions for Python 3.10-3.13
46+
are provided.
47+
48+
Install fvdb_core using the following pip command.
49+
50+
```
51+
pip install fvdb_core==0.3.0+pt28.cu129 --extra-index-url="https://d36m13axqqhiit.cloudfront.net/simple" torch==2.8.0 --extra-index-url https://download.pytorch.org/whl/cu129
52+
```
3653

3754
## Building *f*VDB from Source
3855

3956
### Environment Management
40-
ƒVDB is a Python library implemented as a C++ PyTorch extension. Of course you can build ƒVDB in whatever environment suits you, but we provide three distinct paths to constructing reliable environments for building and running ƒVDB. These are separate options and are not intended to be used together.
41-
42-
1. **RECOMMENDED** [conda](#option-1-conda-environment-recommended)
43-
2. Using [Docker](#option-2-docker-container)
44-
3. [Python virtual environment](#option-3-python-virtual-environment)
4557

46-
`conda` tends to be more flexible since reconfiguring toolchains and modules to suit your larger project can be dynamic, but at the same time this can be a more brittle experience compared to using a virtualized `docker` container. Using `conda` is generally recommended for development and testing, while using `docker` is recommended for CI/CD and deployment.
58+
ƒVDB is a Python library implemented as a C++ Pytorch extension. We provide three paths to
59+
constructing reliable environments for building and running ƒVDB. These are separate options not
60+
intended to be used together (however with modification you can of course use, for example, a conda
61+
or pip environment inside a docker container).
4762

48-
---
63+
1. **RECOMMENDED** [conda](#option-1-setting-up-a-conda-environment-recommended)
64+
2. Using [docker](#option-2-setting-up-a-docker-container)
65+
3. Python virtual environment. [venv](#option-3-setting-up-a-python-virtual-environment)
4966

67+
`conda` tends to be more flexible since reconfiguring toolchains and modules to suit your larger
68+
project can be dynamic, but at the same time this can be a more brittle experience compared to using
69+
a virtualized `docker` container. Using `conda` is generally recommended for development and
70+
testing, while using `docker` is recommended for CI/CD and deployment.
5071

5172
#### **OPTION 1** Conda Environment (Recommended)
5273

@@ -148,7 +169,18 @@ or if you would like to build a packaged wheel for installing in other environme
148169

149170
### Running Tests
150171

151-
To make sure that everything works by running tests:
172+
#### C++ Tests
173+
174+
To run the gtest C++ unit tests
175+
176+
```shell
177+
./build.sh ctest
178+
```
179+
180+
#### Python Tests
181+
182+
To run the pytests
183+
152184
```shell
153185
cd tests
154186
pytest unit
@@ -158,10 +190,13 @@ pytest unit
158190

159191
To build the documentation, simply run:
160192
```shell
161-
python setup.py build_ext --inplace
162-
sphinx-build -E -a docs/ build/sphinx
193+
sphinx-build ./docs -a -E build/sphinx
163194
# View the docs
164195
open build/sphinx/index.html
196+
# View docs as served
197+
cd build/sphinx
198+
python -m http.server
199+
# Open localhost:8000 in browser
165200
```
166201

167202
### Setting up Intellisense with clangd in Visual Studio Code

fvdb/_Cpp.pyi

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,8 +1004,14 @@ class Viewer:
10041004
camera_to_world_matrices: torch.Tensor,
10051005
projection_matrices: torch.Tensor,
10061006
image_sizes: torch.Tensor,
1007-
frustum_near_plane: float = 0.1,
1008-
frustum_far_plane: float = 1000.0,
1007+
frustum_near_plane: float,
1008+
frustum_far_plane: float,
1009+
axis_length: float,
1010+
axis_thickness: float,
1011+
frustum_line_width: float,
1012+
frustum_scale: float,
1013+
frustum_color: tuple[float, float, float],
1014+
visible: bool,
10091015
) -> CameraView: ...
10101016
def has_camera_view(self, name: str) -> bool: ...
10111017
def get_camera_view(self, name: str) -> CameraView: ...

fvdb/grid.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Grid: A single sparse voxel grid with support for efficient operations
1111
1212
Class-methods for creating Grid objects from various sources:
13+
1314
- :meth:`Grid.from_dense()` for dense data
1415
- :meth:`Grid.from_dense_axis_aligned_bounds()` for dense defined by axis-aligned bounds
1516
- :meth:`Grid.from_grid_batch()` for a single grid from a grid batch
@@ -19,8 +20,8 @@
1920
- :meth:`Grid.from_points()` for point clouds
2021
- :meth:`Grid.from_zero_voxels()` for a single grid with zero voxels
2122
22-
Module-level functions for loading and saving grids:
23-
- load_grid/save_grid: Load and save grids to/from .nvdb files
23+
Class/Instance-methods for loading and saving grids:
24+
- from_nanovdb/save_nanovdb: Load and save grids to/from .nvdb files
2425
2526
Grid supports operations like convolution, pooling, interpolation, ray casting,
2627
mesh extraction, and coordinate transformations on sparse voxel data.
@@ -66,7 +67,7 @@ class Grid:
6667
which a grid can be used to index into. This also allows multiple grids to share the same data
6768
storage if desired.
6869
69-
When using a :class:`Grid`, voxel coordinates, there are three important coordinate systems to be aware of:
70+
When using a :class:`Grid`'s voxel coordinates, there are three important coordinate systems to be aware of:
7071
7172
- **World Space**: The continuous 3D coordinate system in which the grid exists.
7273
- **Voxel Space**: The discrete voxel index system, where each voxel is identified by its integer indices (i, j, k).
@@ -589,7 +590,6 @@ def clipped_grid(
589590
def coarsened_grid(self, coarsening_factor: NumericMaxRank1) -> "Grid":
590591
"""
591592
Return a :class:`Grid` representing the coarsened version of this grid.
592-
Each voxel ``[i, j, k]`` in the input is included in the output if it lies within ``ijk_min`` and ``ijk_max``.
593593
594594
Args:
595595
coarsening_factor (NumericMaxRank1): The factor by which to coarsen the grid,
@@ -737,12 +737,12 @@ def dual_grid(self, exclude_border: bool = False) -> "Grid":
737737

738738
def voxel_to_world(self, ijk: torch.Tensor) -> torch.Tensor:
739739
"""
740-
Transform a set of grid-space coordinates to their corresponding positions in world space
740+
Transform a set of voxel-space coordinates to their corresponding positions in world space
741741
using this :class:`Grid`'s origin and voxel size.
742742
743743
.. seealso::
744744
745-
:meth:`world_to_voxel` for the inverse transformation, and :meth:`voxel_to_world_matrix` and :meth:`world_to_voxel_matrix` for
745+
:meth:`world_to_voxel` for the inverse transformation, and :attr:`voxel_to_world_matrix` and :attr:`world_to_voxel_matrix` for
746746
the actual transformation matrices.
747747
748748
@@ -974,7 +974,7 @@ def inject_to(
974974
975975
If you pass in destination data, ``dst``, then ``dst`` will be modified in-place.
976976
If ``dst`` is ``None``, a new :class:`torch.Tensor` will be created with the
977-
shape ``(self.num_voxels, *src.shape[1:])`` and filled with ``default_value``
977+
shape ``(dst_grid.num_voxels, *src.shape[1:])`` and filled with ``default_value``
978978
for any voxels that do not have corresponding data in ``src``.
979979
980980
.. note::
@@ -1028,7 +1028,7 @@ def integrate_tsdf(
10281028
10291029
Args:
10301030
truncation_distance (float): Maximum distance to truncate TSDF values (in world units).
1031-
projection_matrix (torch.Tensor): Camera projection matrix. A tensor-like object with ``shape: (4, 4)``.
1031+
projection_matrix (torch.Tensor): Camera projection matrix. A tensor-like object with ``shape: (3, 3)``.
10321032
cam_to_world_matrix (torch.Tensor): Camera to world transformation matrix. A tensor-like object with ``shape: (4, 4)``.
10331033
tsdf (torch.Tensor): Current TSDF values for each voxel. A :class:`torch.Tensor` with shape: ``(self.num_voxels, 1)``.
10341034
weights (torch.Tensor): Current integration weights for each voxel.
@@ -1120,7 +1120,7 @@ def integrate_tsdf_with_features(
11201120
11211121
Args:
11221122
truncation_distance (float): Maximum distance to truncate TSDF values (in world units).
1123-
projection_matrix (torch.Tensor): Camera projection matrix. A tensor-like object with ``shape: (4, 4)``.
1123+
projection_matrix (torch.Tensor): Camera projection matrix. A tensor-like object with ``shape: (3, 3)``.
11241124
cam_to_world_matrix (torch.Tensor): Camera to world transformation matrix. A tensor-like object with ``shape: (4, 4)``.
11251125
features (torch.Tensor): Current feature values associated with each voxel in this :class:`Grid`.
11261126
A :class:`torch.Tensor` with shape ``(total_voxels, feature_dim)``.
@@ -1325,7 +1325,7 @@ def merged_grid(self, other: "Grid") -> "Grid":
13251325

13261326
def neighbor_indexes(self, ijk: torch.Tensor, extent: int, bitshift: int = 0) -> torch.Tensor:
13271327
"""
1328-
Get indices of neighboring voxels in this :class:`Grid` in an N-ring neighborhood of each
1328+
Get indexes of neighboring voxels in this :class:`Grid` in an N-ring neighborhood of each
13291329
voxel coordinate in ``ijk``.
13301330
13311331
Args:
@@ -1337,9 +1337,9 @@ def neighbor_indexes(self, ijk: torch.Tensor, extent: int, bitshift: int = 0) ->
13371337
Default is 0.
13381338
13391339
Returns:
1340-
neighbor_indices (torch.Tensor): A :class:`torch.Tensor` of shape ``(num_queries, N)``
1341-
containing the linear indices of neighboring voxels for each voxel coordinate in ``ijk``
1342-
in the input. If some neighbors are not active in the grid, their indices will be ``-1``.
1340+
neighbor_indexes (torch.Tensor): A :class:`torch.Tensor` of shape ``(num_queries, N)``
1341+
containing the linear indexes of neighboring voxels for each voxel coordinate in ``ijk``
1342+
in the input. If some neighbors are not active in the grid, their indexes will be ``-1``.
13431343
"""
13441344
jagged_ijk = JaggedTensor(ijk)
13451345
return self._impl.neighbor_indexes(jagged_ijk._impl, extent, bitshift).jdata
@@ -1459,7 +1459,7 @@ def inject_from_dense_cminor(self, dense_data: torch.Tensor, dense_origin: Numer
14591459
14601460
Args:
14611461
dense_data (torch.Tensor): Dense :class:`torch.Tensor` to read from. Shape: ``(dense_size_x, dense_size_y, dense_size_z, channels*)``.
1462-
dense_origins (NumericMaxRank1, optional): Origin of the dense tensor in
1462+
dense_origin (NumericMaxRank1, optional): Origin of the dense tensor in
14631463
voxel space, broadcastable to shape ``(3,)``, integer dtype
14641464
14651465
Returns:
@@ -1490,7 +1490,7 @@ def inject_from_dense_cmajor(self, dense_data: torch.Tensor, dense_origin: Numer
14901490
14911491
Args:
14921492
dense_data (torch.Tensor): Dense :class:`torch.Tensor` to read from. Shape: ``(channels*, dense_size_x, dense_size_y, dense_size_z)``.
1493-
dense_origins (NumericMaxRank1, optional): Origin of the dense tensor in
1493+
dense_origin (NumericMaxRank1, optional): Origin of the dense tensor in
14941494
voxel space, broadcastable to shape ``(3,)``, integer dtype
14951495
14961496
Returns:
@@ -1656,7 +1656,7 @@ def sample_trilinear_with_grad(
16561656
Returns:
16571657
interpolated_data (torch.Tensor): Interpolated data at each point. Shape: ``(num_queries, channels*)``.
16581658
interpolation_gradients (torch.Tensor): Gradients of the interpolated data with respect to world coordinates.
1659-
This is the spatial gradient of the Bézier interpolation at each point.
1659+
This is the spatial gradient of the trilinear interpolation at each point.
16601660
Shape: ``(num_queries, 3, channels*)``.
16611661
"""
16621662
jagged_points = JaggedTensor(points)
@@ -2052,7 +2052,7 @@ def world_to_voxel(self, points: torch.Tensor) -> torch.Tensor:
20522052
20532053
.. seealso::
20542054
2055-
:meth:`voxel_to_world` for the inverse transformation, and :meth:`voxel_to_world_matrix` and :meth:`world_to_voxel_matrix` for
2055+
:meth:`voxel_to_world` for the inverse transformation, and :attr:`voxel_to_world_matrix` and :attr:`world_to_voxel_matrix` for
20562056
the actual transformation matrices.
20572057
20582058
Args:
@@ -2072,7 +2072,7 @@ def inject_to_dense_cminor(
20722072
grid_size: NumericMaxRank1 | None = None,
20732073
) -> torch.Tensor:
20742074
"""
2075-
Inject values from :class:`torch.Tensor` associated with this :class:`Grid` into a
2075+
Write values from a :class:`torch.Tensor` associated with this :class:`Grid` into a
20762076
dense :class:`torch.Tensor`.
20772077
20782078
This is the "C Minor" (channels minor) version, which assumes the ``dense_data`` is in XYZC order. *i.e* the
@@ -2092,8 +2092,8 @@ def inject_to_dense_cminor(
20922092
20932093
.. seealso::
20942094
2095-
:meth:`inject_from_dense_cmajor` for reading from a dense tensor in "C Major" order,
2096-
which assumes the dense tensor has shape ``[channels*, dense_size_x, dense_size_y, dense_size_z]``.
2095+
:meth:`inject_from_dense_cminor` for reading from a dense tensor in "C Minor" order,
2096+
which assumes the dense tensor has shape ``[dense_size_x, dense_size_y, dense_size_z, channels*]``.
20972097
20982098
.. seealso::
20992099
@@ -2131,7 +2131,7 @@ def inject_to_dense_cmajor(
21312131
grid_size: NumericMaxRank1 | None = None,
21322132
) -> torch.Tensor:
21332133
"""
2134-
Write values from :class:`torch.Tensor` associated with this :class:`Grid` into a
2134+
Write values from a :class:`torch.Tensor` associated with this :class:`Grid` into a
21352135
dense :class:`torch.Tensor`.
21362136
21372137
This is the "C Major" (channels major) version, which assumes the ``dense_data`` is in CXYZ order. *i.e* the
@@ -2141,7 +2141,7 @@ def inject_to_dense_cmajor(
21412141
within the range defined by ``min_coord`` and ``grid_size``.
21422142
Voxels not present in the sparse grid are filled with zeros. *.i.e.* this method will copy
21432143
all the voxel values in the range ``[min_coord, min_coord + grid_size)`` into a dense tensor
2144-
of shape ``[dense_size_x, dense_size_y, dense_size_z, channels*]``, such that ``min_coord``
2144+
of shape ``[channels*, dense_size_x, dense_size_y, dense_size_z]``, such that ``min_coord``
21452145
maps to index ``(0, 0, 0)`` in the dense tensor, and ``min_coord + grid_size - 1`` maps to index
21462146
``(dense_size_x - 1, dense_size_y - 1, dense_size_z - 1)`` in the dense tensor.
21472147
@@ -2207,7 +2207,7 @@ def bbox(self) -> torch.Tensor:
22072207
22082208
.. note::
22092209
2210-
The bounding box is inclusive of the minimum voxel and the the maximum voxel.
2210+
The bounding box is inclusive of the minimum voxel and the maximum voxel.
22112211
22122212
*e.g.* if you have a grid with a single voxel at index ``(0, 0, 0)``, the bounding box will be
22132213
``[[0, 0, 0], [0, 0, 0]]``.
@@ -2247,7 +2247,7 @@ def dual_bbox(self) -> torch.Tensor:
22472247
22482248
.. note::
22492249
2250-
The bounding box is inclusive of the minimum voxel and the the maximum voxel.
2250+
The bounding box is inclusive of the minimum voxel and the maximum voxel.
22512251
22522252
*e.g.* if you have a grid with a single voxel at index ``(0, 0, 0)``, the dual grid will contain voxels
22532253
at indices ``(0, 0, 0), (0, 0, 1), (0, 1, 0), ..., (1, 1, 1)``, and the bounding box will be

0 commit comments

Comments
 (0)